collective

Browse / github.com/storytold/effectcraft / patch

Remember the native window and add GNOME Wayland caption buttons

Remember the native window and add GNOME Wayland caption buttons Windows should reopen at their last normal size, or maximized, at any interface size (150% included), and Restore after a maximized restart should return to the last normal size. On GNOME Wayland, where the compositor draws no frame, the window also needs caption buttons. Changes: - Pin the Craft egui family to storytold/egui#3 (`69750b7`, on the 0.37 migration branch). That change stores the interface zoom with the saved geometry, keeps the last normal rectangle while the window is maximized, repeats a requested maximize once X11 has mapped the window, and enables winit's font-free Adwaita c

Status
open
Score
0.00
Adopters
0 other operators
Author
abdallah-shehawey (imported from pull request #2461, open)
Base
bc4b48f67deef2458e9dd3e3b0e592d5f75768a1
Size
+314 −52 in 10 files
Review
pending
Shared
2026-10-28
Id
03a506249e1d37c5a99a079c

How it went

No other operator has reported using it yet.

Diff

raw · ATTRIBUTION.md Cargo.lock Cargo.toml NOTICE ROADMAP.md apps/effectcraft/Cargo.toml apps/effectcraft/src/main.rs apps/effectcraft/src/window.rs contributors/people.toml docs/ui-parity.md

diff --git a/ATTRIBUTION.md b/ATTRIBUTION.md--- a/ATTRIBUTION.md+++ b/ATTRIBUTION.md@@ -11,18 +11,18 @@ by the pinned egui dependency, are reproduced in [`NOTICE`](NOTICE), which every ## Bundled dependency fonts  These fonts are embedded by the pinned egui dependency (epaint_default_fonts 0.37 at-`6d518fc799681571963842f95b55a6b68431b15a`); no font file is added to this repository.+`69750b71a97dc4672e2fbb70863b838e6795c910`); no font file is added to this repository. egui-icons is always embedded, `default_fonts` adds Ubuntu Light and Hack, and `monochrome_emoji_fonts` adds Noto Emoji and emoji-icon-font. The complete copyright notices and licence texts are in [`NOTICE`](NOTICE).  | Asset | Author | Source | Licence | |---|---|---|---|-| `epaint_default_fonts/fonts/egui-icons.ttf` | John Slegers (emoji-icon-font) | https://github.com/storytold/egui/blob/6d518fc799681571963842f95b55a6b68431b15a/crates/epaint_default_fonts/fonts/egui-icons.txt; original https://github.com/jslegers/emoji-icon-font | MIT (complete copyright and permission text in `NOTICE`) |-| `epaint_default_fonts/fonts/emoji-icon-font.ttf` | John Slegers | https://github.com/storytold/egui/blob/6d518fc799681571963842f95b55a6b68431b15a/crates/epaint_default_fonts/fonts/emoji-icon-font-mit-license.txt; original https://github.com/jslegers/emoji-icon-font | MIT (complete copyright and permission text in `NOTICE`) |-| `epaint_default_fonts/fonts/NotoEmoji-Regular.ttf` | Google Inc. (The Noto Project Authors) | https://github.com/storytold/egui/blob/6d518fc799681571963842f95b55a6b68431b15a/crates/epaint_default_fonts/fonts/OFL.txt | OFL-1.1 (complete text in `NOTICE`) |-| `epaint_default_fonts/fonts/Ubuntu-Light.ttf` | Canonical Ltd. and Dalton Maag | https://github.com/storytold/egui/blob/6d518fc799681571963842f95b55a6b68431b15a/crates/epaint_default_fonts/fonts/UFL.txt | Ubuntu Font Licence 1.0 (complete text in `NOTICE`) |-| `epaint_default_fonts/fonts/Hack-Regular.ttf` | Source Foundry Authors; Bitstream Inc. | https://github.com/storytold/egui/blob/6d518fc799681571963842f95b55a6b68431b15a/crates/epaint_default_fonts/fonts/Hack-Regular.txt; original https://github.com/source-foundry/Hack | MIT AND Bitstream-Vera (complete texts in `NOTICE`) |+| `epaint_default_fonts/fonts/egui-icons.ttf` | John Slegers (emoji-icon-font) | https://github.com/storytold/egui/blob/69750b71a97dc4672e2fbb70863b838e6795c910/crates/epaint_default_fonts/fonts/egui-icons.txt; original https://github.com/jslegers/emoji-icon-font | MIT (complete copyright and permission text in `NOTICE`) |+| `epaint_default_fonts/fonts/emoji-icon-font.ttf` | John Slegers | https://github.com/storytold/egui/blob/69750b71a97dc4672e2fbb70863b838e6795c910/crates/epaint_default_fonts/fonts/emoji-icon-font-mit-license.txt; original https://github.com/jslegers/emoji-icon-font | MIT (complete copyright and permission text in `NOTICE`) |+| `epaint_default_fonts/fonts/NotoEmoji-Regular.ttf` | Google Inc. (The Noto Project Authors) | https://github.com/storytold/egui/blob/69750b71a97dc4672e2fbb70863b838e6795c910/crates/epaint_default_fonts/fonts/OFL.txt | OFL-1.1 (complete text in `NOTICE`) |+| `epaint_default_fonts/fonts/Ubuntu-Light.ttf` | Canonical Ltd. and Dalton Maag | https://github.com/storytold/egui/blob/69750b71a97dc4672e2fbb70863b838e6795c910/crates/epaint_default_fonts/fonts/UFL.txt | Ubuntu Font Licence 1.0 (complete text in `NOTICE`) |+| `epaint_default_fonts/fonts/Hack-Regular.ttf` | Source Foundry Authors; Bitstream Inc. | https://github.com/storytold/egui/blob/69750b71a97dc4672e2fbb70863b838e6795c910/crates/epaint_default_fonts/fonts/Hack-Regular.txt; original https://github.com/source-foundry/Hack | MIT AND Bitstream-Vera (complete texts in `NOTICE`) |  ## Asset files diff --git a/Cargo.lock b/Cargo.lock--- a/Cargo.lock+++ b/Cargo.lock@@ -8,6 +8,8 @@ version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad442f58ee04714aaa0ba0a2768c1ea1935b29507bb22ddece8cbbc76db02932" dependencies = [+ "enumn",+ "serde",  "uuid", ] @@ -125,6 +127,7 @@ dependencies = [  "cfg-if",  "getrandom 0.3.4",  "once_cell",+ "serde",  "version_check",  "zerocopy", ]@@ -488,6 +491,9 @@ name = "bitflags" version = "2.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06"+dependencies = [+ "serde_core",+]  [[package]] name = "block-buffer"@@ -1321,10 +1327,11 @@ dependencies = [ [[package]] name = "ecolor" version = "0.37.0"-source = "git+https://github.com/storytold/egui.git?rev=6d518fc799681571963842f95b55a6b68431b15a#6d518fc799681571963842f95b55a6b68431b15a"+source = "git+https://github.com/storytold/egui.git?rev=69750b71a97dc4672e2fbb70863b838e6795c910#69750b71a97dc4672e2fbb70863b838e6795c910" dependencies = [  "bytemuck",  "emath",+ "serde", ]  [[package]]@@ -1338,6 +1345,7 @@ dependencies = [  "effectcraft-ui-egui",  "eframe",  "egui",+ "egui-winit",  "fmv-macos-events",  "log",  "muda",@@ -1664,7 +1672,7 @@ dependencies = [  "effectcraft-raster",  "kurbo",  "linesweeper",- "tiny-skia",+ "tiny-skia 0.12.0", ]  [[package]]@@ -1881,7 +1889,7 @@ dependencies = [ [[package]] name = "eframe" version = "0.37.0"-source = "git+https://github.com/storytold/egui.git?rev=6d518fc799681571963842f95b55a6b68431b15a#6d518fc799681571963842f95b55a6b68431b15a"+source = "git+https://github.com/storytold/egui.git?rev=69750b71a97dc4672e2fbb70863b838e6795c910#69750b71a97dc4672e2fbb70863b838e6795c910" dependencies = [  "ahash",  "bytemuck",@@ -1903,7 +1911,9 @@ dependencies = [  "pollster 1.0.1",  "profiling",  "raw-window-handle",+ "ron",  "send_wrapper",+ "serde",  "static_assertions",  "wasm-bindgen",  "web-sys",@@ -1916,7 +1926,7 @@ dependencies = [ [[package]] name = "egui" version = "0.37.0"-source = "git+https://github.com/storytold/egui.git?rev=6d518fc799681571963842f95b55a6b68431b15a#6d518fc799681571963842f95b55a6b68431b15a"+source = "git+https://github.com/storytold/egui.git?rev=69750b71a97dc4672e2fbb70863b838e6795c910#69750b71a97dc4672e2fbb70863b838e6795c910" dependencies = [  "accesskit",  "ahash",@@ -1927,6 +1937,8 @@ dependencies = [  "log",  "nohash-hasher",  "profiling",+ "ron",+ "serde",  "smallvec",  "unicode-segmentation",  "web-sys",@@ -1935,7 +1947,7 @@ dependencies = [ [[package]] name = "egui-wgpu" version = "0.37.0"-source = "git+https://github.com/storytold/egui.git?rev=6d518fc799681571963842f95b55a6b68431b15a#6d518fc799681571963842f95b55a6b68431b15a"+source = "git+https://github.com/storytold/egui.git?rev=69750b71a97dc4672e2fbb70863b838e6795c910#69750b71a97dc4672e2fbb70863b838e6795c910" dependencies = [  "ahash",  "bytemuck",@@ -1954,7 +1966,7 @@ dependencies = [ [[package]] name = "egui-winit" version = "0.37.0"-source = "git+https://github.com/storytold/egui.git?rev=6d518fc799681571963842f95b55a6b68431b15a#6d518fc799681571963842f95b55a6b68431b15a"+source = "git+https://github.com/storytold/egui.git?rev=69750b71a97dc4672e2fbb70863b838e6795c910#69750b71a97dc4672e2fbb70863b838e6795c910" dependencies = [  "accesskit_winit",  "arboard",@@ -1970,6 +1982,7 @@ dependencies = [  "parking_lot",  "profiling",  "raw-window-handle",+ "serde",  "smithay-clipboard",  "web-time",  "webbrowser",@@ -1980,7 +1993,7 @@ dependencies = [ [[package]] name = "egui_glow" version = "0.37.0"-source = "git+https://github.com/storytold/egui.git?rev=6d518fc799681571963842f95b55a6b68431b15a#6d518fc799681571963842f95b55a6b68431b15a"+source = "git+https://github.com/storytold/egui.git?rev=69750b71a97dc4672e2fbb70863b838e6795c910#69750b71a97dc4672e2fbb70863b838e6795c910" dependencies = [  "bytemuck",  "egui",@@ -1993,7 +2006,7 @@ dependencies = [ [[package]] name = "egui_kittest" version = "0.37.0"-source = "git+https://github.com/storytold/egui.git?rev=6d518fc799681571963842f95b55a6b68431b15a#6d518fc799681571963842f95b55a6b68431b15a"+source = "git+https://github.com/storytold/egui.git?rev=69750b71a97dc4672e2fbb70863b838e6795c910#69750b71a97dc4672e2fbb70863b838e6795c910" dependencies = [  "eframe",  "egui",@@ -2016,9 +2029,10 @@ checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" [[package]] name = "emath" version = "0.37.0"-source = "git+https://github.com/storytold/egui.git?rev=6d518fc799681571963842f95b55a6b68431b15a#6d518fc799681571963842f95b55a6b68431b15a"+source = "git+https://github.com/storytold/egui.git?rev=69750b71a97dc4672e2fbb70863b838e6795c910#69750b71a97dc4672e2fbb70863b838e6795c910" dependencies = [  "bytemuck",+ "serde", ]  [[package]]@@ -2062,10 +2076,21 @@ dependencies = [  "syn 2.0.119", ] +[[package]]+name = "enumn"+version = "0.1.14"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38"+dependencies = [+ "proc-macro2",+ "quote",+ "syn 2.0.119",+]+ [[package]] name = "epaint" version = "0.37.0"-source = "git+https://github.com/storytold/egui.git?rev=6d518fc799681571963842f95b55a6b68431b15a#6d518fc799681571963842f95b55a6b68431b15a"+source = "git+https://github.com/storytold/egui.git?rev=69750b71a97dc4672e2fbb70863b838e6795c910#69750b71a97dc4672e2fbb70863b838e6795c910" dependencies = [  "ahash",  "bytemuck",@@ -2079,6 +2104,7 @@ dependencies = [  "parking_lot",  "profiling",  "self_cell",+ "serde",  "skrifa",  "smallvec",  "unicode-bidi",@@ -2091,7 +2117,7 @@ dependencies = [ [[package]] name = "epaint_default_fonts" version = "0.37.0"-source = "git+https://github.com/storytold/egui.git?rev=6d518fc799681571963842f95b55a6b68431b15a#6d518fc799681571963842f95b55a6b68431b15a"+source = "git+https://github.com/storytold/egui.git?rev=69750b71a97dc4672e2fbb70863b838e6795c910#69750b71a97dc4672e2fbb70863b838e6795c910"  [[package]] name = "equator"@@ -2555,6 +2581,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8eb065f3251655b3c90e22e5e363f310fc5332fb3402e37bbc94752283248f6" dependencies = [  "bytemuck",+ "serde", ]  [[package]]@@ -4819,6 +4846,20 @@ dependencies = [  "windows-sys 0.52.0", ] +[[package]]+name = "ron"+version = "0.12.2"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "81116b9531d61eabc41aeb228e4b6b2435bcca3233b98cf3b3077d4e6e9debb3"+dependencies = [+ "bitflags 2.13.2",+ "once_cell",+ "serde",+ "serde_derive",+ "typeid",+ "unicode-ident",+]+ [[package]] name = "roxmltree" version = "0.21.1"@@ -5003,6 +5044,17 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]]+name = "sctk-adwaita"+version = "0.10.1"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec"+dependencies = [+ "log",+ "smithay-client-toolkit 0.19.2",+ "tiny-skia 0.11.4",+]+ [[package]] name = "security-framework" version = "3.7.0"@@ -5712,6 +5764,20 @@ dependencies = [  "time-core", ] +[[package]]+name = "tiny-skia"+version = "0.11.4"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab"+dependencies = [+ "arrayref",+ "arrayvec",+ "bytemuck",+ "cfg-if",+ "log",+ "tiny-skia-path 0.11.4",+]+ [[package]] name = "tiny-skia" version = "0.12.0"@@ -5723,7 +5789,18 @@ dependencies = [  "bytemuck",  "cfg-if",  "log",- "tiny-skia-path",+ "tiny-skia-path 0.12.0",+]++[[package]]+name = "tiny-skia-path"+version = "0.11.4"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93"+dependencies = [+ "arrayref",+ "bytemuck",+ "strict-num", ]  [[package]]@@ -5872,6 +5949,12 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce898823d8cd0f028662d2f349694b1af0a5cc9be83f1d213a00d3147445e1e" +[[package]]+name = "typeid"+version = "1.0.3"+source = "registry+https://github.com/rust-lang/crates.io-index"+checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c"+ [[package]] name = "typenum" version = "1.20.1"@@ -7026,6 +7109,7 @@ dependencies = [  "raw-window-handle",  "redox_syscall 0.4.1",  "rustix 0.38.44",+ "sctk-adwaita",  "smithay-client-toolkit 0.19.2",  "smol_str",  "tracing",@@ -7512,19 +7596,19 @@ dependencies = [ [[patch.unused]] name = "egui_demo_lib" version = "0.37.0"-source = "git+https://github.com/storytold/egui.git?rev=6d518fc799681571963842f95b55a6b68431b15a#6d518fc799681571963842f95b55a6b68431b15a"+source = "git+https://github.com/storytold/egui.git?rev=69750b71a97dc4672e2fbb70863b838e6795c910#69750b71a97dc4672e2fbb70863b838e6795c910"  [[patch.unused]] name = "egui_extras" version = "0.37.0"-source = "git+https://github.com/storytold/egui.git?rev=6d518fc799681571963842f95b55a6b68431b15a#6d518fc799681571963842f95b55a6b68431b15a"+source = "git+https://github.com/storytold/egui.git?rev=69750b71a97dc4672e2fbb70863b838e6795c910#69750b71a97dc4672e2fbb70863b838e6795c910"  [[patch.unused]] name = "egui_inspection" version = "0.37.0"-source = "git+https://github.com/storytold/egui.git?rev=6d518fc799681571963842f95b55a6b68431b15a#6d518fc799681571963842f95b55a6b68431b15a"+source = "git+https://github.com/storytold/egui.git?rev=69750b71a97dc4672e2fbb70863b838e6795c910#69750b71a97dc4672e2fbb70863b838e6795c910"  [[patch.unused]] name = "egui_system_font_provider" version = "0.37.0"-source = "git+https://github.com/storytold/egui.git?rev=6d518fc799681571963842f95b55a6b68431b15a#6d518fc799681571963842f95b55a6b68431b15a"+source = "git+https://github.com/storytold/egui.git?rev=69750b71a97dc4672e2fbb70863b838e6795c910#69750b71a97dc4672e2fbb70863b838e6795c910"diff --git a/Cargo.toml b/Cargo.toml--- a/Cargo.toml+++ b/Cargo.toml@@ -222,18 +222,18 @@ codegen-units = 1 [patch.crates-io] hayro-syntax = { git = "https://github.com/storytold/craft-libs", rev = "92d392cff132eaf53e13afff77f38b5c11b73aa0", version = "=0.7.2" } hayro-jbig2 = { git = "https://github.com/storytold/craft-libs", rev = "92d392cff132eaf53e13afff77f38b5c11b73aa0", version = "=0.3.0" }-ecolor = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }-emath = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }-epaint = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }-epaint_default_fonts = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }-egui = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }-egui-winit = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }-egui-wgpu = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }-egui_glow = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }-egui_extras = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }-egui_kittest = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }-egui_system_font_provider = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }-eframe = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }-egui_demo_lib = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }-egui_inspection = { git = "https://github.com/storytold/egui.git", rev = "6d518fc799681571963842f95b55a6b68431b15a" }+ecolor = { git = "https://github.com/storytold/egui.git", rev = "69750b71a97dc4672e2fbb70863b838e6795c910" }+emath = { git = "https://github.com/storytold/egui.git", rev = "69750b71a97dc4672e2fbb70863b838e6795c910" }+epaint = { git = "https://github.com/storytold/egui.git", rev = "69750b71a97dc4672e2fbb70863b838e6795c910" }+epaint_default_fonts = { git = "https://github.com/storytold/egui.git", rev = "69750b71a97dc4672e2fbb70863b838e6795c910" }+egui = { git = "https://github.com/storytold/egui.git", rev = "69750b71a97dc4672e2fbb70863b838e6795c910" }+egui-winit = { git = "https://github.com/storytold/egui.git", rev = "69750b71a97dc4672e2fbb70863b838e6795c910" }+egui-wgpu = { git = "https://github.com/storytold/egui.git", rev = "69750b71a97dc4672e2fbb70863b838e6795c910" }+egui_glow = { git = "https://github.com/storytold/egui.git", rev = "69750b71a97dc4672e2fbb70863b838e6795c910" }+egui_extras = { git = "https://github.com/storytold/egui.git", rev = "69750b71a97dc4672e2fbb70863b838e6795c910" }+egui_kittest = { git = "https://github.com/storytold/egui.git", rev = "69750b71a97dc4672e2fbb70863b838e6795c910" }+egui_system_font_provider = { git = "https://github.com/storytold/egui.git", rev = "69750b71a97dc4672e2fbb70863b838e6795c910" }+eframe = { git = "https://github.com/storytold/egui.git", rev = "69750b71a97dc4672e2fbb70863b838e6795c910" }+egui_demo_lib = { git = "https://github.com/storytold/egui.git", rev = "69750b71a97dc4672e2fbb70863b838e6795c910" }+egui_inspection = { git = "https://github.com/storytold/egui.git", rev = "69750b71a97dc4672e2fbb70863b838e6795c910" } winit = { git = "https://github.com/storytold/winit.git", rev = "a517b3846ee8457d84ee4975110bfda6797c44dc" }diff --git a/NOTICE b/NOTICE--- a/NOTICE+++ b/NOTICE@@ -16,7 +16,7 @@ Bundled fonts  EffectCraft embeds Inter, Noto Serif, JetBrains Mono and Noto Sans Symbols 2 from assets/fonts/. The pinned egui dependency (epaint_default_fonts, revision-6d518fc799681571963842f95b55a6b68431b15a) additionally embeds Ubuntu Light, Hack,+69750b71a97dc4672e2fbb70863b838e6795c910) additionally embeds Ubuntu Light, Hack, Noto Emoji, egui-icons and emoji-icon-font through EffectCraft's enabled `default_fonts` and `monochrome_emoji_fonts` features; no font file from that dependency is added to this repository. The complete copyright notices and licence texts required for all of@@ -289,3 +289,29 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.++sctk-adwaita 0.10.1 — Wayland client window decorations+https://crates.io/crates/sctk-adwaita/0.10.1+Built without title fonts; caption controls are drawn as vectors.++MIT License++Copyright (c) 2022 Bartłomiej Maryńczak++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.diff --git a/ROADMAP.md b/ROADMAP.md--- a/ROADMAP.md+++ b/ROADMAP.md@@ -1,8 +1,14 @@ # EffectCraft Roadmap +> **Native-window review follow-up (2026-10-27):** A malformed eframe window record now uses the legacy fallback. Native X11 restores fit only their physical position to connected monitors, preserving eframe size, zoom and maximize state. Synthetic storage and disconnected-monitor regressions cover both cases.+>+> **Dependency integration (2026-10-27):** Reconciled the latest upstream module changes and shared `craft-libs` revision `92d392c` while retaining the window-restoration `egui` revision `69750b7` pending storytold/egui#3. Feature readiness estimates are unchanged.+>+> **Integration validation (2026-10-27):** The upstream synchronization at `e6e512a4` retains native-window persistence and the desktop logic callback documentation. Formatting, Clippy, doctests, PDF dependency checks, layering, assets and WASM passed on Linux. The full test run retains the three previously reproduced Noto Serif/style and GPU wide-penumbra failures; the engine, text and GPU crates are identical to upstream. Parity estimates are unchanged.+ **Stage: alpha** · next: beta, ~30 points of "ready for real work" and ~350–620 h away -> **Last reviewed:** 2026-10-12 · **Last updated:** 2026-10-12 · **Change:** minor (open-issue count, crash list and localization share re-audited against `upstream/main` 470ea7a3) · **Target:** Adobe After Effects 2026 (26.5.0.89)+> **Last reviewed:** 2026-10-12 · **Last updated:** 2026-10-27 · **Change:** trivial (native record fallback and X11 position rescue; previously: upstream integration validation; progress log: windows reopen at their last size or maximized at any interface size, storytold/egui#3) · **Target:** Adobe After Effects 2026 (26.5.0.89)  EffectCraft aims to do what After Effects does, with the same panels, menus and behaviour, written from scratch in Rust. This page is the one-page summary; the evidence is in@@ -117,6 +123,12 @@ Come tell us what matters most to you on [Discord](https://discord.gg/artcraft).  ## Progress log +- 2026-10-27: Native window fallback and X11 position rescue regressions pass; integrated the current shared-library revision without dropping the restoration pin.++- 2026-10-26: Windows reopen at their last size, or maximized, at any interface size (such as 150%),+  and Restore after a maximized restart returns to the last normal size. On GNOME Wayland, where the+  compositor draws no frame, windows get standard minimize, maximize and close buttons. Shared+  change: storytold/egui#3; checked on Linux only. - 2026-10-16: Convert To Bezier Path turns a shape layer's Rectangle, Ellipse or Polystar   path into an editable Path 1 with the same geometry, as After Effects does; it used to   apply to mask RotoBezier only, so a parametric shape path could not be converted at all@@ -155,6 +167,9 @@ Come tell us what matters most to you on [Discord](https://discord.gg/artcraft).  | Date | Change | Summary | |---|---|---|+| 2026-10-27 | trivial | Decode native window records before bypassing the legacy fallback; rescue native X11 positions on connected monitors while preserving size and maximize state; synthetic regressions pass |+| 2026-10-27 | trivial | Record upstream synchronization validation and the three existing font/GPU test failures; parity estimates unchanged |+| 2026-10-26 | trivial | Progress log: windows reopen at their last size or maximized at any interface size; standard caption buttons on GNOME Wayland (storytold/egui#3) | | 2026-10-12 | minor | Open-issue count (134 to 216), crash list and localization share (12% to 18%) updated to match docs/gaps.md | | 2026-10-12 | patch | Roto Brush span validation after trimming; command regressions for both layer edges | | 2026-10-10 | minor | Hours to ≈ 95% for each audience |diff --git a/apps/effectcraft/Cargo.toml b/apps/effectcraft/Cargo.toml--- a/apps/effectcraft/Cargo.toml+++ b/apps/effectcraft/Cargo.toml@@ -15,7 +15,8 @@ effectcraft-host = { workspace = true } effectcraft-ui-egui = { workspace = true } egui = { workspace = true } # `links`: links (Help menu, Discord, Settings) open in the system browser.-eframe = { workspace = true, features = ["default_fonts", "wgpu", "accesskit", "clipboard", "links", "wayland", "x11"] }+eframe = { workspace = true, features = ["persistence", "default_fonts", "wgpu", "accesskit", "clipboard", "links", "wayland", "x11"] }+egui-winit = { version = "0.37", default-features = false, features = ["serde"] } serde = { workspace = true } serde_json = { workspace = true } # The window's monitors, size and position (src/window.rs), and macOS event-loop options (no appdiff --git a/apps/effectcraft/src/main.rs b/apps/effectcraft/src/main.rs--- a/apps/effectcraft/src/main.rs+++ b/apps/effectcraft/src/main.rs@@ -194,17 +194,22 @@ fn main() -> eframe::Result {     let mut launch = launch_guard::Launch::begin(effectcraft_host::config_dir().as_deref(), std::env::var_os("WGPU_BACKEND").is_some());     let on_gl = launch.backends == Some(eframe::wgpu::Backends::GL);     let notice = launch.notice.take();+    let saved_window = effectcraft_host::config_dir().and_then(|dir| window::load(&effectcraft_engine::config::DirConfig::new(dir)));     let options = eframe::NativeOptions {-        viewport: egui::ViewportBuilder::default()-            .with_title("EffectCraft")-            .with_inner_size(window::DEFAULT_SIZE)-            .with_min_inner_size(window::MIN_SIZE)-            .with_drag_and_drop(true)-            // Started for an agent (`--control`): open without taking the user's keyboard focus.-            .with_active(control_port.is_none())-            .with_icon(app_icon())-            // Wayland matches the window to `ai.storyteller.effectcraft.desktop` by this ID.-            .with_app_id(appimage::APP_ID),+        persist_window: control_port.is_none(),+        viewport: window::start_viewport(+            egui::ViewportBuilder::default()+                .with_title("EffectCraft")+                .with_inner_size(window::DEFAULT_SIZE)+                .with_min_inner_size(window::MIN_SIZE)+                .with_drag_and_drop(true)+                // Started for an agent (`--control`): open without taking the user's keyboard focus.+                .with_active(control_port.is_none())+                .with_icon(app_icon())+                // Wayland matches the window to `ai.storyteller.effectcraft.desktop` by this ID.+                .with_app_id(appimage::APP_ID),+            saved_window,+        ),         event_loop_builder: agent_event_loop(control_port.is_some()),         wgpu_options: wgpu_options(launch.backends),         ..Default::default()@@ -261,8 +266,15 @@ fn main() -> eframe::Result {             }             session.load_settings();             // Fit the window to its monitor, or put it back where it was (still hidden).-            let saved_window = session.config.as_deref().and_then(window::load);-            let opened = cc.winit_window().map(|w| window::restore(w, saved_window));+            let has_native_state = window::has_native_window(control_port.is_none(), cc.storage);+            let opened = if has_native_state {+                if let Some(window) = cc.winit_window() {+                    window::fit_native_position(window);+                }+                saved_window+            } else {+                cc.winit_window().map(|w| window::restore(w, saved_window))+            };             let recovery = if control_port.is_none() { session.begin_recovery() } else { None };             let project = files.iter().find(|f| is_project_file(f)).cloned();             if let Some(p) = project {@@ -455,6 +467,11 @@ struct Desktop { }  impl eframe::App for Desktop {+    // Preferences have their own store; eframe persists only the native window.+    fn persist_egui_memory(&self) -> bool {+        false+    }+     /// The desktop host's logic tick: update the macOS menu bar, run the shared app tick and close the IME pass.     fn logic(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {         #[cfg(target_os = "macos")]diff --git a/apps/effectcraft/src/window.rs b/apps/effectcraft/src/window.rs--- a/apps/effectcraft/src/window.rs+++ b/apps/effectcraft/src/window.rs@@ -30,6 +30,22 @@ pub const MIN_SIZE: [f32; 2] = [960.0, 600.0]; /// say how much of the screen they take): a monitor with less room opens it maximized. const SCREEN_ROOM: [f32; 2] = [64.0, 128.0]; +/// A present but unreadable native record does not suppress the legacy position fallback.+pub fn has_native_window(persist_window: bool, storage: Option<&dyn eframe::Storage>) -> bool {+    persist_window && storage.and_then(|s| eframe::get_value::<egui_winit::WindowSettings>(s, "window")).is_some()+}++/// Seed the native builder from legacy preferences before eframe creates the window. Its+/// persisted native state takes precedence on later launches.+pub fn start_viewport(builder: egui::ViewportBuilder, saved: Option<WindowGeometry>) -> egui::ViewportBuilder {+    let Some(saved) = saved else { return builder };+    let size: [f32; 2] = std::array::from_fn(|i| {+        let side = saved.size[i];+        if side.is_finite() && side > 0.0 { side.clamp(MIN_SIZE[i], 16384.0) } else { DEFAULT_SIZE[i] }+    });+    builder.with_inner_size(size).with_maximized(saved.maximized)+}+ /// A monitor: its top-left corner and size in physical pixels, and its scale factor. #[derive(Clone, Copy, Debug, PartialEq)] pub struct Monitor {@@ -143,6 +159,54 @@ pub fn restore(window: &winit::window::Window, saved: Option<WindowGeometry>) ->     g } +/// Move an eframe-restored X11 window onto a connected monitor without changing its native size+/// or maximize request. Wayland places windows itself; other platforms have eframe's own rescue.+pub fn fit_native_position(window: &winit::window::Window) {+    #[cfg(target_os = "linux")]+    {+        use winit::raw_window_handle::{HasWindowHandle as _, RawWindowHandle};+        if !matches!(window.window_handle().map(|h| h.as_raw()), Ok(RawWindowHandle::Xlib(_) | RawWindowHandle::Xcb(_))) {+            return;+        }+        let Ok(pos) = window.outer_position() else { return };+        let size = window.outer_size();+        let handles: Vec<_> = window.available_monitors().collect();+        let monitors: Vec<_> = handles.iter().map(|m| Monitor::new(m.position(), m.size(), m.scale_factor())).collect();+        let home = window.primary_monitor().or_else(|| window.current_monitor()).and_then(|c| handles.iter().position(|m| *m == c));+        if let Some([x, y]) = fitted_native_position([pos.x, pos.y], [size.width, size.height], &monitors, home) {+            window.set_outer_position(PhysicalPosition::new(x, y));+        }+    }+    #[cfg(not(target_os = "linux"))]+    let _ = window;+}++/// Fit only a physical position: native restore sizes and maximized state remain eframe's.+#[cfg(any(target_os = "linux", test))]+fn fitted_native_position(pos: [i32; 2], size: [u32; 2], monitors: &[Monitor], home: Option<usize>) -> Option<[i32; 2]> {+    let shown = monitors+        .iter()+        .filter_map(|m| {+            let span = |i: usize| {+                let start = i64::from(pos[i]).max(m.pos[i]);+                let end = (i64::from(pos[i]) + i64::from(size[i])).min(m.pos[i] + m.size[i]);+                (end - start).max(0)+            };+            let area = span(0).saturating_mul(span(1));+            (area > 0).then_some((area, m))+        })+        .max_by_key(|(area, _)| *area)+        .map(|(_, m)| m);+    let monitor = shown.or_else(|| home.and_then(|i| monitors.get(i))).or(monitors.first())?;+    let fitted = std::array::from_fn(|i| {+        let lo = monitor.pos[i];+        let hi = lo + (monitor.size[i] - i64::from(size[i])).max(0);+        let wanted = if shown.is_some() { i64::from(pos[i]) } else { lo + (hi - lo) / 2 };+        i32::try_from(wanted.clamp(lo, hi)).unwrap_or_default()+    });+    (fitted != pos).then_some(fitted)+}+ /// Per frame: remember the window's geometry for the next launch. Minimized or full screen keeps /// the last one; maximized only sets its flag, so un-maximizing next time restores the last size. pub fn track(ctx: &egui::Context, geometry: &mut Option<WindowGeometry>) {@@ -226,6 +290,54 @@ mod tests {     use super::*;     use effectcraft_engine::config::MemoryConfig; +    /// eframe's storage with only a `"window"` value.+    struct WindowStorage(&'static str);++    impl eframe::Storage for WindowStorage {+        fn get_string(&self, key: &str) -> Option<String> {+            (key == "window").then(|| self.0.to_owned())+        }+        fn set_string(&mut self, _: &str, _: String) {}+        fn remove_string(&mut self, _: &str) {}+        fn flush(&mut self) {}+    }++    /// The legacy preferences' place applies unless eframe really restored a window: not from a+    /// corrupt value, and not in a run that doesn't persist the window (no preferences, control).+    #[test]+    fn only_a_readable_window_eframe_restores_counts_as_native_state() {+        let saved = WindowStorage(+            "(normal_geometry:(outer_position_pixels:Some((x:10.0,y:20.0)),scale_factor:1.0,\+         inner_size_points:(x:800.0,y:600.0),zoom_factor:Some(1.0)),fullscreen:false,maximized:true)",+        );+        assert!(has_native_window(true, Some(&saved)));+        assert!(!has_native_window(false, Some(&saved)));+        assert!(!has_native_window(true, Some(&WindowStorage("(normal_geometry:"))));+        assert!(!has_native_window(true, None));+    }++    #[test]+    fn native_restore_rescues_only_the_position_in_physical_pixels() {+        let two = [monitor(0, 0, 2560, 1440, 1.0), monitor(2560, 0, 1920, 1080, 1.5)];+        assert_eq!(fitted_native_position([2700, 50], [1000, 700], &two, Some(0)), None);+        assert_eq!(fitted_native_position([2700, 50], [1000, 700], &two[..1], Some(0)), Some([780, 370]));+        assert_eq!(fitted_native_position([4300, 900], [1000, 700], &two, Some(0)), Some([3480, 380]));+        assert_eq!(fitted_native_position([9000, 9000], [4000, 2000], &two[..1], Some(0)), Some([0, 0]));+        assert_eq!(fitted_native_position([9000, 9000], [1000, 700], &[], None), None);+    }++    #[test]+    fn legacy_window_state_seeds_the_native_restore_size_and_maximize_request() {+        let geometry = WindowGeometry { pos: None, size: [1170.0, 730.0], maximized: true };+        let builder = start_viewport(egui::ViewportBuilder::default(), Some(geometry));+        assert_eq!(builder.inner_size, Some(egui::vec2(1170.0, 730.0)));+        assert_eq!(builder.maximized, Some(true));+        let corrupt = WindowGeometry { size: [f32::NAN, f32::INFINITY], ..geometry };+        assert_eq!(start_viewport(egui::ViewportBuilder::default(), Some(corrupt)).inner_size, Some(DEFAULT_SIZE.into()));+        let tiny = WindowGeometry { size: [1.0, 1.0], ..geometry };+        assert_eq!(start_viewport(egui::ViewportBuilder::default(), Some(tiny)).inner_size, Some(MIN_SIZE.into()));+    }+     fn monitor(x: i32, y: i32, w: u32, h: u32, scale: f64) -> Monitor {         Monitor::new(PhysicalPosition::new(x, y), PhysicalSize::new(w, h), scale)     }diff --git a/contributors/people.toml b/contributors/people.toml--- a/contributors/people.toml+++ b/contributors/people.toml@@ -98,3 +98,6 @@ sync_github_name = false  [people.ki2pixel] display_name = "ki2pixel"++[people.abdallah-shehawey]+sync_github_name = truediff --git a/docs/ui-parity.md b/docs/ui-parity.md--- a/docs/ui-parity.md+++ b/docs/ui-parity.md@@ -1,6 +1,8 @@ # UI parity -> **Last reviewed:** 2026-10-12 · **Last updated:** 2026-10-18 · **Change:** minor (shape group Contents row elided; path-editing layer bounds #1220; closed issue references re-audited against `upstream/main` 470ea7a3) · **Target:** Adobe After Effects 2026+> **Window restore follow-up (2026-10-27):** Unreadable native records use the legacy fallback; X11 rescues disconnected-monitor positions without changing restored native size or maximize state. Windows/macOS native acceptance remains open.+>+> **Last reviewed:** 2026-10-12 · **Last updated:** 2026-10-27 · **Change:** trivial (native record fallback and X11 position rescue; previously: window size and maximized state survive restarts at any interface size; GNOME Wayland caption buttons, storytold/egui#3) · **Target:** Adobe After Effects 2026  How closely EffectCraft's interaction matches After Effects: tools, handles, snapping, nudging, modifier keys, shortcuts, drag behaviour, numeric entry, panels, context menus and feel. Evidence@@ -49,6 +51,8 @@ Effects running; most of the rest parallelises.  | Date | Change | Summary | |---|---|---|+| 2026-10-27 | trivial | Decode native window records before bypassing the legacy fallback; rescue native X11 positions on connected monitors while preserving size and maximize state; synthetic regressions pass |+| 2026-10-26 | trivial | Window size and maximized state survive restarts at any interface size; standard caption buttons on GNOME Wayland (storytold/egui#3) | | 2026-10-18 | minor | A shape group's inner Contents (After Effects' elided Vectors group) draws no row: the group's path, fill/stroke and Transform list directly under the group | | 2026-10-13 | patch | Layer bounds are suppressed during path-point editing (#1220); shape/mask interaction regressions | | 2026-10-12 | minor | Closed issues (#302, #320, #491, #511, #553 and others) marked as closed instead of open; panels in their own windows (#486) |