mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-24 12:38:11 +08:00
Merge
This commit is contained in:
@@ -4,7 +4,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
pull_request:
|
pull_request: {}
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
INDEX_HTML_HEAD_REPLACEMENT: <script defer data-domain="dev.graphite.rs" data-api="https://graphite.rs/visit/event" src="https://graphite.rs/visit/script.hash.js"></script>
|
INDEX_HTML_HEAD_REPLACEMENT: <script defer data-domain="dev.graphite.rs" data-api="https://graphite.rs/visit/event" src="https://graphite.rs/visit/script.hash.js"></script>
|
||||||
@@ -13,9 +13,10 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: self-hosted
|
runs-on: self-hosted
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: write
|
||||||
deployments: write
|
deployments: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
|
actions: write
|
||||||
env:
|
env:
|
||||||
RUSTC_WRAPPER: /usr/bin/sccache
|
RUSTC_WRAPPER: /usr/bin/sccache
|
||||||
CARGO_INCREMENTAL: 0
|
CARGO_INCREMENTAL: 0
|
||||||
@@ -47,9 +48,11 @@ jobs:
|
|||||||
rustc --version
|
rustc --version
|
||||||
|
|
||||||
- name: ✂ Replace template in <head> of index.html
|
- name: ✂ Replace template in <head> of index.html
|
||||||
|
if: github.ref != 'refs/heads/master'
|
||||||
|
env:
|
||||||
|
INDEX_HTML_HEAD_REPLACEMENT: ""
|
||||||
run: |
|
run: |
|
||||||
# Remove the INDEX_HTML_HEAD_REPLACEMENT environment variable for build links (not master deploys)
|
# Remove the INDEX_HTML_HEAD_REPLACEMENT environment variable for build links (not master deploys)
|
||||||
git rev-parse --abbrev-ref HEAD | grep master > /dev/null || export INDEX_HTML_HEAD_REPLACEMENT=""
|
|
||||||
sed -i "s|<!-- INDEX_HTML_HEAD_REPLACEMENT -->|$INDEX_HTML_HEAD_REPLACEMENT|" frontend/index.html
|
sed -i "s|<!-- INDEX_HTML_HEAD_REPLACEMENT -->|$INDEX_HTML_HEAD_REPLACEMENT|" frontend/index.html
|
||||||
|
|
||||||
- name: 🌐 Build Graphite web code
|
- name: 🌐 Build Graphite web code
|
||||||
@@ -70,6 +73,19 @@ jobs:
|
|||||||
projectName: graphite-dev
|
projectName: graphite-dev
|
||||||
directory: frontend/dist
|
directory: frontend/dist
|
||||||
|
|
||||||
|
- name: 💬 Comment build link URL to commit hash page on GitHub
|
||||||
|
if: github.ref == 'refs/heads/master'
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
gh api \
|
||||||
|
-X POST \
|
||||||
|
-H "Accept: application/vnd.github+json" \
|
||||||
|
/repos/${{ github.repository }}/commits/$(git rev-parse HEAD)/comments \
|
||||||
|
-f body="| 📦 **Build Complete for** $(git rev-parse HEAD) |
|
||||||
|
|-|
|
||||||
|
| ${{ steps.cloudflare.outputs.url }} |"
|
||||||
|
|
||||||
- name: 👕 Lint Graphite web formatting
|
- name: 👕 Lint Graphite web formatting
|
||||||
env:
|
env:
|
||||||
NODE_ENV: production
|
NODE_ENV: production
|
||||||
@@ -91,6 +107,51 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
mold -run cargo test --all-features --workspace
|
mold -run cargo test --all-features --workspace
|
||||||
|
|
||||||
|
- name: 📃 Generate code documentation info for website
|
||||||
|
if: github.ref == 'refs/heads/master'
|
||||||
|
run: |
|
||||||
|
cargo test --package graphite-editor --lib -- messages::message::test::generate_message_tree
|
||||||
|
mkdir -p artifacts-generated
|
||||||
|
mv hierarchical_message_system_tree.txt artifacts-generated/hierarchical_message_system_tree.txt
|
||||||
|
|
||||||
|
- name: 💿 Obtain cache of auto-generated code docs artifacts, to check if they've changed
|
||||||
|
if: github.ref == 'refs/heads/master'
|
||||||
|
id: cache-website-code-docs
|
||||||
|
uses: actions/cache/restore@v3
|
||||||
|
with:
|
||||||
|
path: artifacts
|
||||||
|
key: website-code-docs
|
||||||
|
|
||||||
|
- name: 🔍 Check if auto-generated code docs artifacts changed
|
||||||
|
if: github.ref == 'refs/heads/master'
|
||||||
|
id: website-code-docs-changed
|
||||||
|
run: |
|
||||||
|
if ! diff --brief --recursive artifacts-generated artifacts; then
|
||||||
|
echo "Auto-generated code docs artifacts have changed."
|
||||||
|
rm -rf artifacts
|
||||||
|
mv artifacts-generated artifacts
|
||||||
|
echo "changed=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "Auto-generated code docs artifacts have not changed."
|
||||||
|
rm -rf artifacts
|
||||||
|
rm -rf artifacts-generated
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: 💾 Save cache of auto-generated code docs artifacts
|
||||||
|
if: steps.website-code-docs-changed.outputs.changed == 'true'
|
||||||
|
uses: actions/cache/save@v3
|
||||||
|
with:
|
||||||
|
path: artifacts
|
||||||
|
key: ${{ steps.cache-website-code-docs.outputs.cache-primary-key }}
|
||||||
|
|
||||||
|
- name: ♻️ Trigger website rebuild if the auto-generated code docs artifacts have changed
|
||||||
|
if: steps.website-code-docs-changed.outputs.changed == 'true'
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
rm -rf artifacts
|
||||||
|
gh workflow run website.yml --ref master
|
||||||
|
|
||||||
# miri:
|
# miri:
|
||||||
# runs-on: self-hosted
|
# runs-on: self-hosted
|
||||||
|
|
||||||
|
|||||||
@@ -73,9 +73,10 @@ jobs:
|
|||||||
rustc --version
|
rustc --version
|
||||||
|
|
||||||
- name: ✂ Replace template in <head> of index.html
|
- name: ✂ Replace template in <head> of index.html
|
||||||
|
env:
|
||||||
|
INDEX_HTML_HEAD_REPLACEMENT: ""
|
||||||
run: |
|
run: |
|
||||||
# Remove the INDEX_HTML_HEAD_REPLACEMENT environment variable for build links (not master deploys)
|
# Remove the INDEX_HTML_HEAD_REPLACEMENT environment variable for build links (not master deploys)
|
||||||
export INDEX_HTML_HEAD_REPLACEMENT=""
|
|
||||||
sed -i "s|<!-- INDEX_HTML_HEAD_REPLACEMENT -->|$INDEX_HTML_HEAD_REPLACEMENT|" frontend/index.html
|
sed -i "s|<!-- INDEX_HTML_HEAD_REPLACEMENT -->|$INDEX_HTML_HEAD_REPLACEMENT|" frontend/index.html
|
||||||
|
|
||||||
- name: ⌨ Set build command based on comment
|
- name: ⌨ Set build command based on comment
|
||||||
|
|||||||
@@ -31,11 +31,47 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
tool: zola@0.20.0
|
tool: zola@0.20.0
|
||||||
|
|
||||||
|
- name: 🔍 Check if `website/other` directory changed
|
||||||
|
uses: dorny/paths-filter@v3
|
||||||
|
id: changes
|
||||||
|
with:
|
||||||
|
filters: |
|
||||||
|
website-other:
|
||||||
|
- "website/other/**"
|
||||||
|
|
||||||
- name: ✂ Replace template in <head> of index.html
|
- name: ✂ Replace template in <head> of index.html
|
||||||
run: |
|
run: |
|
||||||
# Remove the INDEX_HTML_HEAD_INCLUSION environment variable for build links (not master deploys)
|
# Remove the INDEX_HTML_HEAD_INCLUSION environment variable for build links (not master deploys)
|
||||||
git rev-parse --abbrev-ref HEAD | grep master > /dev/null || export INDEX_HTML_HEAD_INCLUSION=""
|
git rev-parse --abbrev-ref HEAD | grep master > /dev/null || export INDEX_HTML_HEAD_INCLUSION=""
|
||||||
|
|
||||||
|
- name: 💿 Obtain cache of auto-generated code docs artifacts
|
||||||
|
id: cache-website-code-docs
|
||||||
|
uses: actions/cache/restore@v3
|
||||||
|
with:
|
||||||
|
path: artifacts
|
||||||
|
key: website-code-docs
|
||||||
|
|
||||||
|
- name: 📁 Fallback in case auto-generated code docs artifacts weren't cached
|
||||||
|
if: steps.cache-website-code-docs.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
echo "🦀 Initial system version of Rust:"
|
||||||
|
rustc --version
|
||||||
|
rustup update stable
|
||||||
|
echo "🦀 Latest updated version of Rust:"
|
||||||
|
rustc --version
|
||||||
|
cargo test --package graphite-editor --lib -- messages::message::test::generate_message_tree
|
||||||
|
mkdir artifacts
|
||||||
|
mv hierarchical_message_system_tree.txt artifacts/hierarchical_message_system_tree.txt
|
||||||
|
|
||||||
|
- name: 🚚 Move `artifacts` contents to `website/other/editor-structure`
|
||||||
|
run: |
|
||||||
|
mv artifacts/* website/other/editor-structure
|
||||||
|
|
||||||
|
- name: 🔧 Build auto-generated code docs artifacts into HTML
|
||||||
|
run: |
|
||||||
|
cd website/other/editor-structure
|
||||||
|
node generate.js hierarchical_message_system_tree.txt replacement.html
|
||||||
|
|
||||||
- name: 🌐 Build Graphite website with Zola
|
- name: 🌐 Build Graphite website with Zola
|
||||||
env:
|
env:
|
||||||
MODE: prod
|
MODE: prod
|
||||||
@@ -44,16 +80,8 @@ jobs:
|
|||||||
npm run install-fonts
|
npm run install-fonts
|
||||||
zola --config config.toml build --minify
|
zola --config config.toml build --minify
|
||||||
|
|
||||||
- name: 🔍 Check if `website/other` directory changed
|
|
||||||
uses: dorny/paths-filter@v3
|
|
||||||
id: changes
|
|
||||||
with:
|
|
||||||
filters: |
|
|
||||||
other:
|
|
||||||
- "website/other/**"
|
|
||||||
|
|
||||||
- name: 💿 Restore cache of `website/other/dist` directory, if available and `website/other` didn't change
|
- name: 💿 Restore cache of `website/other/dist` directory, if available and `website/other` didn't change
|
||||||
if: steps.changes.outputs.other != 'true'
|
if: steps.changes.outputs.website-other != 'true'
|
||||||
id: cache-website-other-dist
|
id: cache-website-other-dist
|
||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v3
|
||||||
with:
|
with:
|
||||||
|
|||||||
Generated
+202
-169
@@ -10,9 +10,9 @@ checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ab_glyph"
|
name = "ab_glyph"
|
||||||
version = "0.2.30"
|
version = "0.2.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e0f4f6fbdc5ee39f2ede9f5f3ec79477271a6d6a2baff22310d51736bda6cea"
|
checksum = "e074464580a518d16a7126262fffaaa47af89d4099d4cb403f8ed938ba12ee7d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ab_glyph_rasterizer",
|
"ab_glyph_rasterizer",
|
||||||
"owned_ttf_parser",
|
"owned_ttf_parser",
|
||||||
@@ -546,9 +546,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytemuck_derive"
|
name = "bytemuck_derive"
|
||||||
version = "1.9.3"
|
version = "1.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1"
|
checksum = "441473f2b4b0459a68628c744bc61d23e730fb00128b841d30fa4bb3972257e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -677,9 +677,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.29"
|
version = "1.2.30"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362"
|
checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jobserver",
|
"jobserver",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -815,20 +815,13 @@ checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "codespan-reporting"
|
name = "codespan-reporting"
|
||||||
version = "0.11.1"
|
version = "0.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
|
checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81"
|
||||||
dependencies = [
|
|
||||||
"termcolor",
|
|
||||||
"unicode-width",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "color"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "git+https://github.com/linebender/color.git?rev=a4fa61aff6c3f292b729dc409e7832e5f0166e4a#a4fa61aff6c3f292b729dc409e7832e5f0166e4a"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
|
"termcolor",
|
||||||
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1565,12 +1558,6 @@ dependencies = [
|
|||||||
"rustc_version",
|
"rustc_version",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fixedbitset"
|
|
||||||
version = "0.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fixedbitset"
|
name = "fixedbitset"
|
||||||
version = "0.5.7"
|
version = "0.5.7"
|
||||||
@@ -1605,15 +1592,6 @@ version = "0.1.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "font-types"
|
|
||||||
version = "0.8.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1fa6a5e5a77b5f3f7f9e32879f484aa5b3632ddfbe568a16266c904a6f32cdaf"
|
|
||||||
dependencies = [
|
|
||||||
"bytemuck",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "font-types"
|
name = "font-types"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
@@ -1671,7 +1649,7 @@ dependencies = [
|
|||||||
"objc2-core-foundation",
|
"objc2-core-foundation",
|
||||||
"objc2-core-text",
|
"objc2-core-text",
|
||||||
"objc2-foundation 0.3.1",
|
"objc2-foundation 0.3.1",
|
||||||
"peniko 0.4.0",
|
"peniko",
|
||||||
"read-fonts 0.29.3",
|
"read-fonts 0.29.3",
|
||||||
"roxmltree",
|
"roxmltree",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
@@ -2129,9 +2107,9 @@ checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glow"
|
name = "glow"
|
||||||
version = "0.14.2"
|
version = "0.16.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d51fa363f025f5c111e03f13eda21162faeacb6911fe8caa0c0349f9cf0c4483"
|
checksum = "c5e5ea60d70410161c8bf5da3fdfeaa1c72ed2c15f8bbb9d19fe3a4fad085f08"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"slotmap",
|
"slotmap",
|
||||||
@@ -2306,7 +2284,7 @@ dependencies = [
|
|||||||
"num-traits",
|
"num-traits",
|
||||||
"parley",
|
"parley",
|
||||||
"petgraph 0.7.1",
|
"petgraph 0.7.1",
|
||||||
"rand 0.9.1",
|
"rand 0.9.2",
|
||||||
"rand_chacha 0.9.0",
|
"rand_chacha 0.9.0",
|
||||||
"rustc-hash 2.1.1",
|
"rustc-hash 2.1.1",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -2327,7 +2305,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"math-parser",
|
"math-parser",
|
||||||
"node-macro",
|
"node-macro",
|
||||||
"rand 0.9.1",
|
"rand 0.9.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2359,7 +2337,7 @@ dependencies = [
|
|||||||
"image",
|
"image",
|
||||||
"ndarray",
|
"ndarray",
|
||||||
"node-macro",
|
"node-macro",
|
||||||
"rand 0.9.1",
|
"rand 0.9.2",
|
||||||
"rand_chacha 0.9.0",
|
"rand_chacha 0.9.0",
|
||||||
"serde",
|
"serde",
|
||||||
"specta",
|
"specta",
|
||||||
@@ -2388,7 +2366,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"ndarray",
|
"ndarray",
|
||||||
"node-macro",
|
"node-macro",
|
||||||
"rand 0.9.1",
|
"rand 0.9.2",
|
||||||
"rand_chacha 0.9.0",
|
"rand_chacha 0.9.0",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"tokio",
|
"tokio",
|
||||||
@@ -2587,6 +2565,7 @@ dependencies = [
|
|||||||
"bytemuck",
|
"bytemuck",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"crunchy",
|
"crunchy",
|
||||||
|
"num-traits",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2751,9 +2730,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper-util"
|
name = "hyper-util"
|
||||||
version = "0.1.15"
|
version = "0.1.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f66d5bd4c6f02bf0542fad85d626775bab9258cf795a4256dcaf3161114d1df"
|
checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -2767,7 +2746,7 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"socket2",
|
"socket2 0.6.0",
|
||||||
"system-configuration",
|
"system-configuration",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
@@ -3102,9 +3081,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "io-uring"
|
name = "io-uring"
|
||||||
version = "0.7.8"
|
version = "0.7.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013"
|
checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.1",
|
"bitflags 2.9.1",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
@@ -3346,11 +3325,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kurbo"
|
name = "kurbo"
|
||||||
version = "0.11.2"
|
version = "0.11.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1077d333efea6170d9ccb96d3c3026f300ca0773da4938cc4c811daa6df68b0c"
|
checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
|
"euclid",
|
||||||
"serde",
|
"serde",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
]
|
]
|
||||||
@@ -3435,13 +3415,13 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libredox"
|
name = "libredox"
|
||||||
version = "0.1.4"
|
version = "0.1.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638"
|
checksum = "4488594b9328dee448adb906d8b126d9b7deb7cf5c22161ee591610bb1be83c0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.1",
|
"bitflags 2.9.1",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall 0.5.13",
|
"redox_syscall 0.5.15",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3615,9 +3595,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "metal"
|
name = "metal"
|
||||||
version = "0.29.0"
|
version = "0.31.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21"
|
checksum = "f569fb946490b5743ad69813cb19629130ce9374034abe31614a36402d18f99e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.1",
|
"bitflags 2.9.1",
|
||||||
"block",
|
"block",
|
||||||
@@ -3684,24 +3664,28 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "naga"
|
name = "naga"
|
||||||
version = "23.1.0"
|
version = "25.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "364f94bc34f61332abebe8cad6f6cd82a5b65cff22c828d05d0968911462ca4f"
|
checksum = "2b977c445f26e49757f9aca3631c3b8b836942cb278d69a92e7b80d3b24da632"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"bit-set",
|
"bit-set",
|
||||||
"bitflags 2.9.1",
|
"bitflags 2.9.1",
|
||||||
"cfg_aliases 0.1.1",
|
"cfg_aliases 0.2.1",
|
||||||
"codespan-reporting",
|
"codespan-reporting",
|
||||||
|
"half",
|
||||||
|
"hashbrown 0.15.4",
|
||||||
"hexf-parse",
|
"hexf-parse",
|
||||||
"indexmap 2.10.0",
|
"indexmap 2.10.0",
|
||||||
"log",
|
"log",
|
||||||
"petgraph 0.6.5",
|
"num-traits",
|
||||||
|
"once_cell",
|
||||||
|
"petgraph 0.8.2",
|
||||||
"rustc-hash 1.1.0",
|
"rustc-hash 1.1.0",
|
||||||
"spirv",
|
"spirv",
|
||||||
"termcolor",
|
"strum",
|
||||||
"thiserror 1.0.69",
|
"thiserror 2.0.12",
|
||||||
"unicode-xid",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3895,6 +3879,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
|
"libm",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4266,6 +4251,15 @@ dependencies = [
|
|||||||
"libredox",
|
"libredox",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ordered-float"
|
||||||
|
version = "4.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "os_pipe"
|
name = "os_pipe"
|
||||||
version = "1.2.2"
|
version = "1.2.2"
|
||||||
@@ -4328,7 +4322,7 @@ checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall 0.5.13",
|
"redox_syscall 0.5.15",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
@@ -4341,7 +4335,7 @@ checksum = "13e57638545cf2ba4c3e72cc5715e53b1880b829cc3dbefda3d1700c58efe723"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"fontique",
|
"fontique",
|
||||||
"hashbrown 0.15.4",
|
"hashbrown 0.15.4",
|
||||||
"peniko 0.4.0",
|
"peniko",
|
||||||
"skrifa 0.31.3",
|
"skrifa 0.31.3",
|
||||||
"swash",
|
"swash",
|
||||||
]
|
]
|
||||||
@@ -4372,23 +4366,13 @@ version = "0.2.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
|
checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "peniko"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "git+https://github.com/mTvare6/peniko.git?branch=luminance-clip#cd9aa45fe5a5c3070f18311cf33aad9cc83e5863"
|
|
||||||
dependencies = [
|
|
||||||
"color 0.1.0",
|
|
||||||
"kurbo",
|
|
||||||
"smallvec",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "peniko"
|
name = "peniko"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1f9529efd019889b2a205193c14ffb6e2839b54ed9d2720674f10f4b04d87ac9"
|
checksum = "1f9529efd019889b2a205193c14ffb6e2839b54ed9d2720674f10f4b04d87ac9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"color 0.3.1",
|
"color",
|
||||||
"kurbo",
|
"kurbo",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
]
|
]
|
||||||
@@ -4445,22 +4429,24 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "petgraph"
|
name = "petgraph"
|
||||||
version = "0.6.5"
|
version = "0.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
|
checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fixedbitset 0.4.2",
|
"fixedbitset",
|
||||||
"indexmap 2.10.0",
|
"indexmap 2.10.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "petgraph"
|
name = "petgraph"
|
||||||
version = "0.7.1"
|
version = "0.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772"
|
checksum = "54acf3a685220b533e437e264e4d932cfbdc4cc7ec0cd232ed73c08d03b8a7ca"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fixedbitset 0.5.7",
|
"fixedbitset",
|
||||||
|
"hashbrown 0.15.4",
|
||||||
"indexmap 2.10.0",
|
"indexmap 2.10.0",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4677,17 +4663,16 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "polling"
|
name = "polling"
|
||||||
version = "3.8.0"
|
version = "3.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50"
|
checksum = "8ee9b2fa7a4517d2c91ff5bc6c297a427a96749d15f98fcdbb22c05571a4d4b7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"concurrent-queue",
|
"concurrent-queue",
|
||||||
"hermit-abi",
|
"hermit-abi",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"rustix 1.0.8",
|
"rustix 1.0.8",
|
||||||
"tracing",
|
"windows-sys 0.60.2",
|
||||||
"windows-sys 0.59.0",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4930,7 +4915,7 @@ dependencies = [
|
|||||||
"quinn-udp",
|
"quinn-udp",
|
||||||
"rustc-hash 2.1.1",
|
"rustc-hash 2.1.1",
|
||||||
"rustls",
|
"rustls",
|
||||||
"socket2",
|
"socket2 0.5.10",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
@@ -4946,7 +4931,7 @@ dependencies = [
|
|||||||
"bytes",
|
"bytes",
|
||||||
"getrandom 0.3.3",
|
"getrandom 0.3.3",
|
||||||
"lru-slab",
|
"lru-slab",
|
||||||
"rand 0.9.1",
|
"rand 0.9.2",
|
||||||
"ring",
|
"ring",
|
||||||
"rustc-hash 2.1.1",
|
"rustc-hash 2.1.1",
|
||||||
"rustls",
|
"rustls",
|
||||||
@@ -4967,7 +4952,7 @@ dependencies = [
|
|||||||
"cfg_aliases 0.2.1",
|
"cfg_aliases 0.2.1",
|
||||||
"libc",
|
"libc",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"socket2",
|
"socket2 0.5.10",
|
||||||
"tracing",
|
"tracing",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
@@ -5014,9 +4999,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.9.1"
|
version = "0.9.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
|
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rand_chacha 0.9.0",
|
"rand_chacha 0.9.0",
|
||||||
"rand_core 0.9.3",
|
"rand_core 0.9.3",
|
||||||
@@ -5185,16 +5170,6 @@ dependencies = [
|
|||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "read-fonts"
|
|
||||||
version = "0.25.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f6f9e8a4f503e5c8750e4cd3b32a4e090035c46374b305a15c70bad833dca05f"
|
|
||||||
dependencies = [
|
|
||||||
"bytemuck",
|
|
||||||
"font-types 0.8.4",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "read-fonts"
|
name = "read-fonts"
|
||||||
version = "0.29.3"
|
version = "0.29.3"
|
||||||
@@ -5202,7 +5177,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "04ca636dac446b5664bd16c069c00a9621806895b8bb02c2dc68542b23b8f25d"
|
checksum = "04ca636dac446b5664bd16c069c00a9621806895b8bb02c2dc68542b23b8f25d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"font-types 0.9.0",
|
"font-types",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5212,7 +5187,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "192735ef611aac958468e670cb98432c925426f3cb71521fda202130f7388d91"
|
checksum = "192735ef611aac958468e670cb98432c925426f3cb71521fda202130f7388d91"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"font-types 0.9.0",
|
"font-types",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5226,9 +5201,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.13"
|
version = "0.5.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
|
checksum = "7e8af0dde094006011e6a740d4879319439489813bd0bcdc7d821beaeeff48ec"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.1",
|
"bitflags 2.9.1",
|
||||||
]
|
]
|
||||||
@@ -5724,9 +5699,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.140"
|
version = "1.0.141"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
|
checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
@@ -5943,16 +5918,6 @@ version = "1.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
|
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "skrifa"
|
|
||||||
version = "0.26.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8cc1aa86c26dbb1b63875a7180aa0819709b33348eb5b1491e4321fae388179d"
|
|
||||||
dependencies = [
|
|
||||||
"bytemuck",
|
|
||||||
"read-fonts 0.25.3",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "skrifa"
|
name = "skrifa"
|
||||||
version = "0.31.3"
|
version = "0.31.3"
|
||||||
@@ -6041,6 +6006,16 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "socket2"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "softbuffer"
|
name = "softbuffer"
|
||||||
version = "0.4.6"
|
version = "0.4.6"
|
||||||
@@ -6057,7 +6032,7 @@ dependencies = [
|
|||||||
"objc2-foundation 0.2.2",
|
"objc2-foundation 0.2.2",
|
||||||
"objc2-quartz-core 0.2.2",
|
"objc2-quartz-core 0.2.2",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"redox_syscall 0.5.13",
|
"redox_syscall 0.5.15",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
@@ -6182,6 +6157,28 @@ version = "0.11.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum"
|
||||||
|
version = "0.26.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
|
||||||
|
dependencies = [
|
||||||
|
"strum_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strum_macros"
|
||||||
|
version = "0.26.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
|
||||||
|
dependencies = [
|
||||||
|
"heck 0.5.0",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rustversion",
|
||||||
|
"syn 2.0.104",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "2.6.1"
|
version = "2.6.1"
|
||||||
@@ -6366,9 +6363,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri"
|
name = "tauri"
|
||||||
version = "2.6.2"
|
version = "2.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "124e129c9c0faa6bec792c5948c89e86c90094133b0b9044df0ce5f0a8efaa0d"
|
checksum = "352a4bc7bf6c25f5624227e3641adf475a6535707451b09bb83271df8b7a6ac7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -6416,9 +6413,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-build"
|
name = "tauri-build"
|
||||||
version = "2.3.0"
|
version = "2.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "12f025c389d3adb83114bec704da973142e82fc6ec799c7c750c5e21cefaec83"
|
checksum = "182d688496c06bf08ea896459bf483eb29cdff35c1c4c115fb14053514303064"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cargo_toml",
|
"cargo_toml",
|
||||||
@@ -6438,9 +6435,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-codegen"
|
name = "tauri-codegen"
|
||||||
version = "2.3.0"
|
version = "2.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f5df493a1075a241065bc865ed5ef8d0fbc1e76c7afdc0bf0eccfaa7d4f0e406"
|
checksum = "b54a99a6cd8e01abcfa61508177e6096a4fe2681efecee9214e962f2f073ae4a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"brotli",
|
"brotli",
|
||||||
@@ -6465,9 +6462,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-macros"
|
name = "tauri-macros"
|
||||||
version = "2.3.1"
|
version = "2.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f237fbea5866fa5f2a60a21bea807a2d6e0379db070d89c3a10ac0f2d4649bbc"
|
checksum = "7945b14dc45e23532f2ded6e120170bbdd4af5ceaa45784a6b33d250fbce3f9e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -6479,9 +6476,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin"
|
name = "tauri-plugin"
|
||||||
version = "2.3.0"
|
version = "2.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1d9a0bd00bf1930ad1a604d08b0eb6b2a9c1822686d65d7f4731a7723b8901d3"
|
checksum = "5bd5c1e56990c70a906ef67a9851bbdba9136d26075ee9a2b19c8b46986b3e02"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"glob",
|
"glob",
|
||||||
@@ -6496,9 +6493,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-fs"
|
name = "tauri-plugin-fs"
|
||||||
version = "2.4.0"
|
version = "2.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c341290d31991dbca38b31d412c73dfbdb070bb11536784f19dd2211d13b778f"
|
checksum = "8c6ef84ee2f2094ce093e55106d90d763ba343fad57566992962e8f76d113f99"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"dunce",
|
"dunce",
|
||||||
@@ -6518,9 +6515,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-http"
|
name = "tauri-plugin-http"
|
||||||
version = "2.5.0"
|
version = "2.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b0c1a38da944b357ffa23bafd563b1579f18e6fbd118fcd84769406d35dcc5c7"
|
checksum = "fcde333d97e565a7765aad82f32d8672458f7bd77b6ee653830d5dded9d7b5c2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"cookie_store",
|
"cookie_store",
|
||||||
@@ -6563,9 +6560,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-runtime"
|
name = "tauri-runtime"
|
||||||
version = "2.7.0"
|
version = "2.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9e7bb73d1bceac06c20b3f755b2c8a2cb13b20b50083084a8cf3700daf397ba4"
|
checksum = "2b1cc885be806ea15ff7b0eb47098a7b16323d9228876afda329e34e2d6c4676"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cookie",
|
"cookie",
|
||||||
"dpi",
|
"dpi",
|
||||||
@@ -6585,9 +6582,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-runtime-wry"
|
name = "tauri-runtime-wry"
|
||||||
version = "2.7.1"
|
version = "2.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "902b5aa9035e16f342eb64f8bf06ccdc2808e411a2525ed1d07672fa4e780bad"
|
checksum = "fe653a2fbbef19fe898efc774bc52c8742576342a33d3d028c189b57eb1d2439"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gtk",
|
"gtk",
|
||||||
"http",
|
"http",
|
||||||
@@ -6612,9 +6609,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-utils"
|
name = "tauri-utils"
|
||||||
version = "2.5.0"
|
version = "2.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "41743bbbeb96c3a100d234e5a0b60a46d5aa068f266160862c7afdbf828ca02e"
|
checksum = "9330c15cabfe1d9f213478c9e8ec2b0c76dab26bb6f314b8ad1c8a568c1d186e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"brotli",
|
"brotli",
|
||||||
@@ -6857,7 +6854,7 @@ dependencies = [
|
|||||||
"mio",
|
"mio",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"slab",
|
"slab",
|
||||||
"socket2",
|
"socket2 0.5.10",
|
||||||
"tokio-macros",
|
"tokio-macros",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
@@ -7226,15 +7223,9 @@ checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
version = "0.1.14"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-xid"
|
|
||||||
version = "0.2.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "untrusted"
|
name = "untrusted"
|
||||||
@@ -7342,15 +7333,15 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vello"
|
name = "vello"
|
||||||
version = "0.3.0"
|
version = "0.5.0"
|
||||||
source = "git+https://github.com/mTvare6/vello.git?branch=luminance-clip#cbe3d204cdbe3dd45716ffac6109da1390ec9536"
|
source = "git+https://github.com/linebender/vello.git#daf940230a24cbb123a458b6de95721af47aef98"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"futures-intrusive",
|
"futures-intrusive",
|
||||||
"log",
|
"log",
|
||||||
"peniko 0.2.0",
|
"peniko",
|
||||||
"png",
|
"png",
|
||||||
"skrifa 0.26.6",
|
"skrifa 0.31.3",
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
"vello_encoding",
|
"vello_encoding",
|
||||||
@@ -7360,22 +7351,23 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vello_encoding"
|
name = "vello_encoding"
|
||||||
version = "0.3.0"
|
version = "0.5.0"
|
||||||
source = "git+https://github.com/mTvare6/vello.git?branch=luminance-clip#cbe3d204cdbe3dd45716ffac6109da1390ec9536"
|
source = "git+https://github.com/linebender/vello.git#daf940230a24cbb123a458b6de95721af47aef98"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"guillotiere",
|
"guillotiere",
|
||||||
"peniko 0.2.0",
|
"peniko",
|
||||||
"skrifa 0.26.6",
|
"skrifa 0.31.3",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vello_shaders"
|
name = "vello_shaders"
|
||||||
version = "0.3.0"
|
version = "0.5.0"
|
||||||
source = "git+https://github.com/mTvare6/vello.git?branch=luminance-clip#cbe3d204cdbe3dd45716ffac6109da1390ec9536"
|
source = "git+https://github.com/linebender/vello.git#daf940230a24cbb123a458b6de95721af47aef98"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
|
"log",
|
||||||
"naga",
|
"naga",
|
||||||
"thiserror 2.0.12",
|
"thiserror 2.0.12",
|
||||||
"vello_encoding",
|
"vello_encoding",
|
||||||
@@ -7722,9 +7714,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webpki-roots"
|
name = "webpki-roots"
|
||||||
version = "1.0.1"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8782dd5a41a24eed3a4f40b606249b3e236ca61adf1f25ea4d45c73de122b502"
|
checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
]
|
]
|
||||||
@@ -7773,17 +7765,20 @@ checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wgpu"
|
name = "wgpu"
|
||||||
version = "23.0.1"
|
version = "25.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "80f70000db37c469ea9d67defdc13024ddf9a5f1b89cb2941b812ad7cde1735a"
|
checksum = "ec8fb398f119472be4d80bc3647339f56eb63b2a331f6a3d16e25d8144197dd9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
"cfg_aliases 0.1.1",
|
"bitflags 2.9.1",
|
||||||
|
"cfg_aliases 0.2.1",
|
||||||
"document-features",
|
"document-features",
|
||||||
|
"hashbrown 0.15.4",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"log",
|
"log",
|
||||||
"naga",
|
"naga",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
|
"portable-atomic",
|
||||||
"profiling",
|
"profiling",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
@@ -7798,30 +7793,63 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wgpu-core"
|
name = "wgpu-core"
|
||||||
version = "23.0.1"
|
version = "25.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d63c3c478de8e7e01786479919c8769f62a22eec16788d8c2ac77ce2c132778a"
|
checksum = "f7b882196f8368511d613c6aeec80655160db6646aebddf8328879a88d54e500"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
|
"bit-set",
|
||||||
"bit-vec",
|
"bit-vec",
|
||||||
"bitflags 2.9.1",
|
"bitflags 2.9.1",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"cfg_aliases 0.1.1",
|
"cfg_aliases 0.2.1",
|
||||||
"document-features",
|
"document-features",
|
||||||
|
"hashbrown 0.15.4",
|
||||||
"indexmap 2.10.0",
|
"indexmap 2.10.0",
|
||||||
"log",
|
"log",
|
||||||
"naga",
|
"naga",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
|
"portable-atomic",
|
||||||
"profiling",
|
"profiling",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"rustc-hash 1.1.0",
|
"rustc-hash 1.1.0",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thiserror 1.0.69",
|
"thiserror 2.0.12",
|
||||||
|
"wgpu-core-deps-apple",
|
||||||
|
"wgpu-core-deps-emscripten",
|
||||||
|
"wgpu-core-deps-windows-linux-android",
|
||||||
"wgpu-hal",
|
"wgpu-hal",
|
||||||
"wgpu-types",
|
"wgpu-types",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wgpu-core-deps-apple"
|
||||||
|
version = "25.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cfd488b3239b6b7b185c3b045c39ca6bf8af34467a4c5de4e0b1a564135d093d"
|
||||||
|
dependencies = [
|
||||||
|
"wgpu-hal",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wgpu-core-deps-emscripten"
|
||||||
|
version = "25.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f09ad7aceb3818e52539acc679f049d3475775586f3f4e311c30165cf2c00445"
|
||||||
|
dependencies = [
|
||||||
|
"wgpu-hal",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wgpu-core-deps-windows-linux-android"
|
||||||
|
version = "25.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cba5fb5f7f9c98baa7c889d444f63ace25574833df56f5b817985f641af58e46"
|
||||||
|
dependencies = [
|
||||||
|
"wgpu-hal",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wgpu-executor"
|
name = "wgpu-executor"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -7842,9 +7870,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wgpu-hal"
|
name = "wgpu-hal"
|
||||||
version = "23.0.1"
|
version = "25.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "89364b8a0b211adc7b16aeaf1bd5ad4a919c1154b44c9ce27838213ba05fd821"
|
checksum = "f968767fe4d3d33747bbd1473ccd55bf0f6451f55d733b5597e67b5deab4ad17"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"android_system_properties",
|
"android_system_properties",
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
@@ -7853,13 +7881,15 @@ dependencies = [
|
|||||||
"bitflags 2.9.1",
|
"bitflags 2.9.1",
|
||||||
"block",
|
"block",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"cfg_aliases 0.1.1",
|
"cfg-if",
|
||||||
|
"cfg_aliases 0.2.1",
|
||||||
"core-graphics-types 0.1.3",
|
"core-graphics-types 0.1.3",
|
||||||
"glow",
|
"glow",
|
||||||
"glutin_wgl_sys",
|
"glutin_wgl_sys",
|
||||||
"gpu-alloc",
|
"gpu-alloc",
|
||||||
"gpu-allocator",
|
"gpu-allocator",
|
||||||
"gpu-descriptor",
|
"gpu-descriptor",
|
||||||
|
"hashbrown 0.15.4",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"khronos-egl",
|
"khronos-egl",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -7869,15 +7899,15 @@ dependencies = [
|
|||||||
"naga",
|
"naga",
|
||||||
"ndk-sys 0.5.0+25.2.9519653",
|
"ndk-sys 0.5.0+25.2.9519653",
|
||||||
"objc",
|
"objc",
|
||||||
"once_cell",
|
"ordered-float",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
|
"portable-atomic",
|
||||||
"profiling",
|
"profiling",
|
||||||
"range-alloc",
|
"range-alloc",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"renderdoc-sys",
|
"renderdoc-sys",
|
||||||
"rustc-hash 1.1.0",
|
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thiserror 1.0.69",
|
"thiserror 2.0.12",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"wgpu-types",
|
"wgpu-types",
|
||||||
@@ -7887,12 +7917,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wgpu-types"
|
name = "wgpu-types"
|
||||||
version = "23.0.0"
|
version = "25.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "610f6ff27778148c31093f3b03abc4840f9636d58d597ca2f5977433acfe0068"
|
checksum = "2aa49460c2a8ee8edba3fca54325540d904dd85b2e086ada762767e17d06e8bc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.1",
|
"bitflags 2.9.1",
|
||||||
|
"bytemuck",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
|
"log",
|
||||||
|
"thiserror 2.0.12",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -83,7 +83,7 @@ axum = "0.8"
|
|||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
ron = "0.8"
|
ron = "0.8"
|
||||||
fastnoise-lite = "1.1"
|
fastnoise-lite = "1.1"
|
||||||
wgpu = { version = "23", features = [
|
wgpu = { version = "25.0.2", features = [
|
||||||
# We don't have wgpu on multiple threads (yet) https://github.com/gfx-rs/wgpu/blob/trunk/CHANGELOG.md#wgpu-types-now-send-sync-on-wasm
|
# We don't have wgpu on multiple threads (yet) https://github.com/gfx-rs/wgpu/blob/trunk/CHANGELOG.md#wgpu-types-now-send-sync-on-wasm
|
||||||
"fragile-send-sync-non-atomic-wasm",
|
"fragile-send-sync-non-atomic-wasm",
|
||||||
"spirv",
|
"spirv",
|
||||||
@@ -114,7 +114,7 @@ web-sys = { version = "=0.3.77", features = [
|
|||||||
winit = "0.29"
|
winit = "0.29"
|
||||||
url = "2.5"
|
url = "2.5"
|
||||||
tokio = { version = "1.29", features = ["fs", "macros", "io-std", "rt"] }
|
tokio = { version = "1.29", features = ["fs", "macros", "io-std", "rt"] }
|
||||||
vello = { git = "https://github.com/mTvare6/vello.git", branch = "luminance-clip" } # TODO switch back to stable when a release is made
|
vello = { git = "https://github.com/linebender/vello.git" } # TODO switch back to stable when a release is made
|
||||||
resvg = "0.44"
|
resvg = "0.44"
|
||||||
usvg = "0.44"
|
usvg = "0.44"
|
||||||
rand = { version = "0.9", default-features = false, features = ["std_rng"] }
|
rand = { version = "0.9", default-features = false, features = ["std_rng"] }
|
||||||
|
|||||||
Generated
+1
-1
File diff suppressed because one or more lines are too long
Generated
+1
-1
File diff suppressed because one or more lines are too long
Generated
+1
-1
File diff suppressed because one or more lines are too long
Generated
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Generated
+1
-1
File diff suppressed because one or more lines are too long
Generated
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
name = "graphite-editor"
|
name = "graphite-editor"
|
||||||
publish = false
|
publish = false
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
rust-version = "1.85"
|
rust-version = "1.88"
|
||||||
authors = ["Graphite Authors <contact@graphite.rs>"]
|
authors = ["Graphite Authors <contact@graphite.rs>"]
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
readme = "../README.md"
|
readme = "../README.md"
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ pub const MIN_LENGTH_FOR_SKEW_TRIANGLE_VISIBILITY: f64 = 48.;
|
|||||||
// PATH TOOL
|
// PATH TOOL
|
||||||
pub const MANIPULATOR_GROUP_MARKER_SIZE: f64 = 6.;
|
pub const MANIPULATOR_GROUP_MARKER_SIZE: f64 = 6.;
|
||||||
pub const SELECTION_THRESHOLD: f64 = 10.;
|
pub const SELECTION_THRESHOLD: f64 = 10.;
|
||||||
|
pub const DRILL_THROUGH_THRESHOLD: f64 = 10.;
|
||||||
pub const HIDE_HANDLE_DISTANCE: f64 = 3.;
|
pub const HIDE_HANDLE_DISTANCE: f64 = 3.;
|
||||||
pub const HANDLE_ROTATE_SNAP_ANGLE: f64 = 15.;
|
pub const HANDLE_ROTATE_SNAP_ANGLE: f64 = 15.;
|
||||||
pub const SEGMENT_INSERTION_DISTANCE: f64 = 5.;
|
pub const SEGMENT_INSERTION_DISTANCE: f64 = 5.;
|
||||||
@@ -134,13 +135,15 @@ pub const SCALE_EFFECT: f64 = 0.5;
|
|||||||
|
|
||||||
// COLORS
|
// COLORS
|
||||||
pub const COLOR_OVERLAY_BLUE: &str = "#00a8ff";
|
pub const COLOR_OVERLAY_BLUE: &str = "#00a8ff";
|
||||||
|
pub const COLOR_OVERLAY_BLUE_50: &str = "#00a8ff80";
|
||||||
pub const COLOR_OVERLAY_YELLOW: &str = "#ffc848";
|
pub const COLOR_OVERLAY_YELLOW: &str = "#ffc848";
|
||||||
pub const COLOR_OVERLAY_YELLOW_DULL: &str = "#d7ba8b";
|
pub const COLOR_OVERLAY_YELLOW_DULL: &str = "#d7ba8b";
|
||||||
pub const COLOR_OVERLAY_GREEN: &str = "#63ce63";
|
pub const COLOR_OVERLAY_GREEN: &str = "#63ce63";
|
||||||
pub const COLOR_OVERLAY_RED: &str = "#ef5454";
|
pub const COLOR_OVERLAY_RED: &str = "#ef5454";
|
||||||
pub const COLOR_OVERLAY_GRAY: &str = "#cccccc";
|
pub const COLOR_OVERLAY_GRAY: &str = "#cccccc";
|
||||||
|
pub const COLOR_OVERLAY_GRAY_25: &str = "#cccccc40";
|
||||||
pub const COLOR_OVERLAY_WHITE: &str = "#ffffff";
|
pub const COLOR_OVERLAY_WHITE: &str = "#ffffff";
|
||||||
pub const COLOR_OVERLAY_LABEL_BACKGROUND: &str = "#000000cc";
|
pub const COLOR_OVERLAY_BLACK_75: &str = "#000000bf";
|
||||||
|
|
||||||
// DOCUMENT
|
// DOCUMENT
|
||||||
pub const DEFAULT_DOCUMENT_NAME: &str = "Untitled Document";
|
pub const DEFAULT_DOCUMENT_NAME: &str = "Untitled Document";
|
||||||
|
|||||||
+106
-122
@@ -1,6 +1,6 @@
|
|||||||
use crate::messages::debug::utility_types::MessageLoggingVerbosity;
|
use crate::messages::debug::utility_types::MessageLoggingVerbosity;
|
||||||
use crate::messages::dialog::DialogMessageData;
|
use crate::messages::dialog::DialogMessageContext;
|
||||||
use crate::messages::portfolio::document::node_graph::document_node_definitions;
|
use crate::messages::layout::layout_message_handler::LayoutMessageContext;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
@@ -91,7 +91,7 @@ impl Dispatcher {
|
|||||||
pub fn handle_message<T: Into<Message>>(&mut self, message: T, process_after_all_current: bool) {
|
pub fn handle_message<T: Into<Message>>(&mut self, message: T, process_after_all_current: bool) {
|
||||||
let message = message.into();
|
let message = message.into();
|
||||||
// Add all additional messages to the buffer if it exists (except from the end buffer message)
|
// Add all additional messages to the buffer if it exists (except from the end buffer message)
|
||||||
if !matches!(message, Message::EndBuffer(_)) {
|
if !matches!(message, Message::EndBuffer { .. }) {
|
||||||
if let Some(buffered_queue) = &mut self.buffered_queue {
|
if let Some(buffered_queue) = &mut self.buffered_queue {
|
||||||
Self::schedule_execution(buffered_queue, true, [message]);
|
Self::schedule_execution(buffered_queue, true, [message]);
|
||||||
|
|
||||||
@@ -126,10 +126,112 @@ impl Dispatcher {
|
|||||||
|
|
||||||
// Process the action by forwarding it to the relevant message handler, or saving the FrontendMessage to be sent to the frontend
|
// Process the action by forwarding it to the relevant message handler, or saving the FrontendMessage to be sent to the frontend
|
||||||
match message {
|
match message {
|
||||||
|
Message::Animation(message) => {
|
||||||
|
self.message_handlers.animation_message_handler.process_message(message, &mut queue, ());
|
||||||
|
}
|
||||||
|
Message::Broadcast(message) => self.message_handlers.broadcast_message_handler.process_message(message, &mut queue, ()),
|
||||||
|
Message::Debug(message) => {
|
||||||
|
self.message_handlers.debug_message_handler.process_message(message, &mut queue, ());
|
||||||
|
}
|
||||||
|
Message::Dialog(message) => {
|
||||||
|
let context = DialogMessageContext {
|
||||||
|
portfolio: &self.message_handlers.portfolio_message_handler,
|
||||||
|
preferences: &self.message_handlers.preferences_message_handler,
|
||||||
|
};
|
||||||
|
self.message_handlers.dialog_message_handler.process_message(message, &mut queue, context);
|
||||||
|
}
|
||||||
|
Message::Frontend(message) => {
|
||||||
|
// Handle these messages immediately by returning early
|
||||||
|
if let FrontendMessage::TriggerFontLoad { .. } = message {
|
||||||
|
self.responses.push(message);
|
||||||
|
self.cleanup_queues(false);
|
||||||
|
|
||||||
|
// Return early to avoid running the code after the match block
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// `FrontendMessage`s are saved and will be sent to the frontend after the message queue is done being processed
|
||||||
|
self.responses.push(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::Globals(message) => {
|
||||||
|
self.message_handlers.globals_message_handler.process_message(message, &mut queue, ());
|
||||||
|
}
|
||||||
|
Message::InputPreprocessor(message) => {
|
||||||
|
let keyboard_platform = GLOBAL_PLATFORM.get().copied().unwrap_or_default().as_keyboard_platform_layout();
|
||||||
|
|
||||||
|
self.message_handlers
|
||||||
|
.input_preprocessor_message_handler
|
||||||
|
.process_message(message, &mut queue, InputPreprocessorMessageContext { keyboard_platform });
|
||||||
|
}
|
||||||
|
Message::KeyMapping(message) => {
|
||||||
|
let input = &self.message_handlers.input_preprocessor_message_handler;
|
||||||
|
let actions = self.collect_actions();
|
||||||
|
|
||||||
|
self.message_handlers
|
||||||
|
.key_mapping_message_handler
|
||||||
|
.process_message(message, &mut queue, KeyMappingMessageContext { input, actions });
|
||||||
|
}
|
||||||
|
Message::Layout(message) => {
|
||||||
|
let action_input_mapping = &|action_to_find: &MessageDiscriminant| self.message_handlers.key_mapping_message_handler.action_input_mapping(action_to_find);
|
||||||
|
let context = LayoutMessageContext { action_input_mapping };
|
||||||
|
|
||||||
|
self.message_handlers.layout_message_handler.process_message(message, &mut queue, context);
|
||||||
|
}
|
||||||
|
Message::Portfolio(message) => {
|
||||||
|
let ipp = &self.message_handlers.input_preprocessor_message_handler;
|
||||||
|
let preferences = &self.message_handlers.preferences_message_handler;
|
||||||
|
let current_tool = &self.message_handlers.tool_message_handler.tool_state.tool_data.active_tool_type;
|
||||||
|
let message_logging_verbosity = self.message_handlers.debug_message_handler.message_logging_verbosity;
|
||||||
|
let reset_node_definitions_on_open = self.message_handlers.portfolio_message_handler.reset_node_definitions_on_open;
|
||||||
|
let timing_information = self.message_handlers.animation_message_handler.timing_information();
|
||||||
|
let animation = &self.message_handlers.animation_message_handler;
|
||||||
|
|
||||||
|
self.message_handlers.portfolio_message_handler.process_message(
|
||||||
|
message,
|
||||||
|
&mut queue,
|
||||||
|
PortfolioMessageContext {
|
||||||
|
ipp,
|
||||||
|
preferences,
|
||||||
|
current_tool,
|
||||||
|
message_logging_verbosity,
|
||||||
|
reset_node_definitions_on_open,
|
||||||
|
timing_information,
|
||||||
|
animation,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Message::Preferences(message) => {
|
||||||
|
self.message_handlers.preferences_message_handler.process_message(message, &mut queue, ());
|
||||||
|
}
|
||||||
|
Message::Tool(message) => {
|
||||||
|
let document_id = self.message_handlers.portfolio_message_handler.active_document_id().unwrap();
|
||||||
|
let Some(document) = self.message_handlers.portfolio_message_handler.documents.get_mut(&document_id) else {
|
||||||
|
warn!("Called ToolMessage without an active document.\nGot {message:?}");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let context = ToolMessageContext {
|
||||||
|
document_id,
|
||||||
|
document,
|
||||||
|
input: &self.message_handlers.input_preprocessor_message_handler,
|
||||||
|
persistent_data: &self.message_handlers.portfolio_message_handler.persistent_data,
|
||||||
|
node_graph: &self.message_handlers.portfolio_message_handler.executor,
|
||||||
|
preferences: &self.message_handlers.preferences_message_handler,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.message_handlers.tool_message_handler.process_message(message, &mut queue, context);
|
||||||
|
}
|
||||||
|
Message::Workspace(message) => {
|
||||||
|
self.message_handlers.workspace_message_handler.process_message(message, &mut queue, ());
|
||||||
|
}
|
||||||
|
Message::NoOp => {}
|
||||||
|
Message::Batched { messages } => {
|
||||||
|
messages.iter().for_each(|message| self.handle_message(message.to_owned(), false));
|
||||||
|
}
|
||||||
Message::StartBuffer => {
|
Message::StartBuffer => {
|
||||||
self.buffered_queue = Some(std::mem::take(&mut self.message_queues));
|
self.buffered_queue = Some(std::mem::take(&mut self.message_queues));
|
||||||
}
|
}
|
||||||
Message::EndBuffer(render_metadata) => {
|
Message::EndBuffer { render_metadata } => {
|
||||||
// Assign the message queue to the currently buffered queue
|
// Assign the message queue to the currently buffered queue
|
||||||
if let Some(buffered_queue) = self.buffered_queue.take() {
|
if let Some(buffered_queue) = self.buffered_queue.take() {
|
||||||
self.cleanup_queues(false);
|
self.cleanup_queues(false);
|
||||||
@@ -157,124 +259,6 @@ impl Dispatcher {
|
|||||||
];
|
];
|
||||||
Self::schedule_execution(&mut self.message_queues, false, messages.map(Message::from));
|
Self::schedule_execution(&mut self.message_queues, false, messages.map(Message::from));
|
||||||
}
|
}
|
||||||
Message::NoOp => {}
|
|
||||||
Message::Init => {
|
|
||||||
// Load persistent data from the browser database
|
|
||||||
queue.add(FrontendMessage::TriggerLoadFirstAutoSaveDocument);
|
|
||||||
queue.add(FrontendMessage::TriggerLoadPreferences);
|
|
||||||
|
|
||||||
// Display the menu bar at the top of the window
|
|
||||||
queue.add(MenuBarMessage::SendLayout);
|
|
||||||
|
|
||||||
// Send the information for tooltips and categories for each node/input.
|
|
||||||
queue.add(FrontendMessage::SendUIMetadata {
|
|
||||||
node_descriptions: document_node_definitions::collect_node_descriptions(),
|
|
||||||
node_types: document_node_definitions::collect_node_types(),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Finish loading persistent data from the browser database
|
|
||||||
queue.add(FrontendMessage::TriggerLoadRestAutoSaveDocuments);
|
|
||||||
}
|
|
||||||
Message::Animation(message) => {
|
|
||||||
self.message_handlers.animation_message_handler.process_message(message, &mut queue, ());
|
|
||||||
}
|
|
||||||
Message::Batched(messages) => {
|
|
||||||
messages.iter().for_each(|message| self.handle_message(message.to_owned(), false));
|
|
||||||
}
|
|
||||||
Message::Broadcast(message) => self.message_handlers.broadcast_message_handler.process_message(message, &mut queue, ()),
|
|
||||||
Message::Debug(message) => {
|
|
||||||
self.message_handlers.debug_message_handler.process_message(message, &mut queue, ());
|
|
||||||
}
|
|
||||||
Message::Dialog(message) => {
|
|
||||||
let data = DialogMessageData {
|
|
||||||
portfolio: &self.message_handlers.portfolio_message_handler,
|
|
||||||
preferences: &self.message_handlers.preferences_message_handler,
|
|
||||||
};
|
|
||||||
self.message_handlers.dialog_message_handler.process_message(message, &mut queue, data);
|
|
||||||
}
|
|
||||||
Message::Frontend(message) => {
|
|
||||||
// Handle these messages immediately by returning early
|
|
||||||
if let FrontendMessage::TriggerFontLoad { .. } = message {
|
|
||||||
self.responses.push(message);
|
|
||||||
self.cleanup_queues(false);
|
|
||||||
|
|
||||||
// Return early to avoid running the code after the match block
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
// `FrontendMessage`s are saved and will be sent to the frontend after the message queue is done being processed
|
|
||||||
self.responses.push(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Message::Globals(message) => {
|
|
||||||
self.message_handlers.globals_message_handler.process_message(message, &mut queue, ());
|
|
||||||
}
|
|
||||||
Message::InputPreprocessor(message) => {
|
|
||||||
let keyboard_platform = GLOBAL_PLATFORM.get().copied().unwrap_or_default().as_keyboard_platform_layout();
|
|
||||||
|
|
||||||
self.message_handlers
|
|
||||||
.input_preprocessor_message_handler
|
|
||||||
.process_message(message, &mut queue, InputPreprocessorMessageData { keyboard_platform });
|
|
||||||
}
|
|
||||||
Message::KeyMapping(message) => {
|
|
||||||
let input = &self.message_handlers.input_preprocessor_message_handler;
|
|
||||||
let actions = self.collect_actions();
|
|
||||||
|
|
||||||
self.message_handlers
|
|
||||||
.key_mapping_message_handler
|
|
||||||
.process_message(message, &mut queue, KeyMappingMessageData { input, actions });
|
|
||||||
}
|
|
||||||
Message::Layout(message) => {
|
|
||||||
let action_input_mapping = &|action_to_find: &MessageDiscriminant| self.message_handlers.key_mapping_message_handler.action_input_mapping(action_to_find);
|
|
||||||
|
|
||||||
self.message_handlers.layout_message_handler.process_message(message, &mut queue, action_input_mapping);
|
|
||||||
}
|
|
||||||
Message::Portfolio(message) => {
|
|
||||||
let ipp = &self.message_handlers.input_preprocessor_message_handler;
|
|
||||||
let preferences = &self.message_handlers.preferences_message_handler;
|
|
||||||
let current_tool = &self.message_handlers.tool_message_handler.tool_state.tool_data.active_tool_type;
|
|
||||||
let message_logging_verbosity = self.message_handlers.debug_message_handler.message_logging_verbosity;
|
|
||||||
let reset_node_definitions_on_open = self.message_handlers.portfolio_message_handler.reset_node_definitions_on_open;
|
|
||||||
let timing_information = self.message_handlers.animation_message_handler.timing_information();
|
|
||||||
let animation = &self.message_handlers.animation_message_handler;
|
|
||||||
|
|
||||||
self.message_handlers.portfolio_message_handler.process_message(
|
|
||||||
message,
|
|
||||||
&mut queue,
|
|
||||||
PortfolioMessageData {
|
|
||||||
ipp,
|
|
||||||
preferences,
|
|
||||||
current_tool,
|
|
||||||
message_logging_verbosity,
|
|
||||||
reset_node_definitions_on_open,
|
|
||||||
timing_information,
|
|
||||||
animation,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Message::Preferences(message) => {
|
|
||||||
self.message_handlers.preferences_message_handler.process_message(message, &mut queue, ());
|
|
||||||
}
|
|
||||||
Message::Tool(message) => {
|
|
||||||
let document_id = self.message_handlers.portfolio_message_handler.active_document_id().unwrap();
|
|
||||||
let Some(document) = self.message_handlers.portfolio_message_handler.documents.get_mut(&document_id) else {
|
|
||||||
warn!("Called ToolMessage without an active document.\nGot {message:?}");
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let data = ToolMessageData {
|
|
||||||
document_id,
|
|
||||||
document,
|
|
||||||
input: &self.message_handlers.input_preprocessor_message_handler,
|
|
||||||
persistent_data: &self.message_handlers.portfolio_message_handler.persistent_data,
|
|
||||||
node_graph: &self.message_handlers.portfolio_message_handler.executor,
|
|
||||||
preferences: &self.message_handlers.preferences_message_handler,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.message_handlers.tool_message_handler.process_message(message, &mut queue, data);
|
|
||||||
}
|
|
||||||
Message::Workspace(message) => {
|
|
||||||
self.message_handlers.workspace_message_handler.process_message(message, &mut queue, ());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there are child messages, append the queue to the list of queues
|
// If there are child messages, append the queue to the list of queues
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ pub enum AnimationMessage {
|
|||||||
EnableLivePreview,
|
EnableLivePreview,
|
||||||
DisableLivePreview,
|
DisableLivePreview,
|
||||||
RestartAnimation,
|
RestartAnimation,
|
||||||
SetFrameIndex(f64),
|
SetFrameIndex { frame: f64 },
|
||||||
SetTime(f64),
|
SetTime { time: f64 },
|
||||||
UpdateTime,
|
UpdateTime,
|
||||||
IncrementFrameCounter,
|
IncrementFrameCounter,
|
||||||
SetAnimationTimeMode(AnimationTimeMode),
|
SetAnimationTimeMode { animation_time_mode: AnimationTimeMode },
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ impl AnimationMessageHandler {
|
|||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl MessageHandler<AnimationMessage, ()> for AnimationMessageHandler {
|
impl MessageHandler<AnimationMessage, ()> for AnimationMessageHandler {
|
||||||
fn process_message(&mut self, message: AnimationMessage, responses: &mut VecDeque<Message>, _data: ()) {
|
fn process_message(&mut self, message: AnimationMessage, responses: &mut VecDeque<Message>, _: ()) {
|
||||||
match message {
|
match message {
|
||||||
AnimationMessage::ToggleLivePreview => match self.animation_state {
|
AnimationMessage::ToggleLivePreview => match self.animation_state {
|
||||||
AnimationState::Stopped => responses.add(AnimationMessage::EnableLivePreview),
|
AnimationState::Stopped => responses.add(AnimationMessage::EnableLivePreview),
|
||||||
@@ -82,13 +82,13 @@ impl MessageHandler<AnimationMessage, ()> for AnimationMessageHandler {
|
|||||||
// Update the restart and pause/play buttons
|
// Update the restart and pause/play buttons
|
||||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||||
}
|
}
|
||||||
AnimationMessage::SetFrameIndex(frame) => {
|
AnimationMessage::SetFrameIndex { frame } => {
|
||||||
self.frame_index = frame;
|
self.frame_index = frame;
|
||||||
responses.add(PortfolioMessage::SubmitActiveGraphRender);
|
responses.add(PortfolioMessage::SubmitActiveGraphRender);
|
||||||
// Update the restart and pause/play buttons
|
// Update the restart and pause/play buttons
|
||||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||||
}
|
}
|
||||||
AnimationMessage::SetTime(time) => {
|
AnimationMessage::SetTime { time } => {
|
||||||
self.timestamp = time;
|
self.timestamp = time;
|
||||||
responses.add(AnimationMessage::UpdateTime);
|
responses.add(AnimationMessage::UpdateTime);
|
||||||
}
|
}
|
||||||
@@ -120,7 +120,7 @@ impl MessageHandler<AnimationMessage, ()> for AnimationMessageHandler {
|
|||||||
// Update the restart and pause/play buttons
|
// Update the restart and pause/play buttons
|
||||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||||
}
|
}
|
||||||
AnimationMessage::SetAnimationTimeMode(animation_time_mode) => {
|
AnimationMessage::SetAnimationTimeMode { animation_time_mode } => {
|
||||||
self.animation_time_mode = animation_time_mode;
|
self.animation_time_mode = animation_time_mode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ pub struct BroadcastMessageHandler {
|
|||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl MessageHandler<BroadcastMessage, ()> for BroadcastMessageHandler {
|
impl MessageHandler<BroadcastMessage, ()> for BroadcastMessageHandler {
|
||||||
fn process_message(&mut self, message: BroadcastMessage, responses: &mut VecDeque<Message>, _data: ()) {
|
fn process_message(&mut self, message: BroadcastMessage, responses: &mut VecDeque<Message>, _: ()) {
|
||||||
match message {
|
match message {
|
||||||
// Sub-messages
|
// Sub-messages
|
||||||
BroadcastMessage::TriggerEvent(event) => {
|
BroadcastMessage::TriggerEvent(event) => {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ pub struct DebugMessageHandler {
|
|||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl MessageHandler<DebugMessage, ()> for DebugMessageHandler {
|
impl MessageHandler<DebugMessage, ()> for DebugMessageHandler {
|
||||||
fn process_message(&mut self, message: DebugMessage, responses: &mut VecDeque<Message>, _data: ()) {
|
fn process_message(&mut self, message: DebugMessage, responses: &mut VecDeque<Message>, _: ()) {
|
||||||
match message {
|
match message {
|
||||||
DebugMessage::ToggleTraceLogs => {
|
DebugMessage::ToggleTraceLogs => {
|
||||||
if log::max_level() == log::LevelFilter::Debug {
|
if log::max_level() == log::LevelFilter::Debug {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use crate::messages::layout::utility_types::widget_prelude::*;
|
|||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
#[derive(ExtractField)]
|
#[derive(ExtractField)]
|
||||||
pub struct DialogMessageData<'a> {
|
pub struct DialogMessageContext<'a> {
|
||||||
pub portfolio: &'a PortfolioMessageHandler,
|
pub portfolio: &'a PortfolioMessageHandler,
|
||||||
pub preferences: &'a PreferencesMessageHandler,
|
pub preferences: &'a PreferencesMessageHandler,
|
||||||
}
|
}
|
||||||
@@ -17,14 +17,14 @@ pub struct DialogMessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl MessageHandler<DialogMessage, DialogMessageData<'_>> for DialogMessageHandler {
|
impl MessageHandler<DialogMessage, DialogMessageContext<'_>> for DialogMessageHandler {
|
||||||
fn process_message(&mut self, message: DialogMessage, responses: &mut VecDeque<Message>, data: DialogMessageData) {
|
fn process_message(&mut self, message: DialogMessage, responses: &mut VecDeque<Message>, context: DialogMessageContext) {
|
||||||
let DialogMessageData { portfolio, preferences } = data;
|
let DialogMessageContext { portfolio, preferences } = context;
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
DialogMessage::ExportDialog(message) => self.export_dialog.process_message(message, responses, ExportDialogMessageData { portfolio }),
|
DialogMessage::ExportDialog(message) => self.export_dialog.process_message(message, responses, ExportDialogMessageContext { portfolio }),
|
||||||
DialogMessage::NewDocumentDialog(message) => self.new_document_dialog.process_message(message, responses, ()),
|
DialogMessage::NewDocumentDialog(message) => self.new_document_dialog.process_message(message, responses, ()),
|
||||||
DialogMessage::PreferencesDialog(message) => self.preferences_dialog.process_message(message, responses, PreferencesDialogMessageData { preferences }),
|
DialogMessage::PreferencesDialog(message) => self.preferences_dialog.process_message(message, responses, PreferencesDialogMessageContext { preferences }),
|
||||||
|
|
||||||
DialogMessage::CloseAllDocumentsWithConfirmation => {
|
DialogMessage::CloseAllDocumentsWithConfirmation => {
|
||||||
let dialog = simple_dialogs::CloseAllDocumentsDialog {
|
let dialog = simple_dialogs::CloseAllDocumentsDialog {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use crate::messages::portfolio::document::utility_types::document_metadata::Laye
|
|||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
#[derive(ExtractField)]
|
#[derive(ExtractField)]
|
||||||
pub struct ExportDialogMessageData<'a> {
|
pub struct ExportDialogMessageContext<'a> {
|
||||||
pub portfolio: &'a PortfolioMessageHandler,
|
pub portfolio: &'a PortfolioMessageHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,9 +33,9 @@ impl Default for ExportDialogMessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl MessageHandler<ExportDialogMessage, ExportDialogMessageData<'_>> for ExportDialogMessageHandler {
|
impl MessageHandler<ExportDialogMessage, ExportDialogMessageContext<'_>> for ExportDialogMessageHandler {
|
||||||
fn process_message(&mut self, message: ExportDialogMessage, responses: &mut VecDeque<Message>, data: ExportDialogMessageData) {
|
fn process_message(&mut self, message: ExportDialogMessage, responses: &mut VecDeque<Message>, context: ExportDialogMessageContext) {
|
||||||
let ExportDialogMessageData { portfolio } = data;
|
let ExportDialogMessageContext { portfolio } = context;
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
ExportDialogMessage::FileType(export_type) => self.file_type = export_type,
|
ExportDialogMessage::FileType(export_type) => self.file_type = export_type,
|
||||||
|
|||||||
@@ -4,4 +4,4 @@ mod export_dialog_message_handler;
|
|||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use export_dialog_message::{ExportDialogMessage, ExportDialogMessageDiscriminant};
|
pub use export_dialog_message::{ExportDialogMessage, ExportDialogMessageDiscriminant};
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use export_dialog_message_handler::{ExportDialogMessageData, ExportDialogMessageHandler};
|
pub use export_dialog_message_handler::{ExportDialogMessageContext, ExportDialogMessageHandler};
|
||||||
|
|||||||
@@ -16,4 +16,4 @@ pub mod simple_dialogs;
|
|||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use dialog_message::{DialogMessage, DialogMessageDiscriminant};
|
pub use dialog_message::{DialogMessage, DialogMessageDiscriminant};
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use dialog_message_handler::{DialogMessageData, DialogMessageHandler};
|
pub use dialog_message_handler::{DialogMessageContext, DialogMessageHandler};
|
||||||
|
|||||||
+1
-1
@@ -13,7 +13,7 @@ pub struct NewDocumentDialogMessageHandler {
|
|||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl MessageHandler<NewDocumentDialogMessage, ()> for NewDocumentDialogMessageHandler {
|
impl MessageHandler<NewDocumentDialogMessage, ()> for NewDocumentDialogMessageHandler {
|
||||||
fn process_message(&mut self, message: NewDocumentDialogMessage, responses: &mut VecDeque<Message>, _data: ()) {
|
fn process_message(&mut self, message: NewDocumentDialogMessage, responses: &mut VecDeque<Message>, _: ()) {
|
||||||
match message {
|
match message {
|
||||||
NewDocumentDialogMessage::Name(name) => self.name = name,
|
NewDocumentDialogMessage::Name(name) => self.name = name,
|
||||||
NewDocumentDialogMessage::Infinite(infinite) => self.infinite = infinite,
|
NewDocumentDialogMessage::Infinite(infinite) => self.infinite = infinite,
|
||||||
|
|||||||
@@ -4,4 +4,4 @@ mod preferences_dialog_message_handler;
|
|||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use preferences_dialog_message::{PreferencesDialogMessage, PreferencesDialogMessageDiscriminant};
|
pub use preferences_dialog_message::{PreferencesDialogMessage, PreferencesDialogMessageDiscriminant};
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use preferences_dialog_message_handler::{PreferencesDialogMessageData, PreferencesDialogMessageHandler};
|
pub use preferences_dialog_message_handler::{PreferencesDialogMessageContext, PreferencesDialogMessageHandler};
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use crate::messages::preferences::SelectionMode;
|
|||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
#[derive(ExtractField)]
|
#[derive(ExtractField)]
|
||||||
pub struct PreferencesDialogMessageData<'a> {
|
pub struct PreferencesDialogMessageContext<'a> {
|
||||||
pub preferences: &'a PreferencesMessageHandler,
|
pub preferences: &'a PreferencesMessageHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -14,9 +14,9 @@ pub struct PreferencesDialogMessageData<'a> {
|
|||||||
pub struct PreferencesDialogMessageHandler {}
|
pub struct PreferencesDialogMessageHandler {}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl MessageHandler<PreferencesDialogMessage, PreferencesDialogMessageData<'_>> for PreferencesDialogMessageHandler {
|
impl MessageHandler<PreferencesDialogMessage, PreferencesDialogMessageContext<'_>> for PreferencesDialogMessageHandler {
|
||||||
fn process_message(&mut self, message: PreferencesDialogMessage, responses: &mut VecDeque<Message>, data: PreferencesDialogMessageData) {
|
fn process_message(&mut self, message: PreferencesDialogMessage, responses: &mut VecDeque<Message>, context: PreferencesDialogMessageContext) {
|
||||||
let PreferencesDialogMessageData { preferences } = data;
|
let PreferencesDialogMessageContext { preferences } = context;
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
PreferencesDialogMessage::Confirm => {}
|
PreferencesDialogMessage::Confirm => {}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ pub struct GlobalsMessageHandler {}
|
|||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl MessageHandler<GlobalsMessage, ()> for GlobalsMessageHandler {
|
impl MessageHandler<GlobalsMessage, ()> for GlobalsMessageHandler {
|
||||||
fn process_message(&mut self, message: GlobalsMessage, _responses: &mut VecDeque<Message>, _data: ()) {
|
fn process_message(&mut self, message: GlobalsMessage, _responses: &mut VecDeque<Message>, _: ()) {
|
||||||
match message {
|
match message {
|
||||||
GlobalsMessage::SetPlatform { platform } => {
|
GlobalsMessage::SetPlatform { platform } => {
|
||||||
if GLOBAL_PLATFORM.get() != Some(&platform) {
|
if GLOBAL_PLATFORM.get() != Some(&platform) {
|
||||||
|
|||||||
@@ -19,5 +19,6 @@ pub enum InputMapperMessage {
|
|||||||
|
|
||||||
// Messages
|
// Messages
|
||||||
PointerMove,
|
PointerMove,
|
||||||
|
PointerShake,
|
||||||
WheelScroll,
|
WheelScroll,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use crate::messages::prelude::*;
|
|||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
#[derive(ExtractField)]
|
#[derive(ExtractField)]
|
||||||
pub struct InputMapperMessageData<'a> {
|
pub struct InputMapperMessageContext<'a> {
|
||||||
pub input: &'a InputPreprocessorMessageHandler,
|
pub input: &'a InputPreprocessorMessageHandler,
|
||||||
pub actions: ActionList,
|
pub actions: ActionList,
|
||||||
}
|
}
|
||||||
@@ -18,9 +18,9 @@ pub struct InputMapperMessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl MessageHandler<InputMapperMessage, InputMapperMessageData<'_>> for InputMapperMessageHandler {
|
impl MessageHandler<InputMapperMessage, InputMapperMessageContext<'_>> for InputMapperMessageHandler {
|
||||||
fn process_message(&mut self, message: InputMapperMessage, responses: &mut VecDeque<Message>, data: InputMapperMessageData) {
|
fn process_message(&mut self, message: InputMapperMessage, responses: &mut VecDeque<Message>, context: InputMapperMessageContext) {
|
||||||
let InputMapperMessageData { input, actions } = data;
|
let InputMapperMessageContext { input, actions } = context;
|
||||||
|
|
||||||
if let Some(message) = self.mapping.match_input_message(message, &input.keyboard, actions) {
|
if let Some(message) = self.mapping.match_input_message(message, &input.keyboard, actions) {
|
||||||
responses.add(message);
|
responses.add(message);
|
||||||
|
|||||||
@@ -54,14 +54,15 @@ pub fn input_mappings() -> Mapping {
|
|||||||
entry!(KeyDown(KeyZ); modifiers=[Accel, MouseLeft], action_dispatch=DocumentMessage::Noop),
|
entry!(KeyDown(KeyZ); modifiers=[Accel, MouseLeft], action_dispatch=DocumentMessage::Noop),
|
||||||
//
|
//
|
||||||
// NodeGraphMessage
|
// NodeGraphMessage
|
||||||
entry!(KeyDown(MouseLeft); action_dispatch=NodeGraphMessage::PointerDown {shift_click: false, control_click: false, alt_click: false, right_click: false}),
|
entry!(KeyDown(MouseLeft); action_dispatch=NodeGraphMessage::PointerDown { shift_click: false, control_click: false, alt_click: false, right_click: false }),
|
||||||
entry!(KeyDown(MouseLeft); modifiers=[Shift], action_dispatch=NodeGraphMessage::PointerDown {shift_click: true, control_click: false, alt_click: false, right_click: false}),
|
entry!(KeyDown(MouseLeft); modifiers=[Shift], action_dispatch=NodeGraphMessage::PointerDown { shift_click: true, control_click: false, alt_click: false, right_click: false }),
|
||||||
entry!(KeyDown(MouseLeft); modifiers=[Accel], action_dispatch=NodeGraphMessage::PointerDown {shift_click: false, control_click: true, alt_click: false, right_click: false}),
|
entry!(KeyDown(MouseLeft); modifiers=[Accel], action_dispatch=NodeGraphMessage::PointerDown { shift_click: false, control_click: true, alt_click: false, right_click: false }),
|
||||||
entry!(KeyDown(MouseLeft); modifiers=[Shift, Accel], action_dispatch=NodeGraphMessage::PointerDown {shift_click: true, control_click: true, alt_click: false, right_click: false}),
|
entry!(KeyDown(MouseLeft); modifiers=[Shift, Accel], action_dispatch=NodeGraphMessage::PointerDown { shift_click: true, control_click: true, alt_click: false, right_click: false }),
|
||||||
entry!(KeyDown(MouseLeft); modifiers=[Alt], action_dispatch=NodeGraphMessage::PointerDown {shift_click: false, control_click: false, alt_click: true, right_click: false}),
|
entry!(KeyDown(MouseLeft); modifiers=[Alt], action_dispatch=NodeGraphMessage::PointerDown { shift_click: false, control_click: false, alt_click: true, right_click: false }),
|
||||||
entry!(KeyDown(MouseRight); action_dispatch=NodeGraphMessage::PointerDown {shift_click: false, control_click: false, alt_click: false, right_click: true}),
|
entry!(KeyDown(MouseRight); action_dispatch=NodeGraphMessage::PointerDown { shift_click: false, control_click: false, alt_click: false, right_click: true }),
|
||||||
entry!(DoubleClick(MouseButton::Left); action_dispatch=NodeGraphMessage::EnterNestedNetwork),
|
entry!(DoubleClick(MouseButton::Left); action_dispatch=NodeGraphMessage::EnterNestedNetwork),
|
||||||
entry!(PointerMove; refresh_keys=[Shift], action_dispatch=NodeGraphMessage::PointerMove {shift: Shift}),
|
entry!(PointerMove; refresh_keys=[Shift], action_dispatch=NodeGraphMessage::PointerMove { shift: Shift }),
|
||||||
|
entry!(PointerShake; action_dispatch=NodeGraphMessage::ShakeNode),
|
||||||
entry!(KeyUp(MouseLeft); action_dispatch=NodeGraphMessage::PointerUp),
|
entry!(KeyUp(MouseLeft); action_dispatch=NodeGraphMessage::PointerUp),
|
||||||
entry!(KeyDown(Delete); modifiers=[Accel], action_dispatch=NodeGraphMessage::DeleteSelectedNodes { delete_children: false }),
|
entry!(KeyDown(Delete); modifiers=[Accel], action_dispatch=NodeGraphMessage::DeleteSelectedNodes { delete_children: false }),
|
||||||
entry!(KeyDown(Backspace); modifiers=[Accel], action_dispatch=NodeGraphMessage::DeleteSelectedNodes { delete_children: false }),
|
entry!(KeyDown(Backspace); modifiers=[Accel], action_dispatch=NodeGraphMessage::DeleteSelectedNodes { delete_children: false }),
|
||||||
@@ -317,6 +318,7 @@ pub fn input_mappings() -> Mapping {
|
|||||||
entry!(KeyDown(KeyX); modifiers=[Shift], action_dispatch=ToolMessage::SwapColors),
|
entry!(KeyDown(KeyX); modifiers=[Shift], action_dispatch=ToolMessage::SwapColors),
|
||||||
entry!(KeyDown(KeyC); modifiers=[Alt], action_dispatch=ToolMessage::SelectRandomWorkingColor { primary: true }),
|
entry!(KeyDown(KeyC); modifiers=[Alt], action_dispatch=ToolMessage::SelectRandomWorkingColor { primary: true }),
|
||||||
entry!(KeyDown(KeyC); modifiers=[Alt, Shift], action_dispatch=ToolMessage::SelectRandomWorkingColor { primary: false }),
|
entry!(KeyDown(KeyC); modifiers=[Alt, Shift], action_dispatch=ToolMessage::SelectRandomWorkingColor { primary: false }),
|
||||||
|
entry!(KeyDownNoRepeat(Tab); action_dispatch=ToolMessage::ToggleSelectVsPath),
|
||||||
//
|
//
|
||||||
// DocumentMessage
|
// DocumentMessage
|
||||||
entry!(KeyDown(Space); modifiers=[Control], action_dispatch=DocumentMessage::GraphViewOverlayToggle),
|
entry!(KeyDown(Space); modifiers=[Control], action_dispatch=DocumentMessage::GraphViewOverlayToggle),
|
||||||
@@ -416,7 +418,7 @@ pub fn input_mappings() -> Mapping {
|
|||||||
entry!(KeyDown(Tab); modifiers=[Control], action_dispatch=PortfolioMessage::NextDocument),
|
entry!(KeyDown(Tab); modifiers=[Control], action_dispatch=PortfolioMessage::NextDocument),
|
||||||
entry!(KeyDown(Tab); modifiers=[Control, Shift], action_dispatch=PortfolioMessage::PrevDocument),
|
entry!(KeyDown(Tab); modifiers=[Control, Shift], action_dispatch=PortfolioMessage::PrevDocument),
|
||||||
entry!(KeyDown(KeyW); modifiers=[Accel], action_dispatch=PortfolioMessage::CloseActiveDocumentWithConfirmation),
|
entry!(KeyDown(KeyW); modifiers=[Accel], action_dispatch=PortfolioMessage::CloseActiveDocumentWithConfirmation),
|
||||||
entry!(KeyDown(KeyW); modifiers=[Accel,Alt], action_dispatch=PortfolioMessage::CloseAllDocumentsWithConfirmation),
|
entry!(KeyDown(KeyW); modifiers=[Accel, Alt], action_dispatch=PortfolioMessage::CloseAllDocumentsWithConfirmation),
|
||||||
entry!(KeyDown(KeyO); modifiers=[Accel], action_dispatch=PortfolioMessage::OpenDocument),
|
entry!(KeyDown(KeyO); modifiers=[Accel], action_dispatch=PortfolioMessage::OpenDocument),
|
||||||
entry!(KeyDown(KeyI); modifiers=[Accel], action_dispatch=PortfolioMessage::Import),
|
entry!(KeyDown(KeyI); modifiers=[Accel], action_dispatch=PortfolioMessage::Import),
|
||||||
entry!(KeyDown(KeyX); modifiers=[Accel], action_dispatch=PortfolioMessage::Cut { clipboard: Clipboard::Device }),
|
entry!(KeyDown(KeyX); modifiers=[Accel], action_dispatch=PortfolioMessage::Cut { clipboard: Clipboard::Device }),
|
||||||
@@ -439,7 +441,7 @@ pub fn input_mappings() -> Mapping {
|
|||||||
entry!(KeyDown(Space); modifiers=[Shift], action_dispatch=AnimationMessage::ToggleLivePreview),
|
entry!(KeyDown(Space); modifiers=[Shift], action_dispatch=AnimationMessage::ToggleLivePreview),
|
||||||
entry!(KeyDown(Home); modifiers=[Shift], action_dispatch=AnimationMessage::RestartAnimation),
|
entry!(KeyDown(Home); modifiers=[Shift], action_dispatch=AnimationMessage::RestartAnimation),
|
||||||
];
|
];
|
||||||
let (mut key_up, mut key_down, mut key_up_no_repeat, mut key_down_no_repeat, mut double_click, mut wheel_scroll, mut pointer_move) = mappings;
|
let (mut key_up, mut key_down, mut key_up_no_repeat, mut key_down_no_repeat, mut double_click, mut wheel_scroll, mut pointer_move, mut pointer_shake) = mappings;
|
||||||
|
|
||||||
let sort = |list: &mut KeyMappingEntries| list.0.sort_by(|a, b| b.modifiers.count_ones().cmp(&a.modifiers.count_ones()));
|
let sort = |list: &mut KeyMappingEntries| list.0.sort_by(|a, b| b.modifiers.count_ones().cmp(&a.modifiers.count_ones()));
|
||||||
// Sort the sublists of `key_up`, `key_down`, `key_up_no_repeat`, and `key_down_no_repeat`
|
// Sort the sublists of `key_up`, `key_down`, `key_up_no_repeat`, and `key_down_no_repeat`
|
||||||
@@ -456,6 +458,8 @@ pub fn input_mappings() -> Mapping {
|
|||||||
sort(&mut wheel_scroll);
|
sort(&mut wheel_scroll);
|
||||||
// Sort `pointer_move`
|
// Sort `pointer_move`
|
||||||
sort(&mut pointer_move);
|
sort(&mut pointer_move);
|
||||||
|
// Sort `pointer_shake`
|
||||||
|
sort(&mut pointer_shake);
|
||||||
|
|
||||||
Mapping {
|
Mapping {
|
||||||
key_up,
|
key_up,
|
||||||
@@ -465,6 +469,7 @@ pub fn input_mappings() -> Mapping {
|
|||||||
double_click,
|
double_click,
|
||||||
wheel_scroll,
|
wheel_scroll,
|
||||||
pointer_move,
|
pointer_move,
|
||||||
|
pointer_shake,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
use crate::messages::input_mapper::input_mapper_message_handler::InputMapperMessageData;
|
use crate::messages::input_mapper::input_mapper_message_handler::InputMapperMessageContext;
|
||||||
use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup;
|
use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
#[derive(ExtractField)]
|
#[derive(ExtractField)]
|
||||||
pub struct KeyMappingMessageData<'a> {
|
pub struct KeyMappingMessageContext<'a> {
|
||||||
pub input: &'a InputPreprocessorMessageHandler,
|
pub input: &'a InputPreprocessorMessageHandler,
|
||||||
pub actions: ActionList,
|
pub actions: ActionList,
|
||||||
}
|
}
|
||||||
@@ -14,12 +14,12 @@ pub struct KeyMappingMessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl MessageHandler<KeyMappingMessage, KeyMappingMessageData<'_>> for KeyMappingMessageHandler {
|
impl MessageHandler<KeyMappingMessage, KeyMappingMessageContext<'_>> for KeyMappingMessageHandler {
|
||||||
fn process_message(&mut self, message: KeyMappingMessage, responses: &mut VecDeque<Message>, data: KeyMappingMessageData) {
|
fn process_message(&mut self, message: KeyMappingMessage, responses: &mut VecDeque<Message>, context: KeyMappingMessageContext) {
|
||||||
let KeyMappingMessageData { input, actions } = data;
|
let KeyMappingMessageContext { input, actions } = context;
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
KeyMappingMessage::Lookup(input_message) => self.mapping_handler.process_message(input_message, responses, InputMapperMessageData { input, actions }),
|
KeyMappingMessage::Lookup(input_message) => self.mapping_handler.process_message(input_message, responses, InputMapperMessageContext { input, actions }),
|
||||||
KeyMappingMessage::ModifyMapping(new_layout) => self.mapping_handler.set_mapping(new_layout.into()),
|
KeyMappingMessage::ModifyMapping(new_layout) => self.mapping_handler.set_mapping(new_layout.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,4 +4,4 @@ mod key_mapping_message_handler;
|
|||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use key_mapping_message::{KeyMappingMessage, KeyMappingMessageDiscriminant, MappingVariant, MappingVariantDiscriminant};
|
pub use key_mapping_message::{KeyMappingMessage, KeyMappingMessageDiscriminant, MappingVariant, MappingVariantDiscriminant};
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use key_mapping_message_handler::{KeyMappingMessageData, KeyMappingMessageHandler};
|
pub use key_mapping_message_handler::{KeyMappingMessageContext, KeyMappingMessageHandler};
|
||||||
|
|||||||
@@ -8,4 +8,4 @@ pub mod utility_types;
|
|||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use input_mapper_message::{InputMapperMessage, InputMapperMessageDiscriminant};
|
pub use input_mapper_message::{InputMapperMessage, InputMapperMessageDiscriminant};
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use input_mapper_message_handler::{InputMapperMessageData, InputMapperMessageHandler};
|
pub use input_mapper_message_handler::{InputMapperMessageContext, InputMapperMessageHandler};
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ macro_rules! mapping {
|
|||||||
let mut double_click = KeyMappingEntries::mouse_buttons_arrays();
|
let mut double_click = KeyMappingEntries::mouse_buttons_arrays();
|
||||||
let mut wheel_scroll = KeyMappingEntries::new();
|
let mut wheel_scroll = KeyMappingEntries::new();
|
||||||
let mut pointer_move = KeyMappingEntries::new();
|
let mut pointer_move = KeyMappingEntries::new();
|
||||||
|
let mut pointer_shake = KeyMappingEntries::new();
|
||||||
|
|
||||||
$(
|
$(
|
||||||
// Each of the many entry slices, one specified per action
|
// Each of the many entry slices, one specified per action
|
||||||
@@ -104,6 +105,7 @@ macro_rules! mapping {
|
|||||||
InputMapperMessage::DoubleClick(key) => &mut double_click[key as usize],
|
InputMapperMessage::DoubleClick(key) => &mut double_click[key as usize],
|
||||||
InputMapperMessage::WheelScroll => &mut wheel_scroll,
|
InputMapperMessage::WheelScroll => &mut wheel_scroll,
|
||||||
InputMapperMessage::PointerMove => &mut pointer_move,
|
InputMapperMessage::PointerMove => &mut pointer_move,
|
||||||
|
InputMapperMessage::PointerShake => &mut pointer_shake,
|
||||||
};
|
};
|
||||||
// Push each entry to the corresponding `KeyMappingEntries` list for its input type
|
// Push each entry to the corresponding `KeyMappingEntries` list for its input type
|
||||||
corresponding_list.push(entry.clone());
|
corresponding_list.push(entry.clone());
|
||||||
@@ -111,7 +113,7 @@ macro_rules! mapping {
|
|||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
|
|
||||||
(key_up, key_down, key_up_no_repeat, key_down_no_repeat, double_click, wheel_scroll, pointer_move)
|
(key_up, key_down, key_up_no_repeat, key_down_no_repeat, double_click, wheel_scroll, pointer_move, pointer_shake)
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ pub struct Mapping {
|
|||||||
pub double_click: [KeyMappingEntries; NUMBER_OF_MOUSE_BUTTONS],
|
pub double_click: [KeyMappingEntries; NUMBER_OF_MOUSE_BUTTONS],
|
||||||
pub wheel_scroll: KeyMappingEntries,
|
pub wheel_scroll: KeyMappingEntries,
|
||||||
pub pointer_move: KeyMappingEntries,
|
pub pointer_move: KeyMappingEntries,
|
||||||
|
pub pointer_shake: KeyMappingEntries,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Mapping {
|
impl Default for Mapping {
|
||||||
@@ -47,6 +48,7 @@ impl Mapping {
|
|||||||
InputMapperMessage::DoubleClick(key) => &self.double_click[*key as usize],
|
InputMapperMessage::DoubleClick(key) => &self.double_click[*key as usize],
|
||||||
InputMapperMessage::WheelScroll => &self.wheel_scroll,
|
InputMapperMessage::WheelScroll => &self.wheel_scroll,
|
||||||
InputMapperMessage::PointerMove => &self.pointer_move,
|
InputMapperMessage::PointerMove => &self.pointer_move,
|
||||||
|
InputMapperMessage::PointerShake => &self.pointer_shake,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,6 +61,7 @@ impl Mapping {
|
|||||||
InputMapperMessage::DoubleClick(key) => &mut self.double_click[*key as usize],
|
InputMapperMessage::DoubleClick(key) => &mut self.double_click[*key as usize],
|
||||||
InputMapperMessage::WheelScroll => &mut self.wheel_scroll,
|
InputMapperMessage::WheelScroll => &mut self.wheel_scroll,
|
||||||
InputMapperMessage::PointerMove => &mut self.pointer_move,
|
InputMapperMessage::PointerMove => &mut self.pointer_move,
|
||||||
|
InputMapperMessage::PointerShake => &mut self.pointer_shake,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ pub enum InputPreprocessorMessage {
|
|||||||
PointerDown { editor_mouse_state: EditorMouseState, modifier_keys: ModifierKeys },
|
PointerDown { editor_mouse_state: EditorMouseState, modifier_keys: ModifierKeys },
|
||||||
PointerMove { editor_mouse_state: EditorMouseState, modifier_keys: ModifierKeys },
|
PointerMove { editor_mouse_state: EditorMouseState, modifier_keys: ModifierKeys },
|
||||||
PointerUp { editor_mouse_state: EditorMouseState, modifier_keys: ModifierKeys },
|
PointerUp { editor_mouse_state: EditorMouseState, modifier_keys: ModifierKeys },
|
||||||
|
PointerShake { editor_mouse_state: EditorMouseState, modifier_keys: ModifierKeys },
|
||||||
CurrentTime { timestamp: u64 },
|
CurrentTime { timestamp: u64 },
|
||||||
WheelScroll { editor_mouse_state: EditorMouseState, modifier_keys: ModifierKeys },
|
WheelScroll { editor_mouse_state: EditorMouseState, modifier_keys: ModifierKeys },
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use glam::DVec2;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
#[derive(ExtractField)]
|
#[derive(ExtractField)]
|
||||||
pub struct InputPreprocessorMessageData {
|
pub struct InputPreprocessorMessageContext {
|
||||||
pub keyboard_platform: KeyboardPlatformLayout,
|
pub keyboard_platform: KeyboardPlatformLayout,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,9 +21,9 @@ pub struct InputPreprocessorMessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageData> for InputPreprocessorMessageHandler {
|
impl MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageContext> for InputPreprocessorMessageHandler {
|
||||||
fn process_message(&mut self, message: InputPreprocessorMessage, responses: &mut VecDeque<Message>, data: InputPreprocessorMessageData) {
|
fn process_message(&mut self, message: InputPreprocessorMessage, responses: &mut VecDeque<Message>, context: InputPreprocessorMessageContext) {
|
||||||
let InputPreprocessorMessageData { keyboard_platform } = data;
|
let InputPreprocessorMessageContext { keyboard_platform } = context;
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
InputPreprocessorMessage::BoundsOfViewports { bounds_of_viewports } => {
|
InputPreprocessorMessage::BoundsOfViewports { bounds_of_viewports } => {
|
||||||
@@ -97,8 +97,16 @@ impl MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageData> for
|
|||||||
|
|
||||||
self.translate_mouse_event(mouse_state, false, responses);
|
self.translate_mouse_event(mouse_state, false, responses);
|
||||||
}
|
}
|
||||||
|
InputPreprocessorMessage::PointerShake { editor_mouse_state, modifier_keys } => {
|
||||||
|
self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses);
|
||||||
|
|
||||||
|
let mouse_state = editor_mouse_state.to_mouse_state(&self.viewport_bounds);
|
||||||
|
self.mouse.position = mouse_state.position;
|
||||||
|
|
||||||
|
responses.add(InputMapperMessage::PointerShake);
|
||||||
|
}
|
||||||
InputPreprocessorMessage::CurrentTime { timestamp } => {
|
InputPreprocessorMessage::CurrentTime { timestamp } => {
|
||||||
responses.add(AnimationMessage::SetTime(timestamp as f64));
|
responses.add(AnimationMessage::SetTime { time: timestamp as f64 });
|
||||||
self.time = timestamp;
|
self.time = timestamp;
|
||||||
self.frame_time.advance_timestamp(Duration::from_millis(timestamp));
|
self.frame_time.advance_timestamp(Duration::from_millis(timestamp));
|
||||||
}
|
}
|
||||||
@@ -214,10 +222,10 @@ mod test {
|
|||||||
|
|
||||||
let mut responses = VecDeque::new();
|
let mut responses = VecDeque::new();
|
||||||
|
|
||||||
let data = InputPreprocessorMessageData {
|
let context = InputPreprocessorMessageContext {
|
||||||
keyboard_platform: KeyboardPlatformLayout::Standard,
|
keyboard_platform: KeyboardPlatformLayout::Standard,
|
||||||
};
|
};
|
||||||
input_preprocessor.process_message(message, &mut responses, data);
|
input_preprocessor.process_message(message, &mut responses, context);
|
||||||
|
|
||||||
assert!(input_preprocessor.keyboard.get(Key::Alt as usize));
|
assert!(input_preprocessor.keyboard.get(Key::Alt as usize));
|
||||||
assert_eq!(responses.pop_front(), Some(InputMapperMessage::KeyDown(Key::Alt).into()));
|
assert_eq!(responses.pop_front(), Some(InputMapperMessage::KeyDown(Key::Alt).into()));
|
||||||
@@ -233,10 +241,10 @@ mod test {
|
|||||||
|
|
||||||
let mut responses = VecDeque::new();
|
let mut responses = VecDeque::new();
|
||||||
|
|
||||||
let data = InputPreprocessorMessageData {
|
let context = InputPreprocessorMessageContext {
|
||||||
keyboard_platform: KeyboardPlatformLayout::Standard,
|
keyboard_platform: KeyboardPlatformLayout::Standard,
|
||||||
};
|
};
|
||||||
input_preprocessor.process_message(message, &mut responses, data);
|
input_preprocessor.process_message(message, &mut responses, context);
|
||||||
|
|
||||||
assert!(input_preprocessor.keyboard.get(Key::Control as usize));
|
assert!(input_preprocessor.keyboard.get(Key::Control as usize));
|
||||||
assert_eq!(responses.pop_front(), Some(InputMapperMessage::KeyDown(Key::Control).into()));
|
assert_eq!(responses.pop_front(), Some(InputMapperMessage::KeyDown(Key::Control).into()));
|
||||||
@@ -252,10 +260,10 @@ mod test {
|
|||||||
|
|
||||||
let mut responses = VecDeque::new();
|
let mut responses = VecDeque::new();
|
||||||
|
|
||||||
let data = InputPreprocessorMessageData {
|
let context = InputPreprocessorMessageContext {
|
||||||
keyboard_platform: KeyboardPlatformLayout::Standard,
|
keyboard_platform: KeyboardPlatformLayout::Standard,
|
||||||
};
|
};
|
||||||
input_preprocessor.process_message(message, &mut responses, data);
|
input_preprocessor.process_message(message, &mut responses, context);
|
||||||
|
|
||||||
assert!(input_preprocessor.keyboard.get(Key::Shift as usize));
|
assert!(input_preprocessor.keyboard.get(Key::Shift as usize));
|
||||||
assert_eq!(responses.pop_front(), Some(InputMapperMessage::KeyDown(Key::Shift).into()));
|
assert_eq!(responses.pop_front(), Some(InputMapperMessage::KeyDown(Key::Shift).into()));
|
||||||
@@ -273,10 +281,10 @@ mod test {
|
|||||||
|
|
||||||
let mut responses = VecDeque::new();
|
let mut responses = VecDeque::new();
|
||||||
|
|
||||||
let data = InputPreprocessorMessageData {
|
let context = InputPreprocessorMessageContext {
|
||||||
keyboard_platform: KeyboardPlatformLayout::Standard,
|
keyboard_platform: KeyboardPlatformLayout::Standard,
|
||||||
};
|
};
|
||||||
input_preprocessor.process_message(message, &mut responses, data);
|
input_preprocessor.process_message(message, &mut responses, context);
|
||||||
|
|
||||||
assert!(!input_preprocessor.keyboard.get(Key::Control as usize));
|
assert!(!input_preprocessor.keyboard.get(Key::Control as usize));
|
||||||
assert_eq!(responses.pop_front(), Some(InputMapperMessage::KeyUp(Key::Control).into()));
|
assert_eq!(responses.pop_front(), Some(InputMapperMessage::KeyUp(Key::Control).into()));
|
||||||
@@ -293,10 +301,10 @@ mod test {
|
|||||||
|
|
||||||
let mut responses = VecDeque::new();
|
let mut responses = VecDeque::new();
|
||||||
|
|
||||||
let data = InputPreprocessorMessageData {
|
let context = InputPreprocessorMessageContext {
|
||||||
keyboard_platform: KeyboardPlatformLayout::Standard,
|
keyboard_platform: KeyboardPlatformLayout::Standard,
|
||||||
};
|
};
|
||||||
input_preprocessor.process_message(message, &mut responses, data);
|
input_preprocessor.process_message(message, &mut responses, context);
|
||||||
|
|
||||||
assert!(input_preprocessor.keyboard.get(Key::Control as usize));
|
assert!(input_preprocessor.keyboard.get(Key::Control as usize));
|
||||||
assert!(input_preprocessor.keyboard.get(Key::Shift as usize));
|
assert!(input_preprocessor.keyboard.get(Key::Shift as usize));
|
||||||
|
|||||||
@@ -4,4 +4,4 @@ mod input_preprocessor_message_handler;
|
|||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use input_preprocessor_message::{InputPreprocessorMessage, InputPreprocessorMessageDiscriminant};
|
pub use input_preprocessor_message::{InputPreprocessorMessage, InputPreprocessorMessageDiscriminant};
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use input_preprocessor_message_handler::{InputPreprocessorMessageData, InputPreprocessorMessageHandler};
|
pub use input_preprocessor_message_handler::{InputPreprocessorMessageContext, InputPreprocessorMessageHandler};
|
||||||
|
|||||||
@@ -6,14 +6,53 @@ use graphene_std::text::Font;
|
|||||||
use graphene_std::vector::style::{FillChoice, GradientStops};
|
use graphene_std::vector::style::{FillChoice, GradientStops};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
|
#[derive(ExtractField)]
|
||||||
|
pub struct LayoutMessageContext<'a> {
|
||||||
|
pub action_input_mapping: &'a dyn Fn(&MessageDiscriminant) -> Option<KeysGroup>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, ExtractField)]
|
#[derive(Debug, Clone, Default, ExtractField)]
|
||||||
pub struct LayoutMessageHandler {
|
pub struct LayoutMessageHandler {
|
||||||
layouts: [Layout; LayoutTarget::LayoutTargetLength as usize],
|
layouts: [Layout; LayoutTarget::LayoutTargetLength as usize],
|
||||||
}
|
}
|
||||||
|
|
||||||
enum WidgetValueAction {
|
#[message_handler_data]
|
||||||
Commit,
|
impl MessageHandler<LayoutMessage, LayoutMessageContext<'_>> for LayoutMessageHandler {
|
||||||
Update,
|
fn process_message(&mut self, message: LayoutMessage, responses: &mut std::collections::VecDeque<Message>, context: LayoutMessageContext) {
|
||||||
|
let action_input_mapping = &context.action_input_mapping;
|
||||||
|
|
||||||
|
match message {
|
||||||
|
LayoutMessage::ResendActiveWidget { layout_target, widget_id } => {
|
||||||
|
// Find the updated diff based on the specified layout target
|
||||||
|
let Some(diff) = (match &self.layouts[layout_target as usize] {
|
||||||
|
Layout::MenuLayout(_) => return,
|
||||||
|
Layout::WidgetLayout(layout) => Self::get_widget_path(layout, widget_id).map(|(widget, widget_path)| {
|
||||||
|
// Create a widget update diff for the relevant id
|
||||||
|
let new_value = DiffUpdate::Widget(widget.clone());
|
||||||
|
WidgetDiff { widget_path, new_value }
|
||||||
|
}),
|
||||||
|
}) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
// Resend that diff
|
||||||
|
self.send_diff(vec![diff], layout_target, responses, action_input_mapping);
|
||||||
|
}
|
||||||
|
LayoutMessage::SendLayout { layout, layout_target } => {
|
||||||
|
self.diff_and_send_layout_to_frontend(layout_target, layout, responses, action_input_mapping);
|
||||||
|
}
|
||||||
|
LayoutMessage::WidgetValueCommit { layout_target, widget_id, value } => {
|
||||||
|
self.handle_widget_callback(layout_target, widget_id, value, WidgetValueAction::Commit, responses);
|
||||||
|
}
|
||||||
|
LayoutMessage::WidgetValueUpdate { layout_target, widget_id, value } => {
|
||||||
|
self.handle_widget_callback(layout_target, widget_id, value, WidgetValueAction::Update, responses);
|
||||||
|
responses.add(LayoutMessage::ResendActiveWidget { layout_target, widget_id });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn actions(&self) -> ActionList {
|
||||||
|
actions!(LayoutMessageDiscriminant;)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LayoutMessageHandler {
|
impl LayoutMessageHandler {
|
||||||
@@ -84,7 +123,10 @@ impl LayoutMessageHandler {
|
|||||||
let callback_message = match action {
|
let callback_message = match action {
|
||||||
WidgetValueAction::Commit => (breadcrumb_trail_buttons.on_commit.callback)(&()),
|
WidgetValueAction::Commit => (breadcrumb_trail_buttons.on_commit.callback)(&()),
|
||||||
WidgetValueAction::Update => {
|
WidgetValueAction::Update => {
|
||||||
let update_value = value.as_u64().expect("BreadcrumbTrailButtons update was not of type: u64");
|
let Some(update_value) = value.as_u64() else {
|
||||||
|
error!("BreadcrumbTrailButtons update was not of type: u64");
|
||||||
|
return;
|
||||||
|
};
|
||||||
(breadcrumb_trail_buttons.on_update.callback)(&update_value)
|
(breadcrumb_trail_buttons.on_update.callback)(&update_value)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -94,7 +136,10 @@ impl LayoutMessageHandler {
|
|||||||
let callback_message = match action {
|
let callback_message = match action {
|
||||||
WidgetValueAction::Commit => (checkbox_input.on_commit.callback)(&()),
|
WidgetValueAction::Commit => (checkbox_input.on_commit.callback)(&()),
|
||||||
WidgetValueAction::Update => {
|
WidgetValueAction::Update => {
|
||||||
let update_value = value.as_bool().expect("CheckboxInput update was not of type: bool");
|
let Some(update_value) = value.as_bool() else {
|
||||||
|
error!("CheckboxInput update was not of type: bool");
|
||||||
|
return;
|
||||||
|
};
|
||||||
checkbox_input.checked = update_value;
|
checkbox_input.checked = update_value;
|
||||||
(checkbox_input.on_update.callback)(checkbox_input)
|
(checkbox_input.on_update.callback)(checkbox_input)
|
||||||
}
|
}
|
||||||
@@ -169,7 +214,10 @@ impl LayoutMessageHandler {
|
|||||||
let callback_message = match action {
|
let callback_message = match action {
|
||||||
WidgetValueAction::Commit => (curve_input.on_commit.callback)(&()),
|
WidgetValueAction::Commit => (curve_input.on_commit.callback)(&()),
|
||||||
WidgetValueAction::Update => {
|
WidgetValueAction::Update => {
|
||||||
let curve = serde_json::from_value(value).expect("CurveInput event data could not be deserialized");
|
let Some(curve) = serde_json::from_value(value).ok() else {
|
||||||
|
error!("CurveInput event data could not be deserialized");
|
||||||
|
return;
|
||||||
|
};
|
||||||
curve_input.value = curve;
|
curve_input.value = curve;
|
||||||
(curve_input.on_update.callback)(curve_input)
|
(curve_input.on_update.callback)(curve_input)
|
||||||
}
|
}
|
||||||
@@ -180,13 +228,27 @@ impl LayoutMessageHandler {
|
|||||||
Widget::DropdownInput(dropdown_input) => {
|
Widget::DropdownInput(dropdown_input) => {
|
||||||
let callback_message = match action {
|
let callback_message = match action {
|
||||||
WidgetValueAction::Commit => {
|
WidgetValueAction::Commit => {
|
||||||
let update_value = value.as_u64().unwrap_or_else(|| panic!("DropdownInput commit was not of type `u64`, found {value:?}"));
|
let Some(update_value) = value.as_u64() else {
|
||||||
(dropdown_input.entries.iter().flatten().nth(update_value as usize).unwrap().on_commit.callback)(&())
|
error!("DropdownInput commit was not of type `u64`, found {value:?}");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(entry) = dropdown_input.entries.iter().flatten().nth(update_value as usize) else {
|
||||||
|
error!("DropdownInput commit was not able to find entry for index {update_value}");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
(entry.on_commit.callback)(&())
|
||||||
}
|
}
|
||||||
WidgetValueAction::Update => {
|
WidgetValueAction::Update => {
|
||||||
let update_value = value.as_u64().unwrap_or_else(|| panic!("DropdownInput update was not of type `u64`, found {value:?}"));
|
let Some(update_value) = value.as_u64() else {
|
||||||
|
error!("DropdownInput update was not of type `u64`, found {value:?}");
|
||||||
|
return;
|
||||||
|
};
|
||||||
dropdown_input.selected_index = Some(update_value as u32);
|
dropdown_input.selected_index = Some(update_value as u32);
|
||||||
(dropdown_input.entries.iter().flatten().nth(update_value as usize).unwrap().on_update.callback)(&())
|
let Some(entry) = dropdown_input.entries.iter().flatten().nth(update_value as usize) else {
|
||||||
|
error!("DropdownInput update was not able to find entry for index {update_value}");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
(entry.on_update.callback)(&())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -196,12 +258,27 @@ impl LayoutMessageHandler {
|
|||||||
let callback_message = match action {
|
let callback_message = match action {
|
||||||
WidgetValueAction::Commit => (font_input.on_commit.callback)(&()),
|
WidgetValueAction::Commit => (font_input.on_commit.callback)(&()),
|
||||||
WidgetValueAction::Update => {
|
WidgetValueAction::Update => {
|
||||||
let update_value = value.as_object().expect("FontInput update was not of type: object");
|
let Some(update_value) = value.as_object() else {
|
||||||
let font_family_value = update_value.get("fontFamily").expect("FontInput update does not have a fontFamily");
|
error!("FontInput update was not of type: object");
|
||||||
let font_style_value = update_value.get("fontStyle").expect("FontInput update does not have a fontStyle");
|
return;
|
||||||
|
};
|
||||||
|
let Some(font_family_value) = update_value.get("fontFamily") else {
|
||||||
|
error!("FontInput update does not have a fontFamily");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(font_style_value) = update_value.get("fontStyle") else {
|
||||||
|
error!("FontInput update does not have a fontStyle");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
let font_family = font_family_value.as_str().expect("FontInput update fontFamily was not of type: string");
|
let Some(font_family) = font_family_value.as_str() else {
|
||||||
let font_style = font_style_value.as_str().expect("FontInput update fontStyle was not of type: string");
|
error!("FontInput update fontFamily was not of type: string");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(font_style) = font_style_value.as_str() else {
|
||||||
|
error!("FontInput update fontStyle was not of type: string");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
font_input.font_family = font_family.into();
|
font_input.font_family = font_family.into();
|
||||||
font_input.font_style = font_style.into();
|
font_input.font_style = font_style.into();
|
||||||
@@ -246,7 +323,10 @@ impl LayoutMessageHandler {
|
|||||||
responses.add(callback_message);
|
responses.add(callback_message);
|
||||||
}
|
}
|
||||||
WidgetValueAction::Update => {
|
WidgetValueAction::Update => {
|
||||||
let value = value.as_str().expect("NodeCatalog update was not of type String").to_string();
|
let Some(value) = value.as_str().map(|s| s.to_string()) else {
|
||||||
|
error!("NodeCatalog update was not of type String");
|
||||||
|
return;
|
||||||
|
};
|
||||||
let callback_message = (node_type_input.on_update.callback)(&value);
|
let callback_message = (node_type_input.on_update.callback)(&value);
|
||||||
responses.add(callback_message);
|
responses.add(callback_message);
|
||||||
}
|
}
|
||||||
@@ -257,8 +337,11 @@ impl LayoutMessageHandler {
|
|||||||
responses.add(callback_message);
|
responses.add(callback_message);
|
||||||
}
|
}
|
||||||
WidgetValueAction::Update => match value {
|
WidgetValueAction::Update => match value {
|
||||||
Value::Number(num) => {
|
Value::Number(ref num) => {
|
||||||
let update_value = num.as_f64().unwrap();
|
let Some(update_value) = num.as_f64() else {
|
||||||
|
error!("NumberInput update was not of type: f64, found {value:?}");
|
||||||
|
return;
|
||||||
|
};
|
||||||
number_input.value = Some(update_value);
|
number_input.value = Some(update_value);
|
||||||
let callback_message = (number_input.on_update.callback)(number_input);
|
let callback_message = (number_input.on_update.callback)(number_input);
|
||||||
responses.add(callback_message);
|
responses.add(callback_message);
|
||||||
@@ -284,7 +367,10 @@ impl LayoutMessageHandler {
|
|||||||
let callback_message = match action {
|
let callback_message = match action {
|
||||||
WidgetValueAction::Commit => (reference_point_input.on_commit.callback)(&()),
|
WidgetValueAction::Commit => (reference_point_input.on_commit.callback)(&()),
|
||||||
WidgetValueAction::Update => {
|
WidgetValueAction::Update => {
|
||||||
let update_value = value.as_str().expect("ReferencePointInput update was not of type: u64");
|
let Some(update_value) = value.as_str() else {
|
||||||
|
error!("ReferencePointInput update was not of type: u64");
|
||||||
|
return;
|
||||||
|
};
|
||||||
reference_point_input.value = update_value.into();
|
reference_point_input.value = update_value.into();
|
||||||
(reference_point_input.on_update.callback)(reference_point_input)
|
(reference_point_input.on_update.callback)(reference_point_input)
|
||||||
}
|
}
|
||||||
@@ -294,7 +380,10 @@ impl LayoutMessageHandler {
|
|||||||
}
|
}
|
||||||
Widget::PopoverButton(_) => {}
|
Widget::PopoverButton(_) => {}
|
||||||
Widget::RadioInput(radio_input) => {
|
Widget::RadioInput(radio_input) => {
|
||||||
let update_value = value.as_u64().expect("RadioInput update was not of type: u64");
|
let Some(update_value) = value.as_u64() else {
|
||||||
|
error!("RadioInput update was not of type: u64");
|
||||||
|
return;
|
||||||
|
};
|
||||||
radio_input.selected_index = Some(update_value as u32);
|
radio_input.selected_index = Some(update_value as u32);
|
||||||
let callback_message = match action {
|
let callback_message = match action {
|
||||||
WidgetValueAction::Commit => (radio_input.entries[update_value as usize].on_commit.callback)(&()),
|
WidgetValueAction::Commit => (radio_input.entries[update_value as usize].on_commit.callback)(&()),
|
||||||
@@ -308,7 +397,10 @@ impl LayoutMessageHandler {
|
|||||||
let callback_message = match action {
|
let callback_message = match action {
|
||||||
WidgetValueAction::Commit => (text_area_input.on_commit.callback)(&()),
|
WidgetValueAction::Commit => (text_area_input.on_commit.callback)(&()),
|
||||||
WidgetValueAction::Update => {
|
WidgetValueAction::Update => {
|
||||||
let update_value = value.as_str().expect("TextAreaInput update was not of type: string");
|
let Some(update_value) = value.as_str() else {
|
||||||
|
error!("TextAreaInput update was not of type: string");
|
||||||
|
return;
|
||||||
|
};
|
||||||
text_area_input.value = update_value.into();
|
text_area_input.value = update_value.into();
|
||||||
(text_area_input.on_update.callback)(text_area_input)
|
(text_area_input.on_update.callback)(text_area_input)
|
||||||
}
|
}
|
||||||
@@ -328,7 +420,10 @@ impl LayoutMessageHandler {
|
|||||||
let callback_message = match action {
|
let callback_message = match action {
|
||||||
WidgetValueAction::Commit => (text_input.on_commit.callback)(&()),
|
WidgetValueAction::Commit => (text_input.on_commit.callback)(&()),
|
||||||
WidgetValueAction::Update => {
|
WidgetValueAction::Update => {
|
||||||
let update_value = value.as_str().expect("TextInput update was not of type: string");
|
let Some(update_value) = value.as_str() else {
|
||||||
|
error!("TextInput update was not of type: string");
|
||||||
|
return;
|
||||||
|
};
|
||||||
text_input.value = update_value.into();
|
text_input.value = update_value.into();
|
||||||
(text_input.on_update.callback)(text_input)
|
(text_input.on_update.callback)(text_input)
|
||||||
}
|
}
|
||||||
@@ -340,54 +435,7 @@ impl LayoutMessageHandler {
|
|||||||
Widget::WorkingColorsInput(_) => {}
|
Widget::WorkingColorsInput(_) => {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn custom_data() -> MessageData {
|
|
||||||
// TODO: When <https://github.com/dtolnay/proc-macro2/issues/503> is resolved and released,
|
|
||||||
// TODO: use <https://doc.rust-lang.org/stable/proc_macro/struct.Span.html#method.line> to get
|
|
||||||
// TODO: the line number instead of hardcoding it to the magic number on the following line.
|
|
||||||
// TODO: Also, utilize the line number in the actual output, since it is currently unused.
|
|
||||||
MessageData::new(String::from("Function"), vec![(String::from("Fn(&MessageDiscriminant) -> Option<KeysGroup>"), 350)], file!())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[message_handler_data(CustomData)]
|
|
||||||
impl<F: Fn(&MessageDiscriminant) -> Option<KeysGroup>> MessageHandler<LayoutMessage, F> for LayoutMessageHandler {
|
|
||||||
fn process_message(&mut self, message: LayoutMessage, responses: &mut std::collections::VecDeque<Message>, action_input_mapping: F) {
|
|
||||||
match message {
|
|
||||||
LayoutMessage::ResendActiveWidget { layout_target, widget_id } => {
|
|
||||||
// Find the updated diff based on the specified layout target
|
|
||||||
let Some(diff) = (match &self.layouts[layout_target as usize] {
|
|
||||||
Layout::MenuLayout(_) => return,
|
|
||||||
Layout::WidgetLayout(layout) => Self::get_widget_path(layout, widget_id).map(|(widget, widget_path)| {
|
|
||||||
// Create a widget update diff for the relevant id
|
|
||||||
let new_value = DiffUpdate::Widget(widget.clone());
|
|
||||||
WidgetDiff { widget_path, new_value }
|
|
||||||
}),
|
|
||||||
}) else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
// Resend that diff
|
|
||||||
self.send_diff(vec![diff], layout_target, responses, &action_input_mapping);
|
|
||||||
}
|
|
||||||
LayoutMessage::SendLayout { layout, layout_target } => {
|
|
||||||
self.diff_and_send_layout_to_frontend(layout_target, layout, responses, &action_input_mapping);
|
|
||||||
}
|
|
||||||
LayoutMessage::WidgetValueCommit { layout_target, widget_id, value } => {
|
|
||||||
self.handle_widget_callback(layout_target, widget_id, value, WidgetValueAction::Commit, responses);
|
|
||||||
}
|
|
||||||
LayoutMessage::WidgetValueUpdate { layout_target, widget_id, value } => {
|
|
||||||
self.handle_widget_callback(layout_target, widget_id, value, WidgetValueAction::Update, responses);
|
|
||||||
responses.add(LayoutMessage::ResendActiveWidget { layout_target, widget_id });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn actions(&self) -> ActionList {
|
|
||||||
actions!(LayoutMessageDiscriminant;)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LayoutMessageHandler {
|
|
||||||
/// Diff the update and send to the frontend where necessary
|
/// Diff the update and send to the frontend where necessary
|
||||||
fn diff_and_send_layout_to_frontend(
|
fn diff_and_send_layout_to_frontend(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -419,10 +467,11 @@ impl LayoutMessageHandler {
|
|||||||
self.layouts[layout_target as usize] = new_layout;
|
self.layouts[layout_target as usize] = new_layout;
|
||||||
|
|
||||||
// Update the UI
|
// Update the UI
|
||||||
responses.add(FrontendMessage::UpdateMenuBarLayout {
|
let Some(layout) = self.layouts[layout_target as usize].clone().as_menu_layout(action_input_mapping).map(|x| x.layout) else {
|
||||||
layout_target,
|
error!("Called unwrap_menu_layout on a widget layout");
|
||||||
layout: self.layouts[layout_target as usize].clone().unwrap_menu_layout(action_input_mapping).layout,
|
return;
|
||||||
});
|
};
|
||||||
|
responses.add(FrontendMessage::UpdateMenuBarLayout { layout_target, layout });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -453,3 +502,8 @@ impl LayoutMessageHandler {
|
|||||||
responses.add(message);
|
responses.add(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum WidgetValueAction {
|
||||||
|
Commit,
|
||||||
|
Update,
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
mod layout_message;
|
mod layout_message;
|
||||||
mod layout_message_handler;
|
pub mod layout_message_handler;
|
||||||
|
|
||||||
pub mod utility_types;
|
pub mod utility_types;
|
||||||
|
|
||||||
|
|||||||
@@ -109,14 +109,14 @@ pub enum Layout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Layout {
|
impl Layout {
|
||||||
pub fn unwrap_menu_layout(self, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Option<KeysGroup>) -> MenuLayout {
|
pub fn as_menu_layout(self, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Option<KeysGroup>) -> Option<MenuLayout> {
|
||||||
if let Self::MenuLayout(mut menu) = self {
|
if let Self::MenuLayout(mut menu) = self {
|
||||||
menu.layout
|
menu.layout
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.for_each(|menu_column| menu_column.children.fill_in_shortcut_actions_with_keys(action_input_mapping));
|
.for_each(|menu_column| menu_column.children.fill_in_shortcut_actions_with_keys(action_input_mapping));
|
||||||
menu
|
Some(menu)
|
||||||
} else {
|
} else {
|
||||||
panic!("Called unwrap_menu_layout on a widget layout");
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -332,6 +332,9 @@ pub enum NumberInputMode {
|
|||||||
pub struct NodeCatalog {
|
pub struct NodeCatalog {
|
||||||
pub disabled: bool,
|
pub disabled: bool,
|
||||||
|
|
||||||
|
#[serde(rename = "initialSearchTerm")]
|
||||||
|
pub intial_search: String,
|
||||||
|
|
||||||
// Callbacks
|
// Callbacks
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
#[derivative(Debug = "ignore", PartialEq = "ignore")]
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
use graphene_std::renderer::RenderMetadata;
|
||||||
use graphite_proc_macros::*;
|
use graphite_proc_macros::*;
|
||||||
|
|
||||||
#[impl_message]
|
#[impl_message]
|
||||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
NoOp,
|
// Sub-messages
|
||||||
Init,
|
|
||||||
Batched(Box<[Message]>),
|
|
||||||
StartBuffer,
|
|
||||||
EndBuffer(graphene_std::renderer::RenderMetadata),
|
|
||||||
|
|
||||||
#[child]
|
#[child]
|
||||||
Animation(AnimationMessage),
|
Animation(AnimationMessage),
|
||||||
#[child]
|
#[child]
|
||||||
@@ -36,6 +32,16 @@ pub enum Message {
|
|||||||
Tool(ToolMessage),
|
Tool(ToolMessage),
|
||||||
#[child]
|
#[child]
|
||||||
Workspace(WorkspaceMessage),
|
Workspace(WorkspaceMessage),
|
||||||
|
|
||||||
|
// Messages
|
||||||
|
NoOp,
|
||||||
|
Batched {
|
||||||
|
messages: Box<[Message]>,
|
||||||
|
},
|
||||||
|
StartBuffer,
|
||||||
|
EndBuffer {
|
||||||
|
render_metadata: RenderMetadata,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provides an impl of `specta::Type` for `MessageDiscriminant`, the struct created by `impl_message`.
|
/// Provides an impl of `specta::Type` for `MessageDiscriminant`, the struct created by `impl_message`.
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ use crate::consts::{ASYMPTOTIC_EFFECT, COLOR_OVERLAY_GRAY, DEFAULT_DOCUMENT_NAME
|
|||||||
use crate::messages::input_mapper::utility_types::macros::action_keys;
|
use crate::messages::input_mapper::utility_types::macros::action_keys;
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
|
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
|
||||||
use crate::messages::portfolio::document::node_graph::NodeGraphHandlerData;
|
use crate::messages::portfolio::document::node_graph::NodeGraphMessageContext;
|
||||||
use crate::messages::portfolio::document::overlays::grid_overlays::{grid_overlay, overlay_options};
|
use crate::messages::portfolio::document::overlays::grid_overlays::{grid_overlay, overlay_options};
|
||||||
use crate::messages::portfolio::document::overlays::utility_types::{OverlaysType, OverlaysVisibilitySettings};
|
use crate::messages::portfolio::document::overlays::utility_types::{OverlaysType, OverlaysVisibilitySettings};
|
||||||
use crate::messages::portfolio::document::properties_panel::utility_types::PropertiesPanelMessageHandlerData;
|
use crate::messages::portfolio::document::properties_panel::properties_panel_message_handler::PropertiesPanelMessageContext;
|
||||||
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
|
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
|
||||||
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, DocumentMode, FlipAxis, PTZ};
|
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, DocumentMode, FlipAxis, PTZ};
|
||||||
use crate::messages::portfolio::document::utility_types::network_interface::{FlowType, InputConnector, NodeTemplate};
|
use crate::messages::portfolio::document::utility_types::network_interface::{FlowType, InputConnector, NodeTemplate};
|
||||||
@@ -39,7 +39,7 @@ use graphene_std::vector::style::ViewMode;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
#[derive(ExtractField)]
|
#[derive(ExtractField)]
|
||||||
pub struct DocumentMessageData<'a> {
|
pub struct DocumentMessageContext<'a> {
|
||||||
pub document_id: DocumentId,
|
pub document_id: DocumentId,
|
||||||
pub ipp: &'a InputPreprocessorMessageHandler,
|
pub ipp: &'a InputPreprocessorMessageHandler,
|
||||||
pub persistent_data: &'a PersistentData,
|
pub persistent_data: &'a PersistentData,
|
||||||
@@ -170,9 +170,9 @@ impl Default for DocumentMessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessageHandler {
|
impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMessageHandler {
|
||||||
fn process_message(&mut self, message: DocumentMessage, responses: &mut VecDeque<Message>, data: DocumentMessageData) {
|
fn process_message(&mut self, message: DocumentMessage, responses: &mut VecDeque<Message>, context: DocumentMessageContext) {
|
||||||
let DocumentMessageData {
|
let DocumentMessageContext {
|
||||||
document_id,
|
document_id,
|
||||||
ipp,
|
ipp,
|
||||||
persistent_data,
|
persistent_data,
|
||||||
@@ -180,28 +180,21 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||||||
current_tool,
|
current_tool,
|
||||||
preferences,
|
preferences,
|
||||||
device_pixel_ratio,
|
device_pixel_ratio,
|
||||||
} = data;
|
} = context;
|
||||||
|
|
||||||
let selected_nodes_bounding_box_viewport = self.network_interface.selected_nodes_bounding_box_viewport(&self.breadcrumb_network_path);
|
|
||||||
let selected_visible_layers_bounding_box_viewport = self.selected_visible_layers_bounding_box_viewport();
|
|
||||||
match message {
|
match message {
|
||||||
// Sub-messages
|
// Sub-messages
|
||||||
DocumentMessage::Navigation(message) => {
|
DocumentMessage::Navigation(message) => {
|
||||||
let data = NavigationMessageData {
|
let context = NavigationMessageContext {
|
||||||
network_interface: &mut self.network_interface,
|
network_interface: &mut self.network_interface,
|
||||||
breadcrumb_network_path: &self.breadcrumb_network_path,
|
breadcrumb_network_path: &self.breadcrumb_network_path,
|
||||||
ipp,
|
ipp,
|
||||||
selection_bounds: if self.graph_view_overlay_open {
|
|
||||||
selected_nodes_bounding_box_viewport
|
|
||||||
} else {
|
|
||||||
selected_visible_layers_bounding_box_viewport
|
|
||||||
},
|
|
||||||
document_ptz: &mut self.document_ptz,
|
document_ptz: &mut self.document_ptz,
|
||||||
graph_view_overlay_open: self.graph_view_overlay_open,
|
graph_view_overlay_open: self.graph_view_overlay_open,
|
||||||
preferences,
|
preferences,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.navigation_handler.process_message(message, responses, data);
|
self.navigation_handler.process_message(message, responses, context);
|
||||||
}
|
}
|
||||||
DocumentMessage::Overlays(message) => {
|
DocumentMessage::Overlays(message) => {
|
||||||
let visibility_settings = self.overlays_visibility_settings;
|
let visibility_settings = self.overlays_visibility_settings;
|
||||||
@@ -210,7 +203,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||||||
self.overlays_message_handler.process_message(
|
self.overlays_message_handler.process_message(
|
||||||
message,
|
message,
|
||||||
responses,
|
responses,
|
||||||
OverlaysMessageData {
|
OverlaysMessageContext {
|
||||||
visibility_settings,
|
visibility_settings,
|
||||||
ipp,
|
ipp,
|
||||||
device_pixel_ratio,
|
device_pixel_ratio,
|
||||||
@@ -218,20 +211,20 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
DocumentMessage::PropertiesPanel(message) => {
|
DocumentMessage::PropertiesPanel(message) => {
|
||||||
let properties_panel_message_handler_data = PropertiesPanelMessageHandlerData {
|
let context = PropertiesPanelMessageContext {
|
||||||
network_interface: &mut self.network_interface,
|
network_interface: &mut self.network_interface,
|
||||||
selection_network_path: &self.selection_network_path,
|
selection_network_path: &self.selection_network_path,
|
||||||
document_name: self.name.as_str(),
|
document_name: self.name.as_str(),
|
||||||
executor,
|
executor,
|
||||||
|
persistent_data,
|
||||||
};
|
};
|
||||||
self.properties_panel_message_handler
|
self.properties_panel_message_handler.process_message(message, responses, context);
|
||||||
.process_message(message, responses, (persistent_data, properties_panel_message_handler_data));
|
|
||||||
}
|
}
|
||||||
DocumentMessage::NodeGraph(message) => {
|
DocumentMessage::NodeGraph(message) => {
|
||||||
self.node_graph_handler.process_message(
|
self.node_graph_handler.process_message(
|
||||||
message,
|
message,
|
||||||
responses,
|
responses,
|
||||||
NodeGraphHandlerData {
|
NodeGraphMessageContext {
|
||||||
network_interface: &mut self.network_interface,
|
network_interface: &mut self.network_interface,
|
||||||
selection_network_path: &self.selection_network_path,
|
selection_network_path: &self.selection_network_path,
|
||||||
breadcrumb_network_path: &self.breadcrumb_network_path,
|
breadcrumb_network_path: &self.breadcrumb_network_path,
|
||||||
@@ -246,20 +239,20 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
DocumentMessage::GraphOperation(message) => {
|
DocumentMessage::GraphOperation(message) => {
|
||||||
let data = GraphOperationMessageData {
|
let context = GraphOperationMessageContext {
|
||||||
network_interface: &mut self.network_interface,
|
network_interface: &mut self.network_interface,
|
||||||
collapsed: &mut self.collapsed,
|
collapsed: &mut self.collapsed,
|
||||||
node_graph: &mut self.node_graph_handler,
|
node_graph: &mut self.node_graph_handler,
|
||||||
};
|
};
|
||||||
let mut graph_operation_message_handler = GraphOperationMessageHandler {};
|
let mut graph_operation_message_handler = GraphOperationMessageHandler {};
|
||||||
graph_operation_message_handler.process_message(message, responses, data);
|
graph_operation_message_handler.process_message(message, responses, context);
|
||||||
}
|
}
|
||||||
DocumentMessage::AlignSelectedLayers { axis, aggregate } => {
|
DocumentMessage::AlignSelectedLayers { axis, aggregate } => {
|
||||||
let axis = match axis {
|
let axis = match axis {
|
||||||
AlignAxis::X => DVec2::X,
|
AlignAxis::X => DVec2::X,
|
||||||
AlignAxis::Y => DVec2::Y,
|
AlignAxis::Y => DVec2::Y,
|
||||||
};
|
};
|
||||||
let Some(combined_box) = self.selected_visible_layers_bounding_box_viewport() else {
|
let Some(combined_box) = self.network_interface.selected_layers_artwork_bounding_box_viewport() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -486,7 +479,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||||||
FlipAxis::X => DVec2::new(-1., 1.),
|
FlipAxis::X => DVec2::new(-1., 1.),
|
||||||
FlipAxis::Y => DVec2::new(1., -1.),
|
FlipAxis::Y => DVec2::new(1., -1.),
|
||||||
};
|
};
|
||||||
if let Some([min, max]) = self.selected_visible_and_unlock_layers_bounding_box_viewport() {
|
if let Some([min, max]) = self.network_interface.selected_unlocked_layers_bounding_box_viewport() {
|
||||||
let center = (max + min) / 2.;
|
let center = (max + min) / 2.;
|
||||||
let bbox_trans = DAffine2::from_translation(-center);
|
let bbox_trans = DAffine2::from_translation(-center);
|
||||||
let mut added_transaction = false;
|
let mut added_transaction = false;
|
||||||
@@ -506,7 +499,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||||||
}
|
}
|
||||||
DocumentMessage::RotateSelectedLayers { degrees } => {
|
DocumentMessage::RotateSelectedLayers { degrees } => {
|
||||||
// Get the bounding box of selected layers in viewport space
|
// Get the bounding box of selected layers in viewport space
|
||||||
if let Some([min, max]) = self.selected_visible_and_unlock_layers_bounding_box_viewport() {
|
if let Some([min, max]) = self.network_interface.selected_unlocked_layers_bounding_box_viewport() {
|
||||||
// Calculate the center of the bounding box to use as rotation pivot
|
// Calculate the center of the bounding box to use as rotation pivot
|
||||||
let center = (max + min) / 2.;
|
let center = (max + min) / 2.;
|
||||||
// Transform that moves pivot point to origin
|
// Transform that moves pivot point to origin
|
||||||
@@ -1063,13 +1056,13 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||||||
self.selected_layers_reorder(relative_index_offset, responses);
|
self.selected_layers_reorder(relative_index_offset, responses);
|
||||||
}
|
}
|
||||||
DocumentMessage::ClipLayer { id } => {
|
DocumentMessage::ClipLayer { id } => {
|
||||||
let layer = LayerNodeIdentifier::new(id, &self.network_interface, &[]);
|
let layer = LayerNodeIdentifier::new(id, &self.network_interface);
|
||||||
|
|
||||||
responses.add(DocumentMessage::AddTransaction);
|
responses.add(DocumentMessage::AddTransaction);
|
||||||
responses.add(GraphOperationMessage::ClipModeToggle { layer });
|
responses.add(GraphOperationMessage::ClipModeToggle { layer });
|
||||||
}
|
}
|
||||||
DocumentMessage::SelectLayer { id, ctrl, shift } => {
|
DocumentMessage::SelectLayer { id, ctrl, shift } => {
|
||||||
let layer = LayerNodeIdentifier::new(id, &self.network_interface, &[]);
|
let layer = LayerNodeIdentifier::new(id, &self.network_interface);
|
||||||
|
|
||||||
let mut nodes = vec![];
|
let mut nodes = vec![];
|
||||||
|
|
||||||
@@ -1266,7 +1259,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||||||
responses.add(OverlaysMessage::Draw);
|
responses.add(OverlaysMessage::Draw);
|
||||||
}
|
}
|
||||||
DocumentMessage::ToggleLayerExpansion { id, recursive } => {
|
DocumentMessage::ToggleLayerExpansion { id, recursive } => {
|
||||||
let layer = LayerNodeIdentifier::new(id, &self.network_interface, &[]);
|
let layer = LayerNodeIdentifier::new(id, &self.network_interface);
|
||||||
let metadata = self.metadata();
|
let metadata = self.metadata();
|
||||||
|
|
||||||
let is_collapsed = self.collapsed.0.contains(&layer);
|
let is_collapsed = self.collapsed.0.contains(&layer);
|
||||||
@@ -1323,7 +1316,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||||||
self.network_interface.document_network().nodes.contains_key(node_id))
|
self.network_interface.document_network().nodes.contains_key(node_id))
|
||||||
.filter_map(|(node_id, click_targets)| {
|
.filter_map(|(node_id, click_targets)| {
|
||||||
self.network_interface.is_layer(&node_id, &[]).then(|| {
|
self.network_interface.is_layer(&node_id, &[]).then(|| {
|
||||||
let layer = LayerNodeIdentifier::new(node_id, &self.network_interface, &[]);
|
let layer = LayerNodeIdentifier::new(node_id, &self.network_interface);
|
||||||
(layer, click_targets)
|
(layer, click_targets)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -1683,6 +1676,11 @@ impl DocumentMessageHandler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn click_list_no_parents<'a>(&'a self, ipp: &InputPreprocessorMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + use<'a> {
|
||||||
|
self.click_xray(ipp)
|
||||||
|
.filter(move |&layer| !self.network_interface.is_artboard(&layer.to_node(), &[]) && !layer.has_children(self.network_interface.document_metadata()))
|
||||||
|
}
|
||||||
|
|
||||||
/// Find the deepest layer that has been clicked on from a location in viewport space.
|
/// Find the deepest layer that has been clicked on from a location in viewport space.
|
||||||
pub fn click(&self, ipp: &InputPreprocessorMessageHandler) -> Option<LayerNodeIdentifier> {
|
pub fn click(&self, ipp: &InputPreprocessorMessageHandler) -> Option<LayerNodeIdentifier> {
|
||||||
self.click_list(ipp).last()
|
self.click_list(ipp).last()
|
||||||
@@ -1703,31 +1701,6 @@ impl DocumentMessageHandler {
|
|||||||
.last()
|
.last()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the combined bounding box of the click targets of the selected visible layers in viewport space
|
|
||||||
pub fn selected_visible_layers_bounding_box_viewport(&self) -> Option<[DVec2; 2]> {
|
|
||||||
self.network_interface
|
|
||||||
.selected_nodes()
|
|
||||||
.selected_visible_layers(&self.network_interface)
|
|
||||||
.filter_map(|layer| self.metadata().bounding_box_viewport(layer))
|
|
||||||
.reduce(graphene_std::renderer::Quad::combine_bounds)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn selected_visible_and_unlock_layers_bounding_box_viewport(&self) -> Option<[DVec2; 2]> {
|
|
||||||
self.network_interface
|
|
||||||
.selected_nodes()
|
|
||||||
.selected_visible_and_unlocked_layers(&self.network_interface)
|
|
||||||
.filter_map(|layer| self.metadata().bounding_box_viewport(layer))
|
|
||||||
.reduce(graphene_std::renderer::Quad::combine_bounds)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn selected_visible_and_unlock_layers_bounding_box_document(&self) -> Option<[DVec2; 2]> {
|
|
||||||
self.network_interface
|
|
||||||
.selected_nodes()
|
|
||||||
.selected_visible_and_unlocked_layers(&self.network_interface)
|
|
||||||
.map(|layer| self.metadata().nonzero_bounding_box(layer))
|
|
||||||
.reduce(graphene_std::renderer::Quad::combine_bounds)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn document_network(&self) -> &NodeNetwork {
|
pub fn document_network(&self) -> &NodeNetwork {
|
||||||
self.network_interface.document_network()
|
self.network_interface.document_network()
|
||||||
}
|
}
|
||||||
@@ -2736,7 +2709,22 @@ impl DocumentMessageHandler {
|
|||||||
.tooltip("Add an operation to the end of this layer's chain of nodes")
|
.tooltip("Add an operation to the end of this layer's chain of nodes")
|
||||||
.disabled(!has_selection || has_multiple_selection)
|
.disabled(!has_selection || has_multiple_selection)
|
||||||
.popover_layout({
|
.popover_layout({
|
||||||
let node_chooser = NodeCatalog::new()
|
// Showing only compatible types
|
||||||
|
let compatible_type = selected_layer.and_then(|layer| {
|
||||||
|
let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, &self.network_interface);
|
||||||
|
let node_type = graph_layer.horizontal_layer_flow().nth(1);
|
||||||
|
if let Some(node_id) = node_type {
|
||||||
|
let (output_type, _) = self.network_interface.output_type(&node_id, 0, &self.selection_network_path);
|
||||||
|
Some(format!("type:{}", output_type.nested_type()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut node_chooser = NodeCatalog::new();
|
||||||
|
node_chooser.intial_search = compatible_type.unwrap_or("".to_string());
|
||||||
|
|
||||||
|
let node_chooser = node_chooser
|
||||||
.on_update(move |node_type| {
|
.on_update(move |node_type| {
|
||||||
if let Some(layer) = selected_layer {
|
if let Some(layer) = selected_layer {
|
||||||
NodeGraphMessage::CreateNodeInLayerWithTransaction {
|
NodeGraphMessage::CreateNodeInLayerWithTransaction {
|
||||||
|
|||||||
+12
-12
@@ -14,15 +14,8 @@ use graphene_std::renderer::convert_usvg_path::convert_usvg_path;
|
|||||||
use graphene_std::text::{Font, TypesettingConfig};
|
use graphene_std::text::{Font, TypesettingConfig};
|
||||||
use graphene_std::vector::style::{Fill, Gradient, GradientStops, GradientType, PaintOrder, Stroke, StrokeAlign, StrokeCap, StrokeJoin};
|
use graphene_std::vector::style::{Fill, Gradient, GradientStops, GradientType, PaintOrder, Stroke, StrokeAlign, StrokeCap, StrokeJoin};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
struct ArtboardInfo {
|
|
||||||
input_node: NodeInput,
|
|
||||||
output_nodes: Vec<InputConnector>,
|
|
||||||
merge_node: NodeId,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(ExtractField)]
|
#[derive(ExtractField)]
|
||||||
pub struct GraphOperationMessageData<'a> {
|
pub struct GraphOperationMessageContext<'a> {
|
||||||
pub network_interface: &'a mut NodeNetworkInterface,
|
pub network_interface: &'a mut NodeNetworkInterface,
|
||||||
pub collapsed: &'a mut CollapsedLayers,
|
pub collapsed: &'a mut CollapsedLayers,
|
||||||
pub node_graph: &'a mut NodeGraphMessageHandler,
|
pub node_graph: &'a mut NodeGraphMessageHandler,
|
||||||
@@ -34,9 +27,9 @@ pub struct GraphOperationMessageHandler {}
|
|||||||
// GraphOperationMessageHandler always modified the document network. This is so changes to the layers panel will only affect the document network.
|
// GraphOperationMessageHandler always modified the document network. This is so changes to the layers panel will only affect the document network.
|
||||||
// For changes to the selected network, use NodeGraphMessageHandler. No NodeGraphMessage's should be added here, since they will affect the selected nested network.
|
// For changes to the selected network, use NodeGraphMessageHandler. No NodeGraphMessage's should be added here, since they will affect the selected nested network.
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for GraphOperationMessageHandler {
|
impl MessageHandler<GraphOperationMessage, GraphOperationMessageContext<'_>> for GraphOperationMessageHandler {
|
||||||
fn process_message(&mut self, message: GraphOperationMessage, responses: &mut VecDeque<Message>, data: GraphOperationMessageData) {
|
fn process_message(&mut self, message: GraphOperationMessage, responses: &mut VecDeque<Message>, context: GraphOperationMessageContext) {
|
||||||
let network_interface = data.network_interface;
|
let network_interface = context.network_interface;
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
GraphOperationMessage::FillSet { layer, fill } => {
|
GraphOperationMessage::FillSet { layer, fill } => {
|
||||||
@@ -126,7 +119,7 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
|
|||||||
let primary_input = artboard.inputs.first().expect("Artboard should have a primary input").clone();
|
let primary_input = artboard.inputs.first().expect("Artboard should have a primary input").clone();
|
||||||
if let NodeInput::Node { node_id, .. } = &primary_input {
|
if let NodeInput::Node { node_id, .. } = &primary_input {
|
||||||
if network_interface.is_layer(node_id, &[]) && !network_interface.is_artboard(node_id, &[]) {
|
if network_interface.is_layer(node_id, &[]) && !network_interface.is_artboard(node_id, &[]) {
|
||||||
network_interface.move_layer_to_stack(LayerNodeIdentifier::new(*node_id, network_interface, &[]), artboard_layer, 0, &[]);
|
network_interface.move_layer_to_stack(LayerNodeIdentifier::new(*node_id, network_interface), artboard_layer, 0, &[]);
|
||||||
} else {
|
} else {
|
||||||
network_interface.disconnect_input(&InputConnector::node(artboard_layer.to_node(), 0), &[]);
|
network_interface.disconnect_input(&InputConnector::node(artboard_layer.to_node(), 0), &[]);
|
||||||
network_interface.set_input(&InputConnector::node(id, 0), primary_input, &[]);
|
network_interface.set_input(&InputConnector::node(id, 0), primary_input, &[]);
|
||||||
@@ -323,6 +316,13 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct ArtboardInfo {
|
||||||
|
input_node: NodeInput,
|
||||||
|
output_nodes: Vec<InputConnector>,
|
||||||
|
merge_node: NodeId,
|
||||||
|
}
|
||||||
|
|
||||||
fn usvg_color(c: usvg::Color, a: f32) -> Color {
|
fn usvg_color(c: usvg::Color, a: f32) -> Color {
|
||||||
Color::from_rgbaf32_unchecked(c.red as f32 / 255., c.green as f32 / 255., c.blue as f32 / 255., a)
|
Color::from_rgbaf32_unchecked(c.red as f32 / 255., c.green as f32 / 255., c.blue as f32 / 255., a)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ impl<'a> ModifyInputsContext<'a> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let layer_input_connector = post_node_input_connector.clone();
|
let layer_input_connector = post_node_input_connector;
|
||||||
|
|
||||||
// Sink post_node down to the end of the non layer chain that feeds into post_node, such that pre_node is the layer node at insert_index + 1, or None if insert_index is the last layer
|
// Sink post_node down to the end of the non layer chain that feeds into post_node, such that pre_node is the layer node at insert_index + 1, or None if insert_index is the last layer
|
||||||
loop {
|
loop {
|
||||||
@@ -124,7 +124,7 @@ impl<'a> ModifyInputsContext<'a> {
|
|||||||
pub fn create_layer(&mut self, new_id: NodeId) -> LayerNodeIdentifier {
|
pub fn create_layer(&mut self, new_id: NodeId) -> LayerNodeIdentifier {
|
||||||
let new_merge_node = resolve_document_node_type("Merge").expect("Merge node").default_node_template();
|
let new_merge_node = resolve_document_node_type("Merge").expect("Merge node").default_node_template();
|
||||||
self.network_interface.insert_node(new_id, new_merge_node, &[]);
|
self.network_interface.insert_node(new_id, new_merge_node, &[]);
|
||||||
LayerNodeIdentifier::new(new_id, self.network_interface, &[])
|
LayerNodeIdentifier::new(new_id, self.network_interface)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an artboard as the primary export for the document network
|
/// Creates an artboard as the primary export for the document network
|
||||||
@@ -132,13 +132,13 @@ impl<'a> ModifyInputsContext<'a> {
|
|||||||
let artboard_node_template = resolve_document_node_type("Artboard").expect("Node").node_template_input_override([
|
let artboard_node_template = resolve_document_node_type("Artboard").expect("Node").node_template_input_override([
|
||||||
Some(NodeInput::value(TaggedValue::ArtboardGroup(graphene_std::ArtboardGroupTable::default()), true)),
|
Some(NodeInput::value(TaggedValue::ArtboardGroup(graphene_std::ArtboardGroupTable::default()), true)),
|
||||||
Some(NodeInput::value(TaggedValue::GraphicGroup(graphene_std::GraphicGroupTable::default()), true)),
|
Some(NodeInput::value(TaggedValue::GraphicGroup(graphene_std::GraphicGroupTable::default()), true)),
|
||||||
Some(NodeInput::value(TaggedValue::IVec2(artboard.location), false)),
|
Some(NodeInput::value(TaggedValue::DVec2(artboard.location.into()), false)),
|
||||||
Some(NodeInput::value(TaggedValue::IVec2(artboard.dimensions), false)),
|
Some(NodeInput::value(TaggedValue::DVec2(artboard.dimensions.into()), false)),
|
||||||
Some(NodeInput::value(TaggedValue::Color(artboard.background), false)),
|
Some(NodeInput::value(TaggedValue::Color(artboard.background), false)),
|
||||||
Some(NodeInput::value(TaggedValue::Bool(artboard.clip), false)),
|
Some(NodeInput::value(TaggedValue::Bool(artboard.clip), false)),
|
||||||
]);
|
]);
|
||||||
self.network_interface.insert_node(new_id, artboard_node_template, &[]);
|
self.network_interface.insert_node(new_id, artboard_node_template, &[]);
|
||||||
LayerNodeIdentifier::new(new_id, self.network_interface, &[])
|
LayerNodeIdentifier::new(new_id, self.network_interface)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_boolean_data(&mut self, operation: graphene_std::path_bool::BooleanOperation, layer: LayerNodeIdentifier) {
|
pub fn insert_boolean_data(&mut self, operation: graphene_std::path_bool::BooleanOperation, layer: LayerNodeIdentifier) {
|
||||||
@@ -236,7 +236,7 @@ impl<'a> ModifyInputsContext<'a> {
|
|||||||
self.layer_node.or_else(|| {
|
self.layer_node.or_else(|| {
|
||||||
let export_node = self.network_interface.document_network().exports.first().and_then(|export| export.as_node())?;
|
let export_node = self.network_interface.document_network().exports.first().and_then(|export| export.as_node())?;
|
||||||
if self.network_interface.is_layer(&export_node, &[]) {
|
if self.network_interface.is_layer(&export_node, &[]) {
|
||||||
Some(LayerNodeIdentifier::new(export_node, self.network_interface, &[]))
|
Some(LayerNodeIdentifier::new(export_node, self.network_interface))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -486,8 +486,8 @@ impl<'a> ModifyInputsContext<'a> {
|
|||||||
dimensions.y *= -1;
|
dimensions.y *= -1;
|
||||||
location.y -= dimensions.y;
|
location.y -= dimensions.y;
|
||||||
}
|
}
|
||||||
self.set_input_with_refresh(InputConnector::node(artboard_node_id, 2), NodeInput::value(TaggedValue::IVec2(location), false), false);
|
self.set_input_with_refresh(InputConnector::node(artboard_node_id, 2), NodeInput::value(TaggedValue::DVec2(location.into()), false), false);
|
||||||
self.set_input_with_refresh(InputConnector::node(artboard_node_id, 3), NodeInput::value(TaggedValue::IVec2(dimensions), false), false);
|
self.set_input_with_refresh(InputConnector::node(artboard_node_id, 3), NodeInput::value(TaggedValue::DVec2(dimensions.into()), false), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the input, refresh the properties panel, and run the document graph if skip_rerender is false
|
/// Set the input, refresh the properties panel, and run the document graph if skip_rerender is false
|
||||||
|
|||||||
@@ -11,4 +11,4 @@ pub mod utility_types;
|
|||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use document_message::{DocumentMessage, DocumentMessageDiscriminant};
|
pub use document_message::{DocumentMessage, DocumentMessageDiscriminant};
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use document_message_handler::{DocumentMessageData, DocumentMessageHandler};
|
pub use document_message_handler::{DocumentMessageContext, DocumentMessageHandler};
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ pub mod utility_types;
|
|||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use navigation_message::{NavigationMessage, NavigationMessageDiscriminant};
|
pub use navigation_message::{NavigationMessage, NavigationMessageDiscriminant};
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use navigation_message_handler::{NavigationMessageData, NavigationMessageHandler};
|
pub use navigation_message_handler::{NavigationMessageContext, NavigationMessageHandler};
|
||||||
|
|||||||
@@ -14,11 +14,10 @@ use glam::{DAffine2, DVec2};
|
|||||||
use graph_craft::document::NodeId;
|
use graph_craft::document::NodeId;
|
||||||
|
|
||||||
#[derive(ExtractField)]
|
#[derive(ExtractField)]
|
||||||
pub struct NavigationMessageData<'a> {
|
pub struct NavigationMessageContext<'a> {
|
||||||
pub network_interface: &'a mut NodeNetworkInterface,
|
pub network_interface: &'a mut NodeNetworkInterface,
|
||||||
pub breadcrumb_network_path: &'a [NodeId],
|
pub breadcrumb_network_path: &'a [NodeId],
|
||||||
pub ipp: &'a InputPreprocessorMessageHandler,
|
pub ipp: &'a InputPreprocessorMessageHandler,
|
||||||
pub selection_bounds: Option<[DVec2; 2]>,
|
|
||||||
pub document_ptz: &'a mut PTZ,
|
pub document_ptz: &'a mut PTZ,
|
||||||
pub graph_view_overlay_open: bool,
|
pub graph_view_overlay_open: bool,
|
||||||
pub preferences: &'a PreferencesMessageHandler,
|
pub preferences: &'a PreferencesMessageHandler,
|
||||||
@@ -33,17 +32,16 @@ pub struct NavigationMessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for NavigationMessageHandler {
|
impl MessageHandler<NavigationMessage, NavigationMessageContext<'_>> for NavigationMessageHandler {
|
||||||
fn process_message(&mut self, message: NavigationMessage, responses: &mut VecDeque<Message>, data: NavigationMessageData) {
|
fn process_message(&mut self, message: NavigationMessage, responses: &mut VecDeque<Message>, context: NavigationMessageContext) {
|
||||||
let NavigationMessageData {
|
let NavigationMessageContext {
|
||||||
network_interface,
|
network_interface,
|
||||||
breadcrumb_network_path,
|
breadcrumb_network_path,
|
||||||
ipp,
|
ipp,
|
||||||
selection_bounds,
|
|
||||||
document_ptz,
|
document_ptz,
|
||||||
graph_view_overlay_open,
|
graph_view_overlay_open,
|
||||||
preferences,
|
preferences,
|
||||||
} = data;
|
} = context;
|
||||||
|
|
||||||
fn get_ptz<'a>(document_ptz: &'a PTZ, network_interface: &'a NodeNetworkInterface, graph_view_overlay_open: bool, breadcrumb_network_path: &[NodeId]) -> Option<&'a PTZ> {
|
fn get_ptz<'a>(document_ptz: &'a PTZ, network_interface: &'a NodeNetworkInterface, graph_view_overlay_open: bool, breadcrumb_network_path: &[NodeId]) -> Option<&'a PTZ> {
|
||||||
if !graph_view_overlay_open {
|
if !graph_view_overlay_open {
|
||||||
@@ -386,9 +384,16 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
|||||||
responses.add(DocumentMessage::PTZUpdate);
|
responses.add(DocumentMessage::PTZUpdate);
|
||||||
responses.add(NodeGraphMessage::SetGridAlignedEdges);
|
responses.add(NodeGraphMessage::SetGridAlignedEdges);
|
||||||
}
|
}
|
||||||
|
// Fully zooms in on the selected
|
||||||
NavigationMessage::FitViewportToSelection => {
|
NavigationMessage::FitViewportToSelection => {
|
||||||
|
let selection_bounds = if graph_view_overlay_open {
|
||||||
|
network_interface.selected_nodes_bounding_box_viewport(breadcrumb_network_path)
|
||||||
|
} else {
|
||||||
|
network_interface.selected_layers_artwork_bounding_box_viewport()
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(bounds) = selection_bounds {
|
if let Some(bounds) = selection_bounds {
|
||||||
let Some(ptz) = get_ptz_mut(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else {
|
let Some(ptz) = get_ptz(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else {
|
||||||
log::error!("Could not get node graph PTZ in FitViewportToSelection");
|
log::error!("Could not get node graph PTZ in FitViewportToSelection");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -378,8 +378,8 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
inputs: vec![
|
inputs: vec![
|
||||||
NodeInput::value(TaggedValue::ArtboardGroup(ArtboardGroupTable::default()), true),
|
NodeInput::value(TaggedValue::ArtboardGroup(ArtboardGroupTable::default()), true),
|
||||||
NodeInput::value(TaggedValue::GraphicGroup(GraphicGroupTable::default()), true),
|
NodeInput::value(TaggedValue::GraphicGroup(GraphicGroupTable::default()), true),
|
||||||
NodeInput::value(TaggedValue::IVec2(glam::IVec2::ZERO), false),
|
NodeInput::value(TaggedValue::DVec2(DVec2::ZERO), false),
|
||||||
NodeInput::value(TaggedValue::IVec2(glam::IVec2::new(1920, 1080)), false),
|
NodeInput::value(TaggedValue::DVec2(DVec2::new(1920., 1080.)), false),
|
||||||
NodeInput::value(TaggedValue::Color(Color::WHITE), false),
|
NodeInput::value(TaggedValue::Color(Color::WHITE), false),
|
||||||
NodeInput::value(TaggedValue::Bool(false), false),
|
NodeInput::value(TaggedValue::Bool(false), false),
|
||||||
],
|
],
|
||||||
@@ -396,6 +396,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
x: "X".to_string(),
|
x: "X".to_string(),
|
||||||
y: "Y".to_string(),
|
y: "Y".to_string(),
|
||||||
unit: " px".to_string(),
|
unit: " px".to_string(),
|
||||||
|
is_integer: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@@ -406,6 +407,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
x: "W".to_string(),
|
x: "W".to_string(),
|
||||||
y: "H".to_string(),
|
y: "H".to_string(),
|
||||||
unit: " px".to_string(),
|
unit: " px".to_string(),
|
||||||
|
is_integer: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@@ -1222,6 +1224,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
NodeInput::value(TaggedValue::OptionalF64(TypesettingConfig::default().max_width), false),
|
NodeInput::value(TaggedValue::OptionalF64(TypesettingConfig::default().max_width), false),
|
||||||
NodeInput::value(TaggedValue::OptionalF64(TypesettingConfig::default().max_height), false),
|
NodeInput::value(TaggedValue::OptionalF64(TypesettingConfig::default().max_height), false),
|
||||||
NodeInput::value(TaggedValue::F64(TypesettingConfig::default().tilt), false),
|
NodeInput::value(TaggedValue::F64(TypesettingConfig::default().tilt), false),
|
||||||
|
NodeInput::value(TaggedValue::Bool(false), false),
|
||||||
],
|
],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
@@ -1281,7 +1284,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
),
|
),
|
||||||
InputMetadata::with_name_description_override(
|
InputMetadata::with_name_description_override(
|
||||||
"Tilt",
|
"Tilt",
|
||||||
"Faux italic",
|
"Faux italic.",
|
||||||
WidgetOverride::Number(NumberInputSettings {
|
WidgetOverride::Number(NumberInputSettings {
|
||||||
min: Some(-85.),
|
min: Some(-85.),
|
||||||
max: Some(85.),
|
max: Some(85.),
|
||||||
@@ -1289,6 +1292,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
("Per-Glyph Instances", "Splits each text glyph into its own instance, i.e. row in the table of vector data.").into(),
|
||||||
],
|
],
|
||||||
output_names: vec!["Vector".to_string()],
|
output_names: vec!["Vector".to_string()],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -1299,11 +1303,11 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
},
|
},
|
||||||
DocumentNodeDefinition {
|
DocumentNodeDefinition {
|
||||||
identifier: "Transform",
|
identifier: "Transform",
|
||||||
category: "General",
|
category: "Math: Transform",
|
||||||
node_template: NodeTemplate {
|
node_template: NodeTemplate {
|
||||||
document_node: DocumentNode {
|
document_node: DocumentNode {
|
||||||
inputs: vec![
|
inputs: vec![
|
||||||
NodeInput::value(TaggedValue::VectorData(VectorDataTable::default()), true),
|
NodeInput::value(TaggedValue::DAffine2(DAffine2::default()), true),
|
||||||
NodeInput::value(TaggedValue::DVec2(DVec2::ZERO), false),
|
NodeInput::value(TaggedValue::DVec2(DVec2::ZERO), false),
|
||||||
NodeInput::value(TaggedValue::F64(0.), false),
|
NodeInput::value(TaggedValue::F64(0.), false),
|
||||||
NodeInput::value(TaggedValue::DVec2(DVec2::ONE), false),
|
NodeInput::value(TaggedValue::DVec2(DVec2::ONE), false),
|
||||||
@@ -1313,7 +1317,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
exports: vec![NodeInput::node(NodeId(1), 0)],
|
exports: vec![NodeInput::node(NodeId(1), 0)],
|
||||||
nodes: [
|
nodes: [
|
||||||
DocumentNode {
|
DocumentNode {
|
||||||
inputs: vec![NodeInput::network(concrete!(VectorDataTable), 0)],
|
inputs: vec![NodeInput::network(generic!(T), 0)],
|
||||||
implementation: DocumentNodeImplementation::ProtoNode(memo::monitor::IDENTIFIER),
|
implementation: DocumentNodeImplementation::ProtoNode(memo::monitor::IDENTIFIER),
|
||||||
manual_composition: Some(generic!(T)),
|
manual_composition: Some(generic!(T)),
|
||||||
skip_deduplication: true,
|
skip_deduplication: true,
|
||||||
@@ -1370,7 +1374,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
input_metadata: vec![
|
input_metadata: vec![
|
||||||
("Vector Data", "TODO").into(),
|
("Value", "TODO").into(),
|
||||||
InputMetadata::with_name_description_override(
|
InputMetadata::with_name_description_override(
|
||||||
"Translation",
|
"Translation",
|
||||||
"TODO",
|
"TODO",
|
||||||
@@ -1951,8 +1955,20 @@ fn static_input_properties() -> InputProperties {
|
|||||||
.network_interface
|
.network_interface
|
||||||
.input_data(&node_id, index, "min", context.selection_network_path)
|
.input_data(&node_id, index, "min", context.selection_network_path)
|
||||||
.and_then(|value| value.as_f64());
|
.and_then(|value| value.as_f64());
|
||||||
|
let is_integer = context
|
||||||
|
.network_interface
|
||||||
|
.input_data(&node_id, index, "is_integer", context.selection_network_path)
|
||||||
|
.and_then(|value| value.as_bool())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
Ok(vec![node_properties::coordinate_widget(ParameterWidgetsInfo::new(node_id, index, true, context), &x, &y, &unit, min)])
|
Ok(vec![node_properties::coordinate_widget(
|
||||||
|
ParameterWidgetsInfo::new(node_id, index, true, context),
|
||||||
|
&x,
|
||||||
|
&y,
|
||||||
|
&unit,
|
||||||
|
min,
|
||||||
|
is_integer,
|
||||||
|
)])
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
map.insert(
|
map.insert(
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ pub enum NodeGraphMessage {
|
|||||||
nodes: Vec<(NodeId, NodeTemplate)>,
|
nodes: Vec<(NodeId, NodeTemplate)>,
|
||||||
new_ids: HashMap<NodeId, NodeId>,
|
new_ids: HashMap<NodeId, NodeId>,
|
||||||
},
|
},
|
||||||
|
AddPathNode,
|
||||||
AddImport,
|
AddImport,
|
||||||
AddExport,
|
AddExport,
|
||||||
Init,
|
Init,
|
||||||
@@ -81,6 +82,9 @@ pub enum NodeGraphMessage {
|
|||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
parent: LayerNodeIdentifier,
|
parent: LayerNodeIdentifier,
|
||||||
},
|
},
|
||||||
|
SetChainPosition {
|
||||||
|
node_id: NodeId,
|
||||||
|
},
|
||||||
PasteNodes {
|
PasteNodes {
|
||||||
serialized_nodes: String,
|
serialized_nodes: String,
|
||||||
},
|
},
|
||||||
@@ -97,6 +101,7 @@ pub enum NodeGraphMessage {
|
|||||||
PointerOutsideViewport {
|
PointerOutsideViewport {
|
||||||
shift: Key,
|
shift: Key,
|
||||||
},
|
},
|
||||||
|
ShakeNode,
|
||||||
RemoveImport {
|
RemoveImport {
|
||||||
import_index: usize,
|
import_index: usize,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -10,25 +10,29 @@ use crate::messages::portfolio::document::node_graph::utility_types::{ContextMen
|
|||||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||||
use crate::messages::portfolio::document::utility_types::misc::GroupFolderType;
|
use crate::messages::portfolio::document::utility_types::misc::GroupFolderType;
|
||||||
use crate::messages::portfolio::document::utility_types::network_interface::{
|
use crate::messages::portfolio::document::utility_types::network_interface::{
|
||||||
self, InputConnector, NodeNetworkInterface, NodeTemplate, NodeTypePersistentMetadata, OutputConnector, Previewing, TypeSource,
|
self, FlowType, InputConnector, NodeNetworkInterface, NodeTemplate, NodeTypePersistentMetadata, OutputConnector, Previewing, TypeSource,
|
||||||
};
|
};
|
||||||
use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers, LayerPanelEntry};
|
use crate::messages::portfolio::document::utility_types::nodes::{CollapsedLayers, LayerPanelEntry};
|
||||||
use crate::messages::portfolio::document::utility_types::wires::{GraphWireStyle, WirePath, WirePathUpdate, build_vector_wire};
|
use crate::messages::portfolio::document::utility_types::wires::{GraphWireStyle, WirePath, WirePathUpdate, build_vector_wire};
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
|
use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
|
||||||
use crate::messages::tool::common_functionality::graph_modification_utils::get_clip_mode;
|
use crate::messages::tool::common_functionality::graph_modification_utils::{self, get_clip_mode};
|
||||||
use crate::messages::tool::tool_messages::tool_prelude::{Key, MouseMotion};
|
use crate::messages::tool::tool_messages::tool_prelude::{Key, MouseMotion};
|
||||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||||
|
use bezier_rs::Subpath;
|
||||||
use glam::{DAffine2, DVec2, IVec2};
|
use glam::{DAffine2, DVec2, IVec2};
|
||||||
|
use graph_craft::document::value::TaggedValue;
|
||||||
use graph_craft::document::{DocumentNodeImplementation, NodeId, NodeInput};
|
use graph_craft::document::{DocumentNodeImplementation, NodeId, NodeInput};
|
||||||
use graph_craft::proto::GraphErrors;
|
use graph_craft::proto::GraphErrors;
|
||||||
use graphene_std::math::math_ext::QuadExt;
|
use graphene_std::math::math_ext::QuadExt;
|
||||||
|
use graphene_std::vector::misc::subpath_to_kurbo_bezpath;
|
||||||
use graphene_std::*;
|
use graphene_std::*;
|
||||||
|
use kurbo::{Line, Point};
|
||||||
use renderer::Quad;
|
use renderer::Quad;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
#[derive(Debug, ExtractField)]
|
#[derive(Debug, ExtractField)]
|
||||||
pub struct NodeGraphHandlerData<'a> {
|
pub struct NodeGraphMessageContext<'a> {
|
||||||
pub network_interface: &'a mut NodeNetworkInterface,
|
pub network_interface: &'a mut NodeNetworkInterface,
|
||||||
pub selection_network_path: &'a [NodeId],
|
pub selection_network_path: &'a [NodeId],
|
||||||
pub breadcrumb_network_path: &'a [NodeId],
|
pub breadcrumb_network_path: &'a [NodeId],
|
||||||
@@ -55,6 +59,8 @@ pub struct NodeGraphMessageHandler {
|
|||||||
/// If dragging the selected nodes, this stores the starting position both in viewport and node graph coordinates,
|
/// If dragging the selected nodes, this stores the starting position both in viewport and node graph coordinates,
|
||||||
/// plus a flag indicating if it has been dragged since the mousedown began.
|
/// plus a flag indicating if it has been dragged since the mousedown began.
|
||||||
pub drag_start: Option<(DragStart, bool)>,
|
pub drag_start: Option<(DragStart, bool)>,
|
||||||
|
// Store the selected chain nodes on drag start so they can be reconnected if shaken
|
||||||
|
pub drag_start_chain_nodes: Vec<NodeId>,
|
||||||
/// If dragging the background to create a box selection, this stores its starting point in node graph coordinates,
|
/// If dragging the background to create a box selection, this stores its starting point in node graph coordinates,
|
||||||
/// plus a flag indicating if it has been dragged since the mousedown began.
|
/// plus a flag indicating if it has been dragged since the mousedown began.
|
||||||
box_selection_start: Option<(DVec2, bool)>,
|
box_selection_start: Option<(DVec2, bool)>,
|
||||||
@@ -93,9 +99,9 @@ pub struct NodeGraphMessageHandler {
|
|||||||
|
|
||||||
/// NodeGraphMessageHandler always modifies the network which the selected nodes are in. No GraphOperationMessages should be added here, since those messages will always affect the document network.
|
/// NodeGraphMessageHandler always modifies the network which the selected nodes are in. No GraphOperationMessages should be added here, since those messages will always affect the document network.
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGraphMessageHandler {
|
impl<'a> MessageHandler<NodeGraphMessage, NodeGraphMessageContext<'a>> for NodeGraphMessageHandler {
|
||||||
fn process_message(&mut self, message: NodeGraphMessage, responses: &mut VecDeque<Message>, data: NodeGraphHandlerData<'a>) {
|
fn process_message(&mut self, message: NodeGraphMessage, responses: &mut VecDeque<Message>, context: NodeGraphMessageContext<'a>) {
|
||||||
let NodeGraphHandlerData {
|
let NodeGraphMessageContext {
|
||||||
network_interface,
|
network_interface,
|
||||||
selection_network_path,
|
selection_network_path,
|
||||||
breadcrumb_network_path,
|
breadcrumb_network_path,
|
||||||
@@ -106,7 +112,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
|||||||
graph_fade_artwork_percentage,
|
graph_fade_artwork_percentage,
|
||||||
navigation_handler,
|
navigation_handler,
|
||||||
preferences,
|
preferences,
|
||||||
} = data;
|
} = context;
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
// TODO: automatically remove broadcast messages.
|
// TODO: automatically remove broadcast messages.
|
||||||
@@ -119,6 +125,38 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
|||||||
|
|
||||||
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![new_layer_id] });
|
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![new_layer_id] });
|
||||||
}
|
}
|
||||||
|
NodeGraphMessage::AddPathNode => {
|
||||||
|
let selected_nodes = network_interface.selected_nodes();
|
||||||
|
let mut selected_layers = selected_nodes.selected_layers(network_interface.document_metadata());
|
||||||
|
let first_layer = selected_layers.next();
|
||||||
|
let second_layer = selected_layers.next();
|
||||||
|
let has_single_selection = first_layer.is_some() && second_layer.is_none();
|
||||||
|
|
||||||
|
let compatible_type = first_layer.and_then(|layer| {
|
||||||
|
let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, &network_interface);
|
||||||
|
graph_layer.horizontal_layer_flow().nth(1).and_then(|node_id| {
|
||||||
|
let (output_type, _) = network_interface.output_type(&node_id, 0, &[]);
|
||||||
|
Some(format!("type:{}", output_type.nested_type()))
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let is_compatible = compatible_type.as_deref() == Some("type:Instances<VectorData>");
|
||||||
|
|
||||||
|
if first_layer.is_some() && has_single_selection && is_compatible {
|
||||||
|
if let Some(layer) = first_layer {
|
||||||
|
let node_type = "Path".to_string();
|
||||||
|
let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, &network_interface);
|
||||||
|
let is_modifiable = matches!(graph_layer.find_input("Path", 1), Some(TaggedValue::VectorModification(_)));
|
||||||
|
if !is_modifiable {
|
||||||
|
responses.add(NodeGraphMessage::CreateNodeInLayerWithTransaction {
|
||||||
|
node_type: node_type.clone(),
|
||||||
|
layer: LayerNodeIdentifier::new_unchecked(layer.to_node()),
|
||||||
|
});
|
||||||
|
responses.add(BroadcastEvent::SelectionChanged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
NodeGraphMessage::AddImport => {
|
NodeGraphMessage::AddImport => {
|
||||||
network_interface.add_import(graph_craft::document::value::TaggedValue::None, true, -1, "", "", breadcrumb_network_path);
|
network_interface.add_import(graph_craft::document::value::TaggedValue::None, true, -1, "", "", breadcrumb_network_path);
|
||||||
responses.add(NodeGraphMessage::SendGraph);
|
responses.add(NodeGraphMessage::SendGraph);
|
||||||
@@ -568,6 +606,9 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
|||||||
NodeGraphMessage::MoveNodeToChainStart { node_id, parent } => {
|
NodeGraphMessage::MoveNodeToChainStart { node_id, parent } => {
|
||||||
network_interface.move_node_to_chain_start(&node_id, parent, selection_network_path);
|
network_interface.move_node_to_chain_start(&node_id, parent, selection_network_path);
|
||||||
}
|
}
|
||||||
|
NodeGraphMessage::SetChainPosition { node_id } => {
|
||||||
|
network_interface.set_chain_position(&node_id, selection_network_path);
|
||||||
|
}
|
||||||
NodeGraphMessage::PasteNodes { serialized_nodes } => {
|
NodeGraphMessage::PasteNodes { serialized_nodes } => {
|
||||||
let data = match serde_json::from_str::<Vec<(NodeId, NodeTemplate)>>(&serialized_nodes) {
|
let data = match serde_json::from_str::<Vec<(NodeId, NodeTemplate)>>(&serialized_nodes) {
|
||||||
Ok(d) => d,
|
Ok(d) => d,
|
||||||
@@ -821,6 +862,20 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
|||||||
};
|
};
|
||||||
|
|
||||||
self.drag_start = Some((drag_start, false));
|
self.drag_start = Some((drag_start, false));
|
||||||
|
let selected_chain_nodes = updated_selected
|
||||||
|
.iter()
|
||||||
|
.filter(|node_id| network_interface.is_chain(node_id, selection_network_path))
|
||||||
|
.copied()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
self.drag_start_chain_nodes = selected_chain_nodes
|
||||||
|
.iter()
|
||||||
|
.flat_map(|selected| {
|
||||||
|
network_interface
|
||||||
|
.upstream_flow_back_from_nodes(vec![*selected], selection_network_path, FlowType::PrimaryFlow)
|
||||||
|
.skip(1)
|
||||||
|
.filter(|node_id| network_interface.is_chain(node_id, selection_network_path))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
self.begin_dragging = true;
|
self.begin_dragging = true;
|
||||||
self.node_has_moved_in_drag = false;
|
self.node_has_moved_in_drag = false;
|
||||||
self.update_node_graph_hints(responses);
|
self.update_node_graph_hints(responses);
|
||||||
@@ -1188,10 +1243,39 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
|||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
log::debug!("preferences.graph_wire_style: {:?}", preferences.graph_wire_style);
|
||||||
let (wire, is_stack) = network_interface.vector_wire_from_input(&input, preferences.graph_wire_style, selection_network_path)?;
|
let (wire, is_stack) = network_interface.vector_wire_from_input(&input, preferences.graph_wire_style, selection_network_path)?;
|
||||||
wire.rectangle_intersections_exist(bounding_box[0], bounding_box[1]).then_some((input, is_stack))
|
|
||||||
|
let bbox_rect = kurbo::Rect::new(bounding_box[0].x, bounding_box[0].y, bounding_box[1].x, bounding_box[1].y);
|
||||||
|
|
||||||
|
let p1 = DVec2::new(bbox_rect.x0, bbox_rect.y0);
|
||||||
|
let p2 = DVec2::new(bbox_rect.x1, bbox_rect.y0);
|
||||||
|
let p3 = DVec2::new(bbox_rect.x1, bbox_rect.y1);
|
||||||
|
let p4 = DVec2::new(bbox_rect.x0, bbox_rect.y1);
|
||||||
|
let ps = [p1, p2, p3, p4];
|
||||||
|
|
||||||
|
let inside = wire.is_inside_subpath(&Subpath::from_anchors_linear(ps, true), None, None);
|
||||||
|
|
||||||
|
let wire = subpath_to_kurbo_bezpath(wire);
|
||||||
|
|
||||||
|
let intersect = wire.segments().any(|segment| {
|
||||||
|
let rect = kurbo::Rect::new(bounding_box[0].x, bounding_box[0].y, bounding_box[1].x, bounding_box[1].y);
|
||||||
|
|
||||||
|
let top_line = Line::new(Point::new(rect.x0, rect.y0), Point::new(rect.x1, rect.y0));
|
||||||
|
let bottom_line = Line::new(Point::new(rect.x0, rect.y1), Point::new(rect.x1, rect.y1));
|
||||||
|
let left_line = Line::new(Point::new(rect.x0, rect.y0), Point::new(rect.x0, rect.y1));
|
||||||
|
let right_line = Line::new(Point::new(rect.x1, rect.y0), Point::new(rect.x1, rect.y1));
|
||||||
|
|
||||||
|
!segment.intersect_line(top_line).is_empty()
|
||||||
|
|| !segment.intersect_line(bottom_line).is_empty()
|
||||||
|
|| !segment.intersect_line(left_line).is_empty()
|
||||||
|
|| !segment.intersect_line(right_line).is_empty()
|
||||||
|
});
|
||||||
|
|
||||||
|
(intersect || inside).then_some((input, is_stack))
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// Prioritize vertical thick lines and cancel if there are multiple potential wires
|
// Prioritize vertical thick lines and cancel if there are multiple potential wires
|
||||||
let mut node_wires = Vec::new();
|
let mut node_wires = Vec::new();
|
||||||
let mut stack_wires = Vec::new();
|
let mut stack_wires = Vec::new();
|
||||||
@@ -1270,6 +1354,135 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
|||||||
self.auto_panning.stop(&messages, responses);
|
self.auto_panning.stop(&messages, responses);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
NodeGraphMessage::ShakeNode => {
|
||||||
|
let Some(drag_start) = &self.drag_start else {
|
||||||
|
log::error!("Drag start should be initialized when shaking a node");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(network_metadata) = network_interface.network_metadata(selection_network_path) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let viewport_location = ipp.mouse.position;
|
||||||
|
let point = network_metadata
|
||||||
|
.persistent_metadata
|
||||||
|
.navigation_metadata
|
||||||
|
.node_graph_to_viewport
|
||||||
|
.inverse()
|
||||||
|
.transform_point2(viewport_location);
|
||||||
|
|
||||||
|
// Collect the distance to move the shaken nodes after the undo
|
||||||
|
let graph_delta = IVec2::new(((point.x - drag_start.0.start_x) / 24.).round() as i32, ((point.y - drag_start.0.start_y) / 24.).round() as i32);
|
||||||
|
|
||||||
|
// Undo to the state of the graph before shaking
|
||||||
|
responses.add(DocumentMessage::AbortTransaction);
|
||||||
|
|
||||||
|
// Add a history step to abort to the state before shaking if right clicked
|
||||||
|
responses.add(DocumentMessage::StartTransaction);
|
||||||
|
|
||||||
|
let Some(selected_nodes) = network_interface.selected_nodes_in_nested_network(selection_network_path) else {
|
||||||
|
log::error!("Could not get selected nodes in ShakeNode");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut all_selected_nodes = selected_nodes.0.iter().copied().collect::<HashSet<_>>();
|
||||||
|
for selected_layer in selected_nodes
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
|
.filter(|selected_node| network_interface.is_layer(selected_node, selection_network_path))
|
||||||
|
.copied()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
{
|
||||||
|
for sole_dependent in network_interface.upstream_nodes_below_layer(&selected_layer, selection_network_path) {
|
||||||
|
all_selected_nodes.insert(sole_dependent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for selected_node in &all_selected_nodes {
|
||||||
|
// Handle inputs of selected node
|
||||||
|
for input_index in 0..network_interface.number_of_inputs(selected_node, selection_network_path) {
|
||||||
|
let input_connector = InputConnector::node(*selected_node, input_index);
|
||||||
|
// Only disconnect inputs to non selected nodes
|
||||||
|
if network_interface
|
||||||
|
.upstream_output_connector(&input_connector, selection_network_path)
|
||||||
|
.and_then(|connector| connector.node_id())
|
||||||
|
.is_some_and(|node_id| !all_selected_nodes.contains(&node_id))
|
||||||
|
{
|
||||||
|
responses.add(NodeGraphMessage::DisconnectInput { input_connector });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let number_of_outputs = network_interface.number_of_outputs(selected_node, selection_network_path);
|
||||||
|
let first_deselected_upstream_node = network_interface
|
||||||
|
.upstream_flow_back_from_nodes(vec![*selected_node], selection_network_path, FlowType::PrimaryFlow)
|
||||||
|
.find(|upstream_node| !all_selected_nodes.contains(upstream_node));
|
||||||
|
let Some(outward_wires) = network_interface.outward_wires(selection_network_path) else {
|
||||||
|
log::error!("Could not get output wires in shake input");
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Disconnect output wires to non selected nodes
|
||||||
|
for output_index in 0..number_of_outputs {
|
||||||
|
let output_connector = OutputConnector::node(*selected_node, output_index);
|
||||||
|
if let Some(downstream_connections) = outward_wires.get(&output_connector) {
|
||||||
|
for &input_connector in downstream_connections {
|
||||||
|
if input_connector.node_id().is_some_and(|downstream_node| !all_selected_nodes.contains(&downstream_node)) {
|
||||||
|
responses.add(NodeGraphMessage::DisconnectInput { input_connector });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle reconnection
|
||||||
|
// Find first non selected upstream node by primary flow
|
||||||
|
if let Some(first_deselected_upstream_node) = first_deselected_upstream_node {
|
||||||
|
let Some(downstream_connections_to_first_output) = outward_wires.get(&OutputConnector::node(*selected_node, 0)).cloned() else {
|
||||||
|
log::error!("Could not get downstream_connections_to_first_output in shake node");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
// Reconnect only if all downstream outputs are not selected
|
||||||
|
if !downstream_connections_to_first_output
|
||||||
|
.iter()
|
||||||
|
.any(|connector| connector.node_id().is_some_and(|node_id| all_selected_nodes.contains(&node_id)))
|
||||||
|
{
|
||||||
|
// Find what output on the deselected upstream node to reconnect to
|
||||||
|
for output_index in 0..network_interface.number_of_outputs(&first_deselected_upstream_node, selection_network_path) {
|
||||||
|
let output_connector = &OutputConnector::node(first_deselected_upstream_node, output_index);
|
||||||
|
let Some(outward_wires) = network_interface.outward_wires(selection_network_path) else {
|
||||||
|
log::error!("Could not get output wires in shake input");
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if let Some(inputs) = outward_wires.get(output_connector) {
|
||||||
|
// This can only run once
|
||||||
|
if inputs.iter().any(|input_connector| {
|
||||||
|
input_connector
|
||||||
|
.node_id()
|
||||||
|
.is_some_and(|upstream_node| all_selected_nodes.contains(&upstream_node) && input_connector.input_index() == 0)
|
||||||
|
}) {
|
||||||
|
// Output index is the output of the deselected upstream node to reconnect to
|
||||||
|
for downstream_connections_to_first_output in &downstream_connections_to_first_output {
|
||||||
|
responses.add(NodeGraphMessage::CreateWire {
|
||||||
|
output_connector: OutputConnector::node(first_deselected_upstream_node, output_index),
|
||||||
|
input_connector: *downstream_connections_to_first_output,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set all chain nodes back to chain position
|
||||||
|
// TODO: Fix
|
||||||
|
// for chain_node_to_reset in std::mem::take(&mut self.drag_start_chain_nodes) {
|
||||||
|
// responses.add(NodeGraphMessage::SetChainPosition { node_id: chain_node_to_reset });
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
responses.add(NodeGraphMessage::ShiftSelectedNodesByAmount { graph_delta, rubber_band: false });
|
||||||
|
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||||
|
responses.add(NodeGraphMessage::SendGraph);
|
||||||
|
}
|
||||||
NodeGraphMessage::RemoveImport { import_index: usize } => {
|
NodeGraphMessage::RemoveImport { import_index: usize } => {
|
||||||
network_interface.remove_import(usize, selection_network_path);
|
network_interface.remove_import(usize, selection_network_path);
|
||||||
responses.add(NodeGraphMessage::SendGraph);
|
responses.add(NodeGraphMessage::SendGraph);
|
||||||
@@ -1354,6 +1567,11 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
|||||||
if node_bbox[1].x >= document_bbox[0].x && node_bbox[0].x <= document_bbox[1].x && node_bbox[1].y >= document_bbox[0].y && node_bbox[0].y <= document_bbox[1].y {
|
if node_bbox[1].x >= document_bbox[0].x && node_bbox[0].x <= document_bbox[1].x && node_bbox[1].y >= document_bbox[0].y && node_bbox[0].y <= document_bbox[1].y {
|
||||||
nodes.push(*node_id);
|
nodes.push(*node_id);
|
||||||
}
|
}
|
||||||
|
for error in &self.node_graph_errors {
|
||||||
|
if error.node_path.contains(node_id) {
|
||||||
|
nodes.push(*node_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
responses.add(FrontendMessage::UpdateVisibleNodes { nodes });
|
responses.add(FrontendMessage::UpdateVisibleNodes { nodes });
|
||||||
@@ -1785,6 +2003,12 @@ impl NodeGraphMessageHandler {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.drag_start.is_some() {
|
||||||
|
common.extend(actions!(NodeGraphMessageDiscriminant;
|
||||||
|
ShakeNode,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
common
|
common
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1824,25 +2048,58 @@ impl NodeGraphMessageHandler {
|
|||||||
let selection_all_locked = network_interface.selected_nodes().selected_unlocked_layers(network_interface).count() == 0;
|
let selection_all_locked = network_interface.selected_nodes().selected_unlocked_layers(network_interface).count() == 0;
|
||||||
let selection_all_visible = selected_nodes.selected_nodes().all(|node_id| network_interface.is_visible(node_id, breadcrumb_network_path));
|
let selection_all_visible = selected_nodes.selected_nodes().all(|node_id| network_interface.is_visible(node_id, breadcrumb_network_path));
|
||||||
|
|
||||||
|
let mut selected_layers = selected_nodes.selected_layers(network_interface.document_metadata());
|
||||||
|
let selected_layer = selected_layers.next();
|
||||||
|
let has_multiple_selection = selected_layers.next().is_some();
|
||||||
|
|
||||||
let mut widgets = vec![
|
let mut widgets = vec![
|
||||||
PopoverButton::new()
|
PopoverButton::new()
|
||||||
.icon(Some("Node".to_string()))
|
.icon(Some("Node".to_string()))
|
||||||
.tooltip("New Node (Right Click)")
|
.tooltip("New Node (Right Click)")
|
||||||
.popover_layout({
|
.popover_layout({
|
||||||
let node_chooser = NodeCatalog::new()
|
// Showing only compatible types
|
||||||
.on_update(move |node_type| {
|
let compatible_type = match (selection_includes_layers, has_multiple_selection, selected_layer) {
|
||||||
let node_id = NodeId::new();
|
(true, false, Some(layer)) => {
|
||||||
|
let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, network_interface);
|
||||||
|
let node_type = graph_layer.horizontal_layer_flow().nth(1);
|
||||||
|
if let Some(node_id) = node_type {
|
||||||
|
let (output_type, _) = network_interface.output_type(&node_id, 0, &[]);
|
||||||
|
Some(format!("type:{}", output_type.nested_type()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
Message::Batched(Box::new([
|
let single_layer_selected = selection_includes_layers && !has_multiple_selection;
|
||||||
NodeGraphMessage::CreateNodeFromContextMenu {
|
|
||||||
node_id: Some(node_id),
|
let mut node_chooser = NodeCatalog::new();
|
||||||
|
node_chooser.intial_search = compatible_type.unwrap_or("".to_string());
|
||||||
|
|
||||||
|
let node_chooser = node_chooser
|
||||||
|
.on_update(move |node_type| {
|
||||||
|
if let (true, Some(layer)) = (single_layer_selected, selected_layer) {
|
||||||
|
NodeGraphMessage::CreateNodeInLayerWithTransaction {
|
||||||
node_type: node_type.clone(),
|
node_type: node_type.clone(),
|
||||||
xy: None,
|
layer: LayerNodeIdentifier::new_unchecked(layer.to_node()),
|
||||||
add_transaction: true,
|
|
||||||
}
|
}
|
||||||
.into(),
|
.into()
|
||||||
NodeGraphMessage::SelectedNodesSet { nodes: vec![node_id] }.into(),
|
} else {
|
||||||
]))
|
let node_id = NodeId::new();
|
||||||
|
Message::Batched {
|
||||||
|
messages: Box::new([
|
||||||
|
NodeGraphMessage::CreateNodeFromContextMenu {
|
||||||
|
node_id: Some(node_id),
|
||||||
|
node_type: node_type.clone(),
|
||||||
|
xy: None,
|
||||||
|
add_transaction: true,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
NodeGraphMessage::SelectedNodesSet { nodes: vec![node_id] }.into(),
|
||||||
|
]),
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.widget_holder();
|
.widget_holder();
|
||||||
vec![LayoutGroup::Row { widgets: vec![node_chooser] }]
|
vec![LayoutGroup::Row { widgets: vec![node_chooser] }]
|
||||||
@@ -2113,7 +2370,22 @@ impl NodeGraphMessageHandler {
|
|||||||
.icon(Some("Node".to_string()))
|
.icon(Some("Node".to_string()))
|
||||||
.tooltip("Add an operation to the end of this layer's chain of nodes")
|
.tooltip("Add an operation to the end of this layer's chain of nodes")
|
||||||
.popover_layout({
|
.popover_layout({
|
||||||
let node_chooser = NodeCatalog::new()
|
let layer_identifier = LayerNodeIdentifier::new(layer, &context.network_interface);
|
||||||
|
let compatible_type = {
|
||||||
|
let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer_identifier, &context.network_interface);
|
||||||
|
let node_type = graph_layer.horizontal_layer_flow().nth(1);
|
||||||
|
if let Some(node_id) = node_type {
|
||||||
|
let (output_type, _) = context.network_interface.output_type(&node_id, 0, &[]);
|
||||||
|
Some(format!("type:{}", output_type.nested_type()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut node_chooser = NodeCatalog::new();
|
||||||
|
node_chooser.intial_search = compatible_type.unwrap_or("".to_string());
|
||||||
|
|
||||||
|
let node_chooser = node_chooser
|
||||||
.on_update(move |node_type| {
|
.on_update(move |node_type| {
|
||||||
NodeGraphMessage::CreateNodeInLayerWithTransaction {
|
NodeGraphMessage::CreateNodeInLayerWithTransaction {
|
||||||
node_type: node_type.clone(),
|
node_type: node_type.clone(),
|
||||||
@@ -2364,19 +2636,19 @@ impl NodeGraphMessageHandler {
|
|||||||
let mut ancestors_of_selected = HashSet::new();
|
let mut ancestors_of_selected = HashSet::new();
|
||||||
let mut descendants_of_selected = HashSet::new();
|
let mut descendants_of_selected = HashSet::new();
|
||||||
for selected_layer in &selected_layers {
|
for selected_layer in &selected_layers {
|
||||||
for ancestor in LayerNodeIdentifier::new(*selected_layer, network_interface, &[]).ancestors(network_interface.document_metadata()) {
|
for ancestor in LayerNodeIdentifier::new(*selected_layer, network_interface).ancestors(network_interface.document_metadata()) {
|
||||||
if ancestor != LayerNodeIdentifier::ROOT_PARENT && ancestor.to_node() != *selected_layer {
|
if ancestor != LayerNodeIdentifier::ROOT_PARENT && ancestor.to_node() != *selected_layer {
|
||||||
ancestors_of_selected.insert(ancestor.to_node());
|
ancestors_of_selected.insert(ancestor.to_node());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for descendant in LayerNodeIdentifier::new(*selected_layer, network_interface, &[]).descendants(network_interface.document_metadata()) {
|
for descendant in LayerNodeIdentifier::new(*selected_layer, network_interface).descendants(network_interface.document_metadata()) {
|
||||||
descendants_of_selected.insert(descendant.to_node());
|
descendants_of_selected.insert(descendant.to_node());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (&node_id, node_metadata) in &network_interface.document_network_metadata().persistent_metadata.node_metadata {
|
for (&node_id, node_metadata) in &network_interface.document_network_metadata().persistent_metadata.node_metadata {
|
||||||
if node_metadata.persistent_metadata.is_layer() {
|
if node_metadata.persistent_metadata.is_layer() {
|
||||||
let layer = LayerNodeIdentifier::new(node_id, network_interface, &[]);
|
let layer = LayerNodeIdentifier::new(node_id, network_interface);
|
||||||
|
|
||||||
let children_allowed =
|
let children_allowed =
|
||||||
// The layer has other layers as children along the secondary input's horizontal flow
|
// The layer has other layers as children along the secondary input's horizontal flow
|
||||||
@@ -2557,6 +2829,7 @@ impl Default for NodeGraphMessageHandler {
|
|||||||
node_has_moved_in_drag: false,
|
node_has_moved_in_drag: false,
|
||||||
shift_without_push: false,
|
shift_without_push: false,
|
||||||
box_selection_start: None,
|
box_selection_start: None,
|
||||||
|
drag_start_chain_nodes: Vec::new(),
|
||||||
selection_before_pointer_down: Vec::new(),
|
selection_before_pointer_down: Vec::new(),
|
||||||
disconnecting: None,
|
disconnecting: None,
|
||||||
initial_disconnecting: false,
|
initial_disconnecting: false,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use crate::messages::portfolio::document::utility_types::network_interface::Inpu
|
|||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use choice::enum_choice;
|
use choice::enum_choice;
|
||||||
use dyn_any::DynAny;
|
use dyn_any::DynAny;
|
||||||
use glam::{DAffine2, DVec2, IVec2, UVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use graph_craft::Type;
|
use graph_craft::Type;
|
||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeInput};
|
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeInput};
|
||||||
@@ -21,7 +21,7 @@ use graphene_std::raster::{
|
|||||||
};
|
};
|
||||||
use graphene_std::raster_types::{CPU, GPU, RasterDataTable};
|
use graphene_std::raster_types::{CPU, GPU, RasterDataTable};
|
||||||
use graphene_std::text::Font;
|
use graphene_std::text::Font;
|
||||||
use graphene_std::transform::{Footprint, ReferencePoint};
|
use graphene_std::transform::{Footprint, ReferencePoint, Transform};
|
||||||
use graphene_std::vector::VectorDataTable;
|
use graphene_std::vector::VectorDataTable;
|
||||||
use graphene_std::vector::misc::GridType;
|
use graphene_std::vector::misc::GridType;
|
||||||
use graphene_std::vector::misc::{ArcType, MergeByDistanceAlgorithm};
|
use graphene_std::vector::misc::{ArcType, MergeByDistanceAlgorithm};
|
||||||
@@ -59,13 +59,13 @@ pub fn expose_widget(node_id: NodeId, index: usize, data_type: FrontendGraphData
|
|||||||
} else {
|
} else {
|
||||||
"Expose this parameter as a node input in the graph"
|
"Expose this parameter as a node input in the graph"
|
||||||
})
|
})
|
||||||
.on_update(move |_parameter| {
|
.on_update(move |_parameter| Message::Batched {
|
||||||
Message::Batched(Box::new([NodeGraphMessage::ExposeInput {
|
messages: Box::new([NodeGraphMessage::ExposeInput {
|
||||||
input_connector: InputConnector::node(node_id, index),
|
input_connector: InputConnector::node(node_id, index),
|
||||||
set_to_exposed: !exposed,
|
set_to_exposed: !exposed,
|
||||||
start_transaction: true,
|
start_transaction: true,
|
||||||
}
|
}
|
||||||
.into()]))
|
.into()]),
|
||||||
})
|
})
|
||||||
.widget_holder()
|
.widget_holder()
|
||||||
}
|
}
|
||||||
@@ -160,8 +160,7 @@ pub(crate) fn property_from_type(
|
|||||||
Some("Fraction") => number_widget(default_info, number_input.mode_range().min(min(0.)).max(max(1.))).into(),
|
Some("Fraction") => number_widget(default_info, number_input.mode_range().min(min(0.)).max(max(1.))).into(),
|
||||||
Some("IntegerCount") => number_widget(default_info, number_input.int().min(min(1.))).into(),
|
Some("IntegerCount") => number_widget(default_info, number_input.int().min(min(1.))).into(),
|
||||||
Some("SeedValue") => number_widget(default_info, number_input.int().min(min(0.))).into(),
|
Some("SeedValue") => number_widget(default_info, number_input.int().min(min(0.))).into(),
|
||||||
Some("Resolution") => coordinate_widget(default_info, "W", "H", unit.unwrap_or(" px"), Some(64.)),
|
Some("PixelSize") => coordinate_widget(default_info, "X", "Y", unit.unwrap_or(" px"), None, false),
|
||||||
Some("PixelSize") => coordinate_widget(default_info, "X", "Y", unit.unwrap_or(" px"), None),
|
|
||||||
Some("TextArea") => text_area_widget(default_info).into(),
|
Some("TextArea") => text_area_widget(default_info).into(),
|
||||||
|
|
||||||
// For all other types, use TypeId-based matching
|
// For all other types, use TypeId-based matching
|
||||||
@@ -176,9 +175,8 @@ pub(crate) fn property_from_type(
|
|||||||
Some(x) if x == TypeId::of::<u64>() => number_widget(default_info, number_input.int().min(min(0.))).into(),
|
Some(x) if x == TypeId::of::<u64>() => number_widget(default_info, number_input.int().min(min(0.))).into(),
|
||||||
Some(x) if x == TypeId::of::<bool>() => bool_widget(default_info, CheckboxInput::default()).into(),
|
Some(x) if x == TypeId::of::<bool>() => bool_widget(default_info, CheckboxInput::default()).into(),
|
||||||
Some(x) if x == TypeId::of::<String>() => text_widget(default_info).into(),
|
Some(x) if x == TypeId::of::<String>() => text_widget(default_info).into(),
|
||||||
Some(x) if x == TypeId::of::<DVec2>() => coordinate_widget(default_info, "X", "Y", "", None),
|
Some(x) if x == TypeId::of::<DVec2>() => coordinate_widget(default_info, "X", "Y", "", None, false),
|
||||||
Some(x) if x == TypeId::of::<UVec2>() => coordinate_widget(default_info, "X", "Y", "", Some(0.)),
|
Some(x) if x == TypeId::of::<DAffine2>() => transform_widget(default_info, &mut extra_widgets),
|
||||||
Some(x) if x == TypeId::of::<IVec2>() => coordinate_widget(default_info, "X", "Y", "", None),
|
|
||||||
// ==========================
|
// ==========================
|
||||||
// PRIMITIVE COLLECTION TYPES
|
// PRIMITIVE COLLECTION TYPES
|
||||||
// ==========================
|
// ==========================
|
||||||
@@ -507,7 +505,127 @@ pub fn footprint_widget(parameter_widgets_info: ParameterWidgetsInfo, extra_widg
|
|||||||
last.clone()
|
last.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn coordinate_widget(parameter_widgets_info: ParameterWidgetsInfo, x: &str, y: &str, unit: &str, min: Option<f64>) -> LayoutGroup {
|
pub fn transform_widget(parameter_widgets_info: ParameterWidgetsInfo, extra_widgets: &mut Vec<LayoutGroup>) -> LayoutGroup {
|
||||||
|
let ParameterWidgetsInfo { document_node, node_id, index, .. } = parameter_widgets_info;
|
||||||
|
|
||||||
|
let mut location_widgets = start_widgets(parameter_widgets_info);
|
||||||
|
location_widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||||
|
|
||||||
|
let mut rotation_widgets = vec![TextLabel::new("").widget_holder()];
|
||||||
|
add_blank_assist(&mut rotation_widgets);
|
||||||
|
rotation_widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||||
|
|
||||||
|
let mut scale_widgets = vec![TextLabel::new("").widget_holder()];
|
||||||
|
add_blank_assist(&mut scale_widgets);
|
||||||
|
scale_widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
|
||||||
|
|
||||||
|
let Some(document_node) = document_node else { return LayoutGroup::default() };
|
||||||
|
let Some(input) = document_node.inputs.get(index) else {
|
||||||
|
log::warn!("A widget failed to be built because its node's input index is invalid.");
|
||||||
|
return Vec::new().into();
|
||||||
|
};
|
||||||
|
|
||||||
|
let widgets = if let Some(&TaggedValue::DAffine2(transform)) = input.as_non_exposed_value() {
|
||||||
|
let translation = transform.translation;
|
||||||
|
let rotation = transform.decompose_rotation();
|
||||||
|
let scale = transform.decompose_scale();
|
||||||
|
|
||||||
|
location_widgets.extend_from_slice(&[
|
||||||
|
NumberInput::new(Some(translation.x))
|
||||||
|
.label("X")
|
||||||
|
.unit(" px")
|
||||||
|
.on_update(update_value(
|
||||||
|
move |x: &NumberInput| {
|
||||||
|
let mut transform = transform;
|
||||||
|
transform.translation.x = x.value.unwrap_or(transform.translation.x);
|
||||||
|
TaggedValue::DAffine2(transform)
|
||||||
|
},
|
||||||
|
node_id,
|
||||||
|
index,
|
||||||
|
))
|
||||||
|
.on_commit(commit_value)
|
||||||
|
.widget_holder(),
|
||||||
|
Separator::new(SeparatorType::Related).widget_holder(),
|
||||||
|
NumberInput::new(Some(translation.y))
|
||||||
|
.label("Y")
|
||||||
|
.unit(" px")
|
||||||
|
.on_update(update_value(
|
||||||
|
move |y: &NumberInput| {
|
||||||
|
let mut transform = transform;
|
||||||
|
transform.translation.y = y.value.unwrap_or(transform.translation.y);
|
||||||
|
TaggedValue::DAffine2(transform)
|
||||||
|
},
|
||||||
|
node_id,
|
||||||
|
index,
|
||||||
|
))
|
||||||
|
.on_commit(commit_value)
|
||||||
|
.widget_holder(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
rotation_widgets.extend_from_slice(&[NumberInput::new(Some(rotation.to_degrees()))
|
||||||
|
.unit("°")
|
||||||
|
.mode(NumberInputMode::Range)
|
||||||
|
.range_min(Some(-180.))
|
||||||
|
.range_max(Some(180.))
|
||||||
|
.on_update(update_value(
|
||||||
|
move |r: &NumberInput| {
|
||||||
|
let transform = DAffine2::from_scale_angle_translation(scale, r.value.map(|r| r.to_radians()).unwrap_or(rotation), translation);
|
||||||
|
TaggedValue::DAffine2(transform)
|
||||||
|
},
|
||||||
|
node_id,
|
||||||
|
index,
|
||||||
|
))
|
||||||
|
.on_commit(commit_value)
|
||||||
|
.widget_holder()]);
|
||||||
|
|
||||||
|
scale_widgets.extend_from_slice(&[
|
||||||
|
NumberInput::new(Some(scale.x))
|
||||||
|
.label("W")
|
||||||
|
.unit("x")
|
||||||
|
.on_update(update_value(
|
||||||
|
move |w: &NumberInput| {
|
||||||
|
let transform = DAffine2::from_scale_angle_translation(DVec2::new(w.value.unwrap_or(scale.x), scale.y), rotation, translation);
|
||||||
|
TaggedValue::DAffine2(transform)
|
||||||
|
},
|
||||||
|
node_id,
|
||||||
|
index,
|
||||||
|
))
|
||||||
|
.on_commit(commit_value)
|
||||||
|
.widget_holder(),
|
||||||
|
Separator::new(SeparatorType::Related).widget_holder(),
|
||||||
|
NumberInput::new(Some(scale.y))
|
||||||
|
.label("H")
|
||||||
|
.unit("x")
|
||||||
|
.on_update(update_value(
|
||||||
|
move |h: &NumberInput| {
|
||||||
|
let transform = DAffine2::from_scale_angle_translation(DVec2::new(scale.x, h.value.unwrap_or(scale.y)), rotation, translation);
|
||||||
|
TaggedValue::DAffine2(transform)
|
||||||
|
},
|
||||||
|
node_id,
|
||||||
|
index,
|
||||||
|
))
|
||||||
|
.on_commit(commit_value)
|
||||||
|
.widget_holder(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
vec![
|
||||||
|
LayoutGroup::Row { widgets: location_widgets },
|
||||||
|
LayoutGroup::Row { widgets: rotation_widgets },
|
||||||
|
LayoutGroup::Row { widgets: scale_widgets },
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
vec![LayoutGroup::Row { widgets: location_widgets }]
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some((last, rest)) = widgets.split_last() {
|
||||||
|
*extra_widgets = rest.to_vec();
|
||||||
|
last.clone()
|
||||||
|
} else {
|
||||||
|
LayoutGroup::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn coordinate_widget(parameter_widgets_info: ParameterWidgetsInfo, x: &str, y: &str, unit: &str, min: Option<f64>, is_integer: bool) -> LayoutGroup {
|
||||||
let ParameterWidgetsInfo { document_node, node_id, index, .. } = parameter_widgets_info;
|
let ParameterWidgetsInfo { document_node, node_id, index, .. } = parameter_widgets_info;
|
||||||
|
|
||||||
let mut widgets = start_widgets(parameter_widgets_info);
|
let mut widgets = start_widgets(parameter_widgets_info);
|
||||||
@@ -526,6 +644,7 @@ pub fn coordinate_widget(parameter_widgets_info: ParameterWidgetsInfo, x: &str,
|
|||||||
.unit(unit)
|
.unit(unit)
|
||||||
.min(min.unwrap_or(-((1_u64 << f64::MANTISSA_DIGITS) as f64)))
|
.min(min.unwrap_or(-((1_u64 << f64::MANTISSA_DIGITS) as f64)))
|
||||||
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
|
.is_integer(is_integer)
|
||||||
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(input.value.unwrap(), dvec2.y)), node_id, index))
|
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(input.value.unwrap(), dvec2.y)), node_id, index))
|
||||||
.on_commit(commit_value)
|
.on_commit(commit_value)
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
@@ -535,63 +654,12 @@ pub fn coordinate_widget(parameter_widgets_info: ParameterWidgetsInfo, x: &str,
|
|||||||
.unit(unit)
|
.unit(unit)
|
||||||
.min(min.unwrap_or(-((1_u64 << f64::MANTISSA_DIGITS) as f64)))
|
.min(min.unwrap_or(-((1_u64 << f64::MANTISSA_DIGITS) as f64)))
|
||||||
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
|
.is_integer(is_integer)
|
||||||
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(dvec2.x, input.value.unwrap())), node_id, index))
|
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(dvec2.x, input.value.unwrap())), node_id, index))
|
||||||
.on_commit(commit_value)
|
.on_commit(commit_value)
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
Some(&TaggedValue::IVec2(ivec2)) => {
|
|
||||||
let update_x = move |input: &NumberInput| TaggedValue::IVec2(IVec2::new(input.value.unwrap() as i32, ivec2.y));
|
|
||||||
let update_y = move |input: &NumberInput| TaggedValue::IVec2(IVec2::new(ivec2.x, input.value.unwrap() as i32));
|
|
||||||
widgets.extend_from_slice(&[
|
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
|
||||||
NumberInput::new(Some(ivec2.x as f64))
|
|
||||||
.int()
|
|
||||||
.label(x)
|
|
||||||
.unit(unit)
|
|
||||||
.min(min.unwrap_or(-((1_u64 << f64::MANTISSA_DIGITS) as f64)))
|
|
||||||
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
|
||||||
.on_update(update_value(update_x, node_id, index))
|
|
||||||
.on_commit(commit_value)
|
|
||||||
.widget_holder(),
|
|
||||||
Separator::new(SeparatorType::Related).widget_holder(),
|
|
||||||
NumberInput::new(Some(ivec2.y as f64))
|
|
||||||
.int()
|
|
||||||
.label(y)
|
|
||||||
.unit(unit)
|
|
||||||
.min(min.unwrap_or(-((1_u64 << f64::MANTISSA_DIGITS) as f64)))
|
|
||||||
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
|
||||||
.on_update(update_value(update_y, node_id, index))
|
|
||||||
.on_commit(commit_value)
|
|
||||||
.widget_holder(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
Some(&TaggedValue::UVec2(uvec2)) => {
|
|
||||||
let update_x = move |input: &NumberInput| TaggedValue::UVec2(UVec2::new(input.value.unwrap() as u32, uvec2.y));
|
|
||||||
let update_y = move |input: &NumberInput| TaggedValue::UVec2(UVec2::new(uvec2.x, input.value.unwrap() as u32));
|
|
||||||
widgets.extend_from_slice(&[
|
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
|
||||||
NumberInput::new(Some(uvec2.x as f64))
|
|
||||||
.int()
|
|
||||||
.label(x)
|
|
||||||
.unit(unit)
|
|
||||||
.min(min.unwrap_or(0.))
|
|
||||||
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
|
||||||
.on_update(update_value(update_x, node_id, index))
|
|
||||||
.on_commit(commit_value)
|
|
||||||
.widget_holder(),
|
|
||||||
Separator::new(SeparatorType::Related).widget_holder(),
|
|
||||||
NumberInput::new(Some(uvec2.y as f64))
|
|
||||||
.int()
|
|
||||||
.label(y)
|
|
||||||
.unit(unit)
|
|
||||||
.min(min.unwrap_or(0.))
|
|
||||||
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
|
||||||
.on_update(update_value(update_y, node_id, index))
|
|
||||||
.on_commit(commit_value)
|
|
||||||
.widget_holder(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
Some(&TaggedValue::F64(value)) => {
|
Some(&TaggedValue::F64(value)) => {
|
||||||
widgets.extend_from_slice(&[
|
widgets.extend_from_slice(&[
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
@@ -600,6 +668,7 @@ pub fn coordinate_widget(parameter_widgets_info: ParameterWidgetsInfo, x: &str,
|
|||||||
.unit(unit)
|
.unit(unit)
|
||||||
.min(min.unwrap_or(-((1_u64 << f64::MANTISSA_DIGITS) as f64)))
|
.min(min.unwrap_or(-((1_u64 << f64::MANTISSA_DIGITS) as f64)))
|
||||||
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
|
.is_integer(is_integer)
|
||||||
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(input.value.unwrap(), value)), node_id, index))
|
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(input.value.unwrap(), value)), node_id, index))
|
||||||
.on_commit(commit_value)
|
.on_commit(commit_value)
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
@@ -609,6 +678,7 @@ pub fn coordinate_widget(parameter_widgets_info: ParameterWidgetsInfo, x: &str,
|
|||||||
.unit(unit)
|
.unit(unit)
|
||||||
.min(min.unwrap_or(-((1_u64 << f64::MANTISSA_DIGITS) as f64)))
|
.min(min.unwrap_or(-((1_u64 << f64::MANTISSA_DIGITS) as f64)))
|
||||||
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
|
||||||
|
.is_integer(is_integer)
|
||||||
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(value, input.value.unwrap())), node_id, index))
|
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(value, input.value.unwrap())), node_id, index))
|
||||||
.on_commit(commit_value)
|
.on_commit(commit_value)
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
@@ -1178,7 +1248,7 @@ pub(crate) fn grid_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
|||||||
if let Some(&TaggedValue::GridType(grid_type)) = grid_type_input.as_non_exposed_value() {
|
if let Some(&TaggedValue::GridType(grid_type)) = grid_type_input.as_non_exposed_value() {
|
||||||
match grid_type {
|
match grid_type {
|
||||||
GridType::Rectangular => {
|
GridType::Rectangular => {
|
||||||
let spacing = coordinate_widget(ParameterWidgetsInfo::new(node_id, SpacingInput::<f64>::INDEX, true, context), "W", "H", " px", Some(0.));
|
let spacing = coordinate_widget(ParameterWidgetsInfo::new(node_id, SpacingInput::<f64>::INDEX, true, context), "W", "H", " px", Some(0.), false);
|
||||||
widgets.push(spacing);
|
widgets.push(spacing);
|
||||||
}
|
}
|
||||||
GridType::Isometric => {
|
GridType::Isometric => {
|
||||||
@@ -1188,7 +1258,7 @@ pub(crate) fn grid_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
|||||||
NumberInput::default().label("H").min(0.).unit(" px"),
|
NumberInput::default().label("H").min(0.).unit(" px"),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
let angles = coordinate_widget(ParameterWidgetsInfo::new(node_id, AnglesInput::INDEX, true, context), "", "", "°", None);
|
let angles = coordinate_widget(ParameterWidgetsInfo::new(node_id, AnglesInput::INDEX, true, context), "", "", "°", None, false);
|
||||||
widgets.extend([spacing, angles]);
|
widgets.extend([spacing, angles]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1307,8 +1377,8 @@ pub(crate) fn rectangle_properties(node_id: NodeId, context: &mut NodeProperties
|
|||||||
// Uniform/individual radio input widget
|
// Uniform/individual radio input widget
|
||||||
let uniform = RadioEntryData::new("Uniform")
|
let uniform = RadioEntryData::new("Uniform")
|
||||||
.label("Uniform")
|
.label("Uniform")
|
||||||
.on_update(move |_| {
|
.on_update(move |_| Message::Batched {
|
||||||
Message::Batched(Box::new([
|
messages: Box::new([
|
||||||
NodeGraphMessage::SetInputValue {
|
NodeGraphMessage::SetInputValue {
|
||||||
node_id,
|
node_id,
|
||||||
input_index: IndividualCornerRadiiInput::INDEX,
|
input_index: IndividualCornerRadiiInput::INDEX,
|
||||||
@@ -1321,13 +1391,13 @@ pub(crate) fn rectangle_properties(node_id: NodeId, context: &mut NodeProperties
|
|||||||
value: TaggedValue::F64(uniform_val),
|
value: TaggedValue::F64(uniform_val),
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
]))
|
]),
|
||||||
})
|
})
|
||||||
.on_commit(commit_value);
|
.on_commit(commit_value);
|
||||||
let individual = RadioEntryData::new("Individual")
|
let individual = RadioEntryData::new("Individual")
|
||||||
.label("Individual")
|
.label("Individual")
|
||||||
.on_update(move |_| {
|
.on_update(move |_| Message::Batched {
|
||||||
Message::Batched(Box::new([
|
messages: Box::new([
|
||||||
NodeGraphMessage::SetInputValue {
|
NodeGraphMessage::SetInputValue {
|
||||||
node_id,
|
node_id,
|
||||||
input_index: IndividualCornerRadiiInput::INDEX,
|
input_index: IndividualCornerRadiiInput::INDEX,
|
||||||
@@ -1340,7 +1410,7 @@ pub(crate) fn rectangle_properties(node_id: NodeId, context: &mut NodeProperties
|
|||||||
value: TaggedValue::F64Array4(individual_val),
|
value: TaggedValue::F64Array4(individual_val),
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
]))
|
]),
|
||||||
})
|
})
|
||||||
.on_commit(commit_value);
|
.on_commit(commit_value);
|
||||||
let radio_input = RadioInput::new(vec![uniform, individual]).selected_index(Some(is_individual as u32)).widget_holder();
|
let radio_input = RadioInput::new(vec![uniform, individual]).selected_index(Some(is_individual as u32)).widget_holder();
|
||||||
@@ -1396,9 +1466,9 @@ pub(crate) fn rectangle_properties(node_id: NodeId, context: &mut NodeProperties
|
|||||||
|
|
||||||
pub(crate) fn node_no_properties(node_id: NodeId, context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
pub(crate) fn node_no_properties(node_id: NodeId, context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
|
||||||
let text = if context.network_interface.is_layer(&node_id, context.selection_network_path) {
|
let text = if context.network_interface.is_layer(&node_id, context.selection_network_path) {
|
||||||
"Layer has no properties"
|
"Layer has no parameters"
|
||||||
} else {
|
} else {
|
||||||
"Node has no properties"
|
"Node has no parameters"
|
||||||
};
|
};
|
||||||
string_properties(text)
|
string_properties(text)
|
||||||
}
|
}
|
||||||
@@ -1539,8 +1609,8 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
|||||||
widgets_first_row.push(
|
widgets_first_row.push(
|
||||||
ColorInput::default()
|
ColorInput::default()
|
||||||
.value(fill.clone().into())
|
.value(fill.clone().into())
|
||||||
.on_update(move |x: &ColorInput| {
|
.on_update(move |x: &ColorInput| Message::Batched {
|
||||||
Message::Batched(Box::new([
|
messages: Box::new([
|
||||||
match &fill2 {
|
match &fill2 {
|
||||||
Fill::None => NodeGraphMessage::SetInputValue {
|
Fill::None => NodeGraphMessage::SetInputValue {
|
||||||
node_id,
|
node_id,
|
||||||
@@ -1567,7 +1637,7 @@ pub(crate) fn fill_properties(node_id: NodeId, context: &mut NodePropertiesConte
|
|||||||
value: TaggedValue::Fill(x.value.to_fill(fill2.as_gradient())),
|
value: TaggedValue::Fill(x.value.to_fill(fill2.as_gradient())),
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
]))
|
]),
|
||||||
})
|
})
|
||||||
.on_commit(commit_value)
|
.on_commit(commit_value)
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
|
|||||||
@@ -23,13 +23,12 @@ impl FrontendGraphDataType {
|
|||||||
TaggedValue::U32(_)
|
TaggedValue::U32(_)
|
||||||
| TaggedValue::U64(_)
|
| TaggedValue::U64(_)
|
||||||
| TaggedValue::F64(_)
|
| TaggedValue::F64(_)
|
||||||
| TaggedValue::UVec2(_)
|
|
||||||
| TaggedValue::IVec2(_)
|
|
||||||
| TaggedValue::DVec2(_)
|
| TaggedValue::DVec2(_)
|
||||||
| TaggedValue::OptionalDVec2(_)
|
| TaggedValue::OptionalDVec2(_)
|
||||||
| TaggedValue::F64Array4(_)
|
| TaggedValue::F64Array4(_)
|
||||||
| TaggedValue::VecF64(_)
|
| TaggedValue::VecF64(_)
|
||||||
| TaggedValue::VecDVec2(_) => Self::Number,
|
| TaggedValue::VecDVec2(_)
|
||||||
|
| TaggedValue::DAffine2(_) => Self::Number,
|
||||||
TaggedValue::GraphicGroup(_) | TaggedValue::GraphicElement(_) => Self::Group, // TODO: Is GraphicElement supposed to be included here?
|
TaggedValue::GraphicGroup(_) | TaggedValue::GraphicElement(_) => Self::Group, // TODO: Is GraphicElement supposed to be included here?
|
||||||
TaggedValue::ArtboardGroup(_) => Self::Artboard,
|
TaggedValue::ArtboardGroup(_) => Self::Artboard,
|
||||||
_ => Self::General,
|
_ => Self::General,
|
||||||
|
|||||||
@@ -7,4 +7,4 @@ pub mod utility_types;
|
|||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use overlays_message::{OverlaysMessage, OverlaysMessageDiscriminant};
|
pub use overlays_message::{OverlaysMessage, OverlaysMessageDiscriminant};
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use overlays_message_handler::{OverlaysMessageData, OverlaysMessageHandler};
|
pub use overlays_message_handler::{OverlaysMessageContext, OverlaysMessageHandler};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use super::utility_types::{OverlayProvider, OverlaysVisibilitySettings};
|
|||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
#[derive(ExtractField)]
|
#[derive(ExtractField)]
|
||||||
pub struct OverlaysMessageData<'a> {
|
pub struct OverlaysMessageContext<'a> {
|
||||||
pub visibility_settings: OverlaysVisibilitySettings,
|
pub visibility_settings: OverlaysVisibilitySettings,
|
||||||
pub ipp: &'a InputPreprocessorMessageHandler,
|
pub ipp: &'a InputPreprocessorMessageHandler,
|
||||||
pub device_pixel_ratio: f64,
|
pub device_pixel_ratio: f64,
|
||||||
@@ -18,9 +18,11 @@ pub struct OverlaysMessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl MessageHandler<OverlaysMessage, OverlaysMessageData<'_>> for OverlaysMessageHandler {
|
impl MessageHandler<OverlaysMessage, OverlaysMessageContext<'_>> for OverlaysMessageHandler {
|
||||||
fn process_message(&mut self, message: OverlaysMessage, responses: &mut VecDeque<Message>, data: OverlaysMessageData) {
|
fn process_message(&mut self, message: OverlaysMessage, responses: &mut VecDeque<Message>, context: OverlaysMessageContext) {
|
||||||
let OverlaysMessageData { visibility_settings, ipp, .. } = data;
|
let OverlaysMessageContext { visibility_settings, ipp, .. } = context;
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
let device_pixel_ratio = context.device_pixel_ratio;
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
@@ -30,8 +32,6 @@ impl MessageHandler<OverlaysMessage, OverlaysMessageData<'_>> for OverlaysMessag
|
|||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use wasm_bindgen::JsCast;
|
use wasm_bindgen::JsCast;
|
||||||
|
|
||||||
let device_pixel_ratio = data.device_pixel_ratio;
|
|
||||||
|
|
||||||
let canvas = match &self.canvas {
|
let canvas = match &self.canvas {
|
||||||
Some(canvas) => canvas,
|
Some(canvas) => canvas,
|
||||||
None => {
|
None => {
|
||||||
@@ -40,28 +40,28 @@ impl MessageHandler<OverlaysMessage, OverlaysMessageData<'_>> for OverlaysMessag
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let context = self.context.get_or_insert_with(|| {
|
let canvas_context = self.context.get_or_insert_with(|| {
|
||||||
let context = canvas.get_context("2d").ok().flatten().expect("Failed to get canvas context");
|
let canvas_context = canvas.get_context("2d").ok().flatten().expect("Failed to get canvas context");
|
||||||
context.dyn_into().expect("Context should be a canvas 2d context")
|
canvas_context.dyn_into().expect("Context should be a canvas 2d context")
|
||||||
});
|
});
|
||||||
|
|
||||||
let size = ipp.viewport_bounds.size().as_uvec2();
|
let size = ipp.viewport_bounds.size().as_uvec2();
|
||||||
|
|
||||||
let [a, b, c, d, e, f] = DAffine2::from_scale(DVec2::splat(device_pixel_ratio)).to_cols_array();
|
let [a, b, c, d, e, f] = DAffine2::from_scale(DVec2::splat(device_pixel_ratio)).to_cols_array();
|
||||||
let _ = context.set_transform(a, b, c, d, e, f);
|
let _ = canvas_context.set_transform(a, b, c, d, e, f);
|
||||||
context.clear_rect(0., 0., ipp.viewport_bounds.size().x, ipp.viewport_bounds.size().y);
|
canvas_context.clear_rect(0., 0., ipp.viewport_bounds.size().x, ipp.viewport_bounds.size().y);
|
||||||
let _ = context.reset_transform();
|
let _ = canvas_context.reset_transform();
|
||||||
|
|
||||||
if visibility_settings.all() {
|
if visibility_settings.all() {
|
||||||
responses.add(DocumentMessage::GridOverlays(OverlayContext {
|
responses.add(DocumentMessage::GridOverlays(OverlayContext {
|
||||||
render_context: context.clone(),
|
render_context: canvas_context.clone(),
|
||||||
size: size.as_dvec2(),
|
size: size.as_dvec2(),
|
||||||
device_pixel_ratio,
|
device_pixel_ratio,
|
||||||
visibility_settings: visibility_settings.clone(),
|
visibility_settings: visibility_settings.clone(),
|
||||||
}));
|
}));
|
||||||
for provider in &self.overlay_providers {
|
for provider in &self.overlay_providers {
|
||||||
responses.add(provider(OverlayContext {
|
responses.add(provider(OverlayContext {
|
||||||
render_context: context.clone(),
|
render_context: canvas_context.clone(),
|
||||||
size: size.as_dvec2(),
|
size: size.as_dvec2(),
|
||||||
device_pixel_ratio,
|
device_pixel_ratio,
|
||||||
visibility_settings: visibility_settings.clone(),
|
visibility_settings: visibility_settings.clone(),
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
use super::utility_functions::overlay_canvas_context;
|
use super::utility_functions::overlay_canvas_context;
|
||||||
use crate::consts::{
|
use crate::consts::{
|
||||||
COLOR_OVERLAY_BLUE, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, COLOR_OVERLAY_WHITE, COLOR_OVERLAY_YELLOW, COLOR_OVERLAY_YELLOW_DULL, COMPASS_ROSE_ARROW_SIZE, COMPASS_ROSE_HOVER_RING_DIAMETER,
|
COLOR_OVERLAY_BLUE, COLOR_OVERLAY_BLUE_50, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, COLOR_OVERLAY_WHITE, COLOR_OVERLAY_YELLOW, COLOR_OVERLAY_YELLOW_DULL, COMPASS_ROSE_ARROW_SIZE,
|
||||||
COMPASS_ROSE_MAIN_RING_DIAMETER, COMPASS_ROSE_RING_INNER_DIAMETER, DOWEL_PIN_RADIUS, MANIPULATOR_GROUP_MARKER_SIZE, PIVOT_CROSSHAIR_LENGTH, PIVOT_CROSSHAIR_THICKNESS, PIVOT_DIAMETER,
|
COMPASS_ROSE_HOVER_RING_DIAMETER, COMPASS_ROSE_MAIN_RING_DIAMETER, COMPASS_ROSE_RING_INNER_DIAMETER, DOWEL_PIN_RADIUS, MANIPULATOR_GROUP_MARKER_SIZE, PIVOT_CROSSHAIR_LENGTH,
|
||||||
|
PIVOT_CROSSHAIR_THICKNESS, PIVOT_DIAMETER,
|
||||||
};
|
};
|
||||||
use crate::messages::prelude::Message;
|
use crate::messages::prelude::Message;
|
||||||
use bezier_rs::{Bezier, Subpath};
|
use bezier_rs::{Bezier, Subpath};
|
||||||
@@ -349,6 +350,7 @@ impl OverlayContext {
|
|||||||
self.render_context.rect(corner.x, corner.y, size, size);
|
self.render_context.rect(corner.x, corner.y, size, size);
|
||||||
self.render_context.set_fill_style_str(color_fill);
|
self.render_context.set_fill_style_str(color_fill);
|
||||||
self.render_context.set_stroke_style_str(color_stroke);
|
self.render_context.set_stroke_style_str(color_stroke);
|
||||||
|
self.render_context.set_line_width(1.);
|
||||||
self.render_context.fill();
|
self.render_context.fill();
|
||||||
self.render_context.stroke();
|
self.render_context.stroke();
|
||||||
|
|
||||||
@@ -631,11 +633,9 @@ impl OverlayContext {
|
|||||||
pub fn outline_overlay_bezier(&mut self, bezier: Bezier, transform: DAffine2) {
|
pub fn outline_overlay_bezier(&mut self, bezier: Bezier, transform: DAffine2) {
|
||||||
self.start_dpi_aware_transform();
|
self.start_dpi_aware_transform();
|
||||||
|
|
||||||
let color = Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap()).unwrap().with_alpha(0.05).to_rgba_hex_srgb();
|
|
||||||
|
|
||||||
self.render_context.begin_path();
|
self.render_context.begin_path();
|
||||||
self.bezier_command(bezier, transform, true);
|
self.bezier_command(bezier, transform, true);
|
||||||
self.render_context.set_stroke_style_str(&color);
|
self.render_context.set_stroke_style_str(COLOR_OVERLAY_BLUE_50);
|
||||||
self.render_context.set_line_width(4.);
|
self.render_context.set_line_width(4.);
|
||||||
self.render_context.stroke();
|
self.render_context.stroke();
|
||||||
|
|
||||||
@@ -727,6 +727,7 @@ impl OverlayContext {
|
|||||||
|
|
||||||
let color = color.unwrap_or(COLOR_OVERLAY_BLUE);
|
let color = color.unwrap_or(COLOR_OVERLAY_BLUE);
|
||||||
self.render_context.set_stroke_style_str(color);
|
self.render_context.set_stroke_style_str(color);
|
||||||
|
self.render_context.set_line_width(1.);
|
||||||
self.render_context.stroke();
|
self.render_context.stroke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
mod properties_panel_message;
|
mod properties_panel_message;
|
||||||
mod properties_panel_message_handler;
|
pub mod properties_panel_message_handler;
|
||||||
|
|
||||||
pub mod utility_types;
|
|
||||||
|
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use properties_panel_message::{PropertiesPanelMessage, PropertiesPanelMessageDiscriminant};
|
pub use properties_panel_message::{PropertiesPanelMessage, PropertiesPanelMessageDiscriminant};
|
||||||
|
|||||||
+21
-8
@@ -1,21 +1,34 @@
|
|||||||
use super::utility_types::PropertiesPanelMessageHandlerData;
|
use graphene_std::uuid::NodeId;
|
||||||
|
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::portfolio::document::node_graph::document_node_definitions::NodePropertiesContext;
|
use crate::messages::portfolio::document::node_graph::document_node_definitions::NodePropertiesContext;
|
||||||
|
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
|
||||||
use crate::messages::portfolio::utility_types::PersistentData;
|
use crate::messages::portfolio::utility_types::PersistentData;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
use crate::node_graph_executor::NodeGraphExecutor;
|
||||||
|
|
||||||
|
#[derive(ExtractField)]
|
||||||
|
pub struct PropertiesPanelMessageContext<'a> {
|
||||||
|
pub network_interface: &'a mut NodeNetworkInterface,
|
||||||
|
pub selection_network_path: &'a [NodeId],
|
||||||
|
pub document_name: &'a str,
|
||||||
|
pub executor: &'a mut NodeGraphExecutor,
|
||||||
|
pub persistent_data: &'a PersistentData,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, ExtractField)]
|
#[derive(Debug, Clone, Default, ExtractField)]
|
||||||
pub struct PropertiesPanelMessageHandler {}
|
pub struct PropertiesPanelMessageHandler {}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPanelMessageHandlerData<'_>)> for PropertiesPanelMessageHandler {
|
impl MessageHandler<PropertiesPanelMessage, PropertiesPanelMessageContext<'_>> for PropertiesPanelMessageHandler {
|
||||||
fn process_message(&mut self, message: PropertiesPanelMessage, responses: &mut VecDeque<Message>, (persistent_data, data): (&PersistentData, PropertiesPanelMessageHandlerData)) {
|
fn process_message(&mut self, message: PropertiesPanelMessage, responses: &mut VecDeque<Message>, context: PropertiesPanelMessageContext) {
|
||||||
let PropertiesPanelMessageHandlerData {
|
let PropertiesPanelMessageContext {
|
||||||
network_interface,
|
network_interface,
|
||||||
selection_network_path,
|
selection_network_path,
|
||||||
document_name,
|
document_name,
|
||||||
executor,
|
executor,
|
||||||
} = data;
|
persistent_data,
|
||||||
|
} = context;
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
PropertiesPanelMessage::Clear => {
|
PropertiesPanelMessage::Clear => {
|
||||||
@@ -25,7 +38,7 @@ impl MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPanelMes
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
PropertiesPanelMessage::Refresh => {
|
PropertiesPanelMessage::Refresh => {
|
||||||
let mut context = NodePropertiesContext {
|
let mut node_properties_context = NodePropertiesContext {
|
||||||
persistent_data,
|
persistent_data,
|
||||||
responses,
|
responses,
|
||||||
network_interface,
|
network_interface,
|
||||||
@@ -33,9 +46,9 @@ impl MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPanelMes
|
|||||||
document_name,
|
document_name,
|
||||||
executor,
|
executor,
|
||||||
};
|
};
|
||||||
let properties_sections = NodeGraphMessageHandler::collate_properties(&mut context);
|
let properties_sections = NodeGraphMessageHandler::collate_properties(&mut node_properties_context);
|
||||||
|
|
||||||
context.responses.add(LayoutMessage::SendLayout {
|
node_properties_context.responses.add(LayoutMessage::SendLayout {
|
||||||
layout: Layout::WidgetLayout(WidgetLayout::new(properties_sections)),
|
layout: Layout::WidgetLayout(WidgetLayout::new(properties_sections)),
|
||||||
layout_target: LayoutTarget::PropertiesSections,
|
layout_target: LayoutTarget::PropertiesSections,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
|
|
||||||
use crate::node_graph_executor::NodeGraphExecutor;
|
|
||||||
use graph_craft::document::NodeId;
|
|
||||||
|
|
||||||
pub struct PropertiesPanelMessageHandlerData<'a> {
|
|
||||||
pub network_interface: &'a mut NodeNetworkInterface,
|
|
||||||
pub selection_network_path: &'a [NodeId],
|
|
||||||
pub document_name: &'a str,
|
|
||||||
pub executor: &'a mut NodeGraphExecutor,
|
|
||||||
}
|
|
||||||
@@ -250,12 +250,8 @@ impl LayerNodeIdentifier {
|
|||||||
|
|
||||||
/// Construct a [`LayerNodeIdentifier`], debug asserting that it is a layer node. This should only be used in the document network since the structure is not loaded in nested networks.
|
/// Construct a [`LayerNodeIdentifier`], debug asserting that it is a layer node. This should only be used in the document network since the structure is not loaded in nested networks.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn new(node_id: NodeId, network_interface: &NodeNetworkInterface, network_path: &[NodeId]) -> Self {
|
pub fn new(node_id: NodeId, network_interface: &NodeNetworkInterface) -> Self {
|
||||||
debug_assert!(
|
debug_assert!(network_interface.is_layer(&node_id, &[]), "Layer identifier constructed from non-layer node {node_id}",);
|
||||||
network_interface.is_layer(&node_id, network_path),
|
|
||||||
"Layer identifier constructed from non-layer node {node_id}: {:#?}",
|
|
||||||
network_interface.nested_network(network_path).unwrap().nodes.get(&node_id)
|
|
||||||
);
|
|
||||||
Self::new_unchecked(node_id)
|
Self::new_unchecked(node_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -203,12 +203,12 @@ impl NodeNetworkInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the first downstream layer(inclusive) from a node. If the node is a layer, it will return itself.
|
/// Returns the first downstream layer(inclusive) from a node. If the node is a layer, it will return itself.
|
||||||
pub fn downstream_layer(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> Option<LayerNodeIdentifier> {
|
pub fn downstream_layer_for_chain_node(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> Option<NodeId> {
|
||||||
let mut id = *node_id;
|
let mut id = *node_id;
|
||||||
while !self.is_layer(&id, network_path) {
|
while !self.is_layer(&id, network_path) {
|
||||||
id = self.outward_wires(network_path)?.get(&OutputConnector::node(id, 0))?.first()?.node_id()?;
|
id = self.outward_wires(network_path)?.get(&OutputConnector::node(id, 0))?.first()?.node_id()?;
|
||||||
}
|
}
|
||||||
Some(LayerNodeIdentifier::new(id, self, network_path))
|
Some(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns all downstream layers (inclusive) from a node. If the node is a layer, it will return itself.
|
/// Returns all downstream layers (inclusive) from a node. If the node is a layer, it will return itself.
|
||||||
@@ -388,8 +388,8 @@ impl NodeNetworkInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If a chain node does not have a selected downstream layer, then set the position to absolute
|
// If a chain node does not have a selected downstream layer, then set the position to absolute
|
||||||
let downstream_layer = self.downstream_layer(node_id, network_path);
|
let downstream_layer = self.downstream_layer_for_chain_node(node_id, network_path);
|
||||||
if downstream_layer.is_none_or(|downstream_layer| new_ids.keys().all(|key| *key != downstream_layer.to_node())) {
|
if downstream_layer.is_none_or(|downstream_layer| new_ids.keys().all(|key| *key != downstream_layer)) {
|
||||||
let Some(position) = self.position(node_id, network_path) else {
|
let Some(position) = self.position(node_id, network_path) else {
|
||||||
log::error!("Could not get position in create_node_template");
|
log::error!("Could not get position in create_node_template");
|
||||||
return None;
|
return None;
|
||||||
@@ -1244,7 +1244,7 @@ impl NodeNetworkInterface {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.is_some_and(|reference| reference == "Artboard" && self.connected_to_output(node_id, &[]) && self.is_layer(node_id, &[]))
|
.is_some_and(|reference| reference == "Artboard" && self.connected_to_output(node_id, &[]) && self.is_layer(node_id, &[]))
|
||||||
{
|
{
|
||||||
Some(LayerNodeIdentifier::new(*node_id, self, &[]))
|
Some(LayerNodeIdentifier::new(*node_id, self))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -3025,7 +3025,7 @@ impl NodeNetworkInterface {
|
|||||||
|
|
||||||
// Helper functions for mutable getters
|
// Helper functions for mutable getters
|
||||||
impl NodeNetworkInterface {
|
impl NodeNetworkInterface {
|
||||||
pub fn upstream_chain_nodes(&mut self, network_path: &[NodeId]) -> Vec<NodeId> {
|
pub fn upstream_chain_nodes(&self, network_path: &[NodeId]) -> Vec<NodeId> {
|
||||||
let Some(selected_nodes) = self.selected_nodes_in_nested_network(network_path) else {
|
let Some(selected_nodes) = self.selected_nodes_in_nested_network(network_path) else {
|
||||||
log::error!("Could not get selected nodes in upstream_chain_nodes");
|
log::error!("Could not get selected nodes in upstream_chain_nodes");
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
@@ -3156,7 +3156,7 @@ impl NodeNetworkInterface {
|
|||||||
self.document_metadata.document_to_viewport = transform;
|
self.document_metadata.document_to_viewport = transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_eligible_to_be_layer(&mut self, node_id: &NodeId, network_path: &[NodeId]) -> bool {
|
pub fn is_eligible_to_be_layer(&self, node_id: &NodeId, network_path: &[NodeId]) -> bool {
|
||||||
let Some(node) = self.document_node(node_id, network_path) else {
|
let Some(node) = self.document_node(node_id, network_path) else {
|
||||||
log::error!("Could not get node {node_id} in is_eligible_to_be_layer");
|
log::error!("Could not get node {node_id} in is_eligible_to_be_layer");
|
||||||
return false;
|
return false;
|
||||||
@@ -3362,6 +3362,24 @@ impl NodeNetworkInterface {
|
|||||||
.map(|[a, b]| [node_graph_to_viewport.transform_point2(a), node_graph_to_viewport.transform_point2(b)])
|
.map(|[a, b]| [node_graph_to_viewport.transform_point2(a), node_graph_to_viewport.transform_point2(b)])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn selected_layers_artwork_bounding_box_viewport(&self) -> Option<[DVec2; 2]> {
|
||||||
|
self.selected_nodes()
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
|
.filter(|node| self.is_layer(&node, &[]))
|
||||||
|
.filter_map(|layer| self.document_metadata.bounding_box_viewport(LayerNodeIdentifier::new(*layer, self)))
|
||||||
|
.reduce(Quad::combine_bounds)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn selected_unlocked_layers_bounding_box_viewport(&self) -> Option<[DVec2; 2]> {
|
||||||
|
self.selected_nodes()
|
||||||
|
.0
|
||||||
|
.iter()
|
||||||
|
.filter(|node| self.is_layer(&node, &[]) && !self.is_layer(&node, &[]))
|
||||||
|
.filter_map(|layer| self.document_metadata.bounding_box_viewport(LayerNodeIdentifier::new(*layer, self)))
|
||||||
|
.reduce(Quad::combine_bounds)
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the combined bounding box of the click targets of the selected nodes in the node graph in layer space
|
/// Get the combined bounding box of the click targets of the selected nodes in the node graph in layer space
|
||||||
pub fn selected_nodes_bounding_box(&mut self, network_path: &[NodeId]) -> Option<[DVec2; 2]> {
|
pub fn selected_nodes_bounding_box(&mut self, network_path: &[NodeId]) -> Option<[DVec2; 2]> {
|
||||||
let Some(selected_nodes) = self.selected_nodes_in_nested_network(network_path) else {
|
let Some(selected_nodes) = self.selected_nodes_in_nested_network(network_path) else {
|
||||||
@@ -3451,7 +3469,7 @@ impl NodeNetworkInterface {
|
|||||||
|
|
||||||
let Some(first_root_layer) = self
|
let Some(first_root_layer) = self
|
||||||
.upstream_flow_back_from_nodes(vec![root_node.node_id], &[], FlowType::PrimaryFlow)
|
.upstream_flow_back_from_nodes(vec![root_node.node_id], &[], FlowType::PrimaryFlow)
|
||||||
.find_map(|node_id| if self.is_layer(&node_id, &[]) { Some(LayerNodeIdentifier::new(node_id, self, &[])) } else { None })
|
.find_map(|node_id| if self.is_layer(&node_id, &[]) { Some(LayerNodeIdentifier::new(node_id, self)) } else { None })
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -3467,7 +3485,7 @@ impl NodeNetworkInterface {
|
|||||||
if horizontal_root_node_id == first_root_layer.to_node() {
|
if horizontal_root_node_id == first_root_layer.to_node() {
|
||||||
for current_node_id in horizontal_flow_iter {
|
for current_node_id in horizontal_flow_iter {
|
||||||
if self.is_layer(¤t_node_id, &[]) {
|
if self.is_layer(¤t_node_id, &[]) {
|
||||||
let current_layer_node = LayerNodeIdentifier::new(current_node_id, self, &[]);
|
let current_layer_node = LayerNodeIdentifier::new(current_node_id, self);
|
||||||
if !self.document_metadata.structure.contains_key(¤t_layer_node) {
|
if !self.document_metadata.structure.contains_key(¤t_layer_node) {
|
||||||
if current_node_id == first_root_layer.to_node() {
|
if current_node_id == first_root_layer.to_node() {
|
||||||
awaiting_primary_flow.push((current_node_id, LayerNodeIdentifier::ROOT_PARENT));
|
awaiting_primary_flow.push((current_node_id, LayerNodeIdentifier::ROOT_PARENT));
|
||||||
@@ -3484,7 +3502,7 @@ impl NodeNetworkInterface {
|
|||||||
// Skip the horizontal_root_node_id node
|
// Skip the horizontal_root_node_id node
|
||||||
for current_node_id in horizontal_flow_iter.skip(1) {
|
for current_node_id in horizontal_flow_iter.skip(1) {
|
||||||
if self.is_layer(¤t_node_id, &[]) {
|
if self.is_layer(¤t_node_id, &[]) {
|
||||||
let current_layer_node = LayerNodeIdentifier::new(current_node_id, self, &[]);
|
let current_layer_node = LayerNodeIdentifier::new(current_node_id, self);
|
||||||
if !self.document_metadata.structure.contains_key(¤t_layer_node) {
|
if !self.document_metadata.structure.contains_key(¤t_layer_node) {
|
||||||
awaiting_primary_flow.push((current_node_id, parent_layer_node));
|
awaiting_primary_flow.push((current_node_id, parent_layer_node));
|
||||||
children.push((parent_layer_node, current_layer_node));
|
children.push((parent_layer_node, current_layer_node));
|
||||||
@@ -3505,7 +3523,7 @@ impl NodeNetworkInterface {
|
|||||||
for current_node_id in primary_flow_iter.skip(1) {
|
for current_node_id in primary_flow_iter.skip(1) {
|
||||||
if self.is_layer(¤t_node_id, &[]) {
|
if self.is_layer(¤t_node_id, &[]) {
|
||||||
// Create a new layer for the top of each stack, and add it as a child to the previous parent
|
// Create a new layer for the top of each stack, and add it as a child to the previous parent
|
||||||
let current_layer_node = LayerNodeIdentifier::new(current_node_id, self, &[]);
|
let current_layer_node = LayerNodeIdentifier::new(current_node_id, self);
|
||||||
if !self.document_metadata.structure.contains_key(¤t_layer_node) {
|
if !self.document_metadata.structure.contains_key(¤t_layer_node) {
|
||||||
children.push(current_layer_node);
|
children.push(current_layer_node);
|
||||||
|
|
||||||
@@ -3568,7 +3586,7 @@ impl NodeNetworkInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
stack.extend(self_network_metadata.persistent_metadata.node_metadata.keys().map(|node_id| {
|
stack.extend(self_network_metadata.persistent_metadata.node_metadata.keys().map(|node_id| {
|
||||||
let mut current_path = path.clone();
|
let mut current_path: Vec<NodeId> = path.clone();
|
||||||
current_path.push(*node_id);
|
current_path.push(*node_id);
|
||||||
current_path
|
current_path
|
||||||
}));
|
}));
|
||||||
@@ -5085,12 +5103,45 @@ impl NodeNetworkInterface {
|
|||||||
else {
|
else {
|
||||||
log::error!("Could not set chain position for layer node {node_id}");
|
log::error!("Could not set chain position for layer node {node_id}");
|
||||||
}
|
}
|
||||||
|
// let previous_upstream_node = self.upstream_output_connector(&InputConnector::node(*node_id, 0), network_path).and_then(|output| output.node_id());
|
||||||
|
// let Some(previous_upstream_node_position) = previous_upstream_node.and_then(|upstream| self.position_from_downstream_node(&upstream, network_path)) else {
|
||||||
|
// log::error!("Could not get previous_upstream_node_position");
|
||||||
|
// return;
|
||||||
|
// };
|
||||||
self.unload_upstream_node_click_targets(vec![*node_id], network_path);
|
self.unload_upstream_node_click_targets(vec![*node_id], network_path);
|
||||||
// Reload click target of the layer which encapsulate the chain
|
// Reload click target of the layer which encapsulate the chain
|
||||||
if let Some(downstream_layer) = self.downstream_layer(node_id, network_path) {
|
if let Some(downstream_layer) = self.downstream_layer_for_chain_node(node_id, network_path) {
|
||||||
self.unload_node_click_targets(&downstream_layer.to_node(), network_path);
|
self.unload_node_click_targets(&downstream_layer, network_path);
|
||||||
}
|
}
|
||||||
self.unload_all_nodes_bounding_box(network_path);
|
self.unload_all_nodes_bounding_box(network_path);
|
||||||
|
|
||||||
|
// let Some(new_upstream_node_position) = previous_upstream_node.and_then(|upstream| self.position_from_downstream_node(&upstream, network_path)) else {
|
||||||
|
// log::error!("Could not get new_upstream_node_position");
|
||||||
|
// return;
|
||||||
|
// };
|
||||||
|
// if let Some(previous_upstream_node) = {
|
||||||
|
// let x_delta = new_upstream_node_position.x - previous_upstream_node_position.x;
|
||||||
|
// // Upstream node got shifted to left, so shift all upstream absolute sole dependents
|
||||||
|
// if x_delta != 0 {
|
||||||
|
// let upstream_absolute_nodes = SelectedNodes(
|
||||||
|
// self.upstream_flow_back_from_nodes(vec![previous_upstream_node], network_path, FlowType::UpstreamFlow)
|
||||||
|
// .into_iter()
|
||||||
|
// .filter(|node_id| self.is_absolute(node_id, network_path))
|
||||||
|
// .collect::<Vec<_>>(),
|
||||||
|
// );
|
||||||
|
// let old_selected_nodes = std::mem::replace(self.selected_nodes_mut(network_path).unwrap(), upstream_absolute_nodes);
|
||||||
|
// if x_delta < 0 {
|
||||||
|
// for _ in 0..x_delta.abs() {
|
||||||
|
// self.shift_selected_nodes(Direction::Left, false, network_path);
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// for _ in 0..x_delta.abs() {
|
||||||
|
// self.shift_selected_nodes(Direction::Right, false, network_path);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// let _ = std::mem::replace(self.selected_nodes_mut(network_path).unwrap(), old_selected_nodes);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn valid_upstream_chain_nodes(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> Vec<NodeId> {
|
fn valid_upstream_chain_nodes(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> Vec<NodeId> {
|
||||||
@@ -5205,7 +5256,7 @@ impl NodeNetworkInterface {
|
|||||||
|
|
||||||
/// node_id is the first chain node, not the layer
|
/// node_id is the first chain node, not the layer
|
||||||
fn set_upstream_chain_to_absolute(&mut self, node_id: &NodeId, network_path: &[NodeId]) {
|
fn set_upstream_chain_to_absolute(&mut self, node_id: &NodeId, network_path: &[NodeId]) {
|
||||||
let Some(downstream_layer) = self.downstream_layer(node_id, network_path) else {
|
let Some(downstream_layer) = self.downstream_layer_for_chain_node(node_id, network_path) else {
|
||||||
log::error!("Could not get downstream layer in set_upstream_chain_to_absolute");
|
log::error!("Could not get downstream layer in set_upstream_chain_to_absolute");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -5218,7 +5269,7 @@ impl NodeNetworkInterface {
|
|||||||
if self.is_chain(upstream_id, network_path) {
|
if self.is_chain(upstream_id, network_path) {
|
||||||
self.set_absolute_position(upstream_id, previous_position, network_path);
|
self.set_absolute_position(upstream_id, previous_position, network_path);
|
||||||
// Reload click target of the layer which used to encapsulate the chain
|
// Reload click target of the layer which used to encapsulate the chain
|
||||||
self.unload_node_click_targets(&downstream_layer.to_node(), network_path);
|
self.unload_node_click_targets(&downstream_layer, network_path);
|
||||||
}
|
}
|
||||||
// If there is an upstream layer then stop breaking the chain
|
// If there is an upstream layer then stop breaking the chain
|
||||||
else {
|
else {
|
||||||
@@ -5297,8 +5348,8 @@ impl NodeNetworkInterface {
|
|||||||
// Deselect chain nodes upstream from a selected layer
|
// Deselect chain nodes upstream from a selected layer
|
||||||
if self.is_chain(selected_node, network_path)
|
if self.is_chain(selected_node, network_path)
|
||||||
&& self
|
&& self
|
||||||
.downstream_layer(selected_node, network_path)
|
.downstream_layer_for_chain_node(selected_node, network_path)
|
||||||
.is_some_and(|downstream_layer| node_ids.contains(&downstream_layer.to_node()))
|
.is_some_and(|downstream_layer| node_ids.contains(&downstream_layer))
|
||||||
{
|
{
|
||||||
node_ids.remove(selected_node);
|
node_ids.remove(selected_node);
|
||||||
}
|
}
|
||||||
@@ -5947,31 +5998,6 @@ impl NodeNetworkInterface {
|
|||||||
self.create_wire(&OutputConnector::node(*node_id, 0), &InputConnector::node(parent.to_node(), 1), network_path);
|
self.create_wire(&OutputConnector::node(*node_id, 0), &InputConnector::node(parent.to_node(), 1), network_path);
|
||||||
self.set_chain_position(node_id, network_path);
|
self.set_chain_position(node_id, network_path);
|
||||||
} else {
|
} else {
|
||||||
// TODO: Implement a more robust horizontal shift system when inserting a node into a chain.
|
|
||||||
// This should be done by breaking the chain and shifting the sole dependents for each node upstream of the insertion.
|
|
||||||
// Before inserting the node, shift the layer right 7 units so that all sole dependents are also shifted
|
|
||||||
// let input_connector = InputConnector::node(parent.to_node(), 0);
|
|
||||||
// let old_upstream = self.upstream_output_connector(&input_connector, network_path);
|
|
||||||
// This also needs to disconnect from the downstream layer
|
|
||||||
// self.disconnect_input(&input_connector, network_path);
|
|
||||||
// let Some(selected_nodes) = self.selected_nodes_mut(network_path) else {
|
|
||||||
// log::error!("Could not get selected nodes in move_layer_to_stack");
|
|
||||||
// return;
|
|
||||||
// };
|
|
||||||
// let old_selected_nodes = selected_nodes.replace_with(vec![parent.to_node()]);
|
|
||||||
|
|
||||||
// for _ in 0..7 {
|
|
||||||
// self.shift_selected_nodes(Direction::Left, false, network_path);
|
|
||||||
// }
|
|
||||||
// // Grip drag it back to the right
|
|
||||||
// for _ in 0..7 {
|
|
||||||
// self.shift_selected_nodes(Direction::Right, true, network_path);
|
|
||||||
// }
|
|
||||||
// let _ = self.selected_nodes_mut(network_path).unwrap().replace_with(old_selected_nodes);
|
|
||||||
// if let Some(old_upstream) = old_upstream {
|
|
||||||
// self.create_wire(&old_upstream, &input_connector, network_path);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Insert the node in the gap and set the upstream to a chain
|
// Insert the node in the gap and set the upstream to a chain
|
||||||
self.insert_node_between(node_id, &InputConnector::node(parent.to_node(), 1), 0, network_path);
|
self.insert_node_between(node_id, &InputConnector::node(parent.to_node(), 1), 0, network_path);
|
||||||
self.force_set_upstream_to_chain(node_id, network_path);
|
self.force_set_upstream_to_chain(node_id, network_path);
|
||||||
@@ -6473,6 +6499,7 @@ pub struct Vec2InputSettings {
|
|||||||
pub y: String,
|
pub y: String,
|
||||||
pub unit: String,
|
pub unit: String,
|
||||||
pub min: Option<f64>,
|
pub min: Option<f64>,
|
||||||
|
pub is_integer: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
@@ -6547,6 +6574,7 @@ impl InputPersistentMetadata {
|
|||||||
self.input_data.insert("x".to_string(), json!(vec2_properties.x));
|
self.input_data.insert("x".to_string(), json!(vec2_properties.x));
|
||||||
self.input_data.insert("y".to_string(), json!(vec2_properties.y));
|
self.input_data.insert("y".to_string(), json!(vec2_properties.y));
|
||||||
self.input_data.insert("unit".to_string(), json!(vec2_properties.unit));
|
self.input_data.insert("unit".to_string(), json!(vec2_properties.unit));
|
||||||
|
self.input_data.insert("is_integer".to_string(), Value::Bool(vec2_properties.is_integer));
|
||||||
if let Some(min) = vec2_properties.min {
|
if let Some(min) = vec2_properties.min {
|
||||||
self.input_data.insert("min".to_string(), json!(min));
|
self.input_data.insert("min".to_string(), json!(min));
|
||||||
}
|
}
|
||||||
@@ -6776,13 +6804,6 @@ impl From<DocumentNodePersistentMetadataPropertiesRow> for DocumentNodePersisten
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Serialize, serde::Deserialize)]
|
|
||||||
enum NodePersistentMetadataVersions {
|
|
||||||
DocumentNodePersistentMetadataPropertiesRow(DocumentNodePersistentMetadataPropertiesRow),
|
|
||||||
NodePersistentMetadataInputNames(DocumentNodePersistentMetadataInputNames),
|
|
||||||
NodePersistentMetadata(DocumentNodePersistentMetadata),
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deserialize_node_persistent_metadata<'de, D>(deserializer: D) -> Result<DocumentNodePersistentMetadata, D::Error>
|
fn deserialize_node_persistent_metadata<'de, D>(deserializer: D) -> Result<DocumentNodePersistentMetadata, D::Error>
|
||||||
where
|
where
|
||||||
D: serde::Deserializer<'de>,
|
D: serde::Deserializer<'de>,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use bezier_rs::Subpath;
|
|||||||
use glam::IVec2;
|
use glam::IVec2;
|
||||||
use graph_craft::document::DocumentNode;
|
use graph_craft::document::DocumentNode;
|
||||||
use graph_craft::document::{DocumentNodeImplementation, NodeInput, value::TaggedValue};
|
use graph_craft::document::{DocumentNodeImplementation, NodeInput, value::TaggedValue};
|
||||||
|
use graphene_std::ProtoNodeIdentifier;
|
||||||
use graphene_std::text::TypesettingConfig;
|
use graphene_std::text::TypesettingConfig;
|
||||||
use graphene_std::uuid::NodeId;
|
use graphene_std::uuid::NodeId;
|
||||||
use graphene_std::vector::style::{PaintOrder, StrokeAlign};
|
use graphene_std::vector::style::{PaintOrder, StrokeAlign};
|
||||||
@@ -20,156 +21,440 @@ const TEXT_REPLACEMENTS: &[(&str, &str)] = &[
|
|||||||
("graphene_core::vector::vector_nodes::SubpathSegmentLengthsNode", "graphene_core::vector::SubpathSegmentLengthsNode"),
|
("graphene_core::vector::vector_nodes::SubpathSegmentLengthsNode", "graphene_core::vector::SubpathSegmentLengthsNode"),
|
||||||
];
|
];
|
||||||
|
|
||||||
const REPLACEMENTS: &[(&str, &str)] = &[
|
pub struct NodeReplacement<'a> {
|
||||||
("graphene_core::AddArtboardNode", "graphene_core::graphic_element::AppendArtboardNode"),
|
node: ProtoNodeIdentifier,
|
||||||
("graphene_core::ConstructArtboardNode", "graphene_core::graphic_element::ToArtboardNode"),
|
aliases: &'a [&'a str],
|
||||||
("graphene_core::ToGraphicElementNode", "graphene_core::graphic_element::ToElementNode"),
|
}
|
||||||
("graphene_core::ToGraphicGroupNode", "graphene_core::graphic_element::ToGroupNode"),
|
|
||||||
|
const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[
|
||||||
|
// graphic element
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::graphic_element::append_artboard::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::AddArtboardNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::graphic_element::to_artboard::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ConstructArtboardNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::graphic_element::to_element::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ToGraphicElementNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::graphic_element::to_group::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ToGraphicGroupNode"],
|
||||||
|
},
|
||||||
// math_nodes
|
// math_nodes
|
||||||
("graphene_core::ops::MathNode", "graphene_math_nodes::MathNode"),
|
NodeReplacement {
|
||||||
("graphene_core::ops::AddNode", "graphene_math_nodes::AddNode"),
|
node: graphene_std::math_nodes::math::IDENTIFIER,
|
||||||
("graphene_core::ops::SubtractNode", "graphene_math_nodes::SubtractNode"),
|
aliases: &["graphene_core::ops::MathNode"],
|
||||||
("graphene_core::ops::MultiplyNode", "graphene_math_nodes::MultiplyNode"),
|
},
|
||||||
("graphene_core::ops::DivideNode", "graphene_math_nodes::DivideNode"),
|
NodeReplacement {
|
||||||
("graphene_core::ops::ModuloNode", "graphene_math_nodes::ModuloNode"),
|
node: graphene_std::math_nodes::add::IDENTIFIER,
|
||||||
("graphene_core::ops::ExponentNode", "graphene_math_nodes::ExponentNode"),
|
aliases: &["graphene_core::ops::AddNode"],
|
||||||
("graphene_core::ops::RootNode", "graphene_math_nodes::RootNode"),
|
},
|
||||||
("graphene_core::ops::LogarithmNode", "graphene_math_nodes::LogarithmNode"),
|
NodeReplacement {
|
||||||
("graphene_core::ops::SineNode", "graphene_math_nodes::SineNode"),
|
node: graphene_std::math_nodes::subtract::IDENTIFIER,
|
||||||
("graphene_core::ops::CosineNode", "graphene_math_nodes::CosineNode"),
|
aliases: &["graphene_core::ops::SubtractNode"],
|
||||||
("graphene_core::ops::TangentNode", "graphene_math_nodes::TangentNode"),
|
},
|
||||||
("graphene_core::ops::SineInverseNode", "graphene_math_nodes::SineInverseNode"),
|
NodeReplacement {
|
||||||
("graphene_core::ops::CosineInverseNode", "graphene_math_nodes::CosineInverseNode"),
|
node: graphene_std::math_nodes::multiply::IDENTIFIER,
|
||||||
("graphene_core::ops::TangentInverseNode", "graphene_math_nodes::TangentInverseNode"),
|
aliases: &["graphene_core::ops::MultiplyNode"],
|
||||||
("graphene_core::ops::RandomNode", "graphene_math_nodes::RandomNode"),
|
},
|
||||||
("graphene_core::ops::ToU32Node", "graphene_math_nodes::ToU32Node"),
|
NodeReplacement {
|
||||||
("graphene_core::ops::ToU64Node", "graphene_math_nodes::ToU64Node"),
|
node: graphene_std::math_nodes::divide::IDENTIFIER,
|
||||||
("graphene_core::ops::ToF64Node", "graphene_math_nodes::ToF64Node"),
|
aliases: &["graphene_core::ops::DivideNode"],
|
||||||
("graphene_core::ops::RoundNode", "graphene_math_nodes::RoundNode"),
|
},
|
||||||
("graphene_core::ops::FloorNode", "graphene_math_nodes::FloorNode"),
|
NodeReplacement {
|
||||||
("graphene_core::ops::CeilingNode", "graphene_math_nodes::CeilingNode"),
|
node: graphene_std::math_nodes::modulo::IDENTIFIER,
|
||||||
("graphene_core::ops::MinNode", "graphene_math_nodes::MinNode"),
|
aliases: &["graphene_core::ops::ModuloNode"],
|
||||||
("graphene_core::ops::MaxNode", "graphene_math_nodes::MaxNode"),
|
},
|
||||||
("graphene_core::ops::ClampNode", "graphene_math_nodes::ClampNode"),
|
NodeReplacement {
|
||||||
("graphene_core::ops::EqualsNode", "graphene_math_nodes::EqualsNode"),
|
node: graphene_std::math_nodes::exponent::IDENTIFIER,
|
||||||
("graphene_core::ops::NotEqualsNode", "graphene_math_nodes::NotEqualsNode"),
|
aliases: &["graphene_core::ops::ExponentNode"],
|
||||||
("graphene_core::ops::LessThanNode", "graphene_math_nodes::LessThanNode"),
|
},
|
||||||
("graphene_core::ops::GreaterThanNode", "graphene_math_nodes::GreaterThanNode"),
|
NodeReplacement {
|
||||||
("graphene_core::ops::LogicalOrNode", "graphene_math_nodes::LogicalOrNode"),
|
node: graphene_std::math_nodes::root::IDENTIFIER,
|
||||||
("graphene_core::ops::LogicalAndNode", "graphene_math_nodes::LogicalAndNode"),
|
aliases: &["graphene_core::ops::RootNode"],
|
||||||
("graphene_core::ops::LogicalNotNode", "graphene_math_nodes::LogicalNotNode"),
|
},
|
||||||
("graphene_core::ops::BoolValueNode", "graphene_math_nodes::BoolValueNode"),
|
NodeReplacement {
|
||||||
("graphene_core::ops::NumberValueNode", "graphene_math_nodes::NumberValueNode"),
|
node: graphene_std::math_nodes::logarithm::IDENTIFIER,
|
||||||
("graphene_core::ops::PercentageValueNode", "graphene_math_nodes::PercentageValueNode"),
|
aliases: &["graphene_core::ops::LogarithmNode"],
|
||||||
("graphene_core::ops::CoordinateValueNode", "graphene_math_nodes::CoordinateValueNode"),
|
},
|
||||||
("graphene_core::ops::ConstructVector2", "graphene_math_nodes::CoordinateValueNode"),
|
NodeReplacement {
|
||||||
("graphene_core::ops::Vector2ValueNode", "graphene_math_nodes::CoordinateValueNode"),
|
node: graphene_std::math_nodes::sine::IDENTIFIER,
|
||||||
("graphene_core::ops::ColorValueNode", "graphene_math_nodes::ColorValueNode"),
|
aliases: &["graphene_core::ops::SineNode"],
|
||||||
("graphene_core::ops::GradientValueNode", "graphene_math_nodes::GradientValueNode"),
|
},
|
||||||
("graphene_core::ops::SampleGradientNode", "graphene_math_nodes::SampleGradientNode"),
|
NodeReplacement {
|
||||||
("graphene_core::ops::StringValueNode", "graphene_math_nodes::StringValueNode"),
|
node: graphene_std::math_nodes::cosine::IDENTIFIER,
|
||||||
("graphene_core::ops::DotProductNode", "graphene_math_nodes::DotProductNode"),
|
aliases: &["graphene_core::ops::CosineNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::tangent::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::TangentNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::sine_inverse::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::SineInverseNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::cosine_inverse::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::CosineInverseNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::tangent_inverse::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::TangentInverseNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::random::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::RandomNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::to_u_32::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::ToU32Node"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::to_u_64::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::ToU64Node"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::to_f_64::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::ToF64Node"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::round::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::RoundNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::floor::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::FloorNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::ceiling::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::CeilingNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::min::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::MinNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::max::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::MaxNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::clamp::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::ClampNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::equals::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::EqualsNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::not_equals::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::NotEqualsNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::less_than::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::LessThanNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::greater_than::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::GreaterThanNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::logical_or::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::LogicalOrNode", "graphene_core::ops::LogicAndNode", "graphene_core::logic::LogicAndNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::logical_and::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::LogicalAndNode", "graphene_core::ops::LogicNotNode", "graphene_core::logic::LogicNotNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::logical_not::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::LogicalNotNode", "graphene_core::ops::LogicOrNode", "graphene_core::logic::LogicOrNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::bool_value::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::BoolValueNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::number_value::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::NumberValueNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::percentage_value::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::PercentageValueNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::coordinate_value::IDENTIFIER,
|
||||||
|
aliases: &[
|
||||||
|
"graphene_core::ops::CoordinateValueNode",
|
||||||
|
"graphene_core::ops::ConstructVector2",
|
||||||
|
"graphene_core::ops::Vector2ValueNode",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::color_value::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::ColorValueNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::gradient_value::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::GradientValueNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::sample_gradient::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::SampleGradientNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::string_value::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::StringValueNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::math_nodes::dot_product::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::DotProductNode"],
|
||||||
|
},
|
||||||
// debug
|
// debug
|
||||||
("graphene_core::ops::SizeOfNode", "graphene_core::debug::SizeOfNode"),
|
NodeReplacement {
|
||||||
("graphene_core::ops::SomeNode", "graphene_core::debug::SomeNode"),
|
node: graphene_std::debug::size_of::IDENTIFIER,
|
||||||
("graphene_core::ops::UnwrapNode", "graphene_core::debug::UnwrapNode"),
|
aliases: &["graphene_core::ops::SizeOfNode"],
|
||||||
("graphene_core::ops::CloneNode", "graphene_core::debug::CloneNode"),
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::debug::some::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::SomeNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::debug::unwrap::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::UnwrapNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::debug::clone::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::ops::CloneNode"],
|
||||||
|
},
|
||||||
// ???
|
// ???
|
||||||
("graphene_core::ops::ExtractXyNode", "graphene_core::extract_xy::ExtractXyNode"),
|
NodeReplacement {
|
||||||
("graphene_core::logic::LogicAndNode", "graphene_core::ops::LogicAndNode"),
|
node: graphene_std::extract_xy::extract_xy::IDENTIFIER,
|
||||||
("graphene_core::logic::LogicNotNode", "graphene_core::ops::LogicNotNode"),
|
aliases: &["graphene_core::ops::ExtractXyNode"],
|
||||||
("graphene_core::logic::LogicOrNode", "graphene_core::ops::LogicOrNode"),
|
},
|
||||||
("graphene_core::raster::BlendModeNode", "graphene_core::blending_nodes::BlendModeNode"),
|
NodeReplacement {
|
||||||
("graphene_core::raster::OpacityNode", "graphene_core::blending_nodes::OpacityNode"),
|
node: graphene_std::blending_nodes::blend_mode::IDENTIFIER,
|
||||||
("graphene_core::raster::BlendingNode", "graphene_core::blending_nodes::BlendingNode"),
|
aliases: &["graphene_core::raster::BlendModeNode"],
|
||||||
("graphene_core::vector::GenerateHandlesNode", "graphene_core::vector::AutoTangentsNode"),
|
},
|
||||||
("graphene_core::vector::RemoveHandlesNode", "graphene_core::vector::AutoTangentsNode"),
|
NodeReplacement {
|
||||||
|
node: graphene_std::blending_nodes::opacity::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::raster::OpacityNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::blending_nodes::blending::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::raster::BlendingNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::vector::auto_tangents::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::vector::GenerateHandlesNode", "graphene_core::vector::RemoveHandlesNode"],
|
||||||
|
},
|
||||||
// raster::adjustments
|
// raster::adjustments
|
||||||
("graphene_core::raster::adjustments::LuminanceNode", "graphene_raster_nodes::adjustments::LuminanceNode"),
|
NodeReplacement {
|
||||||
("graphene_core::raster::LuminanceNode", "graphene_raster_nodes::adjustments::LuminanceNode"),
|
node: graphene_std::raster_nodes::adjustments::luminance::IDENTIFIER,
|
||||||
("graphene_core::raster::adjustments::ExtractChannelNode", "graphene_raster_nodes::adjustments::ExtractChannelNode"),
|
aliases: &["graphene_core::raster::adjustments::LuminanceNode", "graphene_core::raster::LuminanceNode"],
|
||||||
("graphene_core::raster::ExtractChannelNode", "graphene_raster_nodes::adjustments::ExtractChannelNode"),
|
},
|
||||||
("graphene_core::raster::adjustments::MakeOpaqueNode", "graphene_raster_nodes::adjustments::MakeOpaqueNode"),
|
NodeReplacement {
|
||||||
("graphene_core::raster::ExtractOpaqueNode", "graphene_raster_nodes::adjustments::MakeOpaqueNode"),
|
node: graphene_std::raster_nodes::adjustments::extract_channel::IDENTIFIER,
|
||||||
(
|
aliases: &["graphene_core::raster::adjustments::ExtractChannelNode", "graphene_core::raster::ExtractChannelNode"],
|
||||||
"graphene_core::raster::adjustments::BrightnessContrastNode",
|
},
|
||||||
"graphene_raster_nodes::adjustments::BrightnessContrastNode",
|
NodeReplacement {
|
||||||
),
|
node: graphene_std::raster_nodes::adjustments::make_opaque::IDENTIFIER,
|
||||||
("graphene_core::raster::adjustments::LevelsNode", "graphene_raster_nodes::adjustments::LevelsNode"),
|
aliases: &["graphene_core::raster::adjustments::MakeOpaqueNode", "graphene_core::raster::ExtractOpaqueNode"],
|
||||||
("graphene_core::raster::LevelsNode", "graphene_raster_nodes::adjustments::LevelsNode"),
|
},
|
||||||
("graphene_core::raster::adjustments::BlackAndWhiteNode", "graphene_raster_nodes::adjustments::BlackAndWhiteNode"),
|
NodeReplacement {
|
||||||
("graphene_core::raster::BlackAndWhiteNode", "graphene_raster_nodes::adjustments::BlackAndWhiteNode"),
|
node: graphene_std::raster_nodes::adjustments::brightness_contrast::IDENTIFIER,
|
||||||
("graphene_core::raster::adjustments::HueSaturationNode", "graphene_raster_nodes::adjustments::HueSaturationNode"),
|
aliases: &["graphene_core::raster::adjustments::BrightnessContrastNode"],
|
||||||
("graphene_core::raster::HueSaturationNode", "graphene_raster_nodes::adjustments::HueSaturationNode"),
|
},
|
||||||
("graphene_core::raster::adjustments::InvertNode", "graphene_raster_nodes::adjustments::InvertNode"),
|
NodeReplacement {
|
||||||
("graphene_core::raster::InvertNode", "graphene_raster_nodes::adjustments::InvertNode"),
|
node: graphene_std::raster_nodes::adjustments::levels::IDENTIFIER,
|
||||||
("graphene_core::raster::InvertRGBNode", "graphene_raster_nodes::adjustments::InvertNode"),
|
aliases: &["graphene_core::raster::adjustments::LevelsNode", "graphene_core::raster::LevelsNode"],
|
||||||
("graphene_core::raster::adjustments::ThresholdNode", "graphene_raster_nodes::adjustments::ThresholdNode"),
|
},
|
||||||
("graphene_core::raster::ThresholdNode", "graphene_raster_nodes::adjustments::ThresholdNode"),
|
NodeReplacement {
|
||||||
("graphene_core::raster::adjustments::BlendNode", "graphene_raster_nodes::adjustments::BlendNode"),
|
node: graphene_std::raster_nodes::adjustments::black_and_white::IDENTIFIER,
|
||||||
("graphene_core::raster::BlendNode", "graphene_raster_nodes::adjustments::BlendNode"),
|
aliases: &["graphene_core::raster::adjustments::BlackAndWhiteNode", "graphene_core::raster::BlackAndWhiteNode"],
|
||||||
("graphene_core::raster::BlendColorPairNode", "graphene_raster_nodes::adjustments::BlendColorPairNode"),
|
},
|
||||||
("graphene_core::raster::adjustments::BlendColorsNode", "graphene_raster_nodes::adjustments::BlendColorsNode"),
|
NodeReplacement {
|
||||||
("graphene_core::raster::BlendColorsNode", "graphene_raster_nodes::adjustments::BlendColorsNode"),
|
node: graphene_std::raster_nodes::adjustments::hue_saturation::IDENTIFIER,
|
||||||
("graphene_core::raster::adjustments::GradientMapNode", "graphene_raster_nodes::adjustments::GradientMapNode"),
|
aliases: &["graphene_core::raster::adjustments::HueSaturationNode", "graphene_core::raster::HueSaturationNode"],
|
||||||
("graphene_core::raster::GradientMapNode", "graphene_raster_nodes::adjustments::GradientMapNode"),
|
},
|
||||||
("graphene_core::raster::adjustments::VibranceNode", "graphene_raster_nodes::adjustments::VibranceNode"),
|
NodeReplacement {
|
||||||
("graphene_core::raster::VibranceNode", "graphene_raster_nodes::adjustments::VibranceNode"),
|
node: graphene_std::raster_nodes::adjustments::invert::IDENTIFIER,
|
||||||
("graphene_core::raster::adjustments::ChannelMixerNode", "graphene_raster_nodes::adjustments::ChannelMixerNode"),
|
aliases: &[
|
||||||
("graphene_core::raster::ChannelMixerNode", "graphene_raster_nodes::adjustments::ChannelMixerNode"),
|
"graphene_core::raster::adjustments::InvertNode",
|
||||||
("graphene_core::raster::adjustments::SelectiveColorNode", "graphene_raster_nodes::adjustments::SelectiveColorNode"),
|
"graphene_core::raster::InvertNode",
|
||||||
("graphene_core::raster::adjustments::PosterizeNode", "graphene_raster_nodes::adjustments::PosterizeNode"),
|
"graphene_core::raster::InvertRGBNode",
|
||||||
("graphene_core::raster::PosterizeNode", "graphene_raster_nodes::adjustments::PosterizeNode"),
|
],
|
||||||
("graphene_core::raster::adjustments::ExposureNode", "graphene_raster_nodes::adjustments::ExposureNode"),
|
},
|
||||||
("graphene_core::raster::ExposureNode", "graphene_raster_nodes::adjustments::ExposureNode"),
|
NodeReplacement {
|
||||||
("graphene_core::raster::adjustments::ColorOverlayNode", "graphene_raster_nodes::adjustments::ColorOverlayNode"),
|
node: graphene_std::raster_nodes::adjustments::threshold::IDENTIFIER,
|
||||||
("graphene_raster_nodes::generate_curves::ColorOverlayNode", "graphene_raster_nodes::adjustments::ColorOverlayNode"),
|
aliases: &["graphene_core::raster::adjustments::ThresholdNode", "graphene_core::raster::ThresholdNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::raster_nodes::adjustments::blend::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::raster::adjustments::BlendNode", "graphene_core::raster::BlendNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::raster_nodes::adjustments::blend_color_pair::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::raster::BlendColorPairNode"],
|
||||||
|
},
|
||||||
|
// this node doesn't seem to exist?
|
||||||
|
// (graphene_std::raster_nodes::adjustments::blend_color::IDENTIFIER, &["graphene_core::raster::adjustments::BlendColorsNode","graphene_core::raster::BlendColorsNode"]),
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::raster_nodes::adjustments::gradient_map::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::raster::adjustments::GradientMapNode", "graphene_core::raster::GradientMapNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::raster_nodes::adjustments::vibrance::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::raster::adjustments::VibranceNode", "graphene_core::raster::VibranceNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::raster_nodes::adjustments::channel_mixer::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::raster::adjustments::ChannelMixerNode", "graphene_core::raster::ChannelMixerNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::raster_nodes::adjustments::selective_color::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::raster::adjustments::SelectiveColorNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::raster_nodes::adjustments::posterize::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::raster::adjustments::PosterizeNode", "graphene_core::raster::PosterizeNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::raster_nodes::adjustments::exposure::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::raster::adjustments::ExposureNode", "graphene_core::raster::ExposureNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::raster_nodes::adjustments::color_overlay::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::raster::adjustments::ColorOverlayNode", "graphene_raster_nodes::generate_curves::ColorOverlayNode"],
|
||||||
|
},
|
||||||
// raster
|
// raster
|
||||||
("graphene_core::raster::adjustments::GenerateCurvesNode", "graphene_raster_nodes::generate_curves::GenerateCurvesNode"),
|
NodeReplacement {
|
||||||
("graphene_std::dehaze::DehazeNode", "graphene_raster_nodes::dehaze::DehazeNode"),
|
node: graphene_std::raster_nodes::generate_curves::generate_curves::IDENTIFIER,
|
||||||
("graphene_std::filter::BlurNode", "graphene_raster_nodes::filter::BlurNode"),
|
aliases: &["graphene_core::raster::adjustments::GenerateCurvesNode"],
|
||||||
(
|
},
|
||||||
"graphene_std::image_color_palette::ImageColorPaletteNode",
|
NodeReplacement {
|
||||||
"graphene_raster_nodes::image_color_palette::ImageColorPaletteNode",
|
node: graphene_std::raster_nodes::dehaze::dehaze::IDENTIFIER,
|
||||||
),
|
aliases: &["graphene_std::dehaze::DehazeNode"],
|
||||||
("graphene_std::raster::SampleImageNode", "graphene_raster_nodes::std_nodes::SampleImageNode"),
|
},
|
||||||
("graphene_std::raster::CombineChannelsNode", "graphene_raster_nodes::std_nodes::CombineChannelsNode"),
|
NodeReplacement {
|
||||||
("graphene_std::raster::MaskNode", "graphene_raster_nodes::std_nodes::MaskNode"),
|
node: graphene_std::raster_nodes::filter::blur::IDENTIFIER,
|
||||||
("graphene_std::raster::ExtendImageToBoundsNode", "graphene_raster_nodes::std_nodes::ExtendImageToBoundsNode"),
|
aliases: &["graphene_std::filter::BlurNode"],
|
||||||
("graphene_std::raster::EmptyImageNode", "graphene_raster_nodes::std_nodes::EmptyImageNode"),
|
},
|
||||||
("graphene_std::raster::ImageValueNode", "graphene_raster_nodes::std_nodes::ImageValueNode"),
|
NodeReplacement {
|
||||||
("graphene_std::raster::NoisePatternNode", "graphene_raster_nodes::std_nodes::NoisePatternNode"),
|
node: graphene_std::raster_nodes::image_color_palette::image_color_palette::IDENTIFIER,
|
||||||
("graphene_std::raster::MandelbrotNode", "graphene_raster_nodes::std_nodes::MandelbrotNode"),
|
aliases: &["graphene_std::image_color_palette::ImageColorPaletteNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::raster_nodes::std_nodes::sample_image::IDENTIFIER,
|
||||||
|
aliases: &["graphene_std::raster::SampleImageNode", "graphene_std::raster::SampleNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::raster_nodes::std_nodes::combine_channels::IDENTIFIER,
|
||||||
|
aliases: &["graphene_std::raster::CombineChannelsNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::raster_nodes::std_nodes::mask::IDENTIFIER,
|
||||||
|
aliases: &["graphene_std::raster::MaskNode", "graphene_std::raster::MaskImageNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::raster_nodes::std_nodes::extend_image_to_bounds::IDENTIFIER,
|
||||||
|
aliases: &["graphene_std::raster::ExtendImageToBoundsNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::raster_nodes::std_nodes::empty_image::IDENTIFIER,
|
||||||
|
aliases: &["graphene_std::raster::EmptyImageNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::raster_nodes::std_nodes::image_value::IDENTIFIER,
|
||||||
|
aliases: &["graphene_std::raster::ImageValueNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::raster_nodes::std_nodes::noise_pattern::IDENTIFIER,
|
||||||
|
aliases: &["graphene_std::raster::NoisePatternNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::raster_nodes::std_nodes::mandelbrot::IDENTIFIER,
|
||||||
|
aliases: &["graphene_std::raster::MandelbrotNode"],
|
||||||
|
},
|
||||||
// text
|
// text
|
||||||
("graphene_core::text::TextGeneratorNode", "graphene_core::text::TextNode"),
|
NodeReplacement {
|
||||||
|
node: graphene_std::text::text::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::text::TextGeneratorNode"],
|
||||||
|
},
|
||||||
// transform
|
// transform
|
||||||
("graphene_core::transform::SetTransformNode", "graphene_core::transform_nodes::ReplaceTransformNode"),
|
NodeReplacement {
|
||||||
("graphene_core::transform::ReplaceTransformNode", "graphene_core::transform_nodes::ReplaceTransformNode"),
|
node: graphene_std::transform_nodes::replace_transform::IDENTIFIER,
|
||||||
("graphene_core::transform::TransformNode", "graphene_core::transform_nodes::TransformNode"),
|
aliases: &["graphene_core::transform::SetTransformNode", "graphene_core::transform::ReplaceTransformNode"],
|
||||||
("graphene_core::transform::BoundlessFootprintNode", "graphene_core::transform_nodes::BoundlessFootprintNode"),
|
},
|
||||||
("graphene_core::transform::FreezeRealTimeNode", "graphene_core::transform_nodes::FreezeRealTimeNode"),
|
NodeReplacement {
|
||||||
|
node: graphene_std::transform_nodes::transform::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::transform::TransformNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::transform_nodes::boundless_footprint::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::transform::BoundlessFootprintNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::transform_nodes::freeze_real_time::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::transform::FreezeRealTimeNode"],
|
||||||
|
},
|
||||||
// ???
|
// ???
|
||||||
("graphene_core::vector::SplinesFromPointsNode", "graphene_core::vector::SplineNode"),
|
NodeReplacement {
|
||||||
("graphene_core::vector::generator_nodes::EllipseGenerator", "graphene_core::vector::generator_nodes::EllipseNode"),
|
node: graphene_std::vector::spline::IDENTIFIER,
|
||||||
("graphene_core::vector::generator_nodes::LineGenerator", "graphene_core::vector::generator_nodes::LineNode"),
|
aliases: &["graphene_core::vector::SplinesFromPointsNode"],
|
||||||
("graphene_core::vector::generator_nodes::RectangleGenerator", "graphene_core::vector::generator_nodes::RectangleNode"),
|
},
|
||||||
(
|
NodeReplacement {
|
||||||
"graphene_core::vector::generator_nodes::RegularPolygonGenerator",
|
node: graphene_std::vector::generator_nodes::ellipse::IDENTIFIER,
|
||||||
"graphene_core::vector::generator_nodes::RegularPolygonNode",
|
aliases: &["graphene_core::vector::generator_nodes::EllipseGenerator"],
|
||||||
),
|
},
|
||||||
("graphene_core::vector::generator_nodes::StarGenerator", "graphene_core::vector::generator_nodes::StarNode"),
|
NodeReplacement {
|
||||||
("graphene_std::executor::BlendGpuImageNode", "graphene_std::gpu_nodes::BlendGpuImageNode"),
|
node: graphene_std::vector::generator_nodes::line::IDENTIFIER,
|
||||||
("graphene_std::raster::SampleNode", "graphene_std::raster::SampleImageNode"),
|
aliases: &["graphene_core::vector::generator_nodes::LineGenerator"],
|
||||||
("graphene_core::transform::CullNode", "graphene_core::ops::IdentityNode"),
|
},
|
||||||
("graphene_std::raster::MaskImageNode", "graphene_std::raster::MaskNode"),
|
NodeReplacement {
|
||||||
("graphene_core::vector::FlattenVectorElementsNode", "graphene_core::vector::FlattenPathNode"),
|
node: graphene_std::vector::generator_nodes::rectangle::IDENTIFIER,
|
||||||
("graphene_std::vector::BooleanOperationNode", "graphene_path_bool::BooleanOperationNode"),
|
aliases: &["graphene_core::vector::generator_nodes::RectangleGenerator"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::vector::generator_nodes::regular_polygon::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::vector::generator_nodes::RegularPolygonGenerator"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::vector::generator_nodes::star::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::vector::generator_nodes::StarGenerator"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::ops::identity::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::transform::CullNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::vector::flatten_path::IDENTIFIER,
|
||||||
|
aliases: &["graphene_core::vector::FlattenVectorElementsNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::path_bool::boolean_operation::IDENTIFIER,
|
||||||
|
aliases: &["graphene_std::vector::BooleanOperationNode"],
|
||||||
|
},
|
||||||
// brush
|
// brush
|
||||||
("graphene_std::brush::BrushStampGeneratorNode", "graphene_brush::brush::BrushStampGeneratorNode"),
|
NodeReplacement {
|
||||||
("graphene_std::brush::BlitNode", "graphene_brush::brush::BlitNode"),
|
node: graphene_std::brush::brush::brush_stamp_generator::IDENTIFIER,
|
||||||
("graphene_std::brush::BrushNode", "graphene_brush::brush::BrushNode"),
|
aliases: &["graphene_std::brush::BrushStampGeneratorNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::brush::brush::blit::IDENTIFIER,
|
||||||
|
aliases: &["graphene_std::brush::BlitNode"],
|
||||||
|
},
|
||||||
|
NodeReplacement {
|
||||||
|
node: graphene_std::brush::brush::brush::IDENTIFIER,
|
||||||
|
aliases: &["graphene_std::brush::BrushNode"],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const REPLACEMENTS: &[(&str, &str)] = &[];
|
||||||
|
|
||||||
pub fn document_migration_string_preprocessing(document_serialized_content: String) -> String {
|
pub fn document_migration_string_preprocessing(document_serialized_content: String) -> String {
|
||||||
TEXT_REPLACEMENTS
|
TEXT_REPLACEMENTS
|
||||||
.iter()
|
.iter()
|
||||||
@@ -195,17 +480,26 @@ pub fn document_migration_upgrades(document: &mut DocumentMessageHandler, reset_
|
|||||||
|
|
||||||
let network = document.network_interface.document_network().clone();
|
let network = document.network_interface.document_network().clone();
|
||||||
|
|
||||||
// Apply string replacements to each node
|
// Apply string and node replacements to each node
|
||||||
|
let mut replacements = HashMap::<&str, ProtoNodeIdentifier>::new();
|
||||||
|
Iterator::chain(
|
||||||
|
NODE_REPLACEMENTS.iter().flat_map(|NodeReplacement { node, aliases }| aliases.iter().map(|old| (*old, node.clone()))),
|
||||||
|
REPLACEMENTS.iter().map(|(old, new)| (*old, ProtoNodeIdentifier::new(new))),
|
||||||
|
)
|
||||||
|
.for_each(|(old, new)| {
|
||||||
|
if replacements.insert(old, new).is_some() {
|
||||||
|
panic!("Duplicate old name `{old}`");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
for (node_id, node, network_path) in network.recursive_nodes() {
|
for (node_id, node, network_path) in network.recursive_nodes() {
|
||||||
if let DocumentNodeImplementation::ProtoNode(protonode_id) = &node.implementation {
|
if let DocumentNodeImplementation::ProtoNode(protonode_id) = &node.implementation {
|
||||||
for (old, new) in REPLACEMENTS {
|
let node_path_without_type_args = protonode_id.name.split('<').next();
|
||||||
let node_path_without_type_args = protonode_id.name.split('<').next();
|
if let Some(new) = node_path_without_type_args.and_then(|node_path| replacements.get(node_path)) {
|
||||||
let mut default_template = NodeTemplate::default();
|
let mut default_template = NodeTemplate::default();
|
||||||
default_template.document_node.implementation = DocumentNodeImplementation::ProtoNode(new.to_string().into());
|
default_template.document_node.implementation = DocumentNodeImplementation::ProtoNode(new.clone());
|
||||||
if node_path_without_type_args == Some(old) {
|
document.network_interface.replace_implementation(node_id, &network_path, &mut default_template);
|
||||||
document.network_interface.replace_implementation(node_id, &network_path, &mut default_template);
|
document.network_interface.set_manual_compostion(node_id, &network_path, Some(graph_craft::Type::Generic("T".into())));
|
||||||
document.network_interface.set_manual_compostion(node_id, &network_path, Some(graph_craft::Type::Generic("T".into())));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -341,7 +635,7 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Upgrade Text node to include line height and character spacing, which were previously hardcoded to 1, from https://github.com/GraphiteEditor/Graphite/pull/2016
|
// Upgrade Text node to include line height and character spacing, which were previously hardcoded to 1, from https://github.com/GraphiteEditor/Graphite/pull/2016
|
||||||
if reference == "Text" && inputs_count != 9 {
|
if reference == "Text" && inputs_count != 10 {
|
||||||
let mut template = resolve_document_node_type(reference)?.default_node_template();
|
let mut template = resolve_document_node_type(reference)?.default_node_template();
|
||||||
document.network_interface.replace_implementation(node_id, network_path, &mut template);
|
document.network_interface.replace_implementation(node_id, network_path, &mut template);
|
||||||
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut template)?;
|
let old_inputs = document.network_interface.replace_inputs(node_id, network_path, &mut template)?;
|
||||||
@@ -395,6 +689,15 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
|
|||||||
},
|
},
|
||||||
network_path,
|
network_path,
|
||||||
);
|
);
|
||||||
|
document.network_interface.set_input(
|
||||||
|
&InputConnector::node(*node_id, 9),
|
||||||
|
if inputs_count >= 10 {
|
||||||
|
old_inputs[9].clone()
|
||||||
|
} else {
|
||||||
|
NodeInput::value(TaggedValue::Bool(false), false)
|
||||||
|
},
|
||||||
|
network_path,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upgrade Sine, Cosine, and Tangent nodes to include a boolean input for whether the output should be in radians, which was previously the only option but is now not the default
|
// Upgrade Sine, Cosine, and Tangent nodes to include a boolean input for whether the output should be in radians, which was previously the only option but is now not the default
|
||||||
@@ -666,6 +969,22 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add the "Depth" parameter to the "Instance Index" node
|
||||||
|
if reference == "Instance Index" && inputs_count == 0 {
|
||||||
|
let mut node_template = resolve_document_node_type(reference)?.default_node_template();
|
||||||
|
document.network_interface.replace_implementation(node_id, network_path, &mut node_template);
|
||||||
|
|
||||||
|
let mut node_path = network_path.to_vec();
|
||||||
|
node_path.push(*node_id);
|
||||||
|
|
||||||
|
document.network_interface.add_import(TaggedValue::None, false, 0, "Primary", "", &node_path);
|
||||||
|
document.network_interface.add_import(TaggedValue::U32(0), false, 1, "Loop Level", "TODO", &node_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================================
|
||||||
|
// PUT ALL MIGRATIONS ABOVE THIS LINE
|
||||||
|
// ==================================
|
||||||
|
|
||||||
// Ensure layers are positioned as stacks if they are upstream siblings of another layer
|
// Ensure layers are positioned as stacks if they are upstream siblings of another layer
|
||||||
document.network_interface.load_structure();
|
document.network_interface.load_structure();
|
||||||
let all_layers = LayerNodeIdentifier::ROOT_PARENT.descendants(document.network_interface.document_metadata()).collect::<Vec<_>>();
|
let all_layers = LayerNodeIdentifier::ROOT_PARENT.descendants(document.network_interface.document_metadata()).collect::<Vec<_>>();
|
||||||
@@ -691,3 +1010,20 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
|
|||||||
|
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_no_duplicate_node_replacements() {
|
||||||
|
let mut hashmap = HashMap::<ProtoNodeIdentifier, u32>::new();
|
||||||
|
NODE_REPLACEMENTS.iter().for_each(|node| {
|
||||||
|
*hashmap.entry(node.node.clone()).or_default() += 1;
|
||||||
|
});
|
||||||
|
let duplicates = hashmap.iter().filter(|(_, count)| **count > 1).map(|(node, _)| &node.name).collect::<Vec<_>>();
|
||||||
|
if duplicates.len() > 0 {
|
||||||
|
panic!("Duplicate entries in `NODE_REPLACEMENTS`: {:?}", duplicates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,11 +19,12 @@ pub struct MenuBarMessageHandler {
|
|||||||
pub spreadsheet_view_open: bool,
|
pub spreadsheet_view_open: bool,
|
||||||
pub message_logging_verbosity: MessageLoggingVerbosity,
|
pub message_logging_verbosity: MessageLoggingVerbosity,
|
||||||
pub reset_node_definitions_on_open: bool,
|
pub reset_node_definitions_on_open: bool,
|
||||||
|
pub single_path_node_compatible_layer_selected: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl MessageHandler<MenuBarMessage, ()> for MenuBarMessageHandler {
|
impl MessageHandler<MenuBarMessage, ()> for MenuBarMessageHandler {
|
||||||
fn process_message(&mut self, message: MenuBarMessage, responses: &mut VecDeque<Message>, _data: ()) {
|
fn process_message(&mut self, message: MenuBarMessage, responses: &mut VecDeque<Message>, _: ()) {
|
||||||
match message {
|
match message {
|
||||||
MenuBarMessage::SendLayout => self.send_layout(responses, LayoutTarget::MenuBar),
|
MenuBarMessage::SendLayout => self.send_layout(responses, LayoutTarget::MenuBar),
|
||||||
}
|
}
|
||||||
@@ -45,6 +46,7 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
let message_logging_verbosity_names = self.message_logging_verbosity == MessageLoggingVerbosity::Names;
|
let message_logging_verbosity_names = self.message_logging_verbosity == MessageLoggingVerbosity::Names;
|
||||||
let message_logging_verbosity_contents = self.message_logging_verbosity == MessageLoggingVerbosity::Contents;
|
let message_logging_verbosity_contents = self.message_logging_verbosity == MessageLoggingVerbosity::Contents;
|
||||||
let reset_node_definitions_on_open = self.reset_node_definitions_on_open;
|
let reset_node_definitions_on_open = self.reset_node_definitions_on_open;
|
||||||
|
let single_path_node_compatible_layer_selected = self.single_path_node_compatible_layer_selected;
|
||||||
|
|
||||||
let menu_bar_entries = vec![
|
let menu_bar_entries = vec![
|
||||||
MenuBarEntry {
|
MenuBarEntry {
|
||||||
@@ -418,9 +420,8 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
disabled: no_active_document || !has_selected_layers,
|
disabled: no_active_document || !has_selected_layers,
|
||||||
children: MenuBarEntryChildren(vec![{
|
children: MenuBarEntryChildren(vec![{
|
||||||
let list = <BooleanOperation as graphene_std::registry::ChoiceTypeStatic>::list();
|
let list = <BooleanOperation as graphene_std::registry::ChoiceTypeStatic>::list();
|
||||||
list.into_iter()
|
list.iter()
|
||||||
.map(|i| i.into_iter())
|
.flat_map(|i| i.iter())
|
||||||
.flatten()
|
|
||||||
.map(move |(operation, info)| MenuBarEntry {
|
.map(move |(operation, info)| MenuBarEntry {
|
||||||
label: info.label.to_string(),
|
label: info.label.to_string(),
|
||||||
icon: info.icon.as_ref().map(|i| i.to_string()),
|
icon: info.icon.as_ref().map(|i| i.to_string()),
|
||||||
@@ -436,6 +437,14 @@ impl LayoutHolder for MenuBarMessageHandler {
|
|||||||
..MenuBarEntry::default()
|
..MenuBarEntry::default()
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
vec![MenuBarEntry {
|
||||||
|
label: "Make Path Editable".into(),
|
||||||
|
icon: Some("NodeShape".into()),
|
||||||
|
shortcut: None,
|
||||||
|
action: MenuBarEntry::create_action(|_| NodeGraphMessage::AddPathNode.into()),
|
||||||
|
disabled: !single_path_node_compatible_layer_selected,
|
||||||
|
..MenuBarEntry::default()
|
||||||
|
}],
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
MenuBarEntry::new_root(
|
MenuBarEntry::new_root(
|
||||||
|
|||||||
@@ -10,4 +10,4 @@ pub mod utility_types;
|
|||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use portfolio_message::{PortfolioMessage, PortfolioMessageDiscriminant};
|
pub use portfolio_message::{PortfolioMessage, PortfolioMessageDiscriminant};
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use portfolio_message_handler::{PortfolioMessageData, PortfolioMessageHandler};
|
pub use portfolio_message_handler::{PortfolioMessageContext, PortfolioMessageHandler};
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ pub enum PortfolioMessage {
|
|||||||
Spreadsheet(SpreadsheetMessage),
|
Spreadsheet(SpreadsheetMessage),
|
||||||
|
|
||||||
// Messages
|
// Messages
|
||||||
|
Init,
|
||||||
DocumentPassMessage {
|
DocumentPassMessage {
|
||||||
document_id: DocumentId,
|
document_id: DocumentId,
|
||||||
message: DocumentMessage,
|
message: DocumentMessage,
|
||||||
|
|||||||
@@ -9,24 +9,27 @@ use crate::messages::debug::utility_types::MessageLoggingVerbosity;
|
|||||||
use crate::messages::dialog::simple_dialogs;
|
use crate::messages::dialog::simple_dialogs;
|
||||||
use crate::messages::frontend::utility_types::FrontendDocumentDetails;
|
use crate::messages::frontend::utility_types::FrontendDocumentDetails;
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::portfolio::document::DocumentMessageData;
|
use crate::messages::portfolio::document::DocumentMessageContext;
|
||||||
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
|
use crate::messages::portfolio::document::graph_operation::utility_types::TransformIn;
|
||||||
|
use crate::messages::portfolio::document::node_graph::document_node_definitions;
|
||||||
use crate::messages::portfolio::document::utility_types::clipboards::{Clipboard, CopyBufferEntry, INTERNAL_CLIPBOARD_COUNT};
|
use crate::messages::portfolio::document::utility_types::clipboards::{Clipboard, CopyBufferEntry, INTERNAL_CLIPBOARD_COUNT};
|
||||||
use crate::messages::portfolio::document::utility_types::network_interface::OutputConnector;
|
use crate::messages::portfolio::document::utility_types::network_interface::OutputConnector;
|
||||||
use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes;
|
use crate::messages::portfolio::document::utility_types::nodes::SelectedNodes;
|
||||||
use crate::messages::portfolio::document_migration::*;
|
use crate::messages::portfolio::document_migration::*;
|
||||||
use crate::messages::preferences::SelectionMode;
|
use crate::messages::preferences::SelectionMode;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||||
use crate::messages::tool::utility_types::{HintData, HintGroup, ToolType};
|
use crate::messages::tool::utility_types::{HintData, HintGroup, ToolType};
|
||||||
use crate::node_graph_executor::{ExportConfig, NodeGraphExecutor};
|
use crate::node_graph_executor::{ExportConfig, NodeGraphExecutor};
|
||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use graph_craft::document::NodeId;
|
use graph_craft::document::NodeId;
|
||||||
|
use graph_craft::document::value::TaggedValue;
|
||||||
use graphene_std::renderer::Quad;
|
use graphene_std::renderer::Quad;
|
||||||
use graphene_std::text::Font;
|
use graphene_std::text::Font;
|
||||||
use std::vec;
|
use std::vec;
|
||||||
|
|
||||||
#[derive(ExtractField)]
|
#[derive(ExtractField)]
|
||||||
pub struct PortfolioMessageData<'a> {
|
pub struct PortfolioMessageContext<'a> {
|
||||||
pub ipp: &'a InputPreprocessorMessageHandler,
|
pub ipp: &'a InputPreprocessorMessageHandler,
|
||||||
pub preferences: &'a PreferencesMessageHandler,
|
pub preferences: &'a PreferencesMessageHandler,
|
||||||
pub current_tool: &'a ToolType,
|
pub current_tool: &'a ToolType,
|
||||||
@@ -54,9 +57,9 @@ pub struct PortfolioMessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMessageHandler {
|
impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for PortfolioMessageHandler {
|
||||||
fn process_message(&mut self, message: PortfolioMessage, responses: &mut VecDeque<Message>, data: PortfolioMessageData) {
|
fn process_message(&mut self, message: PortfolioMessage, responses: &mut VecDeque<Message>, context: PortfolioMessageContext) {
|
||||||
let PortfolioMessageData {
|
let PortfolioMessageContext {
|
||||||
ipp,
|
ipp,
|
||||||
preferences,
|
preferences,
|
||||||
current_tool,
|
current_tool,
|
||||||
@@ -64,7 +67,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
|||||||
reset_node_definitions_on_open,
|
reset_node_definitions_on_open,
|
||||||
timing_information,
|
timing_information,
|
||||||
animation,
|
animation,
|
||||||
} = data;
|
} = context;
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
// Sub-messages
|
// Sub-messages
|
||||||
@@ -77,6 +80,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
|||||||
self.menu_bar_message_handler.has_selected_nodes = false;
|
self.menu_bar_message_handler.has_selected_nodes = false;
|
||||||
self.menu_bar_message_handler.has_selected_layers = false;
|
self.menu_bar_message_handler.has_selected_layers = false;
|
||||||
self.menu_bar_message_handler.has_selection_history = (false, false);
|
self.menu_bar_message_handler.has_selection_history = (false, false);
|
||||||
|
self.menu_bar_message_handler.single_path_node_compatible_layer_selected = false;
|
||||||
self.menu_bar_message_handler.spreadsheet_view_open = self.spreadsheet.spreadsheet_view_open;
|
self.menu_bar_message_handler.spreadsheet_view_open = self.spreadsheet.spreadsheet_view_open;
|
||||||
self.menu_bar_message_handler.message_logging_verbosity = message_logging_verbosity;
|
self.menu_bar_message_handler.message_logging_verbosity = message_logging_verbosity;
|
||||||
self.menu_bar_message_handler.reset_node_definitions_on_open = reset_node_definitions_on_open;
|
self.menu_bar_message_handler.reset_node_definitions_on_open = reset_node_definitions_on_open;
|
||||||
@@ -94,6 +98,30 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
|||||||
let metadata = &document.network_interface.document_network_metadata().persistent_metadata;
|
let metadata = &document.network_interface.document_network_metadata().persistent_metadata;
|
||||||
(!metadata.selection_undo_history.is_empty(), !metadata.selection_redo_history.is_empty())
|
(!metadata.selection_undo_history.is_empty(), !metadata.selection_redo_history.is_empty())
|
||||||
};
|
};
|
||||||
|
self.menu_bar_message_handler.single_path_node_compatible_layer_selected = {
|
||||||
|
let selected_nodes = document.network_interface.selected_nodes();
|
||||||
|
let mut selected_layers = selected_nodes.selected_layers(document.metadata());
|
||||||
|
let first_layer = selected_layers.next();
|
||||||
|
let second_layer = selected_layers.next();
|
||||||
|
let has_single_selection = first_layer.is_some() && second_layer.is_none();
|
||||||
|
|
||||||
|
let compatible_type = first_layer.and_then(|layer| {
|
||||||
|
let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, &document.network_interface);
|
||||||
|
graph_layer.horizontal_layer_flow().nth(1).and_then(|node_id| {
|
||||||
|
let (output_type, _) = document.network_interface.output_type(&node_id, 0, &[]);
|
||||||
|
Some(format!("type:{}", output_type.nested_type()))
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let is_compatible = compatible_type.as_deref() == Some("type:Instances<VectorData>");
|
||||||
|
|
||||||
|
let is_modifiable = first_layer.map_or(false, |layer| {
|
||||||
|
let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, &document.network_interface);
|
||||||
|
matches!(graph_layer.find_input("Path", 1), Some(TaggedValue::VectorModification(_)))
|
||||||
|
});
|
||||||
|
|
||||||
|
first_layer.is_some() && has_single_selection && is_compatible && !is_modifiable
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.menu_bar_message_handler.process_message(message, responses, ());
|
self.menu_bar_message_handler.process_message(message, responses, ());
|
||||||
@@ -104,7 +132,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
|||||||
PortfolioMessage::Document(message) => {
|
PortfolioMessage::Document(message) => {
|
||||||
if let Some(document_id) = self.active_document_id {
|
if let Some(document_id) = self.active_document_id {
|
||||||
if let Some(document) = self.documents.get_mut(&document_id) {
|
if let Some(document) = self.documents.get_mut(&document_id) {
|
||||||
let document_inputs = DocumentMessageData {
|
let document_inputs = DocumentMessageContext {
|
||||||
document_id,
|
document_id,
|
||||||
ipp,
|
ipp,
|
||||||
persistent_data: &self.persistent_data,
|
persistent_data: &self.persistent_data,
|
||||||
@@ -119,9 +147,26 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Messages
|
// Messages
|
||||||
|
PortfolioMessage::Init => {
|
||||||
|
// Load persistent data from the browser database
|
||||||
|
responses.add(FrontendMessage::TriggerLoadFirstAutoSaveDocument);
|
||||||
|
responses.add(FrontendMessage::TriggerLoadPreferences);
|
||||||
|
|
||||||
|
// Display the menu bar at the top of the window
|
||||||
|
responses.add(MenuBarMessage::SendLayout);
|
||||||
|
|
||||||
|
// Send the information for tooltips and categories for each node/input.
|
||||||
|
responses.add(FrontendMessage::SendUIMetadata {
|
||||||
|
node_descriptions: document_node_definitions::collect_node_descriptions(),
|
||||||
|
node_types: document_node_definitions::collect_node_types(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Finish loading persistent data from the browser database
|
||||||
|
responses.add(FrontendMessage::TriggerLoadRestAutoSaveDocuments);
|
||||||
|
}
|
||||||
PortfolioMessage::DocumentPassMessage { document_id, message } => {
|
PortfolioMessage::DocumentPassMessage { document_id, message } => {
|
||||||
if let Some(document) = self.documents.get_mut(&document_id) {
|
if let Some(document) = self.documents.get_mut(&document_id) {
|
||||||
let document_inputs = DocumentMessageData {
|
let document_inputs = DocumentMessageContext {
|
||||||
document_id,
|
document_id,
|
||||||
ipp,
|
ipp,
|
||||||
persistent_data: &self.persistent_data,
|
persistent_data: &self.persistent_data,
|
||||||
@@ -744,6 +789,8 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
|||||||
responses.add(DocumentMessage::GraphViewOverlay { open: node_graph_open });
|
responses.add(DocumentMessage::GraphViewOverlay { open: node_graph_open });
|
||||||
if node_graph_open {
|
if node_graph_open {
|
||||||
responses.add(NodeGraphMessage::UpdateGraphBarRight);
|
responses.add(NodeGraphMessage::UpdateGraphBarRight);
|
||||||
|
responses.add(NodeGraphMessage::UnloadWires);
|
||||||
|
responses.add(NodeGraphMessage::SendWires)
|
||||||
} else {
|
} else {
|
||||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||||
}
|
}
|
||||||
@@ -972,7 +1019,9 @@ impl PortfolioMessageHandler {
|
|||||||
/text>"#
|
/text>"#
|
||||||
// It's a mystery why the `/text>` tag above needs to be missing its `<`, but when it exists it prints the `<` character in the text. However this works with it removed.
|
// It's a mystery why the `/text>` tag above needs to be missing its `<`, but when it exists it prints the `<` character in the text. However this works with it removed.
|
||||||
.to_string();
|
.to_string();
|
||||||
responses.add(Message::EndBuffer(graphene_std::renderer::RenderMetadata::default()));
|
responses.add(Message::EndBuffer {
|
||||||
|
render_metadata: graphene_std::renderer::RenderMetadata::default(),
|
||||||
|
});
|
||||||
responses.add(FrontendMessage::UpdateDocumentArtwork { svg: error });
|
responses.add(FrontendMessage::UpdateDocumentArtwork { svg: error });
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ pub struct SpreadsheetMessageHandler {
|
|||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl MessageHandler<SpreadsheetMessage, ()> for SpreadsheetMessageHandler {
|
impl MessageHandler<SpreadsheetMessage, ()> for SpreadsheetMessageHandler {
|
||||||
fn process_message(&mut self, message: SpreadsheetMessage, responses: &mut VecDeque<Message>, _data: ()) {
|
fn process_message(&mut self, message: SpreadsheetMessage, responses: &mut VecDeque<Message>, _: ()) {
|
||||||
match message {
|
match message {
|
||||||
SpreadsheetMessage::ToggleOpen => {
|
SpreadsheetMessage::ToggleOpen => {
|
||||||
self.spreadsheet_view_open = !self.spreadsheet_view_open;
|
self.spreadsheet_view_open = !self.spreadsheet_view_open;
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ impl Default for PreferencesMessageHandler {
|
|||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl MessageHandler<PreferencesMessage, ()> for PreferencesMessageHandler {
|
impl MessageHandler<PreferencesMessage, ()> for PreferencesMessageHandler {
|
||||||
fn process_message(&mut self, message: PreferencesMessage, responses: &mut VecDeque<Message>, _data: ()) {
|
fn process_message(&mut self, message: PreferencesMessage, responses: &mut VecDeque<Message>, _: ()) {
|
||||||
match message {
|
match message {
|
||||||
// Management messages
|
// Management messages
|
||||||
PreferencesMessage::Load { preferences } => {
|
PreferencesMessage::Load { preferences } => {
|
||||||
|
|||||||
@@ -5,28 +5,28 @@ pub use crate::utility_types::{DebugMessageTree, MessageData};
|
|||||||
pub use crate::messages::animation::{AnimationMessage, AnimationMessageDiscriminant, AnimationMessageHandler};
|
pub use crate::messages::animation::{AnimationMessage, AnimationMessageDiscriminant, AnimationMessageHandler};
|
||||||
pub use crate::messages::broadcast::{BroadcastMessage, BroadcastMessageDiscriminant, BroadcastMessageHandler};
|
pub use crate::messages::broadcast::{BroadcastMessage, BroadcastMessageDiscriminant, BroadcastMessageHandler};
|
||||||
pub use crate::messages::debug::{DebugMessage, DebugMessageDiscriminant, DebugMessageHandler};
|
pub use crate::messages::debug::{DebugMessage, DebugMessageDiscriminant, DebugMessageHandler};
|
||||||
pub use crate::messages::dialog::export_dialog::{ExportDialogMessage, ExportDialogMessageData, ExportDialogMessageDiscriminant, ExportDialogMessageHandler};
|
pub use crate::messages::dialog::export_dialog::{ExportDialogMessage, ExportDialogMessageContext, ExportDialogMessageDiscriminant, ExportDialogMessageHandler};
|
||||||
pub use crate::messages::dialog::new_document_dialog::{NewDocumentDialogMessage, NewDocumentDialogMessageDiscriminant, NewDocumentDialogMessageHandler};
|
pub use crate::messages::dialog::new_document_dialog::{NewDocumentDialogMessage, NewDocumentDialogMessageDiscriminant, NewDocumentDialogMessageHandler};
|
||||||
pub use crate::messages::dialog::preferences_dialog::{PreferencesDialogMessage, PreferencesDialogMessageData, PreferencesDialogMessageDiscriminant, PreferencesDialogMessageHandler};
|
pub use crate::messages::dialog::preferences_dialog::{PreferencesDialogMessage, PreferencesDialogMessageContext, PreferencesDialogMessageDiscriminant, PreferencesDialogMessageHandler};
|
||||||
pub use crate::messages::dialog::{DialogMessage, DialogMessageData, DialogMessageDiscriminant, DialogMessageHandler};
|
pub use crate::messages::dialog::{DialogMessage, DialogMessageContext, DialogMessageDiscriminant, DialogMessageHandler};
|
||||||
pub use crate::messages::frontend::{FrontendMessage, FrontendMessageDiscriminant};
|
pub use crate::messages::frontend::{FrontendMessage, FrontendMessageDiscriminant};
|
||||||
pub use crate::messages::globals::{GlobalsMessage, GlobalsMessageDiscriminant, GlobalsMessageHandler};
|
pub use crate::messages::globals::{GlobalsMessage, GlobalsMessageDiscriminant, GlobalsMessageHandler};
|
||||||
pub use crate::messages::input_mapper::key_mapping::{KeyMappingMessage, KeyMappingMessageData, KeyMappingMessageDiscriminant, KeyMappingMessageHandler};
|
pub use crate::messages::input_mapper::key_mapping::{KeyMappingMessage, KeyMappingMessageContext, KeyMappingMessageDiscriminant, KeyMappingMessageHandler};
|
||||||
pub use crate::messages::input_mapper::{InputMapperMessage, InputMapperMessageData, InputMapperMessageDiscriminant, InputMapperMessageHandler};
|
pub use crate::messages::input_mapper::{InputMapperMessage, InputMapperMessageContext, InputMapperMessageDiscriminant, InputMapperMessageHandler};
|
||||||
pub use crate::messages::input_preprocessor::{InputPreprocessorMessage, InputPreprocessorMessageData, InputPreprocessorMessageDiscriminant, InputPreprocessorMessageHandler};
|
pub use crate::messages::input_preprocessor::{InputPreprocessorMessage, InputPreprocessorMessageContext, InputPreprocessorMessageDiscriminant, InputPreprocessorMessageHandler};
|
||||||
pub use crate::messages::layout::{LayoutMessage, LayoutMessageDiscriminant, LayoutMessageHandler};
|
pub use crate::messages::layout::{LayoutMessage, LayoutMessageDiscriminant, LayoutMessageHandler};
|
||||||
pub use crate::messages::portfolio::document::graph_operation::{GraphOperationMessage, GraphOperationMessageData, GraphOperationMessageDiscriminant, GraphOperationMessageHandler};
|
pub use crate::messages::portfolio::document::graph_operation::{GraphOperationMessage, GraphOperationMessageContext, GraphOperationMessageDiscriminant, GraphOperationMessageHandler};
|
||||||
pub use crate::messages::portfolio::document::navigation::{NavigationMessage, NavigationMessageData, NavigationMessageDiscriminant, NavigationMessageHandler};
|
pub use crate::messages::portfolio::document::navigation::{NavigationMessage, NavigationMessageContext, NavigationMessageDiscriminant, NavigationMessageHandler};
|
||||||
pub use crate::messages::portfolio::document::node_graph::{NodeGraphMessage, NodeGraphMessageDiscriminant, NodeGraphMessageHandler};
|
pub use crate::messages::portfolio::document::node_graph::{NodeGraphMessage, NodeGraphMessageDiscriminant, NodeGraphMessageHandler};
|
||||||
pub use crate::messages::portfolio::document::overlays::{OverlaysMessage, OverlaysMessageData, OverlaysMessageDiscriminant, OverlaysMessageHandler};
|
pub use crate::messages::portfolio::document::overlays::{OverlaysMessage, OverlaysMessageContext, OverlaysMessageDiscriminant, OverlaysMessageHandler};
|
||||||
pub use crate::messages::portfolio::document::properties_panel::{PropertiesPanelMessage, PropertiesPanelMessageDiscriminant, PropertiesPanelMessageHandler};
|
pub use crate::messages::portfolio::document::properties_panel::{PropertiesPanelMessage, PropertiesPanelMessageDiscriminant, PropertiesPanelMessageHandler};
|
||||||
pub use crate::messages::portfolio::document::{DocumentMessage, DocumentMessageData, DocumentMessageDiscriminant, DocumentMessageHandler};
|
pub use crate::messages::portfolio::document::{DocumentMessage, DocumentMessageContext, DocumentMessageDiscriminant, DocumentMessageHandler};
|
||||||
pub use crate::messages::portfolio::menu_bar::{MenuBarMessage, MenuBarMessageDiscriminant, MenuBarMessageHandler};
|
pub use crate::messages::portfolio::menu_bar::{MenuBarMessage, MenuBarMessageDiscriminant, MenuBarMessageHandler};
|
||||||
pub use crate::messages::portfolio::spreadsheet::{SpreadsheetMessage, SpreadsheetMessageDiscriminant};
|
pub use crate::messages::portfolio::spreadsheet::{SpreadsheetMessage, SpreadsheetMessageDiscriminant};
|
||||||
pub use crate::messages::portfolio::{PortfolioMessage, PortfolioMessageData, PortfolioMessageDiscriminant, PortfolioMessageHandler};
|
pub use crate::messages::portfolio::{PortfolioMessage, PortfolioMessageContext, PortfolioMessageDiscriminant, PortfolioMessageHandler};
|
||||||
pub use crate::messages::preferences::{PreferencesMessage, PreferencesMessageDiscriminant, PreferencesMessageHandler};
|
pub use crate::messages::preferences::{PreferencesMessage, PreferencesMessageDiscriminant, PreferencesMessageHandler};
|
||||||
pub use crate::messages::tool::transform_layer::{TransformLayerMessage, TransformLayerMessageDiscriminant, TransformLayerMessageHandler};
|
pub use crate::messages::tool::transform_layer::{TransformLayerMessage, TransformLayerMessageDiscriminant, TransformLayerMessageHandler};
|
||||||
pub use crate::messages::tool::{ToolMessage, ToolMessageData, ToolMessageDiscriminant, ToolMessageHandler};
|
pub use crate::messages::tool::{ToolMessage, ToolMessageContext, ToolMessageDiscriminant, ToolMessageHandler};
|
||||||
pub use crate::messages::workspace::{WorkspaceMessage, WorkspaceMessageDiscriminant, WorkspaceMessageHandler};
|
pub use crate::messages::workspace::{WorkspaceMessage, WorkspaceMessageDiscriminant, WorkspaceMessageHandler};
|
||||||
|
|
||||||
// Message, MessageDiscriminant
|
// Message, MessageDiscriminant
|
||||||
|
|||||||
@@ -357,7 +357,7 @@ pub fn get_text_id(layer: LayerNodeIdentifier, network_interface: &NodeNetworkIn
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Gets properties from the Text node
|
/// Gets properties from the Text node
|
||||||
pub fn get_text(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<(&String, &Font, TypesettingConfig)> {
|
pub fn get_text(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<(&String, &Font, TypesettingConfig, bool)> {
|
||||||
let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs("Text")?;
|
let inputs = NodeGraphLayer::new(layer, network_interface).find_node_inputs("Text")?;
|
||||||
|
|
||||||
let Some(TaggedValue::String(text)) = &inputs[1].as_value() else { return None };
|
let Some(TaggedValue::String(text)) = &inputs[1].as_value() else { return None };
|
||||||
@@ -368,6 +368,9 @@ pub fn get_text(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInter
|
|||||||
let Some(&TaggedValue::OptionalF64(max_width)) = inputs[6].as_value() else { return None };
|
let Some(&TaggedValue::OptionalF64(max_width)) = inputs[6].as_value() else { return None };
|
||||||
let Some(&TaggedValue::OptionalF64(max_height)) = inputs[7].as_value() else { return None };
|
let Some(&TaggedValue::OptionalF64(max_height)) = inputs[7].as_value() else { return None };
|
||||||
let Some(&TaggedValue::F64(tilt)) = inputs[8].as_value() else { return None };
|
let Some(&TaggedValue::F64(tilt)) = inputs[8].as_value() else { return None };
|
||||||
|
let Some(TaggedValue::Bool(per_glyph_instances)) = &inputs[9].as_value() else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
let typesetting = TypesettingConfig {
|
let typesetting = TypesettingConfig {
|
||||||
font_size,
|
font_size,
|
||||||
@@ -377,7 +380,7 @@ pub fn get_text(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInter
|
|||||||
max_height,
|
max_height,
|
||||||
tilt,
|
tilt,
|
||||||
};
|
};
|
||||||
Some((text, font, typesetting))
|
Some((text, font, typesetting, *per_glyph_instances))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_stroke_width(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<f64> {
|
pub fn get_stroke_width(layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> Option<f64> {
|
||||||
|
|||||||
@@ -1000,7 +1000,7 @@ impl ShapeState {
|
|||||||
} else {
|
} else {
|
||||||
// Push both in and out handles into the correct position
|
// Push both in and out handles into the correct position
|
||||||
for ((handle, sign), other_anchor) in handles.iter().zip([1., -1.]).zip(&anchor_positions) {
|
for ((handle, sign), other_anchor) in handles.iter().zip([1., -1.]).zip(&anchor_positions) {
|
||||||
let Some(anchor_vector) = other_anchor.map(|position| (position - anchor_position)) else {
|
let Some(anchor_vector) = other_anchor.map(|position| position - anchor_position) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ mod grid_snapper;
|
|||||||
mod layer_snapper;
|
mod layer_snapper;
|
||||||
mod snap_results;
|
mod snap_results;
|
||||||
|
|
||||||
use crate::consts::{COLOR_OVERLAY_BLUE, COLOR_OVERLAY_LABEL_BACKGROUND, COLOR_OVERLAY_WHITE};
|
use crate::consts::{COLOR_OVERLAY_BLACK_75, COLOR_OVERLAY_BLUE, COLOR_OVERLAY_WHITE};
|
||||||
use crate::messages::portfolio::document::overlays::utility_types::{OverlayContext, Pivot};
|
use crate::messages::portfolio::document::overlays::utility_types::{OverlayContext, Pivot};
|
||||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||||
use crate::messages::portfolio::document::utility_types::misc::{GridSnapTarget, PathSnapTarget, SnapTarget};
|
use crate::messages::portfolio::document::utility_types::misc::{GridSnapTarget, PathSnapTarget, SnapTarget};
|
||||||
@@ -482,7 +482,7 @@ impl SnapManager {
|
|||||||
if !any_align && ind.distribution_equal_distance_horizontal.is_none() && ind.distribution_equal_distance_vertical.is_none() {
|
if !any_align && ind.distribution_equal_distance_horizontal.is_none() && ind.distribution_equal_distance_vertical.is_none() {
|
||||||
let text = format!("[{}] from [{}]", ind.target, ind.source);
|
let text = format!("[{}] from [{}]", ind.target, ind.source);
|
||||||
let transform = DAffine2::from_translation(viewport - DVec2::new(0., 4.));
|
let transform = DAffine2::from_translation(viewport - DVec2::new(0., 4.));
|
||||||
overlay_context.text(&text, COLOR_OVERLAY_WHITE, Some(COLOR_OVERLAY_LABEL_BACKGROUND), transform, 4., [Pivot::Start, Pivot::End]);
|
overlay_context.text(&text, COLOR_OVERLAY_WHITE, Some(COLOR_OVERLAY_BLACK_75), transform, 4., [Pivot::Start, Pivot::End]);
|
||||||
overlay_context.square(viewport, Some(4.), Some(COLOR_OVERLAY_BLUE), Some(COLOR_OVERLAY_BLUE));
|
overlay_context.square(viewport, Some(4.), Some(COLOR_OVERLAY_BLUE), Some(COLOR_OVERLAY_BLUE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,14 +66,22 @@ where
|
|||||||
|
|
||||||
/// Calculates the bounding box of the layer's text, based on the settings for max width and height specified in the typesetting config.
|
/// Calculates the bounding box of the layer's text, based on the settings for max width and height specified in the typesetting config.
|
||||||
pub fn text_bounding_box(layer: LayerNodeIdentifier, document: &DocumentMessageHandler, font_cache: &FontCache) -> Quad {
|
pub fn text_bounding_box(layer: LayerNodeIdentifier, document: &DocumentMessageHandler, font_cache: &FontCache) -> Quad {
|
||||||
let Some((text, font, typesetting)) = get_text(layer, &document.network_interface) else {
|
let Some((text, font, typesetting, per_glyph_instances)) = get_text(layer, &document.network_interface) else {
|
||||||
return Quad::from_box([DVec2::ZERO, DVec2::ZERO]);
|
return Quad::from_box([DVec2::ZERO, DVec2::ZERO]);
|
||||||
};
|
};
|
||||||
|
|
||||||
let font_data = font_cache.get(font).map(|data| load_font(data));
|
let font_data = font_cache.get(font).map(|data| load_font(data));
|
||||||
let far = graphene_std::text::bounding_box(text, font_data, typesetting, false);
|
let far = graphene_std::text::bounding_box(text, font_data, typesetting, false);
|
||||||
|
|
||||||
Quad::from_box([DVec2::ZERO, far])
|
// TODO: Once the instances refactor is complete and per_glyph_instances can be removed (since it'll be the default),
|
||||||
|
// TODO: remove this because the top of the dashed bounding overlay should no longer be based on the first line's baseline.
|
||||||
|
let vertical_offset = if per_glyph_instances {
|
||||||
|
DVec2::NEG_Y * typesetting.font_size * (1. + (typesetting.line_height_ratio - 1.) / 2.)
|
||||||
|
} else {
|
||||||
|
DVec2::ZERO
|
||||||
|
};
|
||||||
|
|
||||||
|
Quad::from_box([DVec2::ZERO + vertical_offset, far + vertical_offset])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn calculate_segment_angle(anchor: PointId, segment: SegmentId, vector_data: &VectorData, prefer_handle_direction: bool) -> Option<f64> {
|
pub fn calculate_segment_angle(anchor: PointId, segment: SegmentId, vector_data: &VectorData, prefer_handle_direction: bool) -> Option<f64> {
|
||||||
|
|||||||
@@ -9,6 +9,6 @@ pub mod utility_types;
|
|||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use tool_message::{ToolMessage, ToolMessageDiscriminant};
|
pub use tool_message::{ToolMessage, ToolMessageDiscriminant};
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use tool_message_handler::{ToolMessageData, ToolMessageHandler};
|
pub use tool_message_handler::{ToolMessageContext, ToolMessageHandler};
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use transform_layer::{TransformLayerMessage, TransformLayerMessageDiscriminant};
|
pub use transform_layer::{TransformLayerMessage, TransformLayerMessageDiscriminant};
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ pub enum ToolMessage {
|
|||||||
SelectRandomWorkingColor {
|
SelectRandomWorkingColor {
|
||||||
primary: bool,
|
primary: bool,
|
||||||
},
|
},
|
||||||
|
ToggleSelectVsPath,
|
||||||
SwapColors,
|
SwapColors,
|
||||||
Undo,
|
Undo,
|
||||||
UpdateCursor,
|
UpdateCursor,
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
use super::common_functionality::shape_editor::ShapeState;
|
use super::common_functionality::shape_editor::ShapeState;
|
||||||
use super::common_functionality::shapes::shape_utility::ShapeType::{self, Ellipse, Line, Rectangle};
|
use super::common_functionality::shapes::shape_utility::ShapeType::{self, Ellipse, Line, Rectangle};
|
||||||
use super::utility_types::{ToolActionHandlerData, ToolFsmState, tool_message_to_tool_type};
|
use super::utility_types::{ToolActionMessageContext, ToolFsmState, tool_message_to_tool_type};
|
||||||
use crate::application::generate_uuid;
|
use crate::application::generate_uuid;
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
use crate::messages::portfolio::document::overlays::utility_types::OverlayProvider;
|
use crate::messages::portfolio::document::overlays::utility_types::OverlayProvider;
|
||||||
use crate::messages::portfolio::utility_types::PersistentData;
|
use crate::messages::portfolio::utility_types::PersistentData;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
use crate::messages::tool::transform_layer::transform_layer_message_handler::TransformLayerMessageContext;
|
||||||
use crate::messages::tool::utility_types::ToolType;
|
use crate::messages::tool::utility_types::ToolType;
|
||||||
use crate::node_graph_executor::NodeGraphExecutor;
|
use crate::node_graph_executor::NodeGraphExecutor;
|
||||||
use graphene_std::raster::color::Color;
|
use graphene_std::raster::color::Color;
|
||||||
|
|
||||||
const ARTBOARD_OVERLAY_PROVIDER: OverlayProvider = |context| DocumentMessage::DrawArtboardOverlays(context).into();
|
const ARTBOARD_OVERLAY_PROVIDER: OverlayProvider = |overlay_context| DocumentMessage::DrawArtboardOverlays(overlay_context).into();
|
||||||
|
|
||||||
#[derive(ExtractField)]
|
#[derive(ExtractField)]
|
||||||
pub struct ToolMessageData<'a> {
|
pub struct ToolMessageContext<'a> {
|
||||||
pub document_id: DocumentId,
|
pub document_id: DocumentId,
|
||||||
pub document: &'a mut DocumentMessageHandler,
|
pub document: &'a mut DocumentMessageHandler,
|
||||||
pub input: &'a InputPreprocessorMessageHandler,
|
pub input: &'a InputPreprocessorMessageHandler,
|
||||||
@@ -31,23 +32,30 @@ pub struct ToolMessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl MessageHandler<ToolMessage, ToolMessageData<'_>> for ToolMessageHandler {
|
impl MessageHandler<ToolMessage, ToolMessageContext<'_>> for ToolMessageHandler {
|
||||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, data: ToolMessageData) {
|
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, context: ToolMessageContext) {
|
||||||
let ToolMessageData {
|
let ToolMessageContext {
|
||||||
document_id,
|
document_id,
|
||||||
document,
|
document,
|
||||||
input,
|
input,
|
||||||
persistent_data,
|
persistent_data,
|
||||||
node_graph,
|
node_graph,
|
||||||
preferences,
|
preferences,
|
||||||
} = data;
|
} = context;
|
||||||
let font_cache = &persistent_data.font_cache;
|
let font_cache = &persistent_data.font_cache;
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
// Messages
|
// Messages
|
||||||
ToolMessage::TransformLayer(message) => self
|
ToolMessage::TransformLayer(message) => self.transform_layer_handler.process_message(
|
||||||
.transform_layer_handler
|
message,
|
||||||
.process_message(message, responses, (document, input, &self.tool_state.tool_data, &mut self.shape_editor)),
|
responses,
|
||||||
|
TransformLayerMessageContext {
|
||||||
|
document,
|
||||||
|
input,
|
||||||
|
tool_data: &self.tool_state.tool_data,
|
||||||
|
shape_editor: &mut self.shape_editor,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
ToolMessage::ActivateToolSelect => responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Select }),
|
ToolMessage::ActivateToolSelect => responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Select }),
|
||||||
ToolMessage::ActivateToolArtboard => responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Artboard }),
|
ToolMessage::ActivateToolArtboard => responses.add_front(ToolMessage::ActivateTool { tool_type: ToolType::Artboard }),
|
||||||
@@ -106,7 +114,7 @@ impl MessageHandler<ToolMessage, ToolMessageData<'_>> for ToolMessageHandler {
|
|||||||
// Send the old and new tools a transition to their FSM Abort states
|
// Send the old and new tools a transition to their FSM Abort states
|
||||||
let mut send_abort_to_tool = |old_tool: ToolType, new_tool: ToolType, update_hints_and_cursor: bool| {
|
let mut send_abort_to_tool = |old_tool: ToolType, new_tool: ToolType, update_hints_and_cursor: bool| {
|
||||||
if let Some(tool) = tool_data.tools.get_mut(&new_tool) {
|
if let Some(tool) = tool_data.tools.get_mut(&new_tool) {
|
||||||
let mut data = ToolActionHandlerData {
|
let mut data = ToolActionMessageContext {
|
||||||
document,
|
document,
|
||||||
document_id,
|
document_id,
|
||||||
global_tool_data: &self.tool_state.document_tool_data,
|
global_tool_data: &self.tool_state.document_tool_data,
|
||||||
@@ -206,7 +214,7 @@ impl MessageHandler<ToolMessage, ToolMessageData<'_>> for ToolMessageHandler {
|
|||||||
// Notify the frontend about the initial working colors
|
// Notify the frontend about the initial working colors
|
||||||
document_data.update_working_colors(responses);
|
document_data.update_working_colors(responses);
|
||||||
|
|
||||||
let mut data = ToolActionHandlerData {
|
let mut data = ToolActionMessageContext {
|
||||||
document,
|
document,
|
||||||
document_id,
|
document_id,
|
||||||
global_tool_data: &self.tool_state.document_tool_data,
|
global_tool_data: &self.tool_state.document_tool_data,
|
||||||
@@ -276,6 +284,16 @@ impl MessageHandler<ToolMessage, ToolMessageData<'_>> for ToolMessageHandler {
|
|||||||
|
|
||||||
document_data.update_working_colors(responses); // TODO: Make this an event
|
document_data.update_working_colors(responses); // TODO: Make this an event
|
||||||
}
|
}
|
||||||
|
ToolMessage::ToggleSelectVsPath => {
|
||||||
|
// If we have the select tool active, toggle to the path tool and vice versa
|
||||||
|
let tool_data = &mut self.tool_state.tool_data;
|
||||||
|
let active_tool_type = tool_data.active_tool_type;
|
||||||
|
if active_tool_type == ToolType::Select {
|
||||||
|
responses.add(ToolMessage::ActivateTool { tool_type: ToolType::Path });
|
||||||
|
} else {
|
||||||
|
responses.add(ToolMessage::ActivateTool { tool_type: ToolType::Select });
|
||||||
|
}
|
||||||
|
}
|
||||||
ToolMessage::SwapColors => {
|
ToolMessage::SwapColors => {
|
||||||
let document_data = &mut self.tool_state.document_tool_data;
|
let document_data = &mut self.tool_state.document_tool_data;
|
||||||
|
|
||||||
@@ -302,7 +320,7 @@ impl MessageHandler<ToolMessage, ToolMessageData<'_>> for ToolMessageHandler {
|
|||||||
let graph_view_overlay_open = document.graph_view_overlay_open();
|
let graph_view_overlay_open = document.graph_view_overlay_open();
|
||||||
|
|
||||||
if tool_type == tool_data.active_tool_type {
|
if tool_type == tool_data.active_tool_type {
|
||||||
let mut data = ToolActionHandlerData {
|
let mut data = ToolActionMessageContext {
|
||||||
document,
|
document,
|
||||||
document_id,
|
document_id,
|
||||||
global_tool_data: &self.tool_state.document_tool_data,
|
global_tool_data: &self.tool_state.document_tool_data,
|
||||||
@@ -351,9 +369,12 @@ impl MessageHandler<ToolMessage, ToolMessageData<'_>> for ToolMessageHandler {
|
|||||||
|
|
||||||
ActivateToolBrush,
|
ActivateToolBrush,
|
||||||
|
|
||||||
|
ToggleSelectVsPath,
|
||||||
|
|
||||||
SelectRandomWorkingColor,
|
SelectRandomWorkingColor,
|
||||||
ResetColors,
|
ResetColors,
|
||||||
SwapColors,
|
SwapColors,
|
||||||
|
|
||||||
Undo,
|
Undo,
|
||||||
);
|
);
|
||||||
list.extend(self.tool_state.tool_data.active_tool().actions());
|
list.extend(self.tool_state.tool_data.active_tool().actions());
|
||||||
|
|||||||
@@ -49,9 +49,9 @@ impl ToolMetadata for ArtboardTool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for ArtboardTool {
|
impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for ArtboardTool {
|
||||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, context: &mut ToolActionMessageContext<'a>) {
|
||||||
self.fsm_state.process_event(message, &mut self.data, tool_data, &(), responses, false);
|
self.fsm_state.process_event(message, &mut self.data, context, &(), responses, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn actions(&self) -> ActionList {
|
fn actions(&self) -> ActionList {
|
||||||
@@ -218,8 +218,8 @@ impl Fsm for ArtboardToolFsmState {
|
|||||||
type ToolData = ArtboardToolData;
|
type ToolData = ArtboardToolData;
|
||||||
type ToolOptions = ();
|
type ToolOptions = ();
|
||||||
|
|
||||||
fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionHandlerData, _tool_options: &(), responses: &mut VecDeque<Message>) -> Self {
|
fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionMessageContext, _tool_options: &(), responses: &mut VecDeque<Message>) -> Self {
|
||||||
let ToolActionHandlerData { document, input, .. } = tool_action_data;
|
let ToolActionMessageContext { document, input, .. } = tool_action_data;
|
||||||
|
|
||||||
let hovered = ArtboardToolData::hovered_artboard(document, input).is_some();
|
let hovered = ArtboardToolData::hovered_artboard(document, input).is_some();
|
||||||
|
|
||||||
|
|||||||
@@ -186,10 +186,10 @@ impl LayoutHolder for BrushTool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for BrushTool {
|
impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for BrushTool {
|
||||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, context: &mut ToolActionMessageContext<'a>) {
|
||||||
let ToolMessage::Brush(BrushToolMessage::UpdateOptions(action)) = message else {
|
let ToolMessage::Brush(BrushToolMessage::UpdateOptions(action)) = message else {
|
||||||
self.fsm_state.process_event(message, &mut self.data, tool_data, &self.options, responses, true);
|
self.fsm_state.process_event(message, &mut self.data, context, &self.options, responses, true);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
match action {
|
match action {
|
||||||
@@ -306,8 +306,15 @@ impl Fsm for BrushToolFsmState {
|
|||||||
type ToolData = BrushToolData;
|
type ToolData = BrushToolData;
|
||||||
type ToolOptions = BrushOptions;
|
type ToolOptions = BrushOptions;
|
||||||
|
|
||||||
fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionHandlerData, tool_options: &Self::ToolOptions, responses: &mut VecDeque<Message>) -> Self {
|
fn transition(
|
||||||
let ToolActionHandlerData {
|
self,
|
||||||
|
event: ToolMessage,
|
||||||
|
tool_data: &mut Self::ToolData,
|
||||||
|
tool_action_data: &mut ToolActionMessageContext,
|
||||||
|
tool_options: &Self::ToolOptions,
|
||||||
|
responses: &mut VecDeque<Message>,
|
||||||
|
) -> Self {
|
||||||
|
let ToolActionMessageContext {
|
||||||
document, global_tool_data, input, ..
|
document, global_tool_data, input, ..
|
||||||
} = tool_action_data;
|
} = tool_action_data;
|
||||||
|
|
||||||
|
|||||||
@@ -40,9 +40,9 @@ impl LayoutHolder for EyedropperTool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for EyedropperTool {
|
impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for EyedropperTool {
|
||||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, context: &mut ToolActionMessageContext<'a>) {
|
||||||
self.fsm_state.process_event(message, &mut self.data, tool_data, &(), responses, true);
|
self.fsm_state.process_event(message, &mut self.data, context, &(), responses, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
advertise_actions!(EyedropperToolMessageDiscriminant;
|
advertise_actions!(EyedropperToolMessageDiscriminant;
|
||||||
@@ -80,8 +80,8 @@ impl Fsm for EyedropperToolFsmState {
|
|||||||
type ToolData = EyedropperToolData;
|
type ToolData = EyedropperToolData;
|
||||||
type ToolOptions = ();
|
type ToolOptions = ();
|
||||||
|
|
||||||
fn transition(self, event: ToolMessage, _tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionHandlerData, _tool_options: &(), responses: &mut VecDeque<Message>) -> Self {
|
fn transition(self, event: ToolMessage, _tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionMessageContext, _tool_options: &(), responses: &mut VecDeque<Message>) -> Self {
|
||||||
let ToolActionHandlerData { global_tool_data, input, .. } = tool_action_data;
|
let ToolActionMessageContext { global_tool_data, input, .. } = tool_action_data;
|
||||||
|
|
||||||
let ToolMessage::Eyedropper(event) = event else { return self };
|
let ToolMessage::Eyedropper(event) = event else { return self };
|
||||||
match (self, event) {
|
match (self, event) {
|
||||||
|
|||||||
@@ -42,9 +42,9 @@ impl LayoutHolder for FillTool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for FillTool {
|
impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for FillTool {
|
||||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, context: &mut ToolActionMessageContext<'a>) {
|
||||||
self.fsm_state.process_event(message, &mut (), tool_data, &(), responses, true);
|
self.fsm_state.process_event(message, &mut (), context, &(), responses, true);
|
||||||
}
|
}
|
||||||
fn actions(&self) -> ActionList {
|
fn actions(&self) -> ActionList {
|
||||||
match self.fsm_state {
|
match self.fsm_state {
|
||||||
@@ -85,8 +85,15 @@ impl Fsm for FillToolFsmState {
|
|||||||
type ToolData = ();
|
type ToolData = ();
|
||||||
type ToolOptions = ();
|
type ToolOptions = ();
|
||||||
|
|
||||||
fn transition(self, event: ToolMessage, _tool_data: &mut Self::ToolData, handler_data: &mut ToolActionHandlerData, _tool_options: &Self::ToolOptions, responses: &mut VecDeque<Message>) -> Self {
|
fn transition(
|
||||||
let ToolActionHandlerData {
|
self,
|
||||||
|
event: ToolMessage,
|
||||||
|
_tool_data: &mut Self::ToolData,
|
||||||
|
handler_data: &mut ToolActionMessageContext,
|
||||||
|
_tool_options: &Self::ToolOptions,
|
||||||
|
responses: &mut VecDeque<Message>,
|
||||||
|
) -> Self {
|
||||||
|
let ToolActionMessageContext {
|
||||||
document, global_tool_data, input, ..
|
document, global_tool_data, input, ..
|
||||||
} = handler_data;
|
} = handler_data;
|
||||||
|
|
||||||
|
|||||||
@@ -117,10 +117,10 @@ impl LayoutHolder for FreehandTool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for FreehandTool {
|
impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for FreehandTool {
|
||||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, context: &mut ToolActionMessageContext<'a>) {
|
||||||
let ToolMessage::Freehand(FreehandToolMessage::UpdateOptions(action)) = message else {
|
let ToolMessage::Freehand(FreehandToolMessage::UpdateOptions(action)) = message else {
|
||||||
self.fsm_state.process_event(message, &mut self.data, tool_data, &self.options, responses, true);
|
self.fsm_state.process_event(message, &mut self.data, context, &self.options, responses, true);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
match action {
|
match action {
|
||||||
@@ -184,8 +184,15 @@ impl Fsm for FreehandToolFsmState {
|
|||||||
type ToolData = FreehandToolData;
|
type ToolData = FreehandToolData;
|
||||||
type ToolOptions = FreehandOptions;
|
type ToolOptions = FreehandOptions;
|
||||||
|
|
||||||
fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionHandlerData, tool_options: &Self::ToolOptions, responses: &mut VecDeque<Message>) -> Self {
|
fn transition(
|
||||||
let ToolActionHandlerData {
|
self,
|
||||||
|
event: ToolMessage,
|
||||||
|
tool_data: &mut Self::ToolData,
|
||||||
|
tool_action_data: &mut ToolActionMessageContext,
|
||||||
|
tool_options: &Self::ToolOptions,
|
||||||
|
responses: &mut VecDeque<Message>,
|
||||||
|
) -> Self {
|
||||||
|
let ToolActionMessageContext {
|
||||||
document,
|
document,
|
||||||
global_tool_data,
|
global_tool_data,
|
||||||
input,
|
input,
|
||||||
|
|||||||
@@ -54,10 +54,10 @@ impl ToolMetadata for GradientTool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for GradientTool {
|
impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for GradientTool {
|
||||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, context: &mut ToolActionMessageContext<'a>) {
|
||||||
let ToolMessage::Gradient(GradientToolMessage::UpdateOptions(action)) = message else {
|
let ToolMessage::Gradient(GradientToolMessage::UpdateOptions(action)) = message else {
|
||||||
self.fsm_state.process_event(message, &mut self.data, tool_data, &self.options, responses, false);
|
self.fsm_state.process_event(message, &mut self.data, context, &self.options, responses, false);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
match action {
|
match action {
|
||||||
@@ -67,7 +67,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for Gradien
|
|||||||
if let Some(selected_gradient) = &mut self.data.selected_gradient {
|
if let Some(selected_gradient) = &mut self.data.selected_gradient {
|
||||||
// Check if the current layer is a raster layer
|
// Check if the current layer is a raster layer
|
||||||
if let Some(layer) = selected_gradient.layer {
|
if let Some(layer) = selected_gradient.layer {
|
||||||
if NodeGraphLayer::is_raster_layer(layer, &mut tool_data.document.network_interface) {
|
if NodeGraphLayer::is_raster_layer(layer, &mut context.document.network_interface) {
|
||||||
return; // Don't proceed if it's a raster layer
|
return; // Don't proceed if it's a raster layer
|
||||||
}
|
}
|
||||||
selected_gradient.gradient.gradient_type = gradient_type;
|
selected_gradient.gradient.gradient_type = gradient_type;
|
||||||
@@ -243,8 +243,15 @@ impl Fsm for GradientToolFsmState {
|
|||||||
type ToolData = GradientToolData;
|
type ToolData = GradientToolData;
|
||||||
type ToolOptions = GradientOptions;
|
type ToolOptions = GradientOptions;
|
||||||
|
|
||||||
fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionHandlerData, tool_options: &Self::ToolOptions, responses: &mut VecDeque<Message>) -> Self {
|
fn transition(
|
||||||
let ToolActionHandlerData {
|
self,
|
||||||
|
event: ToolMessage,
|
||||||
|
tool_data: &mut Self::ToolData,
|
||||||
|
tool_action_data: &mut ToolActionMessageContext,
|
||||||
|
tool_options: &Self::ToolOptions,
|
||||||
|
responses: &mut VecDeque<Message>,
|
||||||
|
) -> Self {
|
||||||
|
let ToolActionMessageContext {
|
||||||
document, global_tool_data, input, ..
|
document, global_tool_data, input, ..
|
||||||
} = tool_action_data;
|
} = tool_action_data;
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ pub mod tool_prelude {
|
|||||||
pub use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
|
pub use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
|
||||||
pub use crate::messages::layout::utility_types::widget_prelude::*;
|
pub use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
pub use crate::messages::prelude::*;
|
pub use crate::messages::prelude::*;
|
||||||
pub use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
pub use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionMessageContext, ToolMetadata, ToolTransition, ToolType};
|
||||||
pub use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
pub use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||||
pub use glam::{DAffine2, DVec2};
|
pub use glam::{DAffine2, DVec2};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,9 +39,9 @@ impl LayoutHolder for NavigateTool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for NavigateTool {
|
impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for NavigateTool {
|
||||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, context: &mut ToolActionMessageContext<'a>) {
|
||||||
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &(), responses, true);
|
self.fsm_state.process_event(message, &mut self.tool_data, context, &(), responses, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn actions(&self) -> ActionList {
|
fn actions(&self) -> ActionList {
|
||||||
@@ -92,7 +92,7 @@ impl Fsm for NavigateToolFsmState {
|
|||||||
self,
|
self,
|
||||||
message: ToolMessage,
|
message: ToolMessage,
|
||||||
tool_data: &mut Self::ToolData,
|
tool_data: &mut Self::ToolData,
|
||||||
ToolActionHandlerData { input, .. }: &mut ToolActionHandlerData,
|
ToolActionMessageContext { input, .. }: &mut ToolActionMessageContext,
|
||||||
_tool_options: &Self::ToolOptions,
|
_tool_options: &Self::ToolOptions,
|
||||||
responses: &mut VecDeque<Message>,
|
responses: &mut VecDeque<Message>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use super::select_tool::extend_lasso;
|
use super::select_tool::extend_lasso;
|
||||||
use super::tool_prelude::*;
|
use super::tool_prelude::*;
|
||||||
use crate::consts::{
|
use crate::consts::{
|
||||||
COLOR_OVERLAY_BLUE, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, DOUBLE_CLICK_MILLISECONDS, DRAG_DIRECTION_MODE_DETERMINATION_THRESHOLD, DRAG_THRESHOLD, HANDLE_ROTATE_SNAP_ANGLE,
|
COLOR_OVERLAY_BLUE, COLOR_OVERLAY_GRAY, COLOR_OVERLAY_GREEN, COLOR_OVERLAY_RED, DOUBLE_CLICK_MILLISECONDS, DRAG_DIRECTION_MODE_DETERMINATION_THRESHOLD, DRAG_THRESHOLD, DRILL_THROUGH_THRESHOLD,
|
||||||
SEGMENT_INSERTION_DISTANCE, SEGMENT_OVERLAY_SIZE, SELECTION_THRESHOLD, SELECTION_TOLERANCE,
|
HANDLE_ROTATE_SNAP_ANGLE, SEGMENT_INSERTION_DISTANCE, SEGMENT_OVERLAY_SIZE, SELECTION_THRESHOLD, SELECTION_TOLERANCE,
|
||||||
};
|
};
|
||||||
use crate::messages::portfolio::document::overlays::utility_functions::{path_overlays, selected_segments};
|
use crate::messages::portfolio::document::overlays::utility_functions::{path_overlays, selected_segments};
|
||||||
use crate::messages::portfolio::document::overlays::utility_types::{DrawHandles, OverlayContext};
|
use crate::messages::portfolio::document::overlays::utility_types::{DrawHandles, OverlayContext};
|
||||||
@@ -11,6 +11,7 @@ use crate::messages::portfolio::document::utility_types::network_interface::Node
|
|||||||
use crate::messages::portfolio::document::utility_types::transformation::Axis;
|
use crate::messages::portfolio::document::utility_types::transformation::Axis;
|
||||||
use crate::messages::preferences::SelectionMode;
|
use crate::messages::preferences::SelectionMode;
|
||||||
use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
|
use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
|
||||||
|
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||||
use crate::messages::tool::common_functionality::pivot::{PivotGizmo, PivotGizmoType, PivotToolSource, pin_pivot_widget, pivot_gizmo_type_widget, pivot_reference_point_widget};
|
use crate::messages::tool::common_functionality::pivot::{PivotGizmo, PivotGizmoType, PivotToolSource, pin_pivot_widget, pivot_gizmo_type_widget, pivot_reference_point_widget};
|
||||||
use crate::messages::tool::common_functionality::shape_editor::{
|
use crate::messages::tool::common_functionality::shape_editor::{
|
||||||
ClosestSegment, ManipulatorAngle, OpposingHandleLengths, SelectedLayerState, SelectedPointsInfo, SelectionChange, SelectionShape, SelectionShapeType, ShapeState,
|
ClosestSegment, ManipulatorAngle, OpposingHandleLengths, SelectedLayerState, SelectedPointsInfo, SelectionChange, SelectionShape, SelectionShapeType, ShapeState,
|
||||||
@@ -18,8 +19,10 @@ use crate::messages::tool::common_functionality::shape_editor::{
|
|||||||
use crate::messages::tool::common_functionality::snapping::{SnapCache, SnapCandidatePoint, SnapConstraint, SnapData, SnapManager};
|
use crate::messages::tool::common_functionality::snapping::{SnapCache, SnapCandidatePoint, SnapConstraint, SnapData, SnapManager};
|
||||||
use crate::messages::tool::common_functionality::utility_functions::{calculate_segment_angle, find_two_param_best_approximate};
|
use crate::messages::tool::common_functionality::utility_functions::{calculate_segment_angle, find_two_param_best_approximate};
|
||||||
use bezier_rs::{Bezier, BezierHandles, TValue};
|
use bezier_rs::{Bezier, BezierHandles, TValue};
|
||||||
|
use graph_craft::document::value::TaggedValue;
|
||||||
use graphene_std::renderer::Quad;
|
use graphene_std::renderer::Quad;
|
||||||
use graphene_std::transform::ReferencePoint;
|
use graphene_std::transform::ReferencePoint;
|
||||||
|
use graphene_std::vector::click_target::ClickTargetType;
|
||||||
use graphene_std::vector::{HandleExt, HandleId, NoHashBuilder, SegmentId, VectorData};
|
use graphene_std::vector::{HandleExt, HandleId, NoHashBuilder, SegmentId, VectorData};
|
||||||
use graphene_std::vector::{ManipulatorPointId, PointId, VectorModificationType};
|
use graphene_std::vector::{ManipulatorPointId, PointId, VectorModificationType};
|
||||||
use std::vec;
|
use std::vec;
|
||||||
@@ -263,6 +266,14 @@ impl LayoutHolder for PathTool {
|
|||||||
.selected_index(Some(self.options.path_overlay_mode as u32))
|
.selected_index(Some(self.options.path_overlay_mode as u32))
|
||||||
.widget_holder();
|
.widget_holder();
|
||||||
|
|
||||||
|
// Works only if a single layer is selected and its type is vectordata
|
||||||
|
let path_node_button = TextButton::new("Make Path Editable")
|
||||||
|
.icon(Some("NodeShape".into()))
|
||||||
|
.tooltip("Make Path Editable")
|
||||||
|
.on_update(|_| NodeGraphMessage::AddPathNode.into())
|
||||||
|
.disabled(!self.tool_data.single_path_node_compatible_layer_selected)
|
||||||
|
.widget_holder();
|
||||||
|
|
||||||
let [_checkbox, _dropdown] = {
|
let [_checkbox, _dropdown] = {
|
||||||
let pivot_gizmo_type_widget = pivot_gizmo_type_widget(self.tool_data.pivot_gizmo.state, PivotToolSource::Path);
|
let pivot_gizmo_type_widget = pivot_gizmo_type_widget(self.tool_data.pivot_gizmo.state, PivotToolSource::Path);
|
||||||
[pivot_gizmo_type_widget[0].clone(), pivot_gizmo_type_widget[2].clone()]
|
[pivot_gizmo_type_widget[0].clone(), pivot_gizmo_type_widget[2].clone()]
|
||||||
@@ -293,6 +304,7 @@ impl LayoutHolder for PathTool {
|
|||||||
unrelated_seperator.clone(),
|
unrelated_seperator.clone(),
|
||||||
path_overlay_mode_widget,
|
path_overlay_mode_widget,
|
||||||
unrelated_seperator.clone(),
|
unrelated_seperator.clone(),
|
||||||
|
path_node_button,
|
||||||
// checkbox.clone(),
|
// checkbox.clone(),
|
||||||
// related_seperator.clone(),
|
// related_seperator.clone(),
|
||||||
// dropdown.clone(),
|
// dropdown.clone(),
|
||||||
@@ -306,8 +318,8 @@ impl LayoutHolder for PathTool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for PathTool {
|
impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for PathTool {
|
||||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, context: &mut ToolActionMessageContext<'a>) {
|
||||||
let updating_point = message == ToolMessage::Path(PathToolMessage::SelectedPointUpdated);
|
let updating_point = message == ToolMessage::Path(PathToolMessage::SelectedPointUpdated);
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
@@ -350,20 +362,20 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for PathToo
|
|||||||
},
|
},
|
||||||
ToolMessage::Path(PathToolMessage::ClosePath) => {
|
ToolMessage::Path(PathToolMessage::ClosePath) => {
|
||||||
responses.add(DocumentMessage::AddTransaction);
|
responses.add(DocumentMessage::AddTransaction);
|
||||||
tool_data.shape_editor.close_selected_path(tool_data.document, responses);
|
context.shape_editor.close_selected_path(context.document, responses);
|
||||||
responses.add(DocumentMessage::EndTransaction);
|
responses.add(DocumentMessage::EndTransaction);
|
||||||
responses.add(OverlaysMessage::Draw);
|
responses.add(OverlaysMessage::Draw);
|
||||||
}
|
}
|
||||||
ToolMessage::Path(PathToolMessage::SwapSelectedHandles) => {
|
ToolMessage::Path(PathToolMessage::SwapSelectedHandles) => {
|
||||||
if tool_data.shape_editor.handle_with_pair_selected(&tool_data.document.network_interface) {
|
if context.shape_editor.handle_with_pair_selected(&context.document.network_interface) {
|
||||||
tool_data.shape_editor.alternate_selected_handles(&tool_data.document.network_interface);
|
context.shape_editor.alternate_selected_handles(&context.document.network_interface);
|
||||||
responses.add(PathToolMessage::SelectedPointUpdated);
|
responses.add(PathToolMessage::SelectedPointUpdated);
|
||||||
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::None });
|
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::None });
|
||||||
responses.add(OverlaysMessage::Draw);
|
responses.add(OverlaysMessage::Draw);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &self.options, responses, true);
|
self.fsm_state.process_event(message, &mut self.tool_data, context, &self.options, responses, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -516,6 +528,12 @@ struct PathToolData {
|
|||||||
started_drawing_from_inside: bool,
|
started_drawing_from_inside: bool,
|
||||||
first_selected_with_single_click: bool,
|
first_selected_with_single_click: bool,
|
||||||
stored_selection: Option<HashMap<LayerNodeIdentifier, SelectedLayerState>>,
|
stored_selection: Option<HashMap<LayerNodeIdentifier, SelectedLayerState>>,
|
||||||
|
last_drill_through_click_position: Option<DVec2>,
|
||||||
|
drill_through_cycle_index: usize,
|
||||||
|
drill_through_cycle_count: usize,
|
||||||
|
hovered_layers: Vec<LayerNodeIdentifier>,
|
||||||
|
ghost_outline: Vec<(Vec<ClickTargetType>, DAffine2)>,
|
||||||
|
single_path_node_compatible_layer_selected: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PathToolData {
|
impl PathToolData {
|
||||||
@@ -524,10 +542,6 @@ impl PathToolData {
|
|||||||
PathToolFsmState::Dragging(self.dragging_state)
|
PathToolFsmState::Dragging(self.dragging_state)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_saved_points(&mut self) {
|
|
||||||
self.saved_points_before_anchor_select_toggle.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn selection_quad(&self, metadata: &DocumentMetadata) -> Quad {
|
pub fn selection_quad(&self, metadata: &DocumentMetadata) -> Quad {
|
||||||
let bbox = self.selection_box(metadata);
|
let bbox = self.selection_box(metadata);
|
||||||
Quad::from_box(bbox)
|
Quad::from_box(bbox)
|
||||||
@@ -576,6 +590,49 @@ impl PathToolData {
|
|||||||
self.selection_status = selection_status;
|
self.selection_status = selection_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn remove_saved_points(&mut self) {
|
||||||
|
self.saved_points_before_anchor_select_toggle.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset_drill_through_cycle(&mut self) {
|
||||||
|
self.last_drill_through_click_position = None;
|
||||||
|
self.drill_through_cycle_index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_drill_through_cycle(&mut self, position: DVec2) -> usize {
|
||||||
|
if self.last_drill_through_click_position.map_or(true, |last_pos| last_pos.distance(position) > DRILL_THROUGH_THRESHOLD) {
|
||||||
|
// New position, reset cycle
|
||||||
|
self.drill_through_cycle_index = 0;
|
||||||
|
} else {
|
||||||
|
// Same position, advance cycle
|
||||||
|
self.drill_through_cycle_index = (self.drill_through_cycle_index + 1) % self.drill_through_cycle_count.max(1);
|
||||||
|
}
|
||||||
|
self.last_drill_through_click_position = Some(position);
|
||||||
|
self.drill_through_cycle_index
|
||||||
|
}
|
||||||
|
|
||||||
|
fn peek_drill_through_index(&self) -> usize {
|
||||||
|
if self.drill_through_cycle_count == 0 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
(self.drill_through_cycle_index + 1) % self.drill_through_cycle_count.max(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_drill_through_mouse_moved(&self, position: DVec2) -> bool {
|
||||||
|
self.last_drill_through_click_position.map_or(true, |last_pos| last_pos.distance(position) > DRILL_THROUGH_THRESHOLD)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_ghost_outline(&mut self, shape_editor: &ShapeState, document: &DocumentMessageHandler) {
|
||||||
|
self.ghost_outline.clear();
|
||||||
|
for &layer in shape_editor.selected_shape_state.keys() {
|
||||||
|
// We probably need to collect here
|
||||||
|
let outline: Vec<ClickTargetType> = document.metadata().layer_with_free_points_outline(layer).cloned().collect();
|
||||||
|
let transform = document.metadata().transform_to_viewport(layer);
|
||||||
|
self.ghost_outline.push((outline, transform));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: This function is for basic point select mode. We definitely need to make a new one for the segment select mode.
|
// TODO: This function is for basic point select mode. We definitely need to make a new one for the segment select mode.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn mouse_down(
|
fn mouse_down(
|
||||||
@@ -619,6 +676,8 @@ impl PathToolData {
|
|||||||
) {
|
) {
|
||||||
responses.add(DocumentMessage::StartTransaction);
|
responses.add(DocumentMessage::StartTransaction);
|
||||||
|
|
||||||
|
self.set_ghost_outline(shape_editor, document);
|
||||||
|
|
||||||
self.last_clicked_point_was_selected = already_selected;
|
self.last_clicked_point_was_selected = already_selected;
|
||||||
|
|
||||||
// If the point is already selected and shift (`extend_selection`) is used, keep the selection unchanged.
|
// If the point is already selected and shift (`extend_selection`) is used, keep the selection unchanged.
|
||||||
@@ -703,6 +762,8 @@ impl PathToolData {
|
|||||||
else if let Some(segment) = shape_editor.upper_closest_segment(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD) {
|
else if let Some(segment) = shape_editor.upper_closest_segment(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD) {
|
||||||
responses.add(DocumentMessage::StartTransaction);
|
responses.add(DocumentMessage::StartTransaction);
|
||||||
|
|
||||||
|
self.set_ghost_outline(shape_editor, document);
|
||||||
|
|
||||||
if segment_editing_mode && !molding_in_segment_edit {
|
if segment_editing_mode && !molding_in_segment_edit {
|
||||||
let layer = segment.layer();
|
let layer = segment.layer();
|
||||||
let segment_id = segment.segment();
|
let segment_id = segment.segment();
|
||||||
@@ -723,7 +784,6 @@ impl PathToolData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.drag_start_pos = input.mouse.position;
|
self.drag_start_pos = input.mouse.position;
|
||||||
|
|
||||||
let viewport_to_document = document.metadata().document_to_viewport.inverse();
|
let viewport_to_document = document.metadata().document_to_viewport.inverse();
|
||||||
self.previous_mouse_position = viewport_to_document.transform_point2(input.mouse.position);
|
self.previous_mouse_position = viewport_to_document.transform_point2(input.mouse.position);
|
||||||
|
|
||||||
@@ -746,6 +806,8 @@ impl PathToolData {
|
|||||||
else if let Some(layer) = document.click(input) {
|
else if let Some(layer) = document.click(input) {
|
||||||
if shape_editor.selected_shape_state.is_empty() {
|
if shape_editor.selected_shape_state.is_empty() {
|
||||||
self.first_selected_with_single_click = true;
|
self.first_selected_with_single_click = true;
|
||||||
|
// This ensures we don't need to double click a second time to get the drill through to work
|
||||||
|
self.last_drill_through_click_position = Some(input.mouse.position);
|
||||||
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![layer.to_node()] });
|
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![layer.to_node()] });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1387,8 +1449,15 @@ impl Fsm for PathToolFsmState {
|
|||||||
type ToolData = PathToolData;
|
type ToolData = PathToolData;
|
||||||
type ToolOptions = PathToolOptions;
|
type ToolOptions = PathToolOptions;
|
||||||
|
|
||||||
fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionHandlerData, tool_options: &Self::ToolOptions, responses: &mut VecDeque<Message>) -> Self {
|
fn transition(
|
||||||
let ToolActionHandlerData { document, input, shape_editor, .. } = tool_action_data;
|
self,
|
||||||
|
event: ToolMessage,
|
||||||
|
tool_data: &mut Self::ToolData,
|
||||||
|
tool_action_data: &mut ToolActionMessageContext,
|
||||||
|
tool_options: &Self::ToolOptions,
|
||||||
|
responses: &mut VecDeque<Message>,
|
||||||
|
) -> Self {
|
||||||
|
let ToolActionMessageContext { document, input, shape_editor, .. } = tool_action_data;
|
||||||
|
|
||||||
update_dynamic_hints(self, responses, shape_editor, document, tool_data, tool_options);
|
update_dynamic_hints(self, responses, shape_editor, document, tool_data, tool_options);
|
||||||
|
|
||||||
@@ -1401,6 +1470,7 @@ impl Fsm for PathToolFsmState {
|
|||||||
(_, PathToolMessage::SelectionChanged) => {
|
(_, PathToolMessage::SelectionChanged) => {
|
||||||
// Set the newly targeted layers to visible
|
// Set the newly targeted layers to visible
|
||||||
let target_layers = document.network_interface.selected_nodes().selected_layers(document.metadata()).collect();
|
let target_layers = document.network_interface.selected_nodes().selected_layers(document.metadata()).collect();
|
||||||
|
|
||||||
shape_editor.set_selected_layers(target_layers);
|
shape_editor.set_selected_layers(target_layers);
|
||||||
|
|
||||||
responses.add(OverlaysMessage::Draw);
|
responses.add(OverlaysMessage::Draw);
|
||||||
@@ -1419,6 +1489,12 @@ impl Fsm for PathToolFsmState {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
(_, PathToolMessage::Overlays(mut overlay_context)) => {
|
(_, PathToolMessage::Overlays(mut overlay_context)) => {
|
||||||
|
if matches!(self, Self::Dragging(_)) {
|
||||||
|
for (outline, transform) in &tool_data.ghost_outline {
|
||||||
|
overlay_context.outline(outline.iter(), *transform, Some(COLOR_OVERLAY_GRAY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: find the segment ids of which the selected points are a part of
|
// TODO: find the segment ids of which the selected points are a part of
|
||||||
|
|
||||||
match tool_options.path_overlay_mode {
|
match tool_options.path_overlay_mode {
|
||||||
@@ -1520,6 +1596,34 @@ impl Fsm for PathToolFsmState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Show outlines for hovered layers with appropriate highlighting
|
||||||
|
let currently_selected_layer = document.network_interface.selected_nodes().selected_layers(document.metadata()).next();
|
||||||
|
let next_selected_index = tool_data.peek_drill_through_index();
|
||||||
|
let mouse_has_moved = tool_data.has_drill_through_mouse_moved(input.mouse.position);
|
||||||
|
|
||||||
|
for (index, &hovered_layer) in tool_data.hovered_layers.iter().enumerate() {
|
||||||
|
// Skip already highlighted selected layer
|
||||||
|
if Some(hovered_layer) == currently_selected_layer {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let layer_to_viewport = document.metadata().transform_to_viewport(hovered_layer);
|
||||||
|
let outline = document.metadata().layer_with_free_points_outline(hovered_layer);
|
||||||
|
|
||||||
|
// Determine highlight color based on drill-through state
|
||||||
|
let color = match (index, mouse_has_moved) {
|
||||||
|
// If the layer is the next selected one and mouse has not moved, highlight it blue
|
||||||
|
(i, false) if i == next_selected_index => COLOR_OVERLAY_BLUE,
|
||||||
|
// If the layer is the first hovered one and mouse has moved, highlight it blue
|
||||||
|
(0, true) => COLOR_OVERLAY_BLUE,
|
||||||
|
// Otherwise, use gray
|
||||||
|
_ => COLOR_OVERLAY_GRAY,
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: Make this draw underneath all other overlays
|
||||||
|
overlay_context.outline(outline, layer_to_viewport, Some(color));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Self::Drawing { selection_shape } => {
|
Self::Drawing { selection_shape } => {
|
||||||
let mut fill_color = graphene_std::Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
|
let mut fill_color = graphene_std::Color::from_rgb_str(COLOR_OVERLAY_BLUE.strip_prefix('#').unwrap())
|
||||||
@@ -1701,9 +1805,9 @@ impl Fsm for PathToolFsmState {
|
|||||||
break_molding,
|
break_molding,
|
||||||
tool_data.temporary_adjacent_handles_while_molding,
|
tool_data.temporary_adjacent_handles_while_molding,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
return PathToolFsmState::Dragging(tool_data.dragging_state);
|
return PathToolFsmState::Dragging(tool_data.dragging_state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let anchor_and_handle_toggled = input.keyboard.get(move_anchor_with_handles as usize);
|
let anchor_and_handle_toggled = input.keyboard.get(move_anchor_with_handles as usize);
|
||||||
@@ -1787,6 +1891,23 @@ impl Fsm for PathToolFsmState {
|
|||||||
tool_data.adjacent_anchor_offset = None;
|
tool_data.adjacent_anchor_offset = None;
|
||||||
tool_data.stored_selection = None;
|
tool_data.stored_selection = None;
|
||||||
|
|
||||||
|
if tool_data.has_drill_through_mouse_moved(input.mouse.position) {
|
||||||
|
tool_data.reset_drill_through_cycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
// When moving the cursor around we want to update the hovered layers
|
||||||
|
let new_hovered_layers: Vec<LayerNodeIdentifier> = document
|
||||||
|
.click_list_no_parents(input)
|
||||||
|
.filter(|&layer| {
|
||||||
|
// Filter out artboards and parent holders, and already selected layers
|
||||||
|
!document.network_interface.is_artboard(&layer.to_node(), &[])
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if tool_data.hovered_layers != new_hovered_layers {
|
||||||
|
tool_data.hovered_layers = new_hovered_layers;
|
||||||
|
}
|
||||||
|
|
||||||
responses.add(OverlaysMessage::Draw);
|
responses.add(OverlaysMessage::Draw);
|
||||||
|
|
||||||
self
|
self
|
||||||
@@ -1987,6 +2108,7 @@ impl Fsm for PathToolFsmState {
|
|||||||
PathToolFsmState::Ready
|
PathToolFsmState::Ready
|
||||||
}
|
}
|
||||||
(_, PathToolMessage::DragStop { extend_selection, .. }) => {
|
(_, PathToolMessage::DragStop { extend_selection, .. }) => {
|
||||||
|
tool_data.ghost_outline.clear();
|
||||||
let extend_selection = input.keyboard.get(extend_selection as usize);
|
let extend_selection = input.keyboard.get(extend_selection as usize);
|
||||||
let drag_occurred = tool_data.drag_start_pos.distance(input.mouse.position) > DRAG_THRESHOLD;
|
let drag_occurred = tool_data.drag_start_pos.distance(input.mouse.position) > DRAG_THRESHOLD;
|
||||||
|
|
||||||
@@ -2130,6 +2252,20 @@ impl Fsm for PathToolFsmState {
|
|||||||
(_, PathToolMessage::DoubleClick { extend_selection, shrink_selection }) => {
|
(_, PathToolMessage::DoubleClick { extend_selection, shrink_selection }) => {
|
||||||
// Double-clicked on a point (flip smooth/sharp behavior)
|
// Double-clicked on a point (flip smooth/sharp behavior)
|
||||||
let nearest_point = shape_editor.find_nearest_point_indices(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD);
|
let nearest_point = shape_editor.find_nearest_point_indices(&document.network_interface, input.mouse.position, SELECTION_THRESHOLD);
|
||||||
|
|
||||||
|
let mut get_drill_through_layer = || -> Option<LayerNodeIdentifier> {
|
||||||
|
let drill_through_layers = document.click_list_no_parents(input).collect::<Vec<LayerNodeIdentifier>>();
|
||||||
|
if drill_through_layers.is_empty() {
|
||||||
|
tool_data.reset_drill_through_cycle();
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
tool_data.drill_through_cycle_count = drill_through_layers.len();
|
||||||
|
let cycle_index = tool_data.next_drill_through_cycle(input.mouse.position);
|
||||||
|
let layer = drill_through_layers.get(cycle_index);
|
||||||
|
if cycle_index == 0 { drill_through_layers.first().copied() } else { layer.copied() }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if nearest_point.is_some() {
|
if nearest_point.is_some() {
|
||||||
// Flip the selected point between smooth and sharp
|
// Flip the selected point between smooth and sharp
|
||||||
if !tool_data.double_click_handled && tool_data.drag_start_pos.distance(input.mouse.position) <= DRAG_THRESHOLD {
|
if !tool_data.double_click_handled && tool_data.drag_start_pos.distance(input.mouse.position) <= DRAG_THRESHOLD {
|
||||||
@@ -2146,17 +2282,17 @@ impl Fsm for PathToolFsmState {
|
|||||||
return PathToolFsmState::Ready;
|
return PathToolFsmState::Ready;
|
||||||
}
|
}
|
||||||
// Double-clicked on a filled region
|
// Double-clicked on a filled region
|
||||||
else if let Some(layer) = document.click(input) {
|
else if let Some(layer) = &get_drill_through_layer() {
|
||||||
let extend_selection = input.keyboard.get(extend_selection as usize);
|
let extend_selection = input.keyboard.get(extend_selection as usize);
|
||||||
let shrink_selection = input.keyboard.get(shrink_selection as usize);
|
let shrink_selection = input.keyboard.get(shrink_selection as usize);
|
||||||
|
|
||||||
if shape_editor.is_selected_layer(layer) {
|
if shape_editor.is_selected_layer(*layer) {
|
||||||
if extend_selection && !tool_data.first_selected_with_single_click {
|
if extend_selection && !tool_data.first_selected_with_single_click {
|
||||||
responses.add(NodeGraphMessage::SelectedNodesRemove { nodes: vec![layer.to_node()] });
|
responses.add(NodeGraphMessage::SelectedNodesRemove { nodes: vec![layer.to_node()] });
|
||||||
|
|
||||||
if let Some(selection) = &tool_data.stored_selection {
|
if let Some(selection) = &tool_data.stored_selection {
|
||||||
let mut selection = selection.clone();
|
let mut selection = selection.clone();
|
||||||
selection.remove(&layer);
|
selection.remove(layer);
|
||||||
shape_editor.selected_shape_state = selection;
|
shape_editor.selected_shape_state = selection;
|
||||||
tool_data.stored_selection = None;
|
tool_data.stored_selection = None;
|
||||||
}
|
}
|
||||||
@@ -2168,19 +2304,19 @@ impl Fsm for PathToolFsmState {
|
|||||||
tool_data.stored_selection = None;
|
tool_data.stored_selection = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let state = shape_editor.selected_shape_state.get_mut(&layer).expect("No state for selected layer");
|
let state = shape_editor.selected_shape_state.get_mut(layer).expect("No state for selected layer");
|
||||||
state.deselect_all_points_in_layer();
|
state.deselect_all_points_in_layer();
|
||||||
state.deselect_all_segments_in_layer();
|
state.deselect_all_segments_in_layer();
|
||||||
} else if !tool_data.first_selected_with_single_click {
|
} else if !tool_data.first_selected_with_single_click {
|
||||||
// Select according to the selected editing mode
|
// Select according to the selected editing mode
|
||||||
let point_editing_mode = tool_options.path_editing_mode.point_editing_mode;
|
let point_editing_mode = tool_options.path_editing_mode.point_editing_mode;
|
||||||
let segment_editing_mode = tool_options.path_editing_mode.segment_editing_mode;
|
let segment_editing_mode = tool_options.path_editing_mode.segment_editing_mode;
|
||||||
shape_editor.select_connected(document, layer, input.mouse.position, point_editing_mode, segment_editing_mode);
|
shape_editor.select_connected(document, *layer, input.mouse.position, point_editing_mode, segment_editing_mode);
|
||||||
|
|
||||||
// Select all the other layers back again
|
// Select all the other layers back again
|
||||||
if let Some(selection) = &tool_data.stored_selection {
|
if let Some(selection) = &tool_data.stored_selection {
|
||||||
let mut selection = selection.clone();
|
let mut selection = selection.clone();
|
||||||
selection.remove(&layer);
|
selection.remove(layer);
|
||||||
|
|
||||||
for (layer, state) in selection {
|
for (layer, state) in selection {
|
||||||
shape_editor.selected_shape_state.insert(layer, state);
|
shape_editor.selected_shape_state.insert(layer, state);
|
||||||
@@ -2259,6 +2395,31 @@ impl Fsm for PathToolFsmState {
|
|||||||
point_select_state: shape_editor.get_dragging_state(&document.network_interface),
|
point_select_state: shape_editor.get_dragging_state(&document.network_interface),
|
||||||
colinear,
|
colinear,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
tool_data.single_path_node_compatible_layer_selected = {
|
||||||
|
let selected_nodes = document.network_interface.selected_nodes();
|
||||||
|
let mut selected_layers = selected_nodes.selected_layers(document.metadata());
|
||||||
|
let first_layer = selected_layers.next();
|
||||||
|
let second_layer = selected_layers.next();
|
||||||
|
let has_single_selection = first_layer.is_some() && second_layer.is_none();
|
||||||
|
|
||||||
|
let compatible_type = first_layer.and_then(|layer| {
|
||||||
|
let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, &document.network_interface);
|
||||||
|
graph_layer.horizontal_layer_flow().nth(1).and_then(|node_id| {
|
||||||
|
let (output_type, _) = document.network_interface.output_type(&node_id, 0, &[]);
|
||||||
|
Some(format!("type:{}", output_type.nested_type()))
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let is_compatible = compatible_type.as_deref() == Some("type:Instances<VectorData>");
|
||||||
|
|
||||||
|
let is_modifiable = first_layer.map_or(false, |layer| {
|
||||||
|
let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, &document.network_interface);
|
||||||
|
matches!(graph_layer.find_input("Path", 1), Some(TaggedValue::VectorModification(_)))
|
||||||
|
});
|
||||||
|
|
||||||
|
first_layer.is_some() && has_single_selection && is_compatible && !is_modifiable
|
||||||
|
};
|
||||||
tool_data.update_selection_status(shape_editor, document);
|
tool_data.update_selection_status(shape_editor, document);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -187,10 +187,10 @@ impl LayoutHolder for PenTool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for PenTool {
|
impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for PenTool {
|
||||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, context: &mut ToolActionMessageContext<'a>) {
|
||||||
let ToolMessage::Pen(PenToolMessage::UpdateOptions(action)) = message else {
|
let ToolMessage::Pen(PenToolMessage::UpdateOptions(action)) = message else {
|
||||||
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &self.options, responses, true);
|
self.fsm_state.process_event(message, &mut self.tool_data, context, &self.options, responses, true);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1403,8 +1403,15 @@ impl Fsm for PenToolFsmState {
|
|||||||
type ToolData = PenToolData;
|
type ToolData = PenToolData;
|
||||||
type ToolOptions = PenOptions;
|
type ToolOptions = PenOptions;
|
||||||
|
|
||||||
fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionHandlerData, tool_options: &Self::ToolOptions, responses: &mut VecDeque<Message>) -> Self {
|
fn transition(
|
||||||
let ToolActionHandlerData {
|
self,
|
||||||
|
event: ToolMessage,
|
||||||
|
tool_data: &mut Self::ToolData,
|
||||||
|
tool_action_data: &mut ToolActionMessageContext,
|
||||||
|
tool_options: &Self::ToolOptions,
|
||||||
|
responses: &mut VecDeque<Message>,
|
||||||
|
) -> Self {
|
||||||
|
let ToolActionMessageContext {
|
||||||
document,
|
document,
|
||||||
global_tool_data,
|
global_tool_data,
|
||||||
input,
|
input,
|
||||||
|
|||||||
@@ -273,8 +273,8 @@ impl LayoutHolder for SelectTool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for SelectTool {
|
impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for SelectTool {
|
||||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, context: &mut ToolActionMessageContext<'a>) {
|
||||||
let mut redraw_reference_pivot = false;
|
let mut redraw_reference_pivot = false;
|
||||||
|
|
||||||
if let ToolMessage::Select(SelectToolMessage::SelectOptions(ref option_update)) = message {
|
if let ToolMessage::Select(SelectToolMessage::SelectOptions(ref option_update)) = message {
|
||||||
@@ -309,7 +309,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for SelectT
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &(), responses, false);
|
self.fsm_state.process_event(message, &mut self.tool_data, context, &(), responses, false);
|
||||||
|
|
||||||
if self.tool_data.pivot_gizmo.pivot.should_refresh_pivot_position() || self.tool_data.selected_layers_changed || redraw_reference_pivot {
|
if self.tool_data.pivot_gizmo.pivot.should_refresh_pivot_position() || self.tool_data.selected_layers_changed || redraw_reference_pivot {
|
||||||
// Send the layout containing the updated pivot position (a bit ugly to do it here not in the fsm but that doesn't have SelectTool)
|
// Send the layout containing the updated pivot position (a bit ugly to do it here not in the fsm but that doesn't have SelectTool)
|
||||||
@@ -584,8 +584,8 @@ impl Fsm for SelectToolFsmState {
|
|||||||
type ToolData = SelectToolData;
|
type ToolData = SelectToolData;
|
||||||
type ToolOptions = ();
|
type ToolOptions = ();
|
||||||
|
|
||||||
fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionHandlerData, _tool_options: &(), responses: &mut VecDeque<Message>) -> Self {
|
fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionMessageContext, _tool_options: &(), responses: &mut VecDeque<Message>) -> Self {
|
||||||
let ToolActionHandlerData { document, input, font_cache, .. } = tool_action_data;
|
let ToolActionMessageContext { document, input, font_cache, .. } = tool_action_data;
|
||||||
|
|
||||||
let ToolMessage::Select(event) = event else { return self };
|
let ToolMessage::Select(event) = event else { return self };
|
||||||
match (self, event) {
|
match (self, event) {
|
||||||
|
|||||||
@@ -163,10 +163,10 @@ impl LayoutHolder for ShapeTool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for ShapeTool {
|
impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for ShapeTool {
|
||||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, context: &mut ToolActionMessageContext<'a>) {
|
||||||
let ToolMessage::Shape(ShapeToolMessage::UpdateOptions(action)) = message else {
|
let ToolMessage::Shape(ShapeToolMessage::UpdateOptions(action)) = message else {
|
||||||
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &self.options, responses, true);
|
self.fsm_state.process_event(message, &mut self.tool_data, context, &self.options, responses, true);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
match action {
|
match action {
|
||||||
@@ -337,14 +337,14 @@ impl Fsm for ShapeToolFsmState {
|
|||||||
self,
|
self,
|
||||||
event: ToolMessage,
|
event: ToolMessage,
|
||||||
tool_data: &mut Self::ToolData,
|
tool_data: &mut Self::ToolData,
|
||||||
ToolActionHandlerData {
|
ToolActionMessageContext {
|
||||||
document,
|
document,
|
||||||
global_tool_data,
|
global_tool_data,
|
||||||
input,
|
input,
|
||||||
preferences,
|
preferences,
|
||||||
shape_editor,
|
shape_editor,
|
||||||
..
|
..
|
||||||
}: &mut ToolActionHandlerData,
|
}: &mut ToolActionMessageContext,
|
||||||
tool_options: &Self::ToolOptions,
|
tool_options: &Self::ToolOptions,
|
||||||
responses: &mut VecDeque<Message>,
|
responses: &mut VecDeque<Message>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
|||||||
@@ -124,10 +124,10 @@ impl LayoutHolder for SplineTool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for SplineTool {
|
impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for SplineTool {
|
||||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, context: &mut ToolActionMessageContext<'a>) {
|
||||||
let ToolMessage::Spline(SplineToolMessage::UpdateOptions(action)) = message else {
|
let ToolMessage::Spline(SplineToolMessage::UpdateOptions(action)) = message else {
|
||||||
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &self.options, responses, true);
|
self.fsm_state.process_event(message, &mut self.tool_data, context, &self.options, responses, true);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
match action {
|
match action {
|
||||||
@@ -242,8 +242,15 @@ impl Fsm for SplineToolFsmState {
|
|||||||
type ToolData = SplineToolData;
|
type ToolData = SplineToolData;
|
||||||
type ToolOptions = SplineOptions;
|
type ToolOptions = SplineOptions;
|
||||||
|
|
||||||
fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionHandlerData, tool_options: &Self::ToolOptions, responses: &mut VecDeque<Message>) -> Self {
|
fn transition(
|
||||||
let ToolActionHandlerData {
|
self,
|
||||||
|
event: ToolMessage,
|
||||||
|
tool_data: &mut Self::ToolData,
|
||||||
|
tool_action_data: &mut ToolActionMessageContext,
|
||||||
|
tool_options: &Self::ToolOptions,
|
||||||
|
responses: &mut VecDeque<Message>,
|
||||||
|
) -> Self {
|
||||||
|
let ToolActionMessageContext {
|
||||||
document,
|
document,
|
||||||
global_tool_data,
|
global_tool_data,
|
||||||
input,
|
input,
|
||||||
|
|||||||
@@ -171,10 +171,10 @@ impl LayoutHolder for TextTool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for TextTool {
|
impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for TextTool {
|
||||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, context: &mut ToolActionMessageContext<'a>) {
|
||||||
let ToolMessage::Text(TextToolMessage::UpdateOptions(action)) = message else {
|
let ToolMessage::Text(TextToolMessage::UpdateOptions(action)) = message else {
|
||||||
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &self.options, responses, true);
|
self.fsm_state.process_event(message, &mut self.tool_data, context, &self.options, responses, true);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
match action {
|
match action {
|
||||||
@@ -329,7 +329,7 @@ impl TextToolData {
|
|||||||
fn load_layer_text_node(&mut self, document: &DocumentMessageHandler) -> Option<()> {
|
fn load_layer_text_node(&mut self, document: &DocumentMessageHandler) -> Option<()> {
|
||||||
let transform = document.metadata().transform_to_viewport(self.layer);
|
let transform = document.metadata().transform_to_viewport(self.layer);
|
||||||
let color = graph_modification_utils::get_fill_color(self.layer, &document.network_interface).unwrap_or(Color::BLACK);
|
let color = graph_modification_utils::get_fill_color(self.layer, &document.network_interface).unwrap_or(Color::BLACK);
|
||||||
let (text, font, typesetting) = graph_modification_utils::get_text(self.layer, &document.network_interface)?;
|
let (text, font, typesetting, _) = graph_modification_utils::get_text(self.layer, &document.network_interface)?;
|
||||||
self.editing_text = Some(EditingText {
|
self.editing_text = Some(EditingText {
|
||||||
text: text.clone(),
|
text: text.clone(),
|
||||||
font: font.clone(),
|
font: font.clone(),
|
||||||
@@ -449,8 +449,15 @@ impl Fsm for TextToolFsmState {
|
|||||||
type ToolData = TextToolData;
|
type ToolData = TextToolData;
|
||||||
type ToolOptions = TextOptions;
|
type ToolOptions = TextOptions;
|
||||||
|
|
||||||
fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, transition_data: &mut ToolActionHandlerData, tool_options: &Self::ToolOptions, responses: &mut VecDeque<Message>) -> Self {
|
fn transition(
|
||||||
let ToolActionHandlerData {
|
self,
|
||||||
|
event: ToolMessage,
|
||||||
|
tool_data: &mut Self::ToolData,
|
||||||
|
transition_data: &mut ToolActionMessageContext,
|
||||||
|
tool_options: &Self::ToolOptions,
|
||||||
|
responses: &mut VecDeque<Message>,
|
||||||
|
) -> Self {
|
||||||
|
let ToolActionMessageContext {
|
||||||
document,
|
document,
|
||||||
global_tool_data,
|
global_tool_data,
|
||||||
input,
|
input,
|
||||||
@@ -517,7 +524,7 @@ impl Fsm for TextToolFsmState {
|
|||||||
bounding_box_manager.render_quad(&mut overlay_context);
|
bounding_box_manager.render_quad(&mut overlay_context);
|
||||||
// Draw red overlay if text is clipped
|
// Draw red overlay if text is clipped
|
||||||
let transformed_quad = layer_transform * bounds;
|
let transformed_quad = layer_transform * bounds;
|
||||||
if let Some((text, font, typesetting)) = graph_modification_utils::get_text(layer.unwrap(), &document.network_interface) {
|
if let Some((text, font, typesetting, _)) = graph_modification_utils::get_text(layer.unwrap(), &document.network_interface) {
|
||||||
let font_data = font_cache.get(font).map(|data| load_font(data));
|
let font_data = font_cache.get(font).map(|data| load_font(data));
|
||||||
if lines_clipping(text.as_str(), font_data, typesetting) {
|
if lines_clipping(text.as_str(), font_data, typesetting) {
|
||||||
overlay_context.line(transformed_quad.0[2], transformed_quad.0[3], Some(COLOR_OVERLAY_RED), Some(3.));
|
overlay_context.line(transformed_quad.0[2], transformed_quad.0[3], Some(COLOR_OVERLAY_RED), Some(3.));
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Handles Blender inspired layer transformation with the <kbd>G</kbd> <kbd>R</kbd> and <kbd>S</kbd> keys for grabbing, rotating and scaling.
|
//! Handles Blender inspired layer transformation with the <kbd>G</kbd>, <kbd>R</kbd>, and <kbd>S</kbd> keys for grabbing, rotating, and scaling.
|
||||||
//!
|
//!
|
||||||
//! Other features include
|
//! Other features include
|
||||||
//! - Typing a number for a precise transformation
|
//! - Typing a number for a precise transformation
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
//! - Escape or right click to cancel
|
//! - Escape or right click to cancel
|
||||||
|
|
||||||
mod transform_layer_message;
|
mod transform_layer_message;
|
||||||
mod transform_layer_message_handler;
|
pub mod transform_layer_message_handler;
|
||||||
|
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use transform_layer_message::{TransformLayerMessage, TransformLayerMessageDiscriminant};
|
pub use transform_layer_message::{TransformLayerMessage, TransformLayerMessageDiscriminant};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::consts::{ANGLE_MEASURE_RADIUS_FACTOR, ARC_MEASURE_RADIUS_FACTOR_RANGE, COLOR_OVERLAY_BLUE, SLOWING_DIVISOR};
|
use crate::consts::{ANGLE_MEASURE_RADIUS_FACTOR, ARC_MEASURE_RADIUS_FACTOR_RANGE, COLOR_OVERLAY_BLUE, COLOR_OVERLAY_GRAY, SLOWING_DIVISOR};
|
||||||
use crate::messages::input_mapper::utility_types::input_mouse::{DocumentPosition, ViewportPosition};
|
use crate::messages::input_mapper::utility_types::input_mouse::{DocumentPosition, ViewportPosition};
|
||||||
use crate::messages::portfolio::document::overlays::utility_types::{OverlayProvider, Pivot};
|
use crate::messages::portfolio::document::overlays::utility_types::{OverlayProvider, Pivot};
|
||||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||||
@@ -12,6 +12,7 @@ use crate::messages::tool::utility_types::{ToolData, ToolType};
|
|||||||
use glam::{DAffine2, DVec2};
|
use glam::{DAffine2, DVec2};
|
||||||
use graphene_std::renderer::Quad;
|
use graphene_std::renderer::Quad;
|
||||||
use graphene_std::vector::ManipulatorPointId;
|
use graphene_std::vector::ManipulatorPointId;
|
||||||
|
use graphene_std::vector::click_target::ClickTargetType;
|
||||||
use graphene_std::vector::{VectorData, VectorModificationType};
|
use graphene_std::vector::{VectorData, VectorModificationType};
|
||||||
use std::f64::consts::{PI, TAU};
|
use std::f64::consts::{PI, TAU};
|
||||||
|
|
||||||
@@ -21,6 +22,14 @@ const TRANSFORM_GRS_OVERLAY_PROVIDER: OverlayProvider = |context| TransformLayer
|
|||||||
const SLOW_KEY: Key = Key::Shift;
|
const SLOW_KEY: Key = Key::Shift;
|
||||||
const INCREMENTS_KEY: Key = Key::Control;
|
const INCREMENTS_KEY: Key = Key::Control;
|
||||||
|
|
||||||
|
#[derive(ExtractField)]
|
||||||
|
pub struct TransformLayerMessageContext<'a> {
|
||||||
|
pub document: &'a DocumentMessageHandler,
|
||||||
|
pub input: &'a InputPreprocessorMessageHandler,
|
||||||
|
pub tool_data: &'a ToolData,
|
||||||
|
pub shape_editor: &'a mut ShapeState,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, ExtractField)]
|
#[derive(Debug, Clone, Default, ExtractField)]
|
||||||
pub struct TransformLayerMessageHandler {
|
pub struct TransformLayerMessageHandler {
|
||||||
pub transform_operation: TransformOperation,
|
pub transform_operation: TransformOperation,
|
||||||
@@ -53,150 +62,20 @@ pub struct TransformLayerMessageHandler {
|
|||||||
handle: DVec2,
|
handle: DVec2,
|
||||||
last_point: DVec2,
|
last_point: DVec2,
|
||||||
grs_pen_handle: bool,
|
grs_pen_handle: bool,
|
||||||
|
|
||||||
|
// Ghost outlines for Path Tool
|
||||||
|
ghost_outline: Vec<(Vec<ClickTargetType>, DAffine2)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransformLayerMessageHandler {
|
impl MessageHandler<TransformLayerMessage, TransformLayerMessageContext<'_>> for TransformLayerMessageHandler {
|
||||||
pub fn is_transforming(&self) -> bool {
|
fn process_message(&mut self, message: TransformLayerMessage, responses: &mut VecDeque<Message>, context: TransformLayerMessageContext) {
|
||||||
self.transform_operation != TransformOperation::None
|
let TransformLayerMessageContext {
|
||||||
}
|
document,
|
||||||
|
input,
|
||||||
|
tool_data,
|
||||||
|
shape_editor,
|
||||||
|
} = context;
|
||||||
|
|
||||||
pub fn hints(&self, responses: &mut VecDeque<Message>) {
|
|
||||||
self.transform_operation.hints(responses, self.local);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn calculate_pivot(
|
|
||||||
document: &DocumentMessageHandler,
|
|
||||||
selected_points: &Vec<&ManipulatorPointId>,
|
|
||||||
vector_data: &VectorData,
|
|
||||||
viewspace: DAffine2,
|
|
||||||
get_location: impl Fn(&ManipulatorPointId) -> Option<DVec2>,
|
|
||||||
gizmo: &mut PivotGizmo,
|
|
||||||
) -> (Option<(DVec2, DVec2)>, Option<[DVec2; 2]>) {
|
|
||||||
let average_position = || {
|
|
||||||
let mut point_count = 0_usize;
|
|
||||||
selected_points.iter().filter_map(|p| get_location(p)).inspect(|_| point_count += 1).sum::<DVec2>() / point_count as f64
|
|
||||||
};
|
|
||||||
let bounds = selected_points.iter().filter_map(|p| get_location(p)).fold(None, |acc: Option<[DVec2; 2]>, point| {
|
|
||||||
if let Some([mut min, mut max]) = acc {
|
|
||||||
min.x = min.x.min(point.x);
|
|
||||||
min.y = min.y.min(point.y);
|
|
||||||
max.x = max.x.max(point.x);
|
|
||||||
max.y = max.y.max(point.y);
|
|
||||||
Some([min, max])
|
|
||||||
} else {
|
|
||||||
Some([point, point])
|
|
||||||
}
|
|
||||||
});
|
|
||||||
gizmo.pivot.recalculate_pivot_for_layer(document, bounds);
|
|
||||||
let position = || {
|
|
||||||
(if !gizmo.state.disabled {
|
|
||||||
match gizmo.state.gizmo_type {
|
|
||||||
PivotGizmoType::Average => None,
|
|
||||||
PivotGizmoType::Active => gizmo.point.and_then(|p| get_location(&p)),
|
|
||||||
PivotGizmoType::Pivot => gizmo.pivot.pivot,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
})
|
|
||||||
.unwrap_or_else(average_position)
|
|
||||||
};
|
|
||||||
let [point] = selected_points.as_slice() else {
|
|
||||||
// Handle the case where there are multiple points
|
|
||||||
let position = position();
|
|
||||||
return (Some((position, position)), bounds);
|
|
||||||
};
|
|
||||||
|
|
||||||
match point {
|
|
||||||
ManipulatorPointId::PrimaryHandle(_) | ManipulatorPointId::EndHandle(_) => {
|
|
||||||
// Get the anchor position and transform it to the pivot
|
|
||||||
let (Some(pivot_position), Some(position)) = (
|
|
||||||
point.get_anchor_position(vector_data).map(|anchor_position| viewspace.transform_point2(anchor_position)),
|
|
||||||
point.get_position(vector_data),
|
|
||||||
) else {
|
|
||||||
return (None, None);
|
|
||||||
};
|
|
||||||
let target = viewspace.transform_point2(position);
|
|
||||||
(Some((pivot_position, target)), None)
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
// Calculate the average position of all selected points
|
|
||||||
let position = position();
|
|
||||||
(Some((position, position)), bounds)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn project_edge_to_quad(edge: DVec2, quad: &Quad, local: bool, axis_constraint: Axis) -> DVec2 {
|
|
||||||
match axis_constraint {
|
|
||||||
Axis::X => {
|
|
||||||
if local {
|
|
||||||
edge.project_onto(quad.top_right() - quad.top_left())
|
|
||||||
} else {
|
|
||||||
edge.with_y(0.)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Axis::Y => {
|
|
||||||
if local {
|
|
||||||
edge.project_onto(quad.bottom_left() - quad.top_left())
|
|
||||||
} else {
|
|
||||||
edge.with_x(0.)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => edge,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_colinear_handles(selected_layers: &[LayerNodeIdentifier], document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
|
|
||||||
for &layer in selected_layers {
|
|
||||||
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { continue };
|
|
||||||
|
|
||||||
for [handle1, handle2] in &vector_data.colinear_manipulators {
|
|
||||||
let manipulator1 = handle1.to_manipulator_point();
|
|
||||||
let manipulator2 = handle2.to_manipulator_point();
|
|
||||||
|
|
||||||
let Some(anchor) = manipulator1.get_anchor_position(&vector_data) else { continue };
|
|
||||||
let Some(pos1) = manipulator1.get_position(&vector_data).map(|pos| pos - anchor) else { continue };
|
|
||||||
let Some(pos2) = manipulator2.get_position(&vector_data).map(|pos| pos - anchor) else { continue };
|
|
||||||
|
|
||||||
let angle = pos1.angle_to(pos2);
|
|
||||||
|
|
||||||
// Check if handles are not colinear (not approximately equal to +/- PI)
|
|
||||||
if (angle - PI).abs() > 1e-6 && (angle + PI).abs() > 1e-6 {
|
|
||||||
let modification_type = VectorModificationType::SetG1Continuous {
|
|
||||||
handles: [*handle1, *handle2],
|
|
||||||
enabled: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
responses.add(GraphOperationMessage::Vector { layer, modification_type });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type TransformData<'a> = (&'a DocumentMessageHandler, &'a InputPreprocessorMessageHandler, &'a ToolData, &'a mut ShapeState);
|
|
||||||
|
|
||||||
pub fn custom_data() -> MessageData {
|
|
||||||
MessageData::new(
|
|
||||||
String::from("TransformData<'a>"),
|
|
||||||
// TODO: When <https://github.com/dtolnay/proc-macro2/issues/503> is resolved and released,
|
|
||||||
// TODO: use <https://doc.rust-lang.org/stable/proc_macro/struct.Span.html#method.line> to get
|
|
||||||
// TODO: the line number instead of hardcoding it to the magic number on the following lines
|
|
||||||
// TODO: which points to the line of the `type TransformData<'a> = ...` definition above.
|
|
||||||
// TODO: Also, utilize the line number in the actual output, since it is currently unused.
|
|
||||||
vec![
|
|
||||||
(String::from("&'a DocumentMessageHandler"), 177),
|
|
||||||
(String::from("&'a InputPreprocessorMessageHandler"), 177),
|
|
||||||
(String::from("&'a ToolData"), 177),
|
|
||||||
(String::from("&'a mut ShapeState"), 177),
|
|
||||||
],
|
|
||||||
file!(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[message_handler_data(CustomData)]
|
|
||||||
impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayerMessageHandler {
|
|
||||||
fn process_message(&mut self, message: TransformLayerMessage, responses: &mut VecDeque<Message>, (document, input, tool_data, shape_editor): TransformData) {
|
|
||||||
let using_path_tool = tool_data.active_tool_type == ToolType::Path;
|
let using_path_tool = tool_data.active_tool_type == ToolType::Path;
|
||||||
let using_select_tool = tool_data.active_tool_type == ToolType::Select;
|
let using_select_tool = tool_data.active_tool_type == ToolType::Select;
|
||||||
let using_pen_tool = tool_data.active_tool_type == ToolType::Pen;
|
let using_pen_tool = tool_data.active_tool_type == ToolType::Pen;
|
||||||
@@ -296,6 +175,12 @@ impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if using_path_tool {
|
||||||
|
for (outline, transform) in &self.ghost_outline {
|
||||||
|
overlay_context.outline(outline.iter(), *transform, Some(COLOR_OVERLAY_GRAY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let viewport_box = input.viewport_bounds.size();
|
let viewport_box = input.viewport_bounds.size();
|
||||||
let axis_constraint = self.transform_operation.axis_constraint();
|
let axis_constraint = self.transform_operation.axis_constraint();
|
||||||
|
|
||||||
@@ -409,6 +294,10 @@ impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayer
|
|||||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if using_path_tool {
|
||||||
|
self.ghost_outline.clear();
|
||||||
|
}
|
||||||
|
|
||||||
responses.add(SelectToolMessage::PivotShift { offset: None, flush: true });
|
responses.add(SelectToolMessage::PivotShift { offset: None, flush: true });
|
||||||
|
|
||||||
if final_transform {
|
if final_transform {
|
||||||
@@ -462,12 +351,15 @@ impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayer
|
|||||||
let selected_points: Vec<&ManipulatorPointId> = shape_editor.selected_points().collect();
|
let selected_points: Vec<&ManipulatorPointId> = shape_editor.selected_points().collect();
|
||||||
let selected_segments = shape_editor.selected_segments().collect::<Vec<_>>();
|
let selected_segments = shape_editor.selected_segments().collect::<Vec<_>>();
|
||||||
|
|
||||||
if (using_path_tool && selected_points.is_empty() && selected_segments.is_empty())
|
if using_path_tool {
|
||||||
|| (!using_path_tool && !using_select_tool && !using_pen_tool && !using_shape_tool)
|
Self::set_ghost_outline(&mut self.ghost_outline, shape_editor, document);
|
||||||
|| selected_layers.is_empty()
|
if (selected_points.is_empty() && selected_segments.is_empty())
|
||||||
|| transform_type.equivalent_to(self.transform_operation)
|
|| (!using_path_tool && !using_select_tool && !using_pen_tool && !using_shape_tool)
|
||||||
{
|
|| selected_layers.is_empty()
|
||||||
return;
|
|| transform_type.equivalent_to(self.transform_operation)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(vector_data) = selected_layers.first().and_then(|&layer| document.network_interface.compute_modified_vector(layer)) {
|
if let Some(vector_data) = selected_layers.first().and_then(|&layer| document.network_interface.compute_modified_vector(layer)) {
|
||||||
@@ -515,6 +407,10 @@ impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayer
|
|||||||
TransformLayerMessage::BeginRotate => responses.add_front(TransformLayerMessage::BeginGRS { operation: TransformType::Rotate }),
|
TransformLayerMessage::BeginRotate => responses.add_front(TransformLayerMessage::BeginGRS { operation: TransformType::Rotate }),
|
||||||
TransformLayerMessage::BeginScale => responses.add_front(TransformLayerMessage::BeginGRS { operation: TransformType::Scale }),
|
TransformLayerMessage::BeginScale => responses.add_front(TransformLayerMessage::BeginGRS { operation: TransformType::Scale }),
|
||||||
TransformLayerMessage::CancelTransformOperation => {
|
TransformLayerMessage::CancelTransformOperation => {
|
||||||
|
if using_path_tool {
|
||||||
|
self.ghost_outline.clear();
|
||||||
|
}
|
||||||
|
|
||||||
if using_pen_tool {
|
if using_pen_tool {
|
||||||
self.typing.clear();
|
self.typing.clear();
|
||||||
|
|
||||||
@@ -774,6 +670,135 @@ impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TransformLayerMessageHandler {
|
||||||
|
pub fn is_transforming(&self) -> bool {
|
||||||
|
self.transform_operation != TransformOperation::None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hints(&self, responses: &mut VecDeque<Message>) {
|
||||||
|
self.transform_operation.hints(responses, self.local);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_ghost_outline(ghost_outline: &mut Vec<(Vec<ClickTargetType>, DAffine2)>, shape_editor: &ShapeState, document: &DocumentMessageHandler) {
|
||||||
|
ghost_outline.clear();
|
||||||
|
for &layer in shape_editor.selected_shape_state.keys() {
|
||||||
|
// We probably need to collect here
|
||||||
|
let outline = document.metadata().layer_with_free_points_outline(layer).cloned().collect();
|
||||||
|
let transform = document.metadata().transform_to_viewport(layer);
|
||||||
|
ghost_outline.push((outline, transform));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculate_pivot(
|
||||||
|
document: &DocumentMessageHandler,
|
||||||
|
selected_points: &Vec<&ManipulatorPointId>,
|
||||||
|
vector_data: &VectorData,
|
||||||
|
viewspace: DAffine2,
|
||||||
|
get_location: impl Fn(&ManipulatorPointId) -> Option<DVec2>,
|
||||||
|
gizmo: &mut PivotGizmo,
|
||||||
|
) -> (Option<(DVec2, DVec2)>, Option<[DVec2; 2]>) {
|
||||||
|
let average_position = || {
|
||||||
|
let mut point_count = 0_usize;
|
||||||
|
selected_points.iter().filter_map(|p| get_location(p)).inspect(|_| point_count += 1).sum::<DVec2>() / point_count as f64
|
||||||
|
};
|
||||||
|
let bounds = selected_points.iter().filter_map(|p| get_location(p)).fold(None, |acc: Option<[DVec2; 2]>, point| {
|
||||||
|
if let Some([mut min, mut max]) = acc {
|
||||||
|
min.x = min.x.min(point.x);
|
||||||
|
min.y = min.y.min(point.y);
|
||||||
|
max.x = max.x.max(point.x);
|
||||||
|
max.y = max.y.max(point.y);
|
||||||
|
Some([min, max])
|
||||||
|
} else {
|
||||||
|
Some([point, point])
|
||||||
|
}
|
||||||
|
});
|
||||||
|
gizmo.pivot.recalculate_pivot_for_layer(document, bounds);
|
||||||
|
let position = || {
|
||||||
|
(if !gizmo.state.disabled {
|
||||||
|
match gizmo.state.gizmo_type {
|
||||||
|
PivotGizmoType::Average => None,
|
||||||
|
PivotGizmoType::Active => gizmo.point.and_then(|p| get_location(&p)),
|
||||||
|
PivotGizmoType::Pivot => gizmo.pivot.pivot,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.unwrap_or_else(average_position)
|
||||||
|
};
|
||||||
|
let [point] = selected_points.as_slice() else {
|
||||||
|
// Handle the case where there are multiple points
|
||||||
|
let position = position();
|
||||||
|
return (Some((position, position)), bounds);
|
||||||
|
};
|
||||||
|
|
||||||
|
match point {
|
||||||
|
ManipulatorPointId::PrimaryHandle(_) | ManipulatorPointId::EndHandle(_) => {
|
||||||
|
// Get the anchor position and transform it to the pivot
|
||||||
|
let (Some(pivot_position), Some(position)) = (
|
||||||
|
point.get_anchor_position(vector_data).map(|anchor_position| viewspace.transform_point2(anchor_position)),
|
||||||
|
point.get_position(vector_data),
|
||||||
|
) else {
|
||||||
|
return (None, None);
|
||||||
|
};
|
||||||
|
let target = viewspace.transform_point2(position);
|
||||||
|
(Some((pivot_position, target)), None)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Calculate the average position of all selected points
|
||||||
|
let position = position();
|
||||||
|
(Some((position, position)), bounds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn project_edge_to_quad(edge: DVec2, quad: &Quad, local: bool, axis_constraint: Axis) -> DVec2 {
|
||||||
|
match axis_constraint {
|
||||||
|
Axis::X => {
|
||||||
|
if local {
|
||||||
|
edge.project_onto(quad.top_right() - quad.top_left())
|
||||||
|
} else {
|
||||||
|
edge.with_y(0.)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Axis::Y => {
|
||||||
|
if local {
|
||||||
|
edge.project_onto(quad.bottom_left() - quad.top_left())
|
||||||
|
} else {
|
||||||
|
edge.with_x(0.)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => edge,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_colinear_handles(selected_layers: &[LayerNodeIdentifier], document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
|
||||||
|
for &layer in selected_layers {
|
||||||
|
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { continue };
|
||||||
|
|
||||||
|
for [handle1, handle2] in &vector_data.colinear_manipulators {
|
||||||
|
let manipulator1 = handle1.to_manipulator_point();
|
||||||
|
let manipulator2 = handle2.to_manipulator_point();
|
||||||
|
|
||||||
|
let Some(anchor) = manipulator1.get_anchor_position(&vector_data) else { continue };
|
||||||
|
let Some(pos1) = manipulator1.get_position(&vector_data).map(|pos| pos - anchor) else { continue };
|
||||||
|
let Some(pos2) = manipulator2.get_position(&vector_data).map(|pos| pos - anchor) else { continue };
|
||||||
|
|
||||||
|
let angle = pos1.angle_to(pos2);
|
||||||
|
|
||||||
|
// Check if handles are not colinear (not approximately equal to +/- PI)
|
||||||
|
if (angle - PI).abs() > 1e-6 && (angle + PI).abs() > 1e-6 {
|
||||||
|
let modification_type = VectorModificationType::SetG1Continuous {
|
||||||
|
handles: [*handle1, *handle2],
|
||||||
|
enabled: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
responses.add(GraphOperationMessage::Vector { layer, modification_type });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_transform_layer {
|
mod test_transform_layer {
|
||||||
use crate::messages::portfolio::document::graph_operation::transform_utils;
|
use crate::messages::portfolio::document::graph_operation::transform_utils;
|
||||||
@@ -1298,7 +1323,7 @@ mod test_transform_layer {
|
|||||||
let document = editor.active_document_mut();
|
let document = editor.active_document_mut();
|
||||||
let group_children = document.network_interface.downstream_layers(&group_layer.to_node(), &[]);
|
let group_children = document.network_interface.downstream_layers(&group_layer.to_node(), &[]);
|
||||||
if !group_children.is_empty() {
|
if !group_children.is_empty() {
|
||||||
Some(LayerNodeIdentifier::new(group_children[0], &document.network_interface, &[]))
|
Some(LayerNodeIdentifier::new(group_children[0], &document.network_interface))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ use std::borrow::Cow;
|
|||||||
use std::fmt::{self, Debug};
|
use std::fmt::{self, Debug};
|
||||||
|
|
||||||
#[derive(ExtractField)]
|
#[derive(ExtractField)]
|
||||||
pub struct ToolActionHandlerData<'a> {
|
pub struct ToolActionMessageContext<'a> {
|
||||||
pub document: &'a mut DocumentMessageHandler,
|
pub document: &'a mut DocumentMessageHandler,
|
||||||
pub document_id: DocumentId,
|
pub document_id: DocumentId,
|
||||||
pub global_tool_data: &'a DocumentToolData,
|
pub global_tool_data: &'a DocumentToolData,
|
||||||
@@ -30,8 +30,8 @@ pub struct ToolActionHandlerData<'a> {
|
|||||||
pub preferences: &'a PreferencesMessageHandler,
|
pub preferences: &'a PreferencesMessageHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ToolCommon: for<'a, 'b> MessageHandler<ToolMessage, &'b mut ToolActionHandlerData<'a>> + LayoutHolder + ToolTransition + ToolMetadata {}
|
pub trait ToolCommon: for<'a, 'b> MessageHandler<ToolMessage, &'b mut ToolActionMessageContext<'a>> + LayoutHolder + ToolTransition + ToolMetadata {}
|
||||||
impl<T> ToolCommon for T where T: for<'a, 'b> MessageHandler<ToolMessage, &'b mut ToolActionHandlerData<'a>> + LayoutHolder + ToolTransition + ToolMetadata {}
|
impl<T> ToolCommon for T where T: for<'a, 'b> MessageHandler<ToolMessage, &'b mut ToolActionMessageContext<'a>> + LayoutHolder + ToolTransition + ToolMetadata {}
|
||||||
|
|
||||||
type Tool = dyn ToolCommon + Send + Sync;
|
type Tool = dyn ToolCommon + Send + Sync;
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ pub trait Fsm {
|
|||||||
/// For example, if the tool's FSM is in a `Ready` state and receives a `DragStart` message as its event, it may decide to send some messages,
|
/// For example, if the tool's FSM is in a `Ready` state and receives a `DragStart` message as its event, it may decide to send some messages,
|
||||||
/// update some internal tool variables, and end by transitioning to a `Drawing` state.
|
/// update some internal tool variables, and end by transitioning to a `Drawing` state.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn transition(self, message: ToolMessage, tool_data: &mut Self::ToolData, transition_data: &mut ToolActionHandlerData, options: &Self::ToolOptions, responses: &mut VecDeque<Message>) -> Self;
|
fn transition(self, message: ToolMessage, tool_data: &mut Self::ToolData, transition_data: &mut ToolActionMessageContext, options: &Self::ToolOptions, responses: &mut VecDeque<Message>) -> Self;
|
||||||
|
|
||||||
/// Implementing this trait function lets a specific tool provide a list of hints (user input actions presently available) to draw in the footer bar.
|
/// Implementing this trait function lets a specific tool provide a list of hints (user input actions presently available) to draw in the footer bar.
|
||||||
fn update_hints(&self, responses: &mut VecDeque<Message>);
|
fn update_hints(&self, responses: &mut VecDeque<Message>);
|
||||||
@@ -82,7 +82,7 @@ pub trait Fsm {
|
|||||||
&mut self,
|
&mut self,
|
||||||
message: ToolMessage,
|
message: ToolMessage,
|
||||||
tool_data: &mut Self::ToolData,
|
tool_data: &mut Self::ToolData,
|
||||||
transition_data: &mut ToolActionHandlerData,
|
transition_data: &mut ToolActionMessageContext,
|
||||||
options: &Self::ToolOptions,
|
options: &Self::ToolOptions,
|
||||||
responses: &mut VecDeque<Message>,
|
responses: &mut VecDeque<Message>,
|
||||||
update_cursor_on_transition: bool,
|
update_cursor_on_transition: bool,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ pub struct WorkspaceMessageHandler {
|
|||||||
|
|
||||||
#[message_handler_data]
|
#[message_handler_data]
|
||||||
impl MessageHandler<WorkspaceMessage, ()> for WorkspaceMessageHandler {
|
impl MessageHandler<WorkspaceMessage, ()> for WorkspaceMessageHandler {
|
||||||
fn process_message(&mut self, message: WorkspaceMessage, _responses: &mut VecDeque<Message>, _data: ()) {
|
fn process_message(&mut self, message: WorkspaceMessage, _responses: &mut VecDeque<Message>, _: ()) {
|
||||||
match message {
|
match message {
|
||||||
// Messages
|
// Messages
|
||||||
WorkspaceMessage::NodeGraphToggleVisibility => {
|
WorkspaceMessage::NodeGraphToggleVisibility => {
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ pub enum NodeGraphUpdate {
|
|||||||
NodeGraphUpdateMessage(NodeGraphUpdateMessage),
|
NodeGraphUpdateMessage(NodeGraphUpdateMessage),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Default)]
|
||||||
pub struct NodeGraphExecutor {
|
pub struct NodeGraphExecutor {
|
||||||
runtime_io: NodeRuntimeIO,
|
runtime_io: NodeRuntimeIO,
|
||||||
futures: HashMap<u64, ExecutionContext>,
|
futures: HashMap<u64, ExecutionContext>,
|
||||||
@@ -66,17 +66,6 @@ struct ExecutionContext {
|
|||||||
export_config: Option<ExportConfig>,
|
export_config: Option<ExportConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for NodeGraphExecutor {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
futures: Default::default(),
|
|
||||||
runtime_io: NodeRuntimeIO::new(),
|
|
||||||
node_graph_hash: 0,
|
|
||||||
old_inspect_node: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NodeGraphExecutor {
|
impl NodeGraphExecutor {
|
||||||
/// A local runtime is useful on threads since having global state causes flakes
|
/// A local runtime is useful on threads since having global state causes flakes
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -394,7 +383,9 @@ impl NodeGraphExecutor {
|
|||||||
return Err(format!("Invalid node graph output type: {node_graph_output:#?}"));
|
return Err(format!("Invalid node graph output type: {node_graph_output:#?}"));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
responses.add(Message::EndBuffer(render_output_metadata));
|
responses.add(Message::EndBuffer {
|
||||||
|
render_metadata: render_output_metadata,
|
||||||
|
});
|
||||||
responses.add(DocumentMessage::RenderScrollbars);
|
responses.add(DocumentMessage::RenderScrollbars);
|
||||||
responses.add(DocumentMessage::RenderRulers);
|
responses.add(DocumentMessage::RenderRulers);
|
||||||
responses.add(OverlaysMessage::Draw);
|
responses.add(OverlaysMessage::Draw);
|
||||||
|
|||||||
@@ -328,6 +328,18 @@ impl NodeRuntime {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skip thumbnails if the layer is too complex (for performance)
|
||||||
|
if graphic_element.render_complexity() > 1000 {
|
||||||
|
let old = thumbnail_renders.insert(parent_network_node_id, Vec::new());
|
||||||
|
if old.is_none_or(|v| !v.is_empty()) {
|
||||||
|
responses.push_back(FrontendMessage::UpdateNodeThumbnail {
|
||||||
|
id: parent_network_node_id,
|
||||||
|
value: "<svg viewBox=\"0 0 10 10\"><title>Dense thumbnail omitted for performance</title><line x1=\"0\" y1=\"10\" x2=\"10\" y2=\"0\" stroke=\"red\" /></svg>".to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let bounds = graphic_element.bounding_box(DAffine2::IDENTITY, true);
|
let bounds = graphic_element.bounding_box(DAffine2::IDENTITY, true);
|
||||||
|
|
||||||
// Render the thumbnail from a `GraphicElement` into an SVG string
|
// Render the thumbnail from a `GraphicElement` into an SVG string
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ impl EditorTestUtils {
|
|||||||
// It isn't sufficient to guard the message dispatch here with a check if the once_cell is empty, because that isn't atomic and the time between checking and handling the dispatch can let multiple through.
|
// It isn't sufficient to guard the message dispatch here with a check if the once_cell is empty, because that isn't atomic and the time between checking and handling the dispatch can let multiple through.
|
||||||
let _ = GLOBAL_PLATFORM.set(Platform::Windows).is_ok();
|
let _ = GLOBAL_PLATFORM.set(Platform::Windows).is_ok();
|
||||||
|
|
||||||
editor.handle_message(Message::Init);
|
editor.handle_message(PortfolioMessage::Init);
|
||||||
|
|
||||||
Self { editor, runtime }
|
Self { editor, runtime }
|
||||||
}
|
}
|
||||||
@@ -326,8 +326,7 @@ pub mod test_prelude {
|
|||||||
pub use crate::node_graph_executor::NodeRuntime;
|
pub use crate::node_graph_executor::NodeRuntime;
|
||||||
pub use crate::test_utils::EditorTestUtils;
|
pub use crate::test_utils::EditorTestUtils;
|
||||||
pub use core::f64;
|
pub use core::f64;
|
||||||
pub use glam::DVec2;
|
pub use glam::{DVec2, IVec2};
|
||||||
pub use glam::IVec2;
|
|
||||||
pub use graph_craft::document::DocumentNode;
|
pub use graph_craft::document::DocumentNode;
|
||||||
pub use graphene_std::raster::{Color, Image};
|
pub use graphene_std::raster::{Color, Image};
|
||||||
pub use graphene_std::transform::Footprint;
|
pub use graphene_std::transform::Footprint;
|
||||||
|
|||||||
@@ -2,15 +2,14 @@ pub use crate::dispatcher::*;
|
|||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
/// Implements a message handler struct for a separate message struct.
|
/// Implements a message handler struct for a separate message struct.
|
||||||
/// - The first generic argument (`M`) is that message struct type, representing a message enum variant to be matched and handled in `process_message()`.
|
/// - The first type argument (`M`) is that message struct type, representing a message enum variant to be matched and handled in `process_message()`.
|
||||||
/// - The second generic argument (`D`) is the type of data that can be passed along by the caller to `process_message()`.
|
/// - The second type argument (`C`) is the type of the context struct that can be passed along by the caller to `process_message()`.
|
||||||
pub trait MessageHandler<M: ToDiscriminant, D>
|
pub trait MessageHandler<M: ToDiscriminant, C>
|
||||||
where
|
where
|
||||||
M::Discriminant: AsMessage,
|
M::Discriminant: AsMessage,
|
||||||
<M::Discriminant as TransitiveChild>::TopParent: TransitiveChild<Parent = <M::Discriminant as TransitiveChild>::TopParent, TopParent = <M::Discriminant as TransitiveChild>::TopParent> + AsMessage,
|
<M::Discriminant as TransitiveChild>::TopParent: TransitiveChild<Parent = <M::Discriminant as TransitiveChild>::TopParent, TopParent = <M::Discriminant as TransitiveChild>::TopParent> + AsMessage,
|
||||||
{
|
{
|
||||||
/// Return true if the Action is consumed.
|
fn process_message(&mut self, message: M, responses: &mut VecDeque<Message>, context: C);
|
||||||
fn process_message(&mut self, message: M, responses: &mut VecDeque<Message>, data: D);
|
|
||||||
|
|
||||||
fn actions(&self) -> ActionList;
|
fn actions(&self) -> ActionList;
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user