This commit is contained in:
mtvare6
2025-07-23 14:29:59 +05:30
159 changed files with 4838 additions and 2997 deletions
+64 -3
View File
@@ -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
+37 -9
View File
@@ -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
View File
@@ -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
View File
@@ -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"] }
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -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"
+4 -1
View File
@@ -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
View File
@@ -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};
+1 -1
View File
@@ -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};
@@ -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};
+1 -1
View File
@@ -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 -1
View File
@@ -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")]
+12 -6
View File
@@ -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 {
@@ -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};
@@ -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(&current_node_id, &[]) { if self.is_layer(&current_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(&current_layer_node) { if !self.document_metadata.structure.contains_key(&current_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(&current_node_id, &[]) { if self.is_layer(&current_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(&current_layer_node) { if !self.document_metadata.structure.contains_key(&current_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(&current_node_id, &[]) { if self.is_layer(&current_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(&current_layer_node) { if !self.document_metadata.structure.contains_key(&current_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(
+1 -1
View File
@@ -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 } => {
+12 -12
View File
@@ -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> {
+1 -1
View File
@@ -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};
+1
View File
@@ -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
} }
+5 -5
View File
@@ -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 => {
+4 -13
View File
@@ -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);
+12
View File
@@ -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
+2 -3
View File
@@ -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;
+4 -5
View File
@@ -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