diff --git a/Cargo.lock b/Cargo.lock index 15696eab..8996f5f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,7 +22,7 @@ dependencies = [ "cavalier_contours", "clap", "console_error_panic_hook", - "cosmic-text", + "cosmic-text 0.15.0", "dwg-thumbnailer", "env_logger", "flate2", @@ -31,13 +31,14 @@ dependencies = [ "glam 0.33.2", "iced", "iced_aw", + "iced_core 0.15.0-dev", "image", "inventory", "js-sys", "log", "lyon_tessellation", "lzma-sys", - "naga", + "naga 27.0.3", "ocs_plugin_api", "open", "printpdf", @@ -348,6 +349,27 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +[[package]] +name = "arboard" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf" +dependencies = [ + "clipboard-win", + "image", + "log", + "objc2 0.6.4", + "objc2-app-kit 0.3.2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation 0.3.2", + "parking_lot", + "percent-encoding", + "windows-sys 0.59.0", + "wl-clipboard-rs", + "x11rb", +] + [[package]] name = "arg_enum_proc_macro" version = "0.3.4" @@ -636,7 +658,16 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ - "bit-vec", + "bit-vec 0.8.0", +] + +[[package]] +name = "bit-set" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ddef2995421ab6a5c779542c81ee77c115206f4ad9d5a8e05f4ff49716a3dd" +dependencies = [ + "bit-vec 0.9.1", ] [[package]] @@ -645,6 +676,12 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" +[[package]] +name = "bit-vec" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51" + [[package]] name = "bit_field" version = "0.10.3" @@ -696,12 +733,6 @@ dependencies = [ "wyz", ] -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - [[package]] name = "block-buffer" version = "0.10.4" @@ -862,43 +893,18 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "calloop" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dbf9978365bac10f54d1d4b04f7ce4427e51f71d61f2fe15e3fed5166474df7" -dependencies = [ - "bitflags 2.13.1", - "polling", - "rustix 1.1.4", - "slab", - "tracing", -] - [[package]] name = "calloop-wayland-source" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" dependencies = [ - "calloop 0.13.0", + "calloop", "rustix 0.38.44", "wayland-backend", "wayland-client", ] -[[package]] -name = "calloop-wayland-source" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138efcf0940a02ebf0cc8d1eff41a1682a46b431630f4c52450d6265876021fa" -dependencies = [ - "calloop 0.14.4", - "rustix 1.1.4", - "wayland-backend", - "wayland-client", -] - [[package]] name = "cavalier_contours" version = "0.7.0" @@ -1031,41 +1037,20 @@ dependencies = [ "error-code", ] -[[package]] -name = "clipboard_macos" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7f4aaa047ba3c3630b080bb9860894732ff23e2aee290a418909aa6d5df38f" -dependencies = [ - "objc2 0.5.2", - "objc2-app-kit 0.2.2", - "objc2-foundation 0.2.2", -] - -[[package]] -name = "clipboard_wayland" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "003f886bc4e2987729d10c1db3424e7f80809f3fc22dbc16c685738887cb37b8" -dependencies = [ - "smithay-clipboard", -] - -[[package]] -name = "clipboard_x11" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd63e33452ffdafd39924c4f05a5dd1e94db646c779c6bd59148a3d95fff5ad4" -dependencies = [ - "thiserror 2.0.18", - "x11rb", -] - [[package]] name = "codespan-reporting" version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "codespan-reporting" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681" dependencies = [ "serde", "termcolor", @@ -1153,7 +1138,7 @@ checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ "bitflags 1.3.2", "core-foundation 0.9.4", - "core-graphics-types 0.1.3", + "core-graphics-types", "foreign-types", "libc", ] @@ -1169,17 +1154,6 @@ dependencies = [ "libc", ] -[[package]] -name = "core-graphics-types" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" -dependencies = [ - "bitflags 2.13.1", - "core-foundation 0.10.1", - "libc", -] - [[package]] name = "core_maths" version = "0.1.1" @@ -1197,14 +1171,38 @@ checksum = "173852283a9a57a3cbe365d86e74dc428a09c50421477d5ad6fe9d9509e37737" dependencies = [ "bitflags 2.13.1", "fontdb", - "harfrust", + "harfrust 0.3.2", "linebender_resource_handle", "log", "rangemap", "rustc-hash 1.1.0", "self_cell", "skrifa 0.37.0", - "smol_str", + "smol_str 0.2.2", + "swash", + "sys-locale", + "unicode-bidi", + "unicode-linebreak", + "unicode-script", + "unicode-segmentation", +] + +[[package]] +name = "cosmic-text" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be17b688510d934ce13f48a2beba700e11583e281e0fda99c22bb256a14eda73" +dependencies = [ + "bitflags 2.13.1", + "fontdb", + "harfrust 0.5.2", + "linebender_resource_handle", + "log", + "rangemap", + "rustc-hash 2.1.3", + "self_cell", + "skrifa 0.40.0", + "smol_str 0.3.6", "swash", "sys-locale", "unicode-bidi", @@ -1265,10 +1263,9 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "cryoglyph" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc795bdbccdbd461736fb163930a009da6597b226d6f6fce33e7a8eb6ec519" +source = "git+https://github.com/iced-rs/cryoglyph.git?rev=53ba3e879539d19ed8162942126a977ec896cc3b#53ba3e879539d19ed8162942126a977ec896cc3b" dependencies = [ - "cosmic-text", + "cosmic-text 0.19.0", "etagere", "lru", "rustc-hash 2.1.3", @@ -1430,9 +1427,8 @@ checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] name = "dpi" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +version = "0.1.1" +source = "git+https://github.com/iced-rs/winit.git?rev=05b8ff17a06562f0a10bb46e6eaacbe2a95cb5ed#05b8ff17a06562f0a10bb46e6eaacbe2a95cb5ed" [[package]] name = "dtor" @@ -1656,6 +1652,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "flate2" version = "1.1.9" @@ -1932,16 +1934,6 @@ dependencies = [ "r-efi 6.0.0", ] -[[package]] -name = "gif" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" -dependencies = [ - "color_quant", - "weezl", -] - [[package]] name = "gif" version = "0.14.2" @@ -1980,9 +1972,9 @@ dependencies = [ [[package]] name = "glow" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e5ea60d70410161c8bf5da3fdfeaa1c72ed2c15f8bbb9d19fe3a4fad085f08" +checksum = "29038e1c483364cc6bb3cf78feee1816002e127c331a1eec55a4d202b9e1adb5" dependencies = [ "js-sys", "slotmap", @@ -2005,35 +1997,18 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3531d702d6c1a3ba92a5fb55a404c7b8c476c8e7ca249951077afcbe4bc807f" -[[package]] -name = "gpu-alloc" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45cf04b2726f02df5508c6de726acdc90cdf97ac771a9a0ffd8ba10a6e696bf9" -dependencies = [ - "bitflags 2.13.1", - "gpu-alloc-types", -] - -[[package]] -name = "gpu-alloc-types" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2bbed164dd10ed526c2e4fe3e721ca4a71c61730e5aafac6844b417b3227058" -dependencies = [ - "bitflags 2.13.1", -] - [[package]] name = "gpu-allocator" -version = "0.27.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd" +checksum = "51255ea7cfaadb6c5f1528d43e92a82acb2b96c43365989a28b2d44ee38f8795" dependencies = [ + "ash", + "hashbrown 0.16.1", "log", "presser", - "thiserror 1.0.69", - "windows 0.58.0", + "thiserror 2.0.18", + "windows 0.62.2", ] [[package]] @@ -2091,6 +2066,19 @@ dependencies = [ "smallvec", ] +[[package]] +name = "harfrust" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9da2e5ae821f6e96664977bf974d6d6a2d6682f9ccee23e62ec1d134246845f9" +dependencies = [ + "bitflags 2.13.1", + "bytemuck", + "core_maths", + "read-fonts 0.37.0", + "smallvec", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -2198,16 +2186,15 @@ dependencies = [ [[package]] name = "iced" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "000e01026c93ba643f8357a3db3ada0e6555265a377f6f9291c472f6dd701fb3" +version = "0.15.0-dev" +source = "git+https://github.com/iced-rs/iced.git?rev=23604ff22ab0aad9e00b9327cb7b8546ed84db39#23604ff22ab0aad9e00b9327cb7b8546ed84db39" dependencies = [ - "iced_core", + "iced_core 0.15.0-dev", "iced_debug", - "iced_futures", - "iced_renderer", + "iced_futures 0.15.0-dev", + "iced_renderer 0.15.0-dev", "iced_runtime", - "iced_widget", + "iced_widget 0.15.0-dev", "iced_winit", "image", "thiserror 2.0.18", @@ -2216,16 +2203,11 @@ dependencies = [ [[package]] name = "iced_aw" version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b7f923642b024c415150b70ef84ee5c11626e3b0af73b954d817d573bcd0a4" +source = "git+https://github.com/tsuza/iced_aw.git?rev=a9301708a4d008dc17f63cbbbb2b2aa06e0e7352#a9301708a4d008dc17f63cbbbb2b2aa06e0e7352" dependencies = [ - "cfg-if", - "chrono", - "iced_core", + "iced_core 0.15.0-dev", "iced_fonts", - "iced_widget", - "num-format", - "num-traits", + "iced_widget 0.15.0-dev", "web-time", ] @@ -2242,19 +2224,36 @@ dependencies = [ "log", "num-traits", "rustc-hash 2.1.3", - "smol_str", + "smol_str 0.2.2", + "thiserror 2.0.18", + "web-time", +] + +[[package]] +name = "iced_core" +version = "0.15.0-dev" +source = "git+https://github.com/iced-rs/iced.git?rev=23604ff22ab0aad9e00b9327cb7b8546ed84db39#23604ff22ab0aad9e00b9327cb7b8546ed84db39" +dependencies = [ + "bitflags 2.13.1", + "bytes", + "glam 0.33.2", + "lilt", + "log", + "num-traits", + "raw-window-handle", + "rustc-hash 2.1.3", + "smol_str 0.2.2", "thiserror 2.0.18", "web-time", ] [[package]] name = "iced_debug" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25035ab0215a620e53f4103e36fc4e59a1fb2817e4bfc38a30ad27b4202ea0be" +version = "0.15.0-dev" +source = "git+https://github.com/iced-rs/iced.git?rev=23604ff22ab0aad9e00b9327cb7b8546ed84db39#23604ff22ab0aad9e00b9327cb7b8546ed84db39" dependencies = [ - "iced_core", - "iced_futures", + "iced_core 0.15.0-dev", + "iced_futures 0.15.0-dev", "log", ] @@ -2264,9 +2263,9 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "214cff7c8499e328774216690e58e315a1a5f8f6fdd1035aed6298e62ffc4c1d" dependencies = [ - "iced_core", + "iced_core 0.14.0", "iced_fonts_macros", - "iced_widget", + "iced_widget 0.14.2", ] [[package]] @@ -2288,7 +2287,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c0c85ccad42dfbec7293c36c018af0ea0dbcc52d137a4a9a0b0f6822a3fdf0a" dependencies = [ "futures", - "iced_core", + "iced_core 0.14.0", + "log", + "rustc-hash 2.1.3", + "wasm-bindgen-futures", + "wasmtimer", +] + +[[package]] +name = "iced_futures" +version = "0.15.0-dev" +source = "git+https://github.com/iced-rs/iced.git?rev=23604ff22ab0aad9e00b9327cb7b8546ed84db39#23604ff22ab0aad9e00b9327cb7b8546ed84db39" +dependencies = [ + "futures", + "iced_core 0.15.0-dev", "log", "rustc-hash 2.1.3", "smol", @@ -2304,10 +2316,28 @@ checksum = "234ca1c2cec4155055f68fa5fad1b5242c496ac8238d80a259bca382fb44a102" dependencies = [ "bitflags 2.13.1", "bytemuck", - "cosmic-text", + "cosmic-text 0.15.0", "half", - "iced_core", - "iced_futures", + "iced_core 0.14.0", + "iced_futures 0.14.0", + "log", + "raw-window-handle", + "rustc-hash 2.1.3", + "thiserror 2.0.18", + "unicode-segmentation", +] + +[[package]] +name = "iced_graphics" +version = "0.15.0-dev" +source = "git+https://github.com/iced-rs/iced.git?rev=23604ff22ab0aad9e00b9327cb7b8546ed84db39#23604ff22ab0aad9e00b9327cb7b8546ed84db39" +dependencies = [ + "bitflags 2.13.1", + "bytemuck", + "cosmic-text 0.19.0", + "half", + "iced_core 0.15.0-dev", + "iced_futures 0.15.0-dev", "image", "kamadak-exif", "log", @@ -2320,11 +2350,10 @@ dependencies = [ [[package]] name = "iced_program" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dfafec2947cda688d8eb00dac337ba11aa60f9ef6335aed343e189d26e4a673" +version = "0.15.0-dev" +source = "git+https://github.com/iced-rs/iced.git?rev=23604ff22ab0aad9e00b9327cb7b8546ed84db39#23604ff22ab0aad9e00b9327cb7b8546ed84db39" dependencies = [ - "iced_graphics", + "iced_graphics 0.15.0-dev", "iced_runtime", ] @@ -2334,7 +2363,17 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "250cc0802408e8c077986ec56c7d07c65f423ee658a4b9fd795a1f2aae5dac05" dependencies = [ - "iced_graphics", + "iced_graphics 0.14.0", + "log", + "thiserror 2.0.18", +] + +[[package]] +name = "iced_renderer" +version = "0.15.0-dev" +source = "git+https://github.com/iced-rs/iced.git?rev=23604ff22ab0aad9e00b9327cb7b8546ed84db39#23604ff22ab0aad9e00b9327cb7b8546ed84db39" +dependencies = [ + "iced_graphics 0.15.0-dev", "iced_tiny_skia", "iced_wgpu", "log", @@ -2343,27 +2382,24 @@ dependencies = [ [[package]] name = "iced_runtime" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1889b819ce4c06674183242e336c8d49465665441396914dc07cc86f44fa8d4" +version = "0.15.0-dev" +source = "git+https://github.com/iced-rs/iced.git?rev=23604ff22ab0aad9e00b9327cb7b8546ed84db39#23604ff22ab0aad9e00b9327cb7b8546ed84db39" dependencies = [ "bytes", - "iced_core", - "iced_futures", - "raw-window-handle", + "iced_core 0.15.0-dev", + "iced_futures 0.15.0-dev", "thiserror 2.0.18", ] [[package]] name = "iced_tiny_skia" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0acf8b75a3bc914aff5f2329fdffc1b36eeaea29dda0e4bd232f1c62e9cc3d" +version = "0.15.0-dev" +source = "git+https://github.com/iced-rs/iced.git?rev=23604ff22ab0aad9e00b9327cb7b8546ed84db39#23604ff22ab0aad9e00b9327cb7b8546ed84db39" dependencies = [ "bytemuck", - "cosmic-text", + "cosmic-text 0.19.0", "iced_debug", - "iced_graphics", + "iced_graphics 0.15.0-dev", "kurbo 0.10.4", "log", "resvg", @@ -2374,18 +2410,16 @@ dependencies = [ [[package]] name = "iced_wgpu" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff144a999b0ca0f8a10257934500060240825c42e950ec0ebee9c8ae30561c13" +version = "0.15.0-dev" +source = "git+https://github.com/iced-rs/iced.git?rev=23604ff22ab0aad9e00b9327cb7b8546ed84db39#23604ff22ab0aad9e00b9327cb7b8546ed84db39" dependencies = [ "bitflags 2.13.1", "bytemuck", "cryoglyph", "futures", - "glam 0.25.0", "guillotiere", "iced_debug", - "iced_graphics", + "iced_graphics 0.15.0-dev", "log", "lyon", "resvg", @@ -2400,7 +2434,21 @@ version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1596afa0d3109c2618e8bc12bae6c11d3064df8f95c42dfce570397dbe957ab" dependencies = [ - "iced_renderer", + "iced_renderer 0.14.0", + "log", + "num-traits", + "rustc-hash 2.1.3", + "thiserror 2.0.18", + "unicode-segmentation", +] + +[[package]] +name = "iced_widget" +version = "0.15.0-dev" +source = "git+https://github.com/iced-rs/iced.git?rev=23604ff22ab0aad9e00b9327cb7b8546ed84db39#23604ff22ab0aad9e00b9327cb7b8546ed84db39" +dependencies = [ + "iced_renderer 0.15.0-dev", + "iced_runtime", "log", "num-traits", "pulldown-cmark", @@ -2411,20 +2459,21 @@ dependencies = [ [[package]] name = "iced_winit" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b7dbedc47562d1de3b9707d939f678b88c382004b7ab5a18f7a7dd723162d75" +version = "0.15.0-dev" +source = "git+https://github.com/iced-rs/iced.git?rev=23604ff22ab0aad9e00b9327cb7b8546ed84db39#23604ff22ab0aad9e00b9327cb7b8546ed84db39" dependencies = [ + "arboard", "iced_debug", "iced_program", + "iced_runtime", "log", "mundy", + "objc2 0.5.2", "rustc-hash 2.1.3", "thiserror 2.0.18", "tracing", "wasm-bindgen-futures", "web-sys", - "window_clipboard", "winit", ] @@ -2438,7 +2487,7 @@ dependencies = [ "byteorder-lite", "color_quant", "exr", - "gif 0.14.2", + "gif", "image-webp", "moxcms", "num-traits", @@ -2448,8 +2497,8 @@ dependencies = [ "rayon", "rgb", "tiff", - "zune-core 0.5.1", - "zune-jpeg 0.5.15", + "zune-core", + "zune-jpeg", ] [[package]] @@ -2698,12 +2747,11 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.103" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" dependencies = [ - "cfg-if", - "futures-util", + "once_cell", "wasm-bindgen", ] @@ -2982,15 +3030,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - [[package]] name = "matext4cgmath" version = "0.1.0" @@ -3065,21 +3104,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "metal" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00c15a6f673ff72ddcc22394663290f870fb224c1bfce55734a75c414150e605" -dependencies = [ - "bitflags 2.13.1", - "block", - "core-graphics-types 0.2.0", - "foreign-types", - "log", - "objc", - "paste", -] - [[package]] name = "minimal-lexical" version = "0.2.1" @@ -3144,11 +3168,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "066cf25f0e8b11ee0df221219010f213ad429855f57c494f995590c861a9a7d8" dependencies = [ "arrayvec", - "bit-set", + "bit-set 0.8.0", "bitflags 2.13.1", "cfg-if", "cfg_aliases", - "codespan-reporting", + "codespan-reporting 0.12.0", + "half", + "hashbrown 0.16.1", + "hexf-parse", + "indexmap", + "libm", + "log", + "num-traits", + "once_cell", + "rustc-hash 1.1.0", + "thiserror 2.0.18", + "unicode-ident", +] + +[[package]] +name = "naga" +version = "29.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2bf919621e7975acb27d881bae2fb993e0d45c8e0446e85e6272971e00dc8df" +dependencies = [ + "arrayvec", + "bit-set 0.9.1", + "bitflags 2.13.1", + "cfg-if", + "cfg_aliases", + "codespan-reporting 0.13.1", "half", "hashbrown 0.16.1", "hexf-parse", @@ -3328,16 +3377,6 @@ dependencies = [ "syn 2.0.119", ] -[[package]] -name = "num-format" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" -dependencies = [ - "arrayvec", - "itoa", -] - [[package]] name = "num-integer" version = "0.1.46" @@ -3390,15 +3429,6 @@ dependencies = [ "syn 2.0.119", ] -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", -] - [[package]] name = "objc-sys" version = "0.3.5" @@ -3552,7 +3582,7 @@ dependencies = [ "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", - "objc2-metal", + "objc2-metal 0.2.2", ] [[package]] @@ -3669,6 +3699,18 @@ dependencies = [ "objc2-foundation 0.2.2", ] +[[package]] +name = "objc2-metal" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0125f776a10d00af4152d74616409f0d4a2053a6f57fa5b7d6aa2854ac04794" +dependencies = [ + "bitflags 2.13.1", + "block2 0.6.2", + "objc2 0.6.4", + "objc2-foundation 0.3.2", +] + [[package]] name = "objc2-quartz-core" version = "0.2.2" @@ -3679,7 +3721,7 @@ dependencies = [ "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", - "objc2-metal", + "objc2-metal 0.2.2", ] [[package]] @@ -3692,6 +3734,7 @@ dependencies = [ "objc2 0.6.4", "objc2-core-foundation", "objc2-foundation 0.3.2", + "objc2-metal 0.3.2", ] [[package]] @@ -3833,6 +3876,16 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "os_pipe" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + [[package]] name = "ouroboros" version = "0.17.2" @@ -3932,6 +3985,17 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "petgraph" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" +dependencies = [ + "fixedbitset", + "hashbrown 0.15.5", + "indexmap", +] + [[package]] name = "pico-args" version = "0.5.0" @@ -4431,6 +4495,18 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" +[[package]] +name = "raw-window-metal" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40d213455a5f1dc59214213c7330e074ddf8114c9a42411eb890c767357ce135" +dependencies = [ + "objc2 0.6.4", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "objc2-quartz-core 0.3.2", +] + [[package]] name = "rawpointer" version = "0.2.1" @@ -4477,6 +4553,17 @@ dependencies = [ "font-types 0.10.1", ] +[[package]] +name = "read-fonts" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b634fabf032fab15307ffd272149b622260f55974d9fad689292a5d33df02e5" +dependencies = [ + "bytemuck", + "core_maths", + "font-types 0.11.3", +] + [[package]] name = "read-fonts" version = "0.39.2" @@ -4576,15 +4663,12 @@ version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8928798c0a55e03c9ca6c4c6846f76377427d2c1e1f7e6de3c06ae57942df43" dependencies = [ - "gif 0.13.3", - "image-webp", "log", "pico-args", "rgb", "svgtypes", "tiny-skia", "usvg", - "zune-jpeg 0.4.21", ] [[package]] @@ -4835,24 +4919,6 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" -[[package]] -name = "rustybuzz" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702" -dependencies = [ - "bitflags 2.13.1", - "bytemuck", - "core_maths", - "log", - "smallvec", - "ttf-parser", - "unicode-bidi-mirroring", - "unicode-ccc", - "unicode-properties", - "unicode-script", -] - [[package]] name = "ryu" version = "1.0.23" @@ -4907,7 +4973,7 @@ dependencies = [ "ab_glyph", "log", "memmap2", - "smithay-client-toolkit 0.19.2", + "smithay-client-toolkit", "tiny-skia", ] @@ -5111,6 +5177,16 @@ dependencies = [ "read-fonts 0.35.0", ] +[[package]] +name = "skrifa" +version = "0.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fbdfe3d2475fbd7ddd1f3e5cf8288a30eb3e5f95832829570cd88115a7434ac" +dependencies = [ + "bytemuck", + "read-fonts 0.37.0", +] + [[package]] name = "skrifa" version = "0.42.1" @@ -5149,8 +5225,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ "bitflags 2.13.1", - "calloop 0.13.0", - "calloop-wayland-source 0.3.0", + "calloop", + "calloop-wayland-source", "cursor-icon", "libc", "log", @@ -5167,44 +5243,6 @@ dependencies = [ "xkeysym", ] -[[package]] -name = "smithay-client-toolkit" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0" -dependencies = [ - "bitflags 2.13.1", - "calloop 0.14.4", - "calloop-wayland-source 0.4.1", - "cursor-icon", - "libc", - "log", - "memmap2", - "rustix 1.1.4", - "thiserror 2.0.18", - "wayland-backend", - "wayland-client", - "wayland-csd-frame", - "wayland-cursor", - "wayland-protocols", - "wayland-protocols-experimental", - "wayland-protocols-misc", - "wayland-protocols-wlr", - "wayland-scanner", - "xkeysym", -] - -[[package]] -name = "smithay-clipboard" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71704c03f739f7745053bde45fa203a46c58d25bc5c4efba1d9a60e9dba81226" -dependencies = [ - "libc", - "smithay-client-toolkit 0.20.0", - "wayland-backend", -] - [[package]] name = "smol" version = "2.0.2" @@ -5231,6 +5269,12 @@ dependencies = [ "serde", ] +[[package]] +name = "smol_str" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aaa7368fcf4852a4c2dd92df0cace6a71f2091ca0a23391ce7f3a31833f1523" + [[package]] name = "softbuffer" version = "0.4.8" @@ -5276,9 +5320,9 @@ dependencies = [ [[package]] name = "spirv" -version = "0.3.0+sdk-1.3.268.0" +version = "0.4.0+sdk-1.4.341.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" +checksum = "d9571ea910ebd84c86af4b3ed27f9dbdc6ad06f17c5f96146b2b671e2976744f" dependencies = [ "bitflags 2.13.1", ] @@ -5473,7 +5517,7 @@ dependencies = [ "half", "quick-error", "weezl", - "zune-jpeg 0.5.15", + "zune-jpeg", ] [[package]] @@ -5634,7 +5678,6 @@ version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ - "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -5660,6 +5703,17 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tree_magic_mini" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8765b90061cba6c22b5831f675da109ae5561588290f9fa2317adab2714d5a6" +dependencies = [ + "memchr 2.8.3", + "nom 8.0.0", + "petgraph", +] + [[package]] name = "truck-base" version = "0.5.0" @@ -5856,24 +5910,12 @@ version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" -[[package]] -name = "unicode-bidi-mirroring" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfa6e8c60bb66d49db113e0125ee8711b7647b5579dc7f5f19c42357ed039fe" - [[package]] name = "unicode-canonical-combining-class" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41c99d5174052d02ce765418e826597a1be18f32c114e35d9e22f92390239561" -[[package]] -name = "unicode-ccc" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce61d488bcdc9bc8b5d1772c404828b17fc481c0a582b5581e95fb233aef503e" - [[package]] name = "unicode-general-category" version = "1.1.0" @@ -5925,12 +5967,6 @@ version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" -[[package]] -name = "unicode-vo" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" - [[package]] name = "unicode-width" version = "0.2.2" @@ -5982,21 +6018,16 @@ dependencies = [ "base64 0.22.1", "data-url", "flate2", - "fontdb", "imagesize", "kurbo 0.11.3", "log", "pico-args", "roxmltree", - "rustybuzz", "simplecss", "siphasher", "strict-num", "svgtypes", "tiny-skia-path", - "unicode-bidi", - "unicode-script", - "unicode-vo", "xmlwriter", ] @@ -6086,9 +6117,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.126" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" dependencies = [ "cfg-if", "once_cell", @@ -6099,19 +6130,23 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.76" +version = "0.4.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" +checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" dependencies = [ + "cfg-if", + "futures-util", "js-sys", + "once_cell", "wasm-bindgen", + "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.126" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6119,9 +6154,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.126" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" dependencies = [ "bumpalo", "proc-macro2", @@ -6132,9 +6167,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.126" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" dependencies = [ "unicode-ident", ] @@ -6213,32 +6248,6 @@ dependencies = [ "wayland-scanner", ] -[[package]] -name = "wayland-protocols-experimental" -version = "20250721.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a1f863128dcaaec790d7b4b396cc9b9a7a079e878e18c47e6c2d2c5a8dcbb1" -dependencies = [ - "bitflags 2.13.1", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "wayland-scanner", -] - -[[package]] -name = "wayland-protocols-misc" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e9567599ef23e09b8dad6e429e5738d4509dfc46b3b21f32841a304d16b29c8" -dependencies = [ - "bitflags 2.13.1", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "wayland-scanner", -] - [[package]] name = "wayland-protocols-plasma" version = "0.3.12" @@ -6290,9 +6299,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.103" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" +checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" dependencies = [ "js-sys", "wasm-bindgen", @@ -6334,19 +6343,20 @@ checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" [[package]] name = "wgpu" -version = "27.0.1" +version = "29.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe68bac7cde125de7a731c3400723cadaaf1703795ad3f4805f187459cd7a77" +checksum = "76e8840e1ba2881d4cbb18d2147627a56af426ff064c0401eb0c8410c6325d07" dependencies = [ "arrayvec", "bitflags 2.13.1", + "bytemuck", "cfg-if", "cfg_aliases", "document-features", "hashbrown 0.16.1", "js-sys", "log", - "naga", + "naga 29.0.4", "parking_lot", "portable-atomic", "profiling", @@ -6363,13 +6373,13 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "27.0.3" +version = "29.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27a75de515543b1897b26119f93731b385a19aea165a1ec5f0e3acecc229cae7" +checksum = "2f519832254e56965a9940c4af57dcb75f702b6f6fa4a0b172f685395843a4d7" dependencies = [ "arrayvec", - "bit-set", - "bit-vec", + "bit-set 0.9.1", + "bit-vec 0.9.1", "bitflags 2.13.1", "bytemuck", "cfg_aliases", @@ -6377,7 +6387,7 @@ dependencies = [ "hashbrown 0.16.1", "indexmap", "log", - "naga", + "naga 29.0.4", "once_cell", "parking_lot", "portable-atomic", @@ -6391,64 +6401,63 @@ dependencies = [ "wgpu-core-deps-wasm", "wgpu-core-deps-windows-linux-android", "wgpu-hal", + "wgpu-naga-bridge", "wgpu-types", ] [[package]] name = "wgpu-core-deps-apple" -version = "27.0.0" +version = "29.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0772ae958e9be0c729561d5e3fd9a19679bcdfb945b8b1a1969d9bfe8056d233" +checksum = "f5e39e26c4c0e07589e67d18546cf79ff45383659fc72fca4dd293358a0347f3" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-emscripten" -version = "27.0.0" +version = "29.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06ac3444a95b0813ecfd81ddb2774b66220b264b3e2031152a4a29fda4da6b5" +checksum = "01e09be551dc939498bdd5f6b2c66e55ab275dad25825267a08605a80fc9f0af" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-wasm" -version = "27.0.0" +version = "29.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b1027dcf3b027a877e44819df7ceb0e2e98578830f8cd34cd6c3c7c2a7a50b7" +checksum = "af1fb1798be2a912497d4c224f72d39bb0cb34af50e8bcc29865bc339c943059" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-windows-linux-android" -version = "27.0.0" +version = "29.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71197027d61a71748e4120f05a9242b2ad142e3c01f8c1b47707945a879a03c3" +checksum = "4e592c1bbef6ad047647ae6e666ebd8cee7a32bb4544d9700ec96cbf73230257" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-hal" -version = "27.0.4" +version = "29.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b21cb61c57ee198bc4aff71aeadff4cbb80b927beb912506af9c780d64313ce" +checksum = "97ace1c17727311c22a46e4e3faf56ea6de81af99dcc839bdfb54857b94d448d" dependencies = [ "android_system_properties", "arrayvec", "ash", - "bit-set", + "bit-set 0.9.1", "bitflags 2.13.1", - "block", + "block2 0.6.2", "bytemuck", "cfg-if", "cfg_aliases", - "core-graphics-types 0.2.0", "glow", "glutin_wgl_sys", - "gpu-alloc", "gpu-allocator", "gpu-descriptor", "hashbrown 0.16.1", @@ -6457,10 +6466,13 @@ dependencies = [ "libc", "libloading", "log", - "metal", - "naga", + "naga 29.0.4", "ndk-sys", - "objc", + "objc2 0.6.4", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "objc2-metal 0.3.2", + "objc2-quartz-core 0.3.2", "once_cell", "ordered-float", "parking_lot", @@ -6469,27 +6481,41 @@ dependencies = [ "profiling", "range-alloc", "raw-window-handle", + "raw-window-metal", "renderdoc-sys", "smallvec", "thiserror 2.0.18", "wasm-bindgen", + "wayland-sys", "web-sys", + "wgpu-naga-bridge", + "wgpu-types", + "windows 0.62.2", + "windows-core 0.62.2", + "windows-result 0.4.1", +] + +[[package]] +name = "wgpu-naga-bridge" +version = "29.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95226013f547544b223281cd16a4fb549aa9dcb562adbda0faae4c73ffbbc161" +dependencies = [ + "naga 29.0.4", "wgpu-types", - "windows 0.58.0", - "windows-core 0.58.0", ] [[package]] name = "wgpu-types" -version = "27.0.1" +version = "29.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afdcf84c395990db737f2dd91628706cb31e86d72e53482320d368e52b5da5eb" +checksum = "84bf84cd9ca8ca45e2b223a3868f1adf9bfc0c66aeac212e76ee7e40fdadf8f5" dependencies = [ "bitflags 2.13.1", "bytemuck", "js-sys", "log", - "thiserror 2.0.18", + "raw-window-handle", "web-sys", ] @@ -6518,20 +6544,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "window_clipboard" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5654226305eaf2dde8853fb482861d28e5dcecbbd40cb88e8393d94bb80d733" -dependencies = [ - "clipboard-win", - "clipboard_macos", - "clipboard_wayland", - "clipboard_x11", - "raw-window-handle", - "thiserror 2.0.18", -] - [[package]] name = "windows" version = "0.58.0" @@ -6865,9 +6877,8 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winit" -version = "0.30.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6755fa58a9f8350bd1e472d4c3fcc25f824ec358933bba33306d0b63df5978d" +version = "0.30.8" +source = "git+https://github.com/iced-rs/winit.git?rev=05b8ff17a06562f0a10bb46e6eaacbe2a95cb5ed#05b8ff17a06562f0a10bb46e6eaacbe2a95cb5ed" dependencies = [ "ahash 0.8.12", "android-activity", @@ -6875,7 +6886,7 @@ dependencies = [ "bitflags 2.13.1", "block2 0.5.1", "bytemuck", - "calloop 0.13.0", + "calloop", "cfg_aliases", "concurrent-queue", "core-foundation 0.9.4", @@ -6897,8 +6908,8 @@ dependencies = [ "redox_syscall 0.4.1", "rustix 0.38.44", "sctk-adwaita", - "smithay-client-toolkit 0.19.2", - "smol_str", + "smithay-client-toolkit", + "smol_str 0.2.2", "tracing", "unicode-segmentation", "wasm-bindgen", @@ -6959,6 +6970,24 @@ version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" +[[package]] +name = "wl-clipboard-rs" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9651471a32e87d96ef3a127715382b2d11cc7c8bb9822ded8a7cc94072eb0a3" +dependencies = [ + "libc", + "log", + "os_pipe", + "rustix 1.1.4", + "thiserror 2.0.18", + "tree_magic_mini", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-wlr", +] + [[package]] name = "wyz" version = "0.5.1" @@ -7157,12 +7186,6 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" -[[package]] -name = "zune-core" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" - [[package]] name = "zune-core" version = "0.5.1" @@ -7178,22 +7201,13 @@ dependencies = [ "simd-adler32", ] -[[package]] -name = "zune-jpeg" -version = "0.4.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" -dependencies = [ - "zune-core 0.4.12", -] - [[package]] name = "zune-jpeg" version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" dependencies = [ - "zune-core 0.5.1", + "zune-core", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index dc6fa5ed..7fb1059c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,8 +48,11 @@ semver = "1" # Forcing the `static` feature builds liblzma from the bundled source, making # the binary self-contained on every platform. lzma-sys = { version = "0.1", features = ["static"], optional = true } -iced = { version = "0.14", features = ["image", "svg", "advanced", "canvas", "smol", "markdown"] } -iced_aw = { version = "0.14.1", features = ["menu"] } +iced = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aad9e00b9327cb7b8546ed84db39", features = ["image", "svg", "advanced", "canvas", "smol", "markdown"] } +iced_core = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aad9e00b9327cb7b8546ed84db39" } +# iced_aw 0.15 compatibility is pending upstream in PR #432. Pin the exact +# reviewed pair of commits so Iced and its additional widgets cannot drift. +iced_aw = { git = "https://github.com/tsuza/iced_aw.git", rev = "a9301708a4d008dc17f63cbbbb2b2aa06e0e7352", features = ["menu", "context_menu"] } bytemuck = { version = "1.25", features = ["derive"] } glam = { version = "0.33", features = ["bytemuck"] } truck-modeling = "0.6" @@ -101,6 +104,10 @@ windows-sys = { version = "0.61", features = ["Win32_UI_Shell", "Win32_UI_Window [patch.crates-io] # Track standard DWG object relationships used by the scene integration. acadrust = { git = "https://github.com/OpenAEC-Foundation/acadifc.git", rev = "a7b1e07" } +# iced_aw's 0.15 branch declares the split Iced crates by version. Resolve +# those declarations to the same Iced commit as the application dependency. +iced_core = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aad9e00b9327cb7b8546ed84db39" } +iced_widget = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aad9e00b9327cb7b8546ed84db39" } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] # Native enables the plugin host runtime (out-of-process plugins). @@ -132,7 +139,7 @@ js-sys = "0.3" bincode = "1.3" # window.open (external URLs) + Blob/anchor file downloads (Save) + fetch() # (Response) for the lazy per-script web font loader (#141). -web-sys = { version = "0.3", features = [ +web-sys = { version = "=0.3.85", features = [ "Window", "Storage", "StorageManager", @@ -176,7 +183,7 @@ web-sys = { version = "0.3", features = [ # `webgl` enables it. `fira-sans` embeds the default UI font — the web has no # system fonts, so without it all iced text (ribbon labels, command line) is # blank. Features merge with the main `iced` dependency. -iced = { version = "0.14", features = ["webgl", "fira-sans"] } +iced = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aad9e00b9327cb7b8546ed84db39", features = ["webgl", "fira-sans"] } [profile.release] strip = true diff --git a/src/app/commands/blocks.rs b/src/app/commands/blocks.rs index 30b249ab..b567a863 100644 --- a/src/app/commands/blocks.rs +++ b/src/app/commands/blocks.rs @@ -61,7 +61,7 @@ impl OpenCADStudio { Some(text) => { self.command_line .push_info("Copied selected text to clipboard."); - Some(iced::clipboard::write(text)) + Some(iced::clipboard::write(text).discard()) } None => Some(Task::none()), }; diff --git a/src/app/config.rs b/src/app/config.rs index 06a8183e..2d88f6a6 100644 --- a/src/app/config.rs +++ b/src/app/config.rs @@ -60,7 +60,7 @@ impl Default for UiThemeConfig { let theme = iced::Theme::Oxocarbon; Self { name: theme.to_string(), - palette: UiThemePalette::from_iced(theme.palette()), + palette: UiThemePalette::from_iced(theme.seed()), } } } @@ -88,12 +88,12 @@ pub struct UiThemePalette { impl Default for UiThemePalette { fn default() -> Self { - Self::from_iced(iced::Theme::Oxocarbon.palette()) + Self::from_iced(iced::Theme::Oxocarbon.seed()) } } impl UiThemePalette { - pub fn from_iced(palette: iced::theme::Palette) -> Self { + pub fn from_iced(palette: iced::theme::palette::Seed) -> Self { Self { background: color_to_rgb(palette.background), text: color_to_rgb(palette.text), @@ -104,8 +104,8 @@ impl UiThemePalette { } } - pub fn to_iced(self) -> iced::theme::Palette { - iced::theme::Palette { + pub fn to_iced(self) -> iced::theme::palette::Seed { + iced::theme::palette::Seed { background: rgb_to_color(self.background), text: rgb_to_color(self.text), primary: rgb_to_color(self.primary), diff --git a/src/app/update/command.rs b/src/app/update/command.rs index f2052961..9101ca6b 100644 --- a/src/app/update/command.rs +++ b/src/app/update/command.rs @@ -1044,7 +1044,9 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task { Message::MTextPasteClip, ); #[cfg(not(target_arch = "wasm32"))] - return iced::clipboard::read().map(Message::MTextPasteClip); + return iced::clipboard::read_text().map(|result| { + Message::MTextPasteClip(result.ok().map(|text| (*text).clone())) + }); } if self.text_inline.is_some() { // Web: the iced text_input can't reach the async clipboard, diff --git a/src/app/update/mod.rs b/src/app/update/mod.rs index 2cf11196..546d8d3a 100644 --- a/src/app/update/mod.rs +++ b/src/app/update/mod.rs @@ -1149,7 +1149,8 @@ impl OpenCADStudio { }); self.command_line .push_output(&format!("Copied path: {}", full_path.display())); - return iced::clipboard::write(full_path.to_string_lossy().into_owned()); + return iced::clipboard::write(full_path.to_string_lossy().into_owned()) + .discard(); } #[cfg(target_arch = "wasm32")] { @@ -1319,7 +1320,7 @@ impl OpenCADStudio { if text.is_empty() { Task::none() } else { - iced::clipboard::write(text) + iced::clipboard::write(text).discard() } } @@ -1334,7 +1335,7 @@ impl OpenCADStudio { if text.is_empty() { Task::none() } else { - iced::clipboard::write(text) + iced::clipboard::write(text).discard() } } @@ -4143,7 +4144,7 @@ impl OpenCADStudio { Message::SetTheme(theme) => { self.ui_theme.name = theme.to_string(); self.ui_theme.palette = - crate::app::config::UiThemePalette::from_iced(theme.palette()); + crate::app::config::UiThemePalette::from_iced(theme.seed()); self.theme_color_inputs = self.ui_theme.palette.hex_values(); self.active_theme = theme; self.persist_settings_if_changed(); @@ -4225,7 +4226,7 @@ impl OpenCADStudio { crate::app::config::builtin_theme(&self.ui_theme.name) { self.ui_theme.palette = - crate::app::config::UiThemePalette::from_iced(theme.palette()); + crate::app::config::UiThemePalette::from_iced(theme.seed()); self.theme_color_inputs = self.ui_theme.palette.hex_values(); self.active_theme = theme; } else { @@ -4441,7 +4442,7 @@ impl OpenCADStudio { std::env::consts::OS, std::env::consts::ARCH, ); - iced::clipboard::write(info) + iced::clipboard::write(info).discard() } // ── Plugin Manager window ───────────────────────────────────── @@ -4652,7 +4653,8 @@ impl OpenCADStudio { std::env::consts::ARCH, crate::plugin::marketplace::REGISTRY_URL, error, - )); + )) + .discard(); } Task::none() } diff --git a/src/app/view/controls.rs b/src/app/view/controls.rs index 43af88c6..572e2c24 100644 --- a/src/app/view/controls.rs +++ b/src/app/view/controls.rs @@ -34,13 +34,13 @@ pub(super) fn viewport_controls<'a>( | iced::widget::button::Status::Pressed ) .then_some(Background::Color( - theme.extended_palette().danger.weak.color + theme.palette().danger.weak.color )), border: Border { radius: 3.0.into(), ..Default::default() }, - text_color: theme.extended_palette().danger.base.color, + text_color: theme.palette().danger.base.color, ..Default::default() }) }; @@ -56,7 +56,7 @@ pub(super) fn viewport_controls<'a>( .on_press(msg) .padding([4, 6]) .style(move |theme: &Theme, status| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = match (active, status) { (_, iced::widget::button::Status::Hovered) => { Some(palette.background.strong) @@ -79,7 +79,7 @@ pub(super) fn viewport_controls<'a>( }; // Render-mode picker, restyled borderless so the outer chip frames it. - let picker = iced::widget::pick_list( + let picker = crate::ui::pick_list( render_modes, Some(RenderModeChoice(render_mode)), |c| Message::SetRenderMode(c.0), @@ -87,7 +87,7 @@ pub(super) fn viewport_controls<'a>( .text_size(11) .padding([4, 6]) .style(move |theme: &Theme, _| { - let text = theme.extended_palette().background.base.text; + let text = theme.palette().background.base.text; iced::widget::pick_list::Style { background: Background::Color(iced::Color::TRANSPARENT), border: Border { @@ -105,7 +105,7 @@ pub(super) fn viewport_controls<'a>( container(iced::widget::Space::new().width(1.0).height(16.0)).style(|theme: &Theme| { iced::widget::container::Style { background: Some(Background::Color( - theme.extended_palette().background.neutral.color.scale_alpha(0.7) + theme.palette().background.neutral.color.scale_alpha(0.7) )), ..Default::default() } @@ -156,7 +156,7 @@ pub(super) fn viewport_controls<'a>( container(bar) .padding(2) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); iced::widget::container::Style { background: Some(Background::Color( palette.background.weak.color.scale_alpha(0.92) diff --git a/src/app/view/mod.rs b/src/app/view/mod.rs index 5665389c..42f71419 100644 --- a/src/app/view/mod.rs +++ b/src/app/view/mod.rs @@ -109,7 +109,7 @@ impl OpenCADStudio { pub fn view_main(&self) -> Element<'_, Message> { let i = self.active_tab; let tab = &self.tabs[i]; - let theme_text = self.active_theme.extended_palette().background.base.text; + let theme_text = self.active_theme.palette().background.base.text; let viewcube_text_color = [ theme_text.r, theme_text.g, @@ -197,7 +197,7 @@ impl OpenCADStudio { sel.vp_size = (size.width, size.height); } scene.sync_tiles_from_panes(size.width, size.height); - Space::new().width(Fill).height(Fill).into() + Space::new().width(Fill).height(Fill) }) .into(); let shaders = pane_grid::PaneGrid::new( @@ -978,7 +978,7 @@ impl OpenCADStudio { .padding([3, 10]) .width(Fill) .style(move |theme: &Theme, status| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = match (is_sel, status) { (true, _) => Some(palette.primary.strong), (_, iced::widget::button::Status::Hovered) => { @@ -1004,7 +1004,7 @@ impl OpenCADStudio { let menu_panel = container(col) .padding(2) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.weak.color)), border: Border { @@ -1055,7 +1055,7 @@ impl OpenCADStudio { .padding([3, 10]) .width(Fill) .style(move |theme: &Theme, status| { - let palette = theme.extended_palette(); + let palette = theme.palette(); iced::widget::button::Style { background: matches!( status, @@ -1074,10 +1074,10 @@ impl OpenCADStudio { col = col.push(btn); } let panel = container(iced::widget::scrollable(col).height(iced::Length::Shrink)) - .max_height(360.0) + .height(iced::Length::Fit.max(360.0)) .padding(2) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.weak.color)), border: Border { @@ -1154,7 +1154,7 @@ impl OpenCADStudio { trace }; let perf_button_style = |theme: &Theme, status: button::Status| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = if matches!(status, button::Status::Hovered) { palette.background.strong } else { @@ -1195,7 +1195,7 @@ impl OpenCADStudio { .padding([2, 6]); let header = row![ text("PERF").size(12).style(|theme: &Theme| iced::widget::text::Style { - color: Some(theme.extended_palette().success.base.color), + color: Some(theme.palette().success.base.color), }), Space::new().width(iced::Length::Fill), copy_btn, @@ -1210,7 +1210,7 @@ impl OpenCADStudio { column![ header, text(summary).size(11).style(|theme: &Theme| iced::widget::text::Style { - color: Some(theme.extended_palette().success.base.color), + color: Some(theme.palette().success.base.color), }), log, ] @@ -1486,7 +1486,7 @@ impl OpenCADStudio { }) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.base.color + theme.palette().background.base.color )), ..Default::default() }) @@ -1536,15 +1536,20 @@ impl OpenCADStudio { // the native (single main window) and web builds. let base: Element<'_, Message> = match self.modal_content() { Some(content) => { + let modal_options = if cfg!(target_arch = "wasm32") + && matches!(self.active_modal, Some(super::ModalKind::PluginManager)) + { + crate::ui::modal::ModalOptions::NOTICE + } else { + crate::ui::modal::ModalOptions::STANDARD + }; crate::ui::modal::modal( composed, self.modal_title(), - // Content width — outer size minus the frame padding. - self.modal_outer_size().map(|s| s.0 - 20.0).unwrap_or(420.0), content, Message::CloseModal, self.modal_offset, - true, + modal_options, ) } None => composed.into(), @@ -1555,25 +1560,24 @@ impl OpenCADStudio { crate::ui::modal::modal( base, "Select Color", - 420.0, iced::widget::container(crate::ui::color_select::color_grid_window( Message::ColorWindowPick, )) - .width(iced::Length::Fixed(420.0)) - .height(iced::Length::Fixed(470.0)), + .width(iced::Length::Fit.max(420.0)) + .height(iced::Length::Fit.max(470.0)), Message::CloseColorPicker, iced::Vector::ZERO, - false, + crate::ui::modal::ModalOptions::MOVABLE_FIXED, ) } else { base } } - /// Outer pixel size (content + title-bar/padding chrome) of the active - /// modal, used to clamp drag so it cannot be pushed off-screen. Mirrors the - /// `sized(..)` dimensions in [`Self::modal_content`]; keep the two in sync. - /// `None` has no active modal. About (content-sized) uses a safe estimate. + /// Conservative outer pixel bounds for the active modal, used to clamp drag + /// so it cannot be pushed off-screen. Mirrors the maximum dimensions in + /// [`Self::modal_content`]; content-sized dialogs may render smaller. + /// `None` has no active modal. About uses a safe estimate. pub(crate) fn modal_outer_size(&self) -> Option<(f32, f32)> { use super::ModalKind::*; // Title bar (~26) + spacing (6) + frame padding (10·2) → ~52 vertical; @@ -1583,8 +1587,17 @@ impl OpenCADStudio { let (w, h) = match self.active_modal? { About => (440, 360), Shortcuts => (720, 520), - Options => (480, 190), - PluginManager => (940, 600), + Options => (520, 500), + PluginManager => { + #[cfg(target_arch = "wasm32")] + { + (self.web_plugin_notice_width(), 230) + } + #[cfg(not(target_arch = "wasm32"))] + { + (940, 600) + } + } UpdateNotice => (560, 460), Layers => (900, 360), LayerStateManager => (720, 420), @@ -2005,7 +2018,7 @@ pub(super) fn doc_tab_bar<'a>(tabs: &'a [DocumentTab], active_tab: usize) -> Ele .height(Fill) .padding([4, 12]) .style(move |theme: &Theme, status| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let background = match (is_active, status) { (false, button::Status::Hovered) => { Some(Background::Color(palette.background.weak.color)) @@ -2053,7 +2066,7 @@ pub(super) fn doc_tab_bar<'a>(tabs: &'a [DocumentTab], active_tab: usize) -> Ele let tab_container = container(row_inner) .height(iced::Length::Fixed(23.0)) .style(move |theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(if is_active { palette.primary.weak.color @@ -2112,10 +2125,10 @@ pub(super) fn doc_tab_bar<'a>(tabs: &'a [DocumentTab], active_tab: usize) -> Ele container(WrapFlow::new(items).spacing_x(0.0).row_h(30.0)) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.base.color, + theme.palette().background.base.color, )), border: Border { - color: theme.extended_palette().background.neutral.color, + color: theme.palette().background.neutral.color, width: 1.0, radius: 0.0.into(), }, @@ -2146,13 +2159,13 @@ pub(super) fn doc_tab_bar<'a>(tabs: &'a [DocumentTab], active_tab: usize) -> Ele // fn start_muted_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().background.base.text.scale_alpha(0.68)), + color: Some(theme.palette().background.base.text.scale_alpha(0.68)), } } fn start_primary_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().primary.base.color), + color: Some(theme.palette().primary.base.color), } } @@ -2196,7 +2209,7 @@ impl canvas::Program for VBarLabel { frame.fill_text(canvas::Text { content: self.text.clone(), position: iced::Point::ORIGIN, - color: theme.extended_palette().background.base.text.scale_alpha(0.72), + color: theme.palette().background.base.text.scale_alpha(0.72), size: iced::Pixels(13.0), align_x: iced::advanced::text::Alignment::Center, align_y: iced::alignment::Vertical::Center, @@ -2223,10 +2236,10 @@ pub(super) fn collapse_bar<'a>(name: &str, on_press: Message) -> Element<'a, Mes .height(Fill) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.base.color, + theme.palette().background.base.color, )), border: Border { - color: theme.extended_palette().background.neutral.color, + color: theme.palette().background.neutral.color, width: 1.0, radius: 0.0.into(), }, @@ -2264,7 +2277,7 @@ pub(super) fn start_page_view<'a>( .on_press(msg) .padding([10, 22]) .style(move |theme: &Theme, status| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = match status { button::Status::Hovered => palette.background.strong, _ => palette.background.weak, @@ -2354,8 +2367,7 @@ pub(super) fn start_page_view<'a>( .height(iced::Length::Fixed(120.0)) .content_fit(iced::ContentFit::Contain), ) - .width(Fill) - .max_width(300.0), + .width(Fill.max(300.0)), ) .interaction(iced::mouse::Interaction::Pointer) .on_press(Message::OpenUrl("https://open-aec.com/".to_string())), @@ -2469,7 +2481,7 @@ pub(super) fn start_page_view<'a>( .height(iced::Length::Fixed(thumb_h)) .style(|theme: &Theme| container::Style { border: Border { - color: theme.extended_palette().background.neutral.color, + color: theme.palette().background.neutral.color, width: 1.0, radius: 6.0.into(), }, @@ -2499,7 +2511,7 @@ pub(super) fn start_page_view<'a>( .width(Fill) .center_x(Fill) .style(|theme: &Theme| { - let pair = theme.extended_palette().danger.base; + let pair = theme.palette().danger.base; container::Style { background: Some(Background::Color(pair.color)), border: Border { @@ -2529,7 +2541,7 @@ pub(super) fn start_page_view<'a>( .height(Fill) .padding(VIDEO_PANEL_PADDING) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.weak.color)), border: Border { @@ -2582,7 +2594,7 @@ pub(super) fn start_page_view<'a>( .padding([8, 10]) .width(Fill) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color( palette.background.base.color.scale_alpha(0.42), @@ -2615,7 +2627,7 @@ pub(super) fn start_page_view<'a>( .width(Fill) .center_x(Fill) .style(|theme: &Theme| { - let pair = theme.extended_palette().primary.base; + let pair = theme.palette().primary.base; container::Style { background: Some(Background::Color(pair.color)), border: Border { @@ -2648,7 +2660,7 @@ pub(super) fn start_page_view<'a>( .height(Fill) .padding(16) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.weak.color)), border: Border { @@ -2697,7 +2709,7 @@ pub(super) fn start_page_view<'a>( .width(Fill) .center_x(Fill) .style(|theme: &Theme| { - let pair = theme.extended_palette().danger.base; + let pair = theme.palette().danger.base; container::Style { background: Some(Background::Color(pair.color)), border: Border { @@ -2731,7 +2743,7 @@ pub(super) fn start_page_view<'a>( .height(Fill) .padding(20) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.weak.color)), border: Border { @@ -2779,7 +2791,7 @@ pub(super) fn start_page_view<'a>( .on_press(Message::StartSectionSelect(section)) .padding([8, 18]) .style(move |theme: &Theme, status| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = match (is_active, status) { (true, _) => Some(palette.primary.weak), (false, button::Status::Hovered) => { @@ -2851,7 +2863,7 @@ pub(super) fn start_page_view<'a>( container(body) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.base.color + theme.palette().background.base.color )), ..Default::default() }) @@ -2956,7 +2968,7 @@ pub(super) fn recent_files_panel<'a>( .padding([6, 12]) .width(Fill) .style(move |theme: &Theme, status| { - let palette = theme.extended_palette(); + let palette = theme.palette(); button::Style { background: matches!(status, button::Status::Hovered).then_some( Background::Color(palette.background.strong.color) @@ -2979,7 +2991,7 @@ pub(super) fn recent_files_panel<'a>( .on_press(Message::RecentRemove(path_for_remove)) .padding([4, 8]) .style(|theme: &Theme, status| { - let palette = theme.extended_palette(); + let palette = theme.palette(); button::Style { background: matches!(status, button::Status::Hovered) .then_some(Background::Color(palette.danger.weak.color)), @@ -3004,7 +3016,7 @@ pub(super) fn recent_files_panel<'a>( // so an over-max entry snaps to the max. const STEP: usize = 5; let step_style = |theme: &Theme, status: button::Status| { - let palette = theme.extended_palette(); + let palette = theme.palette(); button::Style { background: matches!(status, button::Status::Hovered).then_some( Background::Color(palette.background.strong.color) @@ -3058,7 +3070,7 @@ pub(super) fn recent_files_panel<'a>( .height(Fill) .padding(20) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.weak.color)), border: Border { diff --git a/src/app/view/modal.rs b/src/app/view/modal.rs index 0f1981fe..bb84ee9e 100644 --- a/src/app/view/modal.rs +++ b/src/app/view/modal.rs @@ -1,10 +1,15 @@ use super::super::{Message, OpenCADStudio}; -use iced::widget::{button, column, container, pick_list, row, text, Space}; -use iced::{Background, Element, Fill, Theme}; +use iced::widget::{button, column, container, row, text, Space}; +use iced::{Background, Element, Fit, Theme}; impl OpenCADStudio { - /// Title shown in the active modal's title bar, left of the move/close - /// buttons. Keep in sync with the [`Self::modal_content`] dispatch. + #[cfg(target_arch = "wasm32")] + pub(super) fn web_plugin_notice_width(&self) -> u16 { + (self.win_size.0 - 48.0).clamp(280.0, 460.0) as u16 + } + + /// Title shown in the active modal's title bar. Keep in sync with the + /// [`Self::modal_content`] dispatch. pub(super) fn modal_title(&self) -> &'static str { use super::super::ModalKind as K; match self.active_modal { @@ -43,96 +48,138 @@ impl OpenCADStudio { } /// Build the currently-open modal dialog's content (Plan B), or `None`. - /// Each former pop-up window is constructed here and given a bounded size - /// (About shrinks to its content). Rendered as an overlay by `view_main`. + /// Each former pop-up window is constructed here and given a maximum size. + /// Iced 0.15 measures the content first, so smaller dialogs stop at their + /// intrinsic size while overflowing regions become scrollable at the cap. pub(super) fn modal_content<'s>(&'s self) -> Option> { - // Grow every dialog by the shared corner-resize delta (grow-only, so the - // natural size is the floor). The delta resets to zero whenever a modal - // opens/closes, so each dialog starts at its own size. let ex = self.modal_resize; let sized = |e: Element<'s, Message>, w: u16, h: u16| -> Element<'s, Message> { - iced::widget::container(e) - .width(iced::Length::Fixed(w as f32 + ex.x)) - .height(iced::Length::Fixed(h as f32 + ex.y)) + container(e) + .width(Fit.max(w as f32 + ex.x)) + .height(Fit.max(h as f32 + ex.y)) .into() }; Some(match self.active_modal? { - super::super::ModalKind::About => crate::ui::window::about::view_window(), + super::super::ModalKind::About => { + sized(crate::ui::window::about::view_window(), 440, 360) + } super::super::ModalKind::Shortcuts => { - sized(crate::ui::window::shortcuts::view_window(&self.shortcut_overrides), 720, 520) + sized_flow( + ex, + 720, + 520, + |flow| { + crate::ui::window::shortcuts::view_window( + &self.shortcut_overrides, + flow, + ) + }, + ) } super::super::ModalKind::Aliases => { - sized(crate::ui::window::alias_editor::view_window(&self.alias_editor_rows), 480, 520) + sized_flow( + ex, + 480, + 520, + |flow| { + crate::ui::window::alias_editor::view_window( + &self.alias_editor_rows, + flow, + ) + }, + ) } - super::super::ModalKind::Options => sized( - crate::ui::window::options::view_window( - &self.default_save_format, - &self.ui_theme, - &self.theme_color_inputs, - ), + super::super::ModalKind::Options => sized_flow( + ex, 520, 500, + |flow| { + crate::ui::window::options::view_window( + &self.default_save_format, + &self.ui_theme, + &self.theme_color_inputs, + flow, + ) + }, ), super::super::ModalKind::PluginManager => { #[cfg(not(target_arch = "wasm32"))] { - sized( - crate::ui::window::plugin_manager::view_window( - &self.disabled_plugins, - &self.external_plugins, - &self.loaded_plugin_ids, - crate::ui::window::plugin_manager::MarketView { - registry: &self.plugin_registry, - registry_loading: self.plugin_registry_loading, - registry_error: self.plugin_registry_error.as_deref(), - registry_error_details_open: self - .plugin_registry_error_details_open, - input: &self.plugin_repo_input, - search: &self.plugin_search_input, - repos: &self.plugin_repos, - release_tags: &self.repo_release_tags, - selected_tag: &self.repo_selected_tag, - selected_repo: self.selected_plugin_repo.as_deref(), - readmes: &self.plugin_readmes, - readme_loading: &self.plugin_readme_loading, - status: &self.marketplace_status, - }, - &self.active_theme, - ), + sized_flow( + ex, 940, 600, + |flow| { + crate::ui::window::plugin_manager::view_window( + &self.disabled_plugins, + &self.external_plugins, + &self.loaded_plugin_ids, + crate::ui::window::plugin_manager::MarketView { + registry: &self.plugin_registry, + registry_loading: self.plugin_registry_loading, + registry_error: self.plugin_registry_error.as_deref(), + registry_error_details_open: self + .plugin_registry_error_details_open, + input: &self.plugin_repo_input, + search: &self.plugin_search_input, + repos: &self.plugin_repos, + release_tags: &self.repo_release_tags, + selected_tag: &self.repo_selected_tag, + selected_repo: self.selected_plugin_repo.as_deref(), + readmes: &self.plugin_readmes, + readme_loading: &self.plugin_readme_loading, + status: &self.marketplace_status, + }, + &self.active_theme, + flow, + ) + }, ) } #[cfg(target_arch = "wasm32")] { sized( crate::ui::window::plugin_manager::view_web_notice(), - 520, - 260, + self.web_plugin_notice_width(), + 230, ) } } super::super::ModalKind::UpdateNotice => { let latest = self.update_notice_version.as_deref().unwrap_or("?"); let body = self.update_notice_body.as_deref().unwrap_or(""); - sized(crate::ui::window::update_notice::view_window(latest, body), 560, 460) + sized_flow( + ex, + 560, + 460, + |flow| crate::ui::window::update_notice::view_window(latest, body, flow), + ) } super::super::ModalKind::Layers => { let tab = &self.tabs[self.active_tab]; - sized(tab.layers.view_window(self.layer_name_col_w), 900, 360) + sized_flow( + ex, + 900, + 360, + |flow| tab.layers.view_window(self.layer_name_col_w, flow), + ) } super::super::ModalKind::LayerStateManager => { let states = self.tabs[self.active_tab].scene.document.layer_states(); - sized( - crate::ui::window::layer_state_manager::view_window( - states, - self.layer_state_selected.as_deref(), - &self.layer_state_name_buf, - &self.layer_state_description_buf, - &self.layer_state_filter, - ), + sized_flow( + ex, 720, 420, + |flow| { + crate::ui::window::layer_state_manager::view_window( + states.clone(), + self.layer_state_selected.as_deref(), + &self.layer_state_name_buf, + &self.layer_state_description_buf, + &self.layer_state_filter, + flow, + ) + }, ) } super::super::ModalKind::LayerStateEditor => { @@ -155,15 +202,19 @@ impl OpenCADStudio { } } linetypes.sort_by_key(|name| name.to_lowercase()); - sized( - crate::ui::window::layer_state_manager::view_editor( - state, - &self.layer_state_edit_filter, - self.layer_state_edit_color_open, - linetypes, - ), + sized_flow( + ex, 1180, 560, + |flow| { + crate::ui::window::layer_state_manager::view_editor( + state, + &self.layer_state_edit_filter, + self.layer_state_edit_color_open, + linetypes.clone(), + flow, + ) + }, ) } else { sized( @@ -176,21 +227,30 @@ impl OpenCADStudio { } } super::super::ModalKind::Plot => { - sized(crate::ui::window::plot::view_window(&self.plot_dialog), 760, 540) + sized_flow( + ex, + 760, + 540, + |flow| crate::ui::window::plot::view_window(&self.plot_dialog, flow), + ) } super::super::ModalKind::LayoutManager => { let i = self.active_tab; let layouts = self.tabs[i].scene.layout_names(); let current = self.tabs[i].scene.current_layout.clone(); - sized( - crate::ui::window::layout_manager::view_window( - layouts, - &self.layout_manager_selected, - &self.layout_manager_rename_buf, - current, - ), + sized_flow( + ex, 640, 320, + |flow| { + crate::ui::window::layout_manager::view_window( + layouts.clone(), + &self.layout_manager_selected, + &self.layout_manager_rename_buf, + current.clone(), + flow, + ) + }, ) } super::super::ModalKind::ScaleManager => { @@ -206,21 +266,25 @@ impl OpenCADStudio { .map(|(p, d)| format!("{p}:{d}")) .unwrap_or_default(); (name, ratio) - }) + }) .collect(); let current = tab.scene.document.header.current_annotation_scale.clone(); - sized( - crate::ui::style::scale_manager::view_window( - &scales, - &self.scale_manager_selected, - ¤t, - self.scale_rename.as_deref(), - &self.scale_rename_buf, - &self.scale_manager_paper_buf, - &self.scale_manager_drawing_buf, - ), + sized_flow( + ex, 520, 360, + |flow| { + crate::ui::style::scale_manager::view_window( + &scales, + &self.scale_manager_selected, + ¤t, + self.scale_rename.as_deref(), + &self.scale_rename_buf, + &self.scale_manager_paper_buf, + &self.scale_manager_drawing_buf, + flow, + ) + }, ) } super::super::ModalKind::AnnoObjectScale => { @@ -263,22 +327,33 @@ impl OpenCADStudio { (name, ratio, is_member) }) .collect(); - sized( - crate::ui::style::anno_object_scale::view_window(&label, &scales), + sized_flow( + ex, 360, 420, + |flow| { + crate::ui::style::anno_object_scale::view_window( + &label, + &scales, + flow, + ) + }, ) } - super::super::ModalKind::Plotstyle => sized( - crate::ui::style::plotstyle::view_window( - self.active_plot_style.as_ref(), - self.plotstyle_panel_aci, - &self.ps_color_buf, - &self.ps_lineweight_buf, - &self.ps_screening_buf, - ), + super::super::ModalKind::Plotstyle => sized_flow( + ex, 780, 540, + |flow| { + crate::ui::style::plotstyle::view_window( + self.active_plot_style.as_ref(), + self.plotstyle_panel_aci, + &self.ps_color_buf, + &self.ps_lineweight_buf, + &self.ps_screening_buf, + flow, + ) + }, ), super::super::ModalKind::TextStyle => { let tab = &self.tabs[self.active_tab]; @@ -296,27 +371,31 @@ impl OpenCADStudio { .get(&self.textstyle_selected) .map(|s| (s.flags.backward, s.flags.upside_down, s.annotative)) .unwrap_or((false, false, false)); - sized( - crate::ui::style::textstyle::view_window(crate::ui::style::textstyle::TextStyleView { - styles, - selected: &self.textstyle_selected, - current: &tab.scene.document.header.current_text_style_name, - font_buf: &self.textstyle_font, - width_buf: &self.textstyle_width, - oblique_buf: &self.textstyle_oblique, - height_buf: &self.textstyle_height, - bigfont_buf: &self.textstyle_bigfont, - ttf_buf: &self.textstyle_ttf, - backward, - upside_down, - annotative, - rename_active: self.style_rename.as_deref(), - rename_buf: &self.style_rename_buf, - }), - // Wider than the old 620 window: the TTF system-font panel - // (Plan B / web fonts) added a column. + sized_flow( + ex, 860, 480, + |flow| { + crate::ui::style::textstyle::view_window( + crate::ui::style::textstyle::TextStyleView { + styles: styles.clone(), + selected: &self.textstyle_selected, + current: &tab.scene.document.header.current_text_style_name, + font_buf: &self.textstyle_font, + width_buf: &self.textstyle_width, + oblique_buf: &self.textstyle_oblique, + height_buf: &self.textstyle_height, + bigfont_buf: &self.textstyle_bigfont, + ttf_buf: &self.textstyle_ttf, + backward, + upside_down, + annotative, + rename_active: self.style_rename.as_deref(), + rename_buf: &self.style_rename_buf, + }, + flow, + ) + }, ) } super::super::ModalKind::MlStyle => { @@ -336,17 +415,21 @@ impl OpenCADStudio { ObjectType::MLineStyle(s) if s.name == self.mlstyle_selected => Some(s), _ => None, }); - sized( - crate::ui::style::mlstyle::view_window( - styles, - &self.mlstyle_selected, - selected_style, - tab.scene.document.header.multiline_style.clone(), - self.style_rename.as_deref(), - &self.style_rename_buf, - ), + sized_flow( + ex, 620, 420, + |flow| { + crate::ui::style::mlstyle::view_window( + styles.clone(), + &self.mlstyle_selected, + selected_style, + tab.scene.document.header.multiline_style.clone(), + self.style_rename.as_deref(), + &self.style_rename_buf, + flow, + ) + }, ) } super::super::ModalKind::TableStyle => { @@ -366,31 +449,35 @@ impl OpenCADStudio { ObjectType::TableStyle(s) if s.name == self.tablestyle_selected => Some(s), _ => None, }); - sized( - crate::ui::style::tablestyle::view_window( - styles, - &self.tablestyle_selected, - &self.ribbon.active_table_style, - selected_style, - &self.ts_hmargin, - &self.ts_vmargin, - &self.ts_description, - &self.ts_cell_textstyle, - &self.ts_cell_height, - &self.ts_cell_textcolor, - &self.ts_cell_fillcolor, - &self.ts_cell_datatype, - &self.ts_cell_unittype, - &self.ts_cell_format, - &self.ts_border_lw, - &self.ts_border_color, - &self.ts_border_spacing, - self.style_rename.as_deref(), - &self.style_rename_buf, - self.ts_color_open, - ), + sized_flow( + ex, 620, 420, + |flow| { + crate::ui::style::tablestyle::view_window( + styles.clone(), + &self.tablestyle_selected, + &self.ribbon.active_table_style, + selected_style, + &self.ts_hmargin, + &self.ts_vmargin, + &self.ts_description, + &self.ts_cell_textstyle, + &self.ts_cell_height, + &self.ts_cell_textcolor, + &self.ts_cell_fillcolor, + &self.ts_cell_datatype, + &self.ts_cell_unittype, + &self.ts_cell_format, + &self.ts_border_lw, + &self.ts_border_color, + &self.ts_border_spacing, + self.style_rename.as_deref(), + &self.style_rename_buf, + self.ts_color_open, + flow, + ) + }, ) } super::super::ModalKind::MLeaderStyle => { @@ -462,44 +549,50 @@ impl OpenCADStudio { ), None => Default::default(), }; - sized( - crate::ui::style::mleaderstyle::view_window(crate::ui::style::mleaderstyle::MLeaderStyleView { - styles, - selected: &self.mleaderstyle_selected, - style: selected_style, - current: tab.active_mleader_style.clone(), - landing_distance: &self.mls_landing_distance, - landing_gap: &self.mls_landing_gap, - arrowhead_size: &self.mls_arrowhead_size, - text_height: &self.mls_text_height, - scale_factor: &self.mls_scale_factor, - break_gap: &self.mls_break_gap, - first_seg_angle: &self.mls_first_seg_angle, - second_seg_angle: &self.mls_second_seg_angle, - max_points: &self.mls_max_points, - default_text: &self.mls_default_text, - line_color: &self.mls_line_color, - text_color: &self.mls_text_color, - description: &self.mls_description, - align_space: &self.mls_align_space, - block_color: &self.mls_block_color, - block_rotation: &self.mls_block_rotation, - block_scale_x: &self.mls_block_scale_x, - block_scale_y: &self.mls_block_scale_y, - block_scale_z: &self.mls_block_scale_z, - block_opts, - lt_opts, - textstyle_opts, - line_type_name, - arrowhead_name, - text_style_name, - block_content_name, - rename_active: self.style_rename.as_deref(), - rename_buf: &self.style_rename_buf, - color_open: self.mls_color_open, - }), + sized_flow( + ex, 560, 560, + |flow| { + crate::ui::style::mleaderstyle::view_window( + crate::ui::style::mleaderstyle::MLeaderStyleView { + styles: styles.clone(), + selected: &self.mleaderstyle_selected, + style: selected_style, + current: tab.active_mleader_style.clone(), + landing_distance: &self.mls_landing_distance, + landing_gap: &self.mls_landing_gap, + arrowhead_size: &self.mls_arrowhead_size, + text_height: &self.mls_text_height, + scale_factor: &self.mls_scale_factor, + break_gap: &self.mls_break_gap, + first_seg_angle: &self.mls_first_seg_angle, + second_seg_angle: &self.mls_second_seg_angle, + max_points: &self.mls_max_points, + default_text: &self.mls_default_text, + line_color: &self.mls_line_color, + text_color: &self.mls_text_color, + description: &self.mls_description, + align_space: &self.mls_align_space, + block_color: &self.mls_block_color, + block_rotation: &self.mls_block_rotation, + block_scale_x: &self.mls_block_scale_x, + block_scale_y: &self.mls_block_scale_y, + block_scale_z: &self.mls_block_scale_z, + block_opts: block_opts.clone(), + lt_opts: lt_opts.clone(), + textstyle_opts: textstyle_opts.clone(), + line_type_name: line_type_name.clone(), + arrowhead_name: arrowhead_name.clone(), + text_style_name: text_style_name.clone(), + block_content_name: block_content_name.clone(), + rename_active: self.style_rename.as_deref(), + rename_buf: &self.style_rename_buf, + color_open: self.mls_color_open, + }, + flow, + ) + }, ) } super::super::ModalKind::DimStyle => { @@ -562,8 +655,9 @@ impl OpenCADStudio { ), None => Default::default(), }; - sized(crate::ui::style::dimstyle::view_window( - styles, + sized_flow(ex, 720, 560, |flow| { + crate::ui::style::dimstyle::view_window( + styles.clone(), &self.dimstyle_selected, &self.tabs[self.active_tab] .scene @@ -642,20 +736,22 @@ impl OpenCADStudio { dimalttz: &self.ds_dimalttz, dimtolj: &self.ds_dimtolj, dimtzin: &self.ds_dimtzin, - dimblk_name, - dimblk1_name, - dimblk2_name, - dimldrblk_name, - dimltex_name, - dimltex1_name, - dimltex2_name, - block_opts, - lt_opts, + dimblk_name: dimblk_name.clone(), + dimblk1_name: dimblk1_name.clone(), + dimblk2_name: dimblk2_name.clone(), + dimldrblk_name: dimldrblk_name.clone(), + dimltex_name: dimltex_name.clone(), + dimltex1_name: dimltex1_name.clone(), + dimltex2_name: dimltex2_name.clone(), + block_opts: block_opts.clone(), + lt_opts: lt_opts.clone(), color_open: self.ds_color_open.clone(), }, self.style_rename.as_deref(), &self.style_rename_buf, - ), 720, 560) + flow, + ) + }) } super::super::ModalKind::AssocPrompt => sized(default_assoc_dialog_window(), 440, 210), super::super::ModalKind::AecDropWarning => { @@ -738,14 +834,22 @@ impl OpenCADStudio { }; sized(unsaved_changes_dialog_window(&tab_name), 420, 160) } - super::super::ModalKind::PointStyle => sized( - crate::ui::style::point_style::view_window( - self.tabs[self.active_tab].scene.document.header.point_display_mode, - self.point_size_relative, - &self.point_size_buf, - ), + super::super::ModalKind::PointStyle => sized_flow( + ex, 360, 470, + |flow| { + crate::ui::style::point_style::view_window( + self.tabs[self.active_tab] + .scene + .document + .header + .point_display_mode, + self.point_size_relative, + &self.point_size_buf, + flow, + ) + }, ), super::super::ModalKind::AttributeEditor => { let doc = &self.tabs[self.active_tab].scene.document; @@ -763,18 +867,22 @@ impl OpenCADStudio { .map(|s| s.name.trim().to_string()) .filter(|n| !n.is_empty()) .collect(); - sized( - crate::ui::window::attribute_editor::view_window( - &self.attr_editor_block, - &self.attr_editor_rows, - self.attr_editor_selected, - self.attr_editor_tab, - layers, - linetypes, - styles, - ), + sized_flow( + ex, 640, 500, + |flow| { + crate::ui::window::attribute_editor::view_window( + &self.attr_editor_block, + &self.attr_editor_rows, + self.attr_editor_selected, + self.attr_editor_tab, + layers.clone(), + linetypes.clone(), + styles.clone(), + flow, + ) + }, ) } super::super::ModalKind::SaveDialog => { @@ -794,6 +902,20 @@ impl OpenCADStudio { } } +fn sized_flow<'a>( + extra: iced::Vector, + max_width: u16, + max_height: u16, + mut build: impl FnMut(crate::ui::modal::ModalSizing) -> Element<'a, Message>, +) -> Element<'a, Message> { + crate::ui::modal::intrinsic( + build(crate::ui::modal::ModalSizing::INTRINSIC), + build(crate::ui::modal::ModalSizing::FILL), + iced::Size::new(max_width as f32, max_height as f32), + extra, + ) +} + fn dialog_button( label: &'static str, message: Message, @@ -807,7 +929,7 @@ fn dialog_button( } fn dialog_body_style(theme: &Theme) -> container::Style { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.base.color)), text_color: Some(palette.background.base.text), @@ -817,7 +939,7 @@ fn dialog_body_style(theme: &Theme) -> container::Style { fn dialog_muted_text_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().background.base.text.scale_alpha(0.68)), + color: Some(theme.palette().background.base.text.scale_alpha(0.68)), } } @@ -846,7 +968,7 @@ fn save_as_dialog_window<'a>(filename: &'a str, format: &'a str) -> Element<'a, .on_input(Message::SaveDialogFilenameChanged) .size(13) .padding([5, 8]) - .width(Fill), + .width(Fit), ] .align_y(iced::Alignment::Center) .spacing(6) @@ -860,10 +982,10 @@ fn save_as_dialog_window<'a>(filename: &'a str, format: &'a str) -> Element<'a, items.push( row![ label("Format:").width(70), - pick_list(crate::io::SAVE_FORMAT_OPTIONS, sel_fmt, |s: &str| { + crate::ui::pick_list(crate::io::SAVE_FORMAT_OPTIONS, sel_fmt, |s: &str| { Message::SaveDialogFormatChanged(s.to_string()) }) - .width(Fill), + .width(Fit), ] .align_y(iced::Alignment::Center) .spacing(6) @@ -872,7 +994,7 @@ fn save_as_dialog_window<'a>(filename: &'a str, format: &'a str) -> Element<'a, items.push(Space::new().height(16).into()); items.push( row![ - Space::new().width(Fill), + Space::new().width(Fit), dialog_button("Save as...", Message::SaveDialogConfirm, button::primary), Space::new().width(8), dialog_button("Cancel", Message::SaveDialogCancel, button::secondary), @@ -885,8 +1007,8 @@ fn save_as_dialog_window<'a>(filename: &'a str, format: &'a str) -> Element<'a, container(body) .style(dialog_body_style) .padding([14, 16]) - .width(Fill) - .height(Fill) + .width(Fit) + .height(Fit) .into() } @@ -908,7 +1030,7 @@ fn unsaved_changes_dialog_window(name: &str) -> Element<'static, Message> { .spacing(0), ) .style(dialog_body_style) - .center(Fill) + .center(Fit) .padding([24, 28]) .into() } @@ -932,11 +1054,11 @@ fn file_in_use_dialog_window(path: &str, error: &str) -> Element<'static, Messag Close it there and retry, or save this drawing under a different name." ) .size(13) - .width(Fill), + .width(Fit), Space::new().height(12), - text(path_line).size(11).style(dialog_muted_text_style).width(Fill), + text(path_line).size(11).style(dialog_muted_text_style).width(Fit), Space::new().height(4), - text(details).size(11).style(dialog_muted_text_style).width(Fill), + text(details).size(11).style(dialog_muted_text_style).width(Fit), Space::new().height(18), row![ dialog_button( @@ -962,8 +1084,8 @@ fn file_in_use_dialog_window(path: &str, error: &str) -> Element<'static, Messag ) .style(dialog_body_style) .padding([18, 20]) - .width(Fill) - .height(Fill) + .width(Fit) + .height(Fit) .into() } @@ -985,9 +1107,9 @@ fn external_change_dialog_window(path: &str) -> Element<'static, Message> { save your local work elsewhere, or explicitly overwrite it." ) .size(13) - .width(Fill), + .width(Fit), Space::new().height(12), - text(path_line).size(11).style(dialog_muted_text_style).width(Fill), + text(path_line).size(11).style(dialog_muted_text_style).width(Fit), Space::new().height(18), row![ dialog_button( @@ -1019,8 +1141,8 @@ fn external_change_dialog_window(path: &str) -> Element<'static, Message> { ) .style(dialog_body_style) .padding([18, 20]) - .width(Fill) - .height(Fill) + .width(Fit) + .height(Fit) .into() } @@ -1054,7 +1176,7 @@ fn aec_drop_dialog_window(count: usize, target: &str, src_version: &str) -> Elem .spacing(0), ) .style(dialog_body_style) - .center(Fill) + .center(Fit) .padding([24, 28]) .into() } @@ -1093,7 +1215,7 @@ fn layer_delete_warning_window(names: &[String], count: usize) -> Element<'stati .spacing(0), ) .style(dialog_body_style) - .center(Fill) + .center(Fit) .padding([24, 28]) .into() } @@ -1113,7 +1235,7 @@ fn default_assoc_dialog_window() -> Element<'static, Message> { .style(dialog_muted_text_style), iced::widget::Space::new().height(22), row![ - iced::widget::Space::new().width(Fill), + iced::widget::Space::new().width(Fit), dialog_button("Not now", Message::AssocPromptNo, button::secondary), iced::widget::Space::new().width(8), dialog_button( @@ -1127,7 +1249,7 @@ fn default_assoc_dialog_window() -> Element<'static, Message> { .spacing(0), ) .style(dialog_body_style) - .center(Fill) + .center(Fit) .padding([24, 28]) .into() } diff --git a/src/app/view/overlay.rs b/src/app/view/overlay.rs index a9ba0d98..2f1b977f 100644 --- a/src/app/view/overlay.rs +++ b/src/app/view/overlay.rs @@ -1,6 +1,6 @@ use super::super::Message; use iced::widget::{ - button, column, container, mouse_area, pick_list, row, stack, text, text_input, + button, column, container, mouse_area, row, stack, text, text_input, Space, }; use iced::{Background, Border, Color, Element, Fill, Theme}; @@ -36,7 +36,7 @@ pub(super) fn text_inline_overlay( let panel = container(field) .style(move |theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.weak.color)), border: Border { @@ -211,7 +211,7 @@ impl iced::widget::canvas::Program for MTextPreview { ); frame.fill( &rect, - theme.extended_palette().primary.base.color.scale_alpha(0.45), + theme.palette().primary.base.color.scale_alpha(0.45), ); } } @@ -247,7 +247,7 @@ impl iced::widget::canvas::Program for MTextPreview { frame.stroke( &path, Stroke::default() - .with_color(theme.extended_palette().warning.base.color) + .with_color(theme.palette().warning.base.color) .with_width(1.5), ); } else if collapsed { @@ -271,7 +271,7 @@ impl iced::widget::canvas::Program for MTextPreview { frame.stroke( &path, Stroke::default() - .with_color(theme.extended_palette().warning.base.color) + .with_color(theme.palette().warning.base.color) .with_width(1.5), ); } @@ -318,11 +318,14 @@ pub(super) fn mtext_editor_overlay<'a>( modal_offset: iced::Vector, modal_resize: iced::Vector, ) -> Element<'a, Message> { + let sizing = crate::ui::modal::ModalSizing::from_resize(modal_resize); + let width = sizing.width; + let height = sizing.height; use super::super::mtext_editor::{JustifyChoice, MTextFmt, ParaAlign}; use iced::widget::canvas; let btn_style = |theme: &Theme, status: button::Status| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = match status { button::Status::Hovered | button::Status::Pressed => palette.background.strong, _ => palette.background.weak, @@ -366,7 +369,7 @@ pub(super) fn mtext_editor_overlay<'a>( } else { styles }; - let style_pl = pick_list(style_opts, Some(ed.style.clone()), Message::MTextStyle) + let style_pl = crate::ui::pick_list(style_opts, Some(ed.style.clone()), Message::MTextStyle) .text_size(11) .width(iced::Length::Fixed(96.0)); let font_sel = if ed.font.trim().is_empty() { @@ -374,7 +377,7 @@ pub(super) fn mtext_editor_overlay<'a>( } else { ed.font.clone() }; - let font_pl = pick_list( + let font_pl = crate::ui::pick_list( MTEXT_FONTS .iter() .map(|s| s.to_string()) @@ -432,14 +435,15 @@ pub(super) fn mtext_editor_overlay<'a>( include_bytes!("../../../assets/icons/mt_lower.svg"), Message::MTextFmt(MTextFmt::Lowercase) ), - iced::widget::Space::new().width(Fill), + iced::widget::Space::new().width(width), color_pl, ] .spacing(4) - .align_y(iced::Alignment::Center); + .align_y(iced::Alignment::Center) + .width(width); // ── Row 2: oblique / width / char-spacing · align · line spacing · OK ─ - let justify = pick_list( + let justify = crate::ui::pick_list( JustifyChoice::ALL, Some(JustifyChoice(ed.attachment)), |c| Message::MTextJustify(c.0), @@ -487,7 +491,8 @@ pub(super) fn mtext_editor_overlay<'a>( .style(btn_style), ] .spacing(4) - .align_y(iced::Alignment::Center); + .align_y(iced::Alignment::Center) + .width(width); // ── Body: the rendered preview (the editor is preview-only). It fills the // space left by the toolbars, so the resizable modal's extra height flows @@ -587,11 +592,11 @@ pub(super) fn mtext_editor_overlay<'a>( vertical: Scrollbar::default(), horizontal: Scrollbar::default(), }) - .width(Fill) - .height(Fill), + .width(width) + .height(height), ) .style(move |theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.base.color)), border: Border { @@ -603,8 +608,8 @@ pub(super) fn mtext_editor_overlay<'a>( } }) .padding(2) - .width(Fill) - .height(Fill) + .width(width) + .height(height) .into() }; @@ -613,32 +618,31 @@ pub(super) fn mtext_editor_overlay<'a>( // buffer, so there is no separate Cancel. let action_bar = container( row![ - iced::widget::Space::new().width(Fill), + iced::widget::Space::new().width(width), crate::ui::style::style_manager::tb_button("Apply", Message::MTextApply, true), ] .align_y(iced::Alignment::Center), ) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.weak.color + theme.palette().background.weak.color )), ..Default::default() }) - .width(Fill) + .width(width) .padding([5, 8]); // The shared modal frame supplies the panel background, drag title bar, - // ✕ (which also cancels) and the resize grip. The content is a Fill column - // wrapped here in a fixed box (natural 660×480, grown by the shared resize - // delta) so the modal frame shrinks to it and the corner grip can drag it. + // ✕ (which also cancels) and the resize grip. Iced 0.15 measures this + // content first and clamps it to the user-growable maximum. let content = container( column![action_bar, row1, row2, body] .spacing(6) - .width(Fill) - .height(Fill), + .width(width) + .height(height), ) - .width(iced::Length::Fixed(660.0 + modal_resize.x)) - .height(iced::Length::Fixed(480.0 + modal_resize.y)); + .width(iced::Length::Fit.max(660.0 + modal_resize.x)) + .height(iced::Length::Fit.max(480.0 + modal_resize.y)); let _ = canvas_size; // positioned & sized by the modal frame now // `modal` sizes its stack from the base layer, so the base must fill the @@ -647,11 +651,10 @@ pub(super) fn mtext_editor_overlay<'a>( crate::ui::modal::modal( iced::widget::Space::new().width(Fill).height(Fill), "Text Editor", - 660.0 + modal_resize.x, content, Message::MTextCancel, modal_offset, - true, + crate::ui::modal::ModalOptions::STANDARD, ) } @@ -678,7 +681,7 @@ pub(super) fn viewport_context_menu_overlay( container(iced::widget::Space::new().width(Fill).height(1)) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.weak.color, + theme.palette().background.weak.color, )), ..Default::default() }) @@ -838,10 +841,10 @@ pub(super) fn snap_override_overlay(pos: iced::Point) -> Element<'static, Messag button::Status::Hovered | button::Status::Pressed ) .then_some(Background::Color( - theme.extended_palette().primary.weak.color + theme.palette().primary.weak.color )), border: Border::default(), - text_color: theme.extended_palette().background.base.text, + text_color: theme.palette().background.base.text, ..Default::default() }) .padding(2); @@ -849,7 +852,7 @@ pub(super) fn snap_override_overlay(pos: iced::Point) -> Element<'static, Messag btn, container(text(label).size(11)) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.strong.color)), border: Border { @@ -878,7 +881,7 @@ pub(super) fn snap_override_overlay(pos: iced::Point) -> Element<'static, Messag let panel = container(grid) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.weak.color)), border: Border { @@ -919,7 +922,7 @@ pub(super) fn qselect_overlay<'a>( types: &[String], properties: &[(String, String)], ) -> Element<'a, Message> { - use iced::widget::{checkbox, pick_list}; + use iced::widget::{checkbox}; let mut type_options: Vec = vec![QSELECT_ANY_TYPE.to_string()]; type_options.extend(types.iter().cloned()); @@ -971,7 +974,7 @@ pub(super) fn qselect_overlay<'a>( button(text(lbl).size(12)) .on_press(msg) .style(move |theme: &Theme, st| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = match ( primary, matches!(st, button::Status::Hovered | button::Status::Pressed), @@ -1005,7 +1008,7 @@ pub(super) fn qselect_overlay<'a>( Space::new().height(10), row![ label("Object type:"), - pick_list(type_options, Some(type_sel), |s: String| { + crate::ui::pick_list(type_options, Some(type_sel), |s: String| { if s == QSELECT_ANY_TYPE { Message::QSelectSetType(None) } else { @@ -1019,7 +1022,7 @@ pub(super) fn qselect_overlay<'a>( Space::new().height(6), row![ label("Property:"), - pick_list( + crate::ui::pick_list( prop_options, Some(prop_sel), |p: crate::app::QSelectPropertyChoice| { @@ -1037,7 +1040,7 @@ pub(super) fn qselect_overlay<'a>( Space::new().height(6), row![ label("Operator:"), - pick_list( + crate::ui::pick_list( op_options, Some(state.operator), Message::QSelectSetOperator @@ -1074,7 +1077,7 @@ pub(super) fn qselect_overlay<'a>( .padding(16) .width(iced::Length::Fixed(400.0)) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.weak.color)), border: Border { diff --git a/src/app/view/viewcube.rs b/src/app/view/viewcube.rs index 25d0640a..fd37d96d 100644 --- a/src/app/view/viewcube.rs +++ b/src/app/view/viewcube.rs @@ -1,7 +1,7 @@ use super::super::Message; use crate::scene::{VIEWCUBE_PX, VIEWCUBE_REGION_PX}; use iced::widget::{ - button, container, mouse_area, pick_list, stack, Space, + button, container, mouse_area, stack, Space, }; use iced::{Background, Border, Element, Theme}; @@ -36,13 +36,13 @@ fn vc_btn<'a>(content: Element<'a, Message>, size: f32, msg: Message) -> Element iced::widget::button::Status::Hovered | iced::widget::button::Status::Pressed ) .then_some(Background::Color( - theme.extended_palette().primary.weak.color + theme.palette().primary.weak.color )), border: Border { radius: 3.0.into(), ..Default::default() }, - text_color: theme.extended_palette().background.base.text, + text_color: theme.palette().background.base.text, ..Default::default() }) .into() @@ -155,14 +155,14 @@ pub(super) fn viewcube_ucs_picker<'a>(current: String, names: Vec) -> El } else { current }; - pick_list(options, Some(selected), Message::SetViewcubeUcs) + crate::ui::pick_list(options, Some(selected), Message::SetViewcubeUcs) .text_size(11) .padding([2, 6]) // Fixed width so the caller can centre it under the cube centre with a // simple half-width offset (content-sized width would drift off-centre). .width(iced::Length::Fixed(UCS_PICKER_W)) .style(move |theme: &Theme, _| { - let palette = theme.extended_palette(); + let palette = theme.palette(); iced::widget::pick_list::Style { background: Background::Color(palette.background.weak.color), border: Border { diff --git a/src/scene/pipeline/device_capabilities.rs b/src/scene/pipeline/device_capabilities.rs index 3f271582..dfe483c9 100644 --- a/src/scene/pipeline/device_capabilities.rs +++ b/src/scene/pipeline/device_capabilities.rs @@ -31,7 +31,7 @@ impl DeviceCapabilities { fn from_limits(limits: &wgpu::Limits) -> Self { Self { max_storage_buffers_per_shader_stage: limits.max_storage_buffers_per_shader_stage, - max_inter_stage_shader_components: limits.max_inter_stage_shader_components, + max_inter_stage_shader_components: limits.max_inter_stage_shader_variables, max_vertex_attributes: limits.max_vertex_attributes, } } diff --git a/src/scene/pipeline/hatch_gpu/mod.rs b/src/scene/pipeline/hatch_gpu/mod.rs index 44b44695..822026bc 100644 --- a/src/scene/pipeline/hatch_gpu/mod.rs +++ b/src/scene/pipeline/hatch_gpu/mod.rs @@ -68,8 +68,8 @@ impl HatchGpu { }; let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("hatch.pipeline_layout"), - bind_group_layouts: &[frame_bind_group_layout, &bind_group_layout], - push_constant_ranges: &[], + bind_group_layouts: &[frame_bind_group_layout, &bind_group_layout].map(Some), + immediate_size: 0, }); let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { label: Some(if uses_storage { @@ -108,8 +108,8 @@ impl HatchGpu { }, depth_stencil: Some(wgpu::DepthStencilState { format: wgpu::TextureFormat::Depth24PlusStencil8, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::LessEqual, + depth_write_enabled: Some(true), + depth_compare: Some(wgpu::CompareFunction::LessEqual), stencil: content_stencil.clone(), bias: wgpu::DepthBiasState { constant: 1, @@ -132,7 +132,7 @@ impl HatchGpu { })], compilation_options: wgpu::PipelineCompilationOptions::default(), }), - multiview: None, + multiview_mask: None, cache: None, }); let backend = match backend_kind { diff --git a/src/scene/pipeline/image_gpu.rs b/src/scene/pipeline/image_gpu.rs index c196cf78..5232b400 100644 --- a/src/scene/pipeline/image_gpu.rs +++ b/src/scene/pipeline/image_gpu.rs @@ -122,7 +122,7 @@ impl ImageGpu { address_mode_w: wgpu::AddressMode::ClampToEdge, mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear, - mipmap_filter: wgpu::FilterMode::Nearest, + mipmap_filter: wgpu::MipmapFilterMode::Nearest, ..Default::default() }); diff --git a/src/scene/pipeline/mesh_gpu.rs b/src/scene/pipeline/mesh_gpu.rs index dcfe3f4c..0e2a5f65 100644 --- a/src/scene/pipeline/mesh_gpu.rs +++ b/src/scene/pipeline/mesh_gpu.rs @@ -501,7 +501,7 @@ pub fn create_material_bind_group( address_mode_w: address, mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear, - mipmap_filter: wgpu::FilterMode::Linear, + mipmap_filter: wgpu::MipmapFilterMode::Linear, ..Default::default() }) }; diff --git a/src/scene/pipeline/mod.rs b/src/scene/pipeline/mod.rs index 30a45b31..5247f7db 100644 --- a/src/scene/pipeline/mod.rs +++ b/src/scene/pipeline/mod.rs @@ -380,14 +380,14 @@ impl Pipeline { let wire_const_bgl = wire_mode .uses_storage() .then(|| wire_gpu::WireConst::bind_group_layout(device)); - let mut wire_bgls: Vec<&wgpu::BindGroupLayout> = vec![&frame_bgl]; + let mut wire_bgls: Vec> = vec![Some(&frame_bgl)]; if let Some(bgl) = &wire_const_bgl { - wire_bgls.push(bgl); + wire_bgls.push(Some(bgl)); } let wire_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("wire.pipeline_layout"), bind_group_layouts: &wire_bgls, - push_constant_ranges: &[], + immediate_size: 0, }); let depth_tex = create_depth_texture(device, Size::new(1, 1)); @@ -438,8 +438,8 @@ impl Pipeline { }, depth_stencil: Some(wgpu::DepthStencilState { format: wgpu::TextureFormat::Depth24PlusStencil8, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::LessEqual, + depth_write_enabled: Some(true), + depth_compare: Some(wgpu::CompareFunction::LessEqual), stencil: content_stencil.clone(), bias: wgpu::DepthBiasState::default(), }), @@ -458,7 +458,7 @@ impl Pipeline { })], compilation_options: wgpu::PipelineCompilationOptions::default(), }), - multiview: None, + multiview_mask: None, cache: None, }); @@ -476,7 +476,7 @@ impl Pipeline { let clip_mask_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("clip_mask.pipeline_layout"), bind_group_layouts: &[], - push_constant_ranges: &[], + immediate_size: 0, }); let clip_mask_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("clip_mask.pipeline"), @@ -502,8 +502,8 @@ impl Pipeline { }, depth_stencil: Some(wgpu::DepthStencilState { format: wgpu::TextureFormat::Depth24PlusStencil8, - depth_write_enabled: false, - depth_compare: wgpu::CompareFunction::Always, + depth_write_enabled: Some(false), + depth_compare: Some(wgpu::CompareFunction::Always), stencil: wgpu::StencilState { front: wgpu::StencilFaceState { compare: wgpu::CompareFunction::Always, @@ -537,7 +537,7 @@ impl Pipeline { })], compilation_options: wgpu::PipelineCompilationOptions::default(), }), - multiview: None, + multiview_mask: None, cache: None, }); @@ -559,8 +559,8 @@ impl Pipeline { }, depth_stencil: Some(wgpu::DepthStencilState { format: wgpu::TextureFormat::Depth24PlusStencil8, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::LessEqual, + depth_write_enabled: Some(true), + depth_compare: Some(wgpu::CompareFunction::LessEqual), stencil: content_stencil.clone(), bias: wgpu::DepthBiasState::default(), }), @@ -579,7 +579,7 @@ impl Pipeline { })], compilation_options: wgpu::PipelineCompilationOptions::default(), }), - multiview: None, + multiview_mask: None, cache: None, }); @@ -602,8 +602,8 @@ impl Pipeline { }, depth_stencil: Some(wgpu::DepthStencilState { format: wgpu::TextureFormat::Depth24PlusStencil8, - depth_write_enabled: false, - depth_compare: wgpu::CompareFunction::Always, + depth_write_enabled: Some(false), + depth_compare: Some(wgpu::CompareFunction::Always), stencil: content_stencil.clone(), bias: wgpu::DepthBiasState::default(), }), @@ -622,7 +622,7 @@ impl Pipeline { })], compilation_options: wgpu::PipelineCompilationOptions::default(), }), - multiview: None, + multiview_mask: None, cache: None, }); @@ -654,8 +654,8 @@ impl Pipeline { let wipeout_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("wipeout.pipeline_layout"), - bind_group_layouts: &[&frame_bgl, &wipeout_bgl1], - push_constant_ranges: &[], + bind_group_layouts: &[&frame_bgl, &wipeout_bgl1].map(Some), + immediate_size: 0, }); let wipeout_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { @@ -681,8 +681,8 @@ impl Pipeline { }, depth_stencil: Some(wgpu::DepthStencilState { format: wgpu::TextureFormat::Depth24PlusStencil8, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::LessEqual, + depth_write_enabled: Some(true), + depth_compare: Some(wgpu::CompareFunction::LessEqual), stencil: content_stencil.clone(), // Bias TOWARD the camera: a wipeout must win against geometry at // its own depth (a block's wipeout + shapes are coincident at @@ -710,7 +710,7 @@ impl Pipeline { })], compilation_options: wgpu::PipelineCompilationOptions::default(), }), - multiview: None, + multiview_mask: None, cache: None, }); @@ -850,8 +850,8 @@ impl Pipeline { let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("mesh.cull.layout"), - bind_group_layouts: &[&bgl], - push_constant_ranges: &[], + bind_group_layouts: &[&bgl].map(Some), + immediate_size: 0, }); let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor { @@ -1027,8 +1027,8 @@ impl Pipeline { let mesh_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("mesh.pipeline_layout"), - bind_group_layouts: &[&frame_bgl, &mesh_material_bgl], - push_constant_ranges: &[], + bind_group_layouts: &[&frame_bgl, &mesh_material_bgl].map(Some), + immediate_size: 0, }); let mesh_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { @@ -1047,8 +1047,8 @@ impl Pipeline { }, depth_stencil: Some(wgpu::DepthStencilState { format: wgpu::TextureFormat::Depth24PlusStencil8, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::LessEqual, + depth_write_enabled: Some(true), + depth_compare: Some(wgpu::CompareFunction::LessEqual), stencil: content_stencil.clone(), bias: wgpu::DepthBiasState { constant: 1, @@ -1071,7 +1071,7 @@ impl Pipeline { })], compilation_options: wgpu::PipelineCompilationOptions::default(), }), - multiview: None, + multiview_mask: None, cache: None, }); @@ -1096,8 +1096,8 @@ impl Pipeline { }, depth_stencil: Some(wgpu::DepthStencilState { format: wgpu::TextureFormat::Depth24PlusStencil8, - depth_write_enabled: false, - depth_compare: wgpu::CompareFunction::LessEqual, + depth_write_enabled: Some(false), + depth_compare: Some(wgpu::CompareFunction::LessEqual), stencil: content_stencil.clone(), bias: wgpu::DepthBiasState { constant: 1, @@ -1120,7 +1120,7 @@ impl Pipeline { })], compilation_options: wgpu::PipelineCompilationOptions::default(), }), - multiview: None, + multiview_mask: None, cache: None, }); @@ -1144,8 +1144,8 @@ impl Pipeline { }, depth_stencil: Some(wgpu::DepthStencilState { format: wgpu::TextureFormat::Depth24PlusStencil8, - depth_write_enabled: false, - depth_compare: wgpu::CompareFunction::Always, + depth_write_enabled: Some(false), + depth_compare: Some(wgpu::CompareFunction::Always), stencil: content_stencil.clone(), bias: wgpu::DepthBiasState::default(), }), @@ -1164,7 +1164,7 @@ impl Pipeline { })], compilation_options: wgpu::PipelineCompilationOptions::default(), }), - multiview: None, + multiview_mask: None, cache: None, }) }; @@ -1199,8 +1199,8 @@ impl Pipeline { }, depth_stencil: Some(wgpu::DepthStencilState { format: wgpu::TextureFormat::Depth24PlusStencil8, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::LessEqual, + depth_write_enabled: Some(true), + depth_compare: Some(wgpu::CompareFunction::LessEqual), stencil: content_stencil.clone(), bias: wgpu::DepthBiasState::default(), }), @@ -1219,7 +1219,7 @@ impl Pipeline { })], compilation_options: wgpu::PipelineCompilationOptions::default(), }), - multiview: None, + multiview_mask: None, cache: None, }) }; @@ -1246,8 +1246,8 @@ impl Pipeline { }, depth_stencil: Some(wgpu::DepthStencilState { format: wgpu::TextureFormat::Depth24PlusStencil8, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::LessEqual, + depth_write_enabled: Some(true), + depth_compare: Some(wgpu::CompareFunction::LessEqual), stencil: content_stencil.clone(), bias: wgpu::DepthBiasState { constant: 1, @@ -1270,7 +1270,7 @@ impl Pipeline { })], compilation_options: wgpu::PipelineCompilationOptions::default(), }), - multiview: None, + multiview_mask: None, cache: None, }); @@ -1284,8 +1284,8 @@ impl Pipeline { let face3d_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("face3d.pipeline_layout"), - bind_group_layouts: &[&frame_bgl], - push_constant_ranges: &[], + bind_group_layouts: &[&frame_bgl].map(Some), + immediate_size: 0, }); let face3d_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { @@ -1304,8 +1304,8 @@ impl Pipeline { }, depth_stencil: Some(wgpu::DepthStencilState { format: wgpu::TextureFormat::Depth24PlusStencil8, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::LessEqual, + depth_write_enabled: Some(true), + depth_compare: Some(wgpu::CompareFunction::LessEqual), stencil: content_stencil.clone(), bias: wgpu::DepthBiasState { constant: 1, @@ -1328,7 +1328,7 @@ impl Pipeline { })], compilation_options: wgpu::PipelineCompilationOptions::default(), }), - multiview: None, + multiview_mask: None, cache: None, }); @@ -1351,8 +1351,8 @@ impl Pipeline { }, depth_stencil: Some(wgpu::DepthStencilState { format: wgpu::TextureFormat::Depth24PlusStencil8, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::LessEqual, + depth_write_enabled: Some(true), + depth_compare: Some(wgpu::CompareFunction::LessEqual), stencil: content_stencil.clone(), bias: wgpu::DepthBiasState { constant: 1, @@ -1375,7 +1375,7 @@ impl Pipeline { })], compilation_options: wgpu::PipelineCompilationOptions::default(), }), - multiview: None, + multiview_mask: None, cache: None, }); @@ -1418,8 +1418,8 @@ impl Pipeline { let image_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("image.pipeline_layout"), - bind_group_layouts: &[&frame_bgl, &image_bgl1], - push_constant_ranges: &[], + bind_group_layouts: &[&frame_bgl, &image_bgl1].map(Some), + immediate_size: 0, }); let image_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { @@ -1445,8 +1445,8 @@ impl Pipeline { }, depth_stencil: Some(wgpu::DepthStencilState { format: wgpu::TextureFormat::Depth24PlusStencil8, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::LessEqual, + depth_write_enabled: Some(true), + depth_compare: Some(wgpu::CompareFunction::LessEqual), stencil: content_stencil.clone(), bias: wgpu::DepthBiasState::default(), }), @@ -1465,7 +1465,7 @@ impl Pipeline { })], compilation_options: wgpu::PipelineCompilationOptions::default(), }), - multiview: None, + multiview_mask: None, cache: None, }); @@ -1533,8 +1533,8 @@ impl Pipeline { let blit_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("blit.pipeline_layout"), - bind_group_layouts: &[&blit_bgl], - push_constant_ranges: &[], + bind_group_layouts: &[&blit_bgl].map(Some), + immediate_size: 0, }); let blit_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { @@ -1572,7 +1572,7 @@ impl Pipeline { })], compilation_options: wgpu::PipelineCompilationOptions::default(), }), - multiview: None, + multiview_mask: None, cache: None, }); @@ -2874,6 +2874,7 @@ impl Pipeline { }), timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); // MSAA texture is clip-bounds-sized, so viewport starts at (0, 0). pass.set_viewport(0.0, 0.0, vp.width as f32, vp.height as f32, 0.0, 1.0); @@ -2918,6 +2919,7 @@ impl Pipeline { }), timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); pass.set_viewport(0.0, 0.0, vp.width as f32, vp.height as f32, 0.0, 1.0); pass.set_pipeline(&self.image_pipeline); @@ -2968,6 +2970,7 @@ impl Pipeline { }), timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); pass.set_viewport(0.0, 0.0, vp.width as f32, vp.height as f32, 0.0, 1.0); pass.set_bind_group(0, &self.uniform_bind_group, &[]); @@ -3373,6 +3376,7 @@ impl Pipeline { }), timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); pass.set_viewport(0.0, 0.0, vp.width as f32, vp.height as f32, 0.0, 1.0); pass.set_bind_group(0, &self.uniform_bind_group, &[]); @@ -3423,6 +3427,7 @@ impl Pipeline { }), timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); pass.set_viewport(0.0, 0.0, vp.width as f32, vp.height as f32, 0.0, 1.0); pass.set_pipeline(&self.wire_pipeline); @@ -3465,6 +3470,7 @@ impl Pipeline { }), timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); pass.set_viewport(0.0, 0.0, vp.width as f32, vp.height as f32, 0.0, 1.0); pass.set_pipeline(&self.wire_pipeline); @@ -3559,6 +3565,7 @@ impl Pipeline { }), timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); pass.set_viewport(0.0, 0.0, vp.width as f32, vp.height as f32, 0.0, 1.0); pass.set_pipeline(&self.text_pipeline); @@ -3604,6 +3611,7 @@ impl Pipeline { }), timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); pass.set_viewport(0.0, 0.0, vp.width as f32, vp.height as f32, 0.0, 1.0); pass.set_pipeline(&self.wipeout_pipeline); @@ -3646,6 +3654,7 @@ impl Pipeline { }), timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); pass.set_viewport(0.0, 0.0, vp.width as f32, vp.height as f32, 0.0, 1.0); pass.set_bind_group(0, &self.uniform_bind_group, &[]); @@ -3697,6 +3706,7 @@ impl Pipeline { depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); // No draw calls — the pass itself triggers the MSAA resolve. } @@ -3722,6 +3732,7 @@ impl Pipeline { depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); pass.set_viewport( surface_dest.x as f32, diff --git a/src/scene/pipeline/text_gpu.rs b/src/scene/pipeline/text_gpu.rs index 6ca752a2..851407ee 100644 --- a/src/scene/pipeline/text_gpu.rs +++ b/src/scene/pipeline/text_gpu.rs @@ -223,7 +223,7 @@ impl TextAtlasGpu { address_mode_w: wgpu::AddressMode::ClampToEdge, mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear, - mipmap_filter: wgpu::FilterMode::Nearest, + mipmap_filter: wgpu::MipmapFilterMode::Nearest, ..Default::default() }); let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { @@ -267,8 +267,8 @@ pub fn create_pipelines( }); let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("text.pipeline.layout"), - bind_group_layouts: &[frame_bgl, atlas_bgl], - push_constant_ranges: &[], + bind_group_layouts: &[frame_bgl, atlas_bgl].map(Some), + immediate_size: 0, }); let create = |label, depth_write_enabled, depth_compare| { device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { @@ -306,16 +306,20 @@ pub fn create_pipelines( mask: !0, alpha_to_coverage_enabled: false, }, - multiview: None, + multiview_mask: None, cache: None, }) }; ( - create("text.pipeline", true, wgpu::CompareFunction::LessEqual), + create( + "text.pipeline", + Some(true), + Some(wgpu::CompareFunction::LessEqual), + ), create( "text.highlight.pipeline", - false, - wgpu::CompareFunction::Always, + Some(false), + Some(wgpu::CompareFunction::Always), ), ) } diff --git a/src/scene/pipeline/viewcube.rs b/src/scene/pipeline/viewcube.rs index 74f3d0b8..23a83c25 100644 --- a/src/scene/pipeline/viewcube.rs +++ b/src/scene/pipeline/viewcube.rs @@ -510,8 +510,8 @@ impl ViewCubeText { }); let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("vc.text_layout"), - bind_group_layouts: &[&bgl], - push_constant_ranges: &[], + bind_group_layouts: &[&bgl].map(Some), + immediate_size: 0, }); let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { label: Some("vc.text_shader"), @@ -545,7 +545,7 @@ impl ViewCubeText { })], compilation_options: wgpu::PipelineCompilationOptions::default(), }), - multiview: None, + multiview_mask: None, cache: None, }); let vertex_capacity = MAX_VERTS as u32; @@ -782,6 +782,7 @@ impl ViewCubeText { depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); pass.set_viewport( clip.x as f32, @@ -1172,8 +1173,8 @@ impl ViewCubePipeline { }); let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("vc.layout"), - bind_group_layouts: &[&bgl], - push_constant_ranges: &[], + bind_group_layouts: &[&bgl].map(Some), + immediate_size: 0, }); let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { label: Some("vc.shader"), @@ -1199,8 +1200,8 @@ impl ViewCubePipeline { }, depth_stencil: Some(wgpu::DepthStencilState { format: wgpu::TextureFormat::Depth24PlusStencil8, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::Less, + depth_write_enabled: Some(true), + depth_compare: Some(wgpu::CompareFunction::Less), stencil: wgpu::StencilState::default(), bias: wgpu::DepthBiasState::default(), }), @@ -1215,7 +1216,7 @@ impl ViewCubePipeline { })], compilation_options: wgpu::PipelineCompilationOptions::default(), }), - multiview: None, + multiview_mask: None, cache: None, }); let text = ViewCubeText::new(device, queue, format); @@ -1303,6 +1304,7 @@ impl ViewCubePipeline { }), timestamp_writes: None, occlusion_query_set: None, + multiview_mask: None, }); pass.set_viewport( clip.x as f32, diff --git a/src/scene/pipeline/wire_gpu.rs b/src/scene/pipeline/wire_gpu.rs index 9a034e5b..01b79a61 100644 --- a/src/scene/pipeline/wire_gpu.rs +++ b/src/scene/pipeline/wire_gpu.rs @@ -50,7 +50,9 @@ fn instance_buffer_mapped( }); { let mut view = buf.slice(..).get_mapped_range_mut(); - view[..bytes.len()].copy_from_slice(bytes); + if !bytes.is_empty() { + view.slice(..bytes.len()).copy_from_slice(bytes); + } } buf.unmap(); buf diff --git a/src/ui/color_select.rs b/src/ui/color_select.rs index bb2af84b..4d0a6103 100644 --- a/src/ui/color_select.rs +++ b/src/ui/color_select.rs @@ -8,7 +8,7 @@ use crate::ui::properties::acad_color_display; use acadrust::types::Color as AcadColor; use iced::advanced::layout::{self, Layout}; use iced::advanced::widget::{self, Widget}; -use iced::advanced::{mouse, overlay, renderer, Clipboard, Shell}; +use iced::advanced::{mouse, overlay, renderer, Shell}; use iced::widget::{button, column, container, row, scrollable, text}; use iced::{Background, Border, Color, Element, Event, Length, Point, Rectangle, Renderer, Size, Theme, Vector}; @@ -69,7 +69,7 @@ fn swatch<'a>(bg: Color) -> Element<'a, Message> { .style(move |theme: &Theme| container::Style { background: Some(Background::Color(bg)), border: Border { - color: theme.extended_palette().background.neutral.color, + color: theme.palette().background.neutral.color, width: 1.0, radius: 2.0.into(), }, @@ -118,7 +118,7 @@ pub fn color_selector<'a>( let popup = container(color_list(extras, on_select, on_more)) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.weak.color)), border: Border { @@ -141,7 +141,7 @@ pub fn color_selector<'a>( } fn list_row_style(theme: &Theme, status: button::Status) -> button::Style { - let palette = theme.extended_palette(); + let palette = theme.palette(); let hovered = matches!(status, button::Status::Hovered); let text_color = if hovered { palette.background.strong.text @@ -230,9 +230,9 @@ pub fn color_grid_window(on_pick: impl Fn(AcadColor) -> Message) -> Element<'sta background: Some(Background::Color(bg)), border: Border { color: if matches!(status, button::Status::Hovered) { - theme.extended_palette().primary.base.color + theme.palette().primary.base.color } else { - theme.extended_palette().background.neutral.color + theme.palette().background.neutral.color }, width: if matches!(status, button::Status::Hovered) { 1.5 @@ -241,7 +241,7 @@ pub fn color_grid_window(on_pick: impl Fn(AcadColor) -> Message) -> Element<'sta }, radius: 1.0.into(), }, - text_color: theme.extended_palette().background.base.text, + text_color: theme.palette().background.base.text, ..Default::default() }) .padding(0), @@ -255,19 +255,19 @@ pub fn color_grid_window(on_pick: impl Fn(AcadColor) -> Message) -> Element<'sta column![ text("Select Color").size(13), row![chip(AcadColor::ByLayer, "ByLayer"), chip(AcadColor::ByBlock, "ByBlock")].spacing(6), - scrollable(grid).height(Length::Fill), + scrollable(grid).height(Length::Fit), ] .spacing(8), ) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.weak.color + theme.palette().background.weak.color )), ..Default::default() }) .padding(10) - .width(Length::Fill) - .height(Length::Fill) + .width(Length::Fit) + .height(Length::Fit) .into() } @@ -289,22 +289,14 @@ struct Floating<'a> { } impl<'a> Widget for Floating<'a> { - fn children(&self) -> Vec { - vec![widget::Tree::new(&self.base), widget::Tree::new(&self.popup)] - } - - fn diff(&self, tree: &mut widget::Tree) { - tree.diff_children(&[self.base.as_widget(), self.popup.as_widget()]); + fn diff(&mut self, tree: &mut widget::Tree) { + tree.diff_children(&mut [&mut self.base, &mut self.popup]); } fn size(&self) -> Size { self.base.as_widget().size() } - fn size_hint(&self) -> Size { - self.base.as_widget().size_hint() - } - fn layout( &mut self, tree: &mut widget::Tree, @@ -323,7 +315,6 @@ impl<'a> Widget for Floating<'a> { layout: Layout<'_>, cursor: mouse::Cursor, renderer: &Renderer, - clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, viewport: &Rectangle, ) { @@ -333,7 +324,6 @@ impl<'a> Widget for Floating<'a> { layout, cursor, renderer, - clipboard, shell, viewport, ); @@ -469,13 +459,12 @@ impl overlay::Overlay for FloatingOverlay<'_, '_> { layout: Layout<'_>, cursor: mouse::Cursor, renderer: &Renderer, - clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, ) { let child = layout.children().next().unwrap(); let vp = child.bounds(); self.popup.as_widget_mut().update( - self.tree, event, child, cursor, renderer, clipboard, shell, &vp, + self.tree, event, child, cursor, renderer, shell, &vp, ); } diff --git a/src/ui/command_line.rs b/src/ui/command_line.rs index 469881ac..dc769c4f 100644 --- a/src/ui/command_line.rs +++ b/src/ui/command_line.rs @@ -402,7 +402,7 @@ impl CommandLine { .on_press(Message::CommandOptionPick(opt.keyword.clone())) .padding([1, 6]) .style(|theme: &Theme, status| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = if matches!( status, button::Status::Hovered | button::Status::Pressed @@ -431,7 +431,7 @@ impl CommandLine { }); let prompt = container( text("Command:").size(11).style(|theme: &Theme| iced::widget::text::Style { - color: Some(theme.extended_palette().success.base.color), + color: Some(theme.palette().success.base.color), }), ) .padding([5, 8]); @@ -445,7 +445,7 @@ impl CommandLine { .on_press(Message::CommandLiteralToggle) .padding([2, 6]) .style(move |theme: &Theme, status| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = if literal_active { palette.primary.weak } else if matches!( @@ -508,7 +508,7 @@ impl CommandLine { .width(Length::Fill) .padding([2, 8]) .style(move |theme: &Theme, status| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = if is_selected { palette.primary.weak } else if matches!( @@ -567,9 +567,9 @@ impl CommandLine { .on_action(Message::CommandHistoryEdit) .size(11) .padding([2, 8]) - .max_height(180.0) + .height(Length::Fit.max(180.0)) .style(|theme: &Theme, _status| { - let palette = theme.extended_palette(); + let palette = theme.palette(); text_editor::Style { background: Background::Color(palette.background.base.color), border: Border::default(), @@ -625,7 +625,7 @@ impl CommandLine { container(history_rows) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.base.color, + theme.palette().background.base.color, )), ..Default::default() }) @@ -646,7 +646,7 @@ impl CommandLine { history_divider, container(input_row) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.weakest.color)), ..Default::default() @@ -658,7 +658,7 @@ impl CommandLine { .center_y(Length::Fixed(30.0)), ]) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.base.color)), border: Border { @@ -669,8 +669,7 @@ impl CommandLine { ..Default::default() } }) - .width(Length::Fill) - .max_width(720.0) + .width(Length::Fill.max(720.0)) .into() } } @@ -733,7 +732,7 @@ pub fn ranked_matches( /// Flat button style for the history dropdown's Copy / Clear strip: a subtle /// filled pill that brightens on hover. fn header_btn_style(theme: &Theme, status: button::Status) -> button::Style { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = if matches!(status, button::Status::Hovered | button::Status::Pressed) { palette.background.weak } else { @@ -752,7 +751,7 @@ fn header_btn_style(theme: &Theme, status: button::Status) -> button::Style { } fn history_color(theme: &Theme, kind: &EntryKind) -> Color { - let palette = theme.extended_palette(); + let palette = theme.palette(); match kind { EntryKind::Command => palette.background.base.text, EntryKind::Output => palette.background.base.text.scale_alpha(0.72), diff --git a/src/ui/icons.rs b/src/ui/icons.rs index 50aa23db..2f72b740 100644 --- a/src/ui/icons.rs +++ b/src/ui/icons.rs @@ -74,6 +74,8 @@ pub const SAVE: &[u8] = include_bytes!("../../assets/icons/ui/save.svg"); pub const FILE_EXPORT: &[u8] = include_bytes!("../../assets/icons/ui/file_export.svg"); pub const PRINT: &[u8] = include_bytes!("../../assets/icons/ui/print.svg"); pub const HEART: &[u8] = include_bytes!("../../assets/icons/ui/heart.svg"); +#[cfg(target_arch = "wasm32")] +pub const GEAR: &[u8] = include_bytes!("../../assets/icons/ui/gear.svg"); pub const DOT: &[u8] = include_bytes!("../../assets/icons/ui/dot.svg"); pub const DIRTY_DOT: &[u8] = include_bytes!("../../assets/icons/ui/dirty_dot.svg"); pub const ARROW_LONG_RIGHT: &[u8] = include_bytes!("../../assets/icons/ui/arrow_long_right.svg"); @@ -130,7 +132,7 @@ struct SemanticColors { impl SemanticColors { fn from_theme(theme: &Theme) -> Self { - let palette = theme.extended_palette(); + let palette = theme.palette(); Self { background: color_hex(palette.background.strong.color), text: color_hex(palette.background.base.text), @@ -257,12 +259,12 @@ fn semantic_handle(bytes: &'static [u8], theme: &Theme) -> svg::Handle { fn palette_key(theme: &Theme) -> [[u8; 4]; 6] { let palette = theme.palette(); [ - palette.background.into_rgba8(), - palette.text.into_rgba8(), - palette.primary.into_rgba8(), - palette.success.into_rgba8(), - palette.warning.into_rgba8(), - palette.danger.into_rgba8(), + palette.background.base.color.into_rgba8(), + palette.background.base.text.into_rgba8(), + palette.primary.base.color.into_rgba8(), + palette.success.base.color.into_rgba8(), + palette.warning.base.color.into_rgba8(), + palette.danger.base.color.into_rgba8(), ] } @@ -395,7 +397,7 @@ pub fn themed<'a, M: 'a>(bytes: &'static [u8], size: f32) -> Element<'a, M> { .width(size) .height(size) .style(|theme: &Theme, _| svg::Style { - color: Some(theme.extended_palette().background.base.text), + color: Some(theme.palette().background.base.text), }) .into() } @@ -408,7 +410,7 @@ pub fn themed_secondary<'a, M: 'a>(bytes: &'static [u8], size: f32) -> Element<' .style(|theme: &Theme, _| svg::Style { color: Some( theme - .extended_palette() + .palette() .background .base .text @@ -426,7 +428,7 @@ pub fn themed_disabled<'a, M: 'a>(bytes: &'static [u8], size: f32) -> Element<'a .style(|theme: &Theme, _| svg::Style { color: Some( theme - .extended_palette() + .palette() .background .base .text @@ -442,7 +444,7 @@ pub fn themed_primary<'a, M: 'a>(bytes: &'static [u8], size: f32) -> Element<'a, .width(size) .height(size) .style(|theme: &Theme, _| svg::Style { - color: Some(theme.extended_palette().primary.base.color), + color: Some(theme.palette().primary.base.color), }) .into() } @@ -453,7 +455,7 @@ pub fn themed_success<'a, M: 'a>(bytes: &'static [u8], size: f32) -> Element<'a, .width(size) .height(size) .style(|theme: &Theme, _| svg::Style { - color: Some(theme.extended_palette().success.base.color), + color: Some(theme.palette().success.base.color), }) .into() } @@ -464,7 +466,7 @@ pub fn themed_warning<'a, M: 'a>(bytes: &'static [u8], size: f32) -> Element<'a, .width(size) .height(size) .style(|theme: &Theme, _| svg::Style { - color: Some(theme.extended_palette().warning.base.color), + color: Some(theme.palette().warning.base.color), }) .into() } @@ -475,7 +477,7 @@ pub fn themed_danger<'a, M: 'a>(bytes: &'static [u8], size: f32) -> Element<'a, .width(size) .height(size) .style(|theme: &Theme, _| svg::Style { - color: Some(theme.extended_palette().danger.base.color), + color: Some(theme.palette().danger.base.color), }) .into() } @@ -486,7 +488,7 @@ pub fn themed_danger_text<'a, M: 'a>(bytes: &'static [u8], size: f32) -> Element .width(size) .height(size) .style(|theme: &Theme, _| svg::Style { - color: Some(theme.extended_palette().danger.base.text), + color: Some(theme.palette().danger.base.text), }) .into() } diff --git a/src/ui/mod.rs b/src/ui/mod.rs index d6ce49f5..db6b164d 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -2,6 +2,23 @@ /// Change this to scale the ribbon, layer manager rows, and property panel rows uniformly. pub const ROW_H: f32 = 26.0; +/// Iced 0.15 separates pick-list construction from the selection callback. +/// Keep the application's established call shape while the rest of the UI +/// migrates independently. +pub fn pick_list<'a, T, L, V, Message>( + options: L, + selected: Option, + on_select: impl Fn(T) -> Message + 'a, +) -> iced::widget::PickList<'a, T, L, V, Message> +where + T: PartialEq + Clone + ToString + 'a, + L: std::borrow::Borrow<[T]> + 'a, + V: std::borrow::Borrow + 'a, + Message: Clone + 'a, +{ + iced::widget::pick_list(selected, options, |value| value.to_string()).on_select(on_select) +} + /// Place `content` at fixed top-left coordinates inside a fill-sized layer. /// Negative coordinates clamp to the layer edge. pub fn pin_at<'a, Message: 'a>( diff --git a/src/ui/modal.rs b/src/ui/modal.rs index 64a0a887..da18ad36 100644 --- a/src/ui/modal.rs +++ b/src/ui/modal.rs @@ -6,106 +6,324 @@ //! so both stack dialogs here — one code path for every platform. use crate::app::Message; -use iced::widget::{button, column, container, mouse_area, opaque, row, stack}; -use iced::{Background, Border, Element, Length, Padding, Theme, Vector}; +use iced::advanced::layout::{self, Layout}; +use iced::advanced::widget::{self, Widget}; +use iced::advanced::{mouse, overlay, renderer, Shell}; +use iced::widget::{button, column, container, mouse_area, opaque, row, stack, Space}; +use iced::{ + Background, Border, Element, Event, Length, Padding, Rectangle, Renderer, Size, Theme, Vector, +}; -/// Stack `content` over `base` behind a dimmed backdrop, framed with a -/// draggable title bar (the ✕ close button at its right end). The backdrop only -/// dims and blocks clicks from reaching the view beneath — it does **not** -/// dismiss the dialog; closing is the ✕ button alone (`on_close`). +#[derive(Debug, Clone, Copy)] +pub struct ModalOptions { + movable: bool, + resizable: bool, + neutral_close: bool, +} + +/// Sizing used inside an intrinsically measured modal. +/// +/// [`INTRINSIC`](Self::INTRINSIC) lets each widget report the most space it can +/// use, while [`FILL`](Self::FILL) distributes the resulting shared frame to +/// sibling toolbars, panes and scroll areas. +#[derive(Debug, Clone, Copy)] +pub struct ModalSizing { + pub width: Length, + pub height: Length, +} + +impl ModalSizing { + pub const INTRINSIC: Self = Self { + width: Length::Fluid(iced_core::length::Constraint::Max), + height: Length::Fluid(iced_core::length::Constraint::Max), + }; + + pub const FILL: Self = Self { + width: Length::Fill, + height: Length::Fill, + }; + + pub fn from_resize(_resize: Vector) -> Self { + Self::FILL + } +} + +/// Measure an intrinsic copy of `content`, then lay the fill copy out in the +/// resulting shared frame. `extra` grows that frame when the resize handle is +/// dragged; `max` only caps the initial intrinsic pass. +pub fn intrinsic<'a>( + measurement: impl Into>, + content: impl Into>, + max: Size, + extra: Vector, +) -> Element<'a, Message> { + Element::new(Intrinsic { + children: [measurement.into(), content.into()], + max, + extra, + }) +} + +struct Intrinsic<'a> { + children: [Element<'a, Message>; 2], + max: Size, + extra: Vector, +} + +impl Widget for Intrinsic<'_> { + fn diff(&mut self, tree: &mut widget::Tree) { + tree.diff_children(&mut self.children); + } + + fn size(&self) -> Size { + Size::new( + Length::Fit.max(self.max.width + self.extra.x), + Length::Fit.max(self.max.height + self.extra.y), + ) + } + + fn layout( + &mut self, + tree: &mut widget::Tree, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + let measure_max = Size::new( + self.max.width.min(limits.max().width), + self.max.height.min(limits.max().height), + ); + let measure_limits = layout::Limits::new(Size::ZERO, measure_max); + let measured = self.children[0] + .as_widget_mut() + .layout(&mut tree.children[0], renderer, &measure_limits); + let size = Size::new( + (measured.size().width + self.extra.x).min(limits.max().width), + (measured.size().height + self.extra.y).min(limits.max().height), + ); + let final_limits = layout::Limits::new(size, size); + let content = self.children[1] + .as_widget_mut() + .layout(&mut tree.children[1], renderer, &final_limits); + + layout::Node::with_children(size, vec![measured, content]) + } + + fn operate( + &mut self, + tree: &mut widget::Tree, + layout: Layout<'_>, + renderer: &Renderer, + operation: &mut dyn widget::Operation, + ) { + self.children[1] + .as_widget_mut() + .operate(&mut tree.children[1], layout.child(1), renderer, operation); + } + + fn update( + &mut self, + tree: &mut widget::Tree, + event: &Event, + layout: Layout<'_>, + cursor: mouse::Cursor, + renderer: &Renderer, + shell: &mut Shell<'_, Message>, + viewport: &Rectangle, + ) { + self.children[1].as_widget_mut().update( + &mut tree.children[1], + event, + layout.child(1), + cursor, + renderer, + shell, + viewport, + ); + } + + fn mouse_interaction( + &self, + tree: &widget::Tree, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + renderer: &Renderer, + ) -> mouse::Interaction { + self.children[1] + .as_widget() + .mouse_interaction( + &tree.children[1], + layout.child(1), + cursor, + viewport, + renderer, + ) + } + + fn draw( + &self, + tree: &widget::Tree, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + ) { + self.children[1] + .as_widget() + .draw( + &tree.children[1], + renderer, + theme, + style, + layout.child(1), + cursor, + viewport, + ); + } + + fn overlay<'b>( + &'b mut self, + tree: &'b mut widget::Tree, + layout: Layout<'b>, + renderer: &Renderer, + viewport: &Rectangle, + translation: Vector, + ) -> Option> { + self.children[1] + .as_widget_mut() + .overlay( + &mut tree.children[1], + layout.child(1), + renderer, + viewport, + translation, + ) + } +} + +impl ModalOptions { + pub const STANDARD: Self = Self { + movable: true, + resizable: true, + neutral_close: false, + }; + + pub const MOVABLE_FIXED: Self = Self { + movable: true, + resizable: false, + neutral_close: false, + }; + + pub const NOTICE: Self = Self { + movable: true, + resizable: false, + neutral_close: true, + }; +} + +/// Stack `content` over `base` behind a dimmed backdrop, framed with a title bar +/// (the ✕ close button at its right end). The backdrop only dims and blocks +/// clicks from reaching the view beneath — it does **not** dismiss the dialog; +/// closing is the ✕ button alone (`on_close`). /// /// `offset` shifts the dialog from screen-centre so it can be dragged by its /// title bar; pass `Vector::ZERO` to keep it centred. pub fn modal<'a>( base: impl Into>, title: &'a str, - title_width: f32, content: impl Into>, on_close: Message, offset: Vector, - resizable: bool, + options: ModalOptions, ) -> Element<'a, Message> { - let close = button(crate::ui::icons::themed_danger( - crate::ui::icons::CLOSE, - 13.0, - )) - .on_press(on_close) - .padding([1, 7]) - .style(close_style); + let close = if options.neutral_close { + button(crate::ui::icons::themed_secondary( + crate::ui::icons::CLOSE, + 13.0, + )) + .on_press(on_close) + .padding([1, 7]) + .style(neutral_close_style) + } else { + button(crate::ui::icons::themed_danger( + crate::ui::icons::CLOSE, + 13.0, + )) + .on_press(on_close) + .padding([1, 7]) + .style(close_style) + }; - // Draggable title bar: a grip handle next to the ✕. Kept `Shrink` (no - // `Fill`) so a single Fill child can't blow the dialog out to the full - // screen width — the dialog stays sized to its content. Pressing the grip - // starts a drag (handled in `update`). - let grip = mouse_area( - container(crate::ui::icons::themed_primary(crate::ui::icons::MOVE, 14.0)) - .padding([1, 7]) - .style(|theme: &Theme| container::Style { - background: Some(Background::Color( - theme.extended_palette().background.weakest.color, - )), - border: Border { - radius: 4.0.into(), - ..Default::default() - }, - ..Default::default() - }), - ) - .on_press(Message::ModalGrab) - .interaction(iced::mouse::Interaction::Grab); - - // The dialog name is centred across the dialog width with the grip + ✕ - // overlaid at the right edge. The bar takes an explicit `title_width` - // (the caller's content width) instead of `Fill` — a Fill child inside - // the Shrink frame would blow the dialog out to the full screen. + // The dialog name is centred across the content width with the ✕ overlaid + // at the right edge. The title bar is itself an overlay: the content layer + // below determines the intrinsic modal width before `Fill` is resolved. let title_text = iced::widget::text(title).size(15); + let title_surface: Element<'a, Message> = container(title_text) + .width(Length::Fill) + .height(Length::Fixed(24.0)) + .align_x(iced::alignment::Horizontal::Center) + .align_y(iced::alignment::Vertical::Center) + .into(); + let title_surface: Element<'a, Message> = if options.movable { + mouse_area(title_surface) + .on_press(Message::ModalGrab) + .interaction(iced::mouse::Interaction::Grab) + .into() + } else { + title_surface + }; + let controls: Element<'a, Message> = row![close].align_y(iced::Center).into(); let title_bar = stack![ - container(title_text) - .width(Length::Fixed(title_width)) - .height(Length::Fixed(24.0)) - .align_x(iced::alignment::Horizontal::Center) - .align_y(iced::alignment::Vertical::Center), - container(row![grip, close].spacing(6).align_y(iced::Center)) - .width(Length::Fixed(title_width)) + title_surface, + container(controls) + .width(Length::Fill) .height(Length::Fixed(24.0)) .align_x(iced::alignment::Horizontal::Right) .align_y(iced::alignment::Vertical::Center), - ]; + ] + .width(Length::Fill) + .height(Length::Fixed(24.0)); let panel_style = |theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.base.color, + theme.palette().background.base.color, )), border: Border { - color: theme.extended_palette().background.neutral.color, + color: theme.palette().background.neutral.color, width: 1.0, radius: 6.0.into(), }, ..Default::default() }; - // The dialog is always sized to its content (the caller fixes the content's - // width/height, growing it by the shared resize delta). When `resizable`, a - // corner grip is appended bottom-right; dragging it drives `ModalResizeGrab` - // + the shared drag move/release, which bumps that delta. The title bar sits - // top-right above the content either way (`align_x(Right)`). - let body = if resizable { + // The first stack layer dictates its intrinsic size. Its top spacer reserves + // room for the title, while the actual title bar and resize grip overlay it + // without influencing the modal dimensions. + let body_base = column![ + Space::new().height(Length::Fixed(24.0)), + content.into(), + ] + .spacing(6); + let mut body = stack![body_base, title_bar]; + if options.resizable { let resize = mouse_area( container(crate::ui::icons::themed_primary(crate::ui::icons::RESIZE, 15.0)) .padding([0, 2]), ) .on_press(Message::ModalResizeGrab) .interaction(iced::mouse::Interaction::Grab); - column![title_bar, content.into(), resize] - } else { - column![title_bar, content.into()] - }; - let framed: Element<'a, Message> = container( - body.spacing(6).align_x(iced::alignment::Horizontal::Right), - ) - .padding(10) - .style(panel_style) - .into(); + body = body.push( + container(resize) + .width(Length::Fill) + .height(Length::Fill) + .align_x(iced::alignment::Horizontal::Right) + .align_y(iced::alignment::Vertical::Bottom), + ); + } + let framed: Element<'a, Message> = + container(body).padding(10).style(panel_style).into(); // Position via asymmetric padding (padding is non-negative): shifting a // centred box by `d` on an axis needs (near − far) padding = 2·d there. @@ -126,7 +344,7 @@ pub fn modal<'a>( .style(|theme: &Theme| container::Style { background: Some(Background::Color( theme - .extended_palette() + .palette() .background .strongest .color @@ -148,7 +366,7 @@ pub fn modal<'a>( } fn close_style(theme: &Theme, status: button::Status) -> button::Style { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = match status { button::Status::Hovered | button::Status::Pressed => palette.danger.strong, _ => palette.background.weakest, @@ -163,3 +381,20 @@ fn close_style(theme: &Theme, status: button::Status) -> button::Style { ..Default::default() } } + +fn neutral_close_style(theme: &Theme, status: button::Status) -> button::Style { + let palette = theme.palette(); + let pair = match status { + button::Status::Hovered | button::Status::Pressed => palette.background.weak, + _ => palette.background.weakest, + }; + button::Style { + background: Some(Background::Color(pair.color)), + text_color: pair.text, + border: Border { + radius: 4.0.into(), + ..Default::default() + }, + ..Default::default() + } +} diff --git a/src/ui/overlay.rs b/src/ui/overlay.rs index a300ef59..3cdc3502 100644 --- a/src/ui/overlay.rs +++ b/src/ui/overlay.rs @@ -332,9 +332,9 @@ fn draw_grip_marker(frame: &mut canvas::Frame, grip: &GripMarker, theme: &Theme) }; if grip.is_hot { - frame.fill(&path, theme.extended_palette().danger.base.color); + frame.fill(&path, theme.palette().danger.base.color); } else { - let palette = theme.extended_palette(); + let palette = theme.palette(); let color = palette.primary.base.color; frame.fill( &path, @@ -435,7 +435,7 @@ impl canvas::Program for SelectionCanvas { // Filled bars in the pane_grid spacing gaps, so adjacent panes read as // distinct viewports. Drawn first so all other overlays sit on top. if !self.dividers.is_empty() { - let divider = theme.extended_palette().background.neutral.color; + let divider = theme.palette().background.neutral.color; for d in &self.dividers { let bar = canvas::Path::rectangle( Point::new(d.x, d.y), @@ -450,13 +450,13 @@ impl canvas::Program for SelectionCanvas { // under the cursor, and drag a translucent ghost card along the cursor // so the pane is visibly "moving". if let Some(src) = self.pane_move_rect { - let accent = theme.extended_palette().primary.base.color; + let accent = theme.palette().primary.base.color; // Source pane: dimmed + dashed-feel outline (it has been lifted). let src_path = canvas::Path::rectangle(Point::new(src.x, src.y), iced::Size::new(src.width, src.height)); frame.fill( &src_path, - theme.extended_palette().background.strong.color.scale_alpha(0.28), + theme.palette().background.strong.color.scale_alpha(0.28), ); frame.stroke( &src_path, @@ -515,9 +515,9 @@ impl canvas::Program for SelectionCanvas { theme: &Theme, ) { let base = if crossing { - theme.extended_palette().success.base.color + theme.palette().success.base.color } else { - theme.extended_palette().primary.base.color + theme.palette().primary.base.color }; let fill = base.scale_alpha(0.12); let stroke = base.scale_alpha(0.9); @@ -547,9 +547,9 @@ impl canvas::Program for SelectionCanvas { if self.selection.poly_active && self.selection.poly_points.len() > 1 { let base = if self.selection.poly_crossing { - theme.extended_palette().success.base.color + theme.palette().success.base.color } else { - theme.extended_palette().primary.base.color + theme.palette().primary.base.color }; let fill = base.scale_alpha(0.12); let stroke = base.scale_alpha(0.9); @@ -909,7 +909,7 @@ impl canvas::Program for SelectionCanvas { if !over_viewcube && !over_divider && !self.pan_mode && !self.suppressed { if let Some(cp) = self.selection.last_move_pos { let color = theme - .extended_palette() + .palette() .background .base .text @@ -956,7 +956,7 @@ impl canvas::Program for SelectionCanvas { // the hovered object sits on a locked layer (issue: locked // objects are visible + snappable but not selectable/editable). if self.hover_locked { - let warning = theme.extended_palette().warning.base; + let warning = theme.palette().warning.base; let amber = warning.color.scale_alpha(0.98); let dark = warning.text; let bx = cp.x + sq + 7.0; @@ -1005,7 +1005,7 @@ impl canvas::Program for SelectionCanvas { } // ── Object Snap Tracking ───────────────────────────────────────────── - let track_color = theme.extended_palette().primary.base.color.scale_alpha(0.7); + let track_color = theme.palette().primary.base.color.scale_alpha(0.7); // The alignment line the cursor is currently locked to — drawn at its // real angle from the acquired point through the lock and a little // beyond, dashed so it reads as a construction guide. This covers the @@ -2110,7 +2110,7 @@ impl DynInputCanvas { canvas::Stroke { width: 1.0, style: canvas::Style::Solid( - theme.extended_palette().background.neutral.color.scale_alpha(0.9) + theme.palette().background.neutral.color.scale_alpha(0.9) ), line_dash: canvas::LineDash { segments: &[2.0, 3.0], offset: 0 }, ..Default::default() @@ -2161,7 +2161,7 @@ impl DynInputCanvas { } fn box_colors(b: &DynBox, theme: &Theme) -> (Color, Color, Color) { - let palette = theme.extended_palette(); + let palette = theme.palette(); if b.active { ( palette.primary.weak.color, @@ -2188,7 +2188,7 @@ impl DynInputCanvas { if self.prompt.is_empty() { return; } - let palette = theme.extended_palette(); + let palette = theme.palette(); let pw = (self.prompt.len() as f32 * DYN_CHAR_W) + DYN_PAD * 2.0; let rect = canvas::Path::rectangle(pos, Size { width: pw, height: DYN_BOX_H }); frame.fill(&rect, palette.background.strong.color); diff --git a/src/ui/popup/selection_filter_popup.rs b/src/ui/popup/selection_filter_popup.rs index 085e6677..45b57b61 100644 --- a/src/ui/popup/selection_filter_popup.rs +++ b/src/ui/popup/selection_filter_popup.rs @@ -39,7 +39,7 @@ pub fn menu_entries( let divider = container(iced::widget::Space::new().height(1)) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.weak.color, + theme.palette().background.weak.color, )), ..Default::default() }) @@ -83,7 +83,7 @@ fn empty_row() -> Element<'static, Message> { text("No objects").size(11).style(|theme: &Theme| text::Style { color: Some( theme - .extended_palette() + .palette() .background .base .text diff --git a/src/ui/popup/snap_popup.rs b/src/ui/popup/snap_popup.rs index 6259a9f4..7ffde3d5 100644 --- a/src/ui/popup/snap_popup.rs +++ b/src/ui/popup/snap_popup.rs @@ -22,7 +22,7 @@ pub fn menu_entries<'a>(snapper: &'a Snapper) -> Vec> { let divider = container(iced::widget::Space::new().height(1)) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.weak.color, + theme.palette().background.weak.color, )), ..Default::default() }) diff --git a/src/ui/properties.rs b/src/ui/properties.rs index feb5b3ae..9ca65354 100644 --- a/src/ui/properties.rs +++ b/src/ui/properties.rs @@ -97,7 +97,7 @@ impl canvas::Program for HatchPatternPreview { use crate::scene::model::hatch_model::{HatchModel, HatchPattern}; let mut frame = canvas::Frame::new(renderer, bounds.size()); - let palette = theme.extended_palette(); + let palette = theme.palette(); let pad = 4.0; let sample = canvas::Path::rectangle( Point::new(pad, pad), @@ -342,7 +342,7 @@ impl PropertiesPanel { let header = container(text("Properties").size(12)) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.weak.color, + theme.palette().background.weak.color, )), ..Default::default() }) @@ -372,7 +372,7 @@ impl PropertiesPanel { let title_bar = container(title_content) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.weakest.color)), border: Border { @@ -405,7 +405,7 @@ impl PropertiesPanel { container(column![header, title_bar, content]) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.base.color)), border: Border { @@ -434,7 +434,7 @@ impl PropertiesPanel { .style(muted_text_style), ) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.weakest.color)), border: Border { @@ -456,7 +456,7 @@ impl PropertiesPanel { Some( container(col) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.base.color)), border: Border { @@ -478,7 +478,7 @@ impl PropertiesPanel { // Section header let hdr = container(text(§ion.title).size(10)) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.weak.color)), border: Border { @@ -665,7 +665,7 @@ impl PropertiesPanel { row![ container(text("?").size(10)) .style(move |theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.strong.color)), border: Border { @@ -896,7 +896,7 @@ impl PropertiesPanel { ) .on_press(Message::PropEditChoiceToggle) .style(|theme: &Theme, status| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = match status { button::Status::Hovered | button::Status::Pressed => palette.background.weak, _ => palette.background.base, @@ -917,7 +917,7 @@ impl PropertiesPanel { .height(Length::Fixed(ROW_H - 6.0)); let head = container(row![input, caret].align_y(iced::Center)) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.base.color)), border: Border { @@ -956,7 +956,7 @@ impl PropertiesPanel { .style(container::bordered_box) .padding(2) .width(200) - .max_height(220.0); + .height(Length::Fit.max(220.0)); prop_row_widget( label, @@ -1011,7 +1011,7 @@ impl PropertiesPanel { ) .on_press(Message::PropHatchPatternPickerToggle(current.to_string())) .style(move |theme: &Theme, status| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let hovered = matches!(status, button::Status::Hovered | button::Status::Pressed); button::Style { background: Some(Background::Color(if hovered { @@ -1072,7 +1072,7 @@ impl PropertiesPanel { ) .on_press(Message::PropHatchPatternChanged(name)) .style(move |theme: &Theme, status| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let hovered = matches!(status, button::Status::Hovered | button::Status::Pressed); let pair = if selected { @@ -1126,7 +1126,7 @@ impl PropertiesPanel { .style(container::bordered_box) .padding(8) .width(348) - .max_height(360.0); + .height(Length::Fit.max(360.0)); prop_row_widget( label, @@ -1175,9 +1175,9 @@ pub fn color_picker_dropdown<'a>( background: Some(Background::Color(bg)), border: Border { color: if matches!(status, button::Status::Hovered) { - theme.extended_palette().primary.base.color + theme.palette().primary.base.color } else { - theme.extended_palette().background.neutral.color + theme.palette().background.neutral.color }, width: if matches!(status, button::Status::Hovered) { 1.5 @@ -1186,7 +1186,7 @@ pub fn color_picker_dropdown<'a>( }, radius: 2.0.into(), }, - text_color: theme.extended_palette().background.base.text, + text_color: theme.palette().background.base.text, ..Default::default() }) .padding(0), @@ -1222,7 +1222,7 @@ pub fn color_picker_dropdown<'a>( let mut col = column![container(inner) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.base.color)), border: Border { @@ -1258,9 +1258,9 @@ pub fn color_picker_dropdown<'a>( background: Some(Background::Color(bg)), border: Border { color: if matches!(status, button::Status::Hovered) { - theme.extended_palette().primary.base.color + theme.palette().primary.base.color } else { - theme.extended_palette().background.neutral.color + theme.palette().background.neutral.color }, width: if matches!(status, button::Status::Hovered) { 1.5 @@ -1269,7 +1269,7 @@ pub fn color_picker_dropdown<'a>( }, radius: 1.0.into(), }, - text_color: theme.extended_palette().background.base.text, + text_color: theme.palette().background.base.text, ..Default::default() }) .padding(0), @@ -1281,7 +1281,7 @@ pub fn color_picker_dropdown<'a>( col = col.push( container(scrollable(rows).height(160)) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.base.color)), border: Border { @@ -1309,7 +1309,7 @@ fn render_stepper_row<'a>(label: &'a str, display: &'a str) -> Element<'a, Messa .on_press(Message::PropVertexStep(delta)) .padding([0, 6]) .style(|theme: &Theme, status| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = match status { button::Status::Hovered | button::Status::Pressed => palette.background.weak, _ => palette.background.base, @@ -1346,12 +1346,12 @@ fn render_bool_row<'a>(label: &'a str, field: &'static str, value: bool) -> Elem text(btn_label) .size(FONT_SZ) .style(move |theme: &Theme| iced::widget::text::Style { - color: value.then_some(theme.extended_palette().warning.base.color), + color: value.then_some(theme.palette().warning.base.color), }), ) .on_press(Message::PropBoolToggle(field)) .style(move |theme: &Theme, status| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = match status { button::Status::Hovered | button::Status::Pressed => palette.background.weak, _ => palette.background.base, @@ -1480,7 +1480,7 @@ fn render_group_row( let label_col = container(label_btn) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.weakest.color, + theme.palette().background.weakest.color, )), ..Default::default() }) @@ -1498,7 +1498,7 @@ fn render_group_row( let value_col = container(value_field) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.base.color, + theme.palette().background.base.color, )), ..Default::default() }) @@ -1516,7 +1516,7 @@ fn render_group_row( .height(Length::Fixed(ROW_H)) .style(|theme: &Theme| container::Style { border: Border { - color: theme.extended_palette().background.neutral.color, + color: theme.palette().background.neutral.color, width: 1.0, radius: 0.0.into(), }, @@ -1548,7 +1548,7 @@ fn prop_row_widget<'a>(label: &'a str, widget: Element<'a, Message>) -> Element< ) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.weakest.color, + theme.palette().background.weakest.color, )), ..Default::default() }) @@ -1564,7 +1564,7 @@ fn prop_row_widget<'a>(label: &'a str, widget: Element<'a, Message>) -> Element< let value_col = container(widget) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.base.color, + theme.palette().background.base.color, )), ..Default::default() }) @@ -1581,7 +1581,7 @@ fn prop_row_widget<'a>(label: &'a str, widget: Element<'a, Message>) -> Element< .height(Length::Fixed(ROW_H)) .style(|theme: &Theme| container::Style { border: Border { - color: theme.extended_palette().background.neutral.color, + color: theme.palette().background.neutral.color, width: 1.0, radius: 0.0.into(), }, @@ -1655,7 +1655,7 @@ fn aci_label(idx: u8) -> &'static str { // ── Widget style helpers ────────────────────────────────────────────────── fn combo_btn_style(theme: &Theme, status: button::Status) -> button::Style { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = match status { button::Status::Hovered | button::Status::Pressed => palette.background.weak, _ => palette.background.base, @@ -1673,7 +1673,7 @@ fn combo_btn_style(theme: &Theme, status: button::Status) -> button::Style { } fn text_input_style(theme: &Theme, status: text_input::Status) -> text_input::Style { - let palette = theme.extended_palette(); + let palette = theme.palette(); let border_color = match status { text_input::Status::Focused { .. } => palette.primary.base.color, _ => palette.background.neutral.color, @@ -1700,7 +1700,7 @@ fn combo_input_style(theme: &Theme, status: text_input::Status) -> text_input::S /// focus highlight, so it reads as plain text, unlike the bordered editable /// fields) yet with a visible selection colour so Ctrl+C copy is discoverable. fn ro_input_style(theme: &Theme, _status: text_input::Status) -> text_input::Style { - let palette = theme.extended_palette(); + let palette = theme.palette(); text_input::Style { background: Background::Color(palette.background.base.color), border: Border { @@ -1717,13 +1717,13 @@ fn ro_input_style(theme: &Theme, _status: text_input::Status) -> text_input::Sty fn muted_text_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().background.base.text.scale_alpha(0.72)), + color: Some(theme.palette().background.base.text.scale_alpha(0.72)), } } fn hint_text_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().background.base.text.scale_alpha(0.48)), + color: Some(theme.palette().background.base.text.scale_alpha(0.48)), } } diff --git a/src/ui/ribbon/collapse.rs b/src/ui/ribbon/collapse.rs index c7c193e2..6b833477 100644 --- a/src/ui/ribbon/collapse.rs +++ b/src/ui/ribbon/collapse.rs @@ -13,7 +13,7 @@ use std::sync::Arc; use iced::advanced::layout::{self, Layout}; use iced::advanced::widget::{self, Widget}; -use iced::advanced::{mouse, overlay, renderer, Clipboard, Renderer as _, Shell}; +use iced::advanced::{mouse, overlay, renderer, Renderer as _, Shell}; use iced::{ Background, Border, Element, Event, Length, Point, Rectangle, Renderer, Shadow, Size, Theme, Vector, @@ -184,28 +184,16 @@ impl<'a> CollapsePanels<'a> { } impl<'a> Widget for CollapsePanels<'a> { - fn children(&self) -> Vec { - let mut v = Vec::with_capacity(self.panels.len() * SLOTS); - for p in &self.panels { - v.push(widget::Tree::new(&p.full)); - v.push(widget::Tree::new(&p.compact)); - v.push(widget::Tree::new(&p.button)); - v.push(widget::Tree::new(&p.tight)); - v.push(widget::Tree::new(&p.flyout)); + fn diff(&mut self, tree: &mut widget::Tree) { + let mut refs = Vec::with_capacity(self.panels.len() * SLOTS); + for p in &mut self.panels { + refs.push(&mut p.full); + refs.push(&mut p.compact); + refs.push(&mut p.button); + refs.push(&mut p.tight); + refs.push(&mut p.flyout); } - v - } - - fn diff(&self, tree: &mut widget::Tree) { - let mut refs: Vec<&dyn Widget> = Vec::new(); - for p in &self.panels { - refs.push(p.full.as_widget()); - refs.push(p.compact.as_widget()); - refs.push(p.button.as_widget()); - refs.push(p.tight.as_widget()); - refs.push(p.flyout.as_widget()); - } - tree.diff_children(&refs); + tree.diff_children(&mut refs); } fn size(&self) -> Size { @@ -362,7 +350,6 @@ impl<'a> Widget for CollapsePanels<'a> { layout: Layout<'_>, cursor: mouse::Cursor, renderer: &Renderer, - clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, viewport: &Rectangle, ) { @@ -376,7 +363,6 @@ impl<'a> Widget for CollapsePanels<'a> { child_layout, cursor, renderer, - clipboard, shell, viewport, ); @@ -477,7 +463,7 @@ impl<'a> Widget for CollapsePanels<'a> { shadow: Shadow::default(), snap: true, }, - Background::Color(theme.extended_palette().background.neutral.color), + Background::Color(theme.palette().background.neutral.color), ); } } @@ -611,7 +597,6 @@ impl overlay::Overlay for FlyoutOverlay<'_, '_> { layout: Layout<'_>, cursor: mouse::Cursor, renderer: &Renderer, - clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, ) { let child = layout.children().next().unwrap(); @@ -627,7 +612,7 @@ impl overlay::Overlay for FlyoutOverlay<'_, '_> { self.flyout .as_widget_mut() - .update(self.tree, event, child, cursor, renderer, clipboard, shell, &vp); + .update(self.tree, event, child, cursor, renderer, shell, &vp); } fn operate( diff --git a/src/ui/ribbon/mod.rs b/src/ui/ribbon/mod.rs index b86be31e..79f74166 100644 --- a/src/ui/ribbon/mod.rs +++ b/src/ui/ribbon/mod.rs @@ -423,7 +423,7 @@ impl Ribbon { button(text(module.title()).size(12)) .on_press(Message::RibbonSelectTab(i)) .style(move |theme: &Theme, status| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let accent = if is_contextual { palette.warning.base } else { @@ -470,9 +470,9 @@ impl Ribbon { border: Border { color: if is_active { if is_contextual { - theme.extended_palette().warning.base.color + theme.palette().warning.base.color } else { - theme.extended_palette().primary.base.color + theme.palette().primary.base.color } } else { Color::TRANSPARENT @@ -526,7 +526,7 @@ impl Ribbon { let tab_bar = container(tab_row) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.base.color, + theme.palette().background.base.color, )), ..Default::default() }) @@ -647,10 +647,10 @@ impl Ribbon { )) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.weakest.color, + theme.palette().background.weakest.color, )), border: Border { - color: theme.extended_palette().background.neutral.color, + color: theme.palette().background.neutral.color, width: 1.0, radius: 0.0.into(), }, @@ -672,10 +672,10 @@ impl Ribbon { let tool_bar = container(tool_area) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.weakest.color, + theme.palette().background.weakest.color, )), border: Border { - color: theme.extended_palette().background.neutral.color, + color: theme.palette().background.neutral.color, width: 1.0, radius: 0.0.into(), }, @@ -827,7 +827,7 @@ impl Ribbon { .style(move |theme: &Theme| iced::widget::text::Style { color: (!is_current).then_some( theme - .extended_palette() + .palette() .background .base .text @@ -895,7 +895,7 @@ impl Ribbon { .style(move |theme: &Theme| container::Style { background: Some(Background::Color(lc)), border: Border { - color: theme.extended_palette().background.strong.color, + color: theme.palette().background.strong.color, width: 1.0, radius: 1.0.into(), }, @@ -924,7 +924,7 @@ impl Ribbon { .style(move |theme: &Theme| iced::widget::text::Style { color: (!is_active).then_some( theme - .extended_palette() + .palette() .background .base .text @@ -981,7 +981,7 @@ impl Ribbon { .padding([3, 4]) .style(|theme: &Theme| container::Style { border: Border { - color: theme.extended_palette().background.neutral.color, + color: theme.palette().background.neutral.color, width: 1.0, radius: 0.0.into(), }, @@ -1055,7 +1055,7 @@ impl Ribbon { .style(move |theme: &Theme| iced::widget::text::Style { color: (!is_sel).then_some( theme - .extended_palette() + .palette() .background .base .text @@ -1154,7 +1154,7 @@ impl Ribbon { .style(move |theme: &Theme| iced::widget::text::Style { color: (!is_cur).then_some( theme - .extended_palette() + .palette() .background .base .text @@ -1207,7 +1207,7 @@ impl Ribbon { .style(move |theme: &Theme| iced::widget::text::Style { color: (!is_cur).then_some( theme - .extended_palette() + .palette() .background .base .text diff --git a/src/ui/ribbon/widgets.rs b/src/ui/ribbon/widgets.rs index d6b3b0dd..fbd2db8f 100644 --- a/src/ui/ribbon/widgets.rs +++ b/src/ui/ribbon/widgets.rs @@ -146,7 +146,7 @@ pub(super) fn make_icon_dim(icon: IconKind, size: f32, dim: bool) -> Element<'st IconKind::Glyph(s) => text(s) .size(size * 0.7) .style(|theme: &Theme| iced::widget::text::Style { - color: Some(theme.extended_palette().background.base.text.scale_alpha(0.42)), + color: Some(theme.palette().background.base.text.scale_alpha(0.42)), }) .into(), IconKind::Svg(bytes) => icons::semantic_disabled(bytes, size), @@ -179,7 +179,7 @@ pub(super) fn tool_btn_style( is_active: bool, status: button::Status, ) -> button::Style { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = match (is_active, status) { (true, _) => palette.primary.weak, (_, button::Status::Hovered) => palette.background.weak, @@ -208,7 +208,7 @@ pub(super) fn combo_btn_style( status: button::Status, radius: f32, ) -> button::Style { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = if is_open { palette.primary.weak } else if matches!(status, button::Status::Hovered | button::Status::Pressed) { @@ -233,7 +233,7 @@ pub(super) fn combo_btn_style( } pub(super) fn popup_row_style(theme: &Theme, status: button::Status) -> button::Style { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = if matches!(status, button::Status::Hovered | button::Status::Pressed) { palette.background.weak } else { @@ -247,7 +247,7 @@ pub(super) fn popup_row_style(theme: &Theme, status: button::Status) -> button:: } pub(super) fn popup_panel_style(theme: &Theme) -> container::Style { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.base.color)), border: Border { @@ -261,14 +261,14 @@ pub(super) fn popup_panel_style(theme: &Theme) -> container::Style { pub(super) fn muted_text_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().background.base.text.scale_alpha(0.72)), + color: Some(theme.palette().background.base.text.scale_alpha(0.72)), } } pub(super) fn tool_label_style(theme: &Theme, dim: bool) -> iced::widget::text::Style { iced::widget::text::Style { color: dim.then_some( - theme.extended_palette().background.base.text.scale_alpha(0.42), + theme.palette().background.base.text.scale_alpha(0.42), ), } } @@ -280,7 +280,7 @@ pub(super) fn make_tip(tip: String) -> Element<'static, Message> { } pub(super) fn tip_style(theme: &Theme) -> container::Style { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.strong.color)), border: Border { @@ -632,7 +632,7 @@ pub(super) fn render_large<'a>( .style(move |theme: &Theme| container::Style { background: Some(Background::Color(lc)), border: Border { - color: theme.extended_palette().background.strong.color, + color: theme.palette().background.strong.color, width: 1.0, radius: 1.0.into(), }, @@ -778,7 +778,7 @@ pub(super) fn render_large<'a>( .style(move |theme: &Theme| container::Style { background: Some(Background::Color(c)), border: Border { - color: theme.extended_palette().background.strong.color, + color: theme.palette().background.strong.color, width: 1.0, radius: 1.0.into(), }, @@ -1088,7 +1088,7 @@ pub(super) fn top_hist_btn_style( open: bool, status: button::Status, ) -> button::Style { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = match (active, open, status) { (false, _, _) => palette.background.weakest, (_, true, _) => palette.primary.weak, diff --git a/src/ui/side_toolbar.rs b/src/ui/side_toolbar.rs index 9bad95bf..d3adb3f6 100644 --- a/src/ui/side_toolbar.rs +++ b/src/ui/side_toolbar.rs @@ -29,7 +29,7 @@ fn tip_panel(label: &'static str) -> Element<'static, Message> { container(text(label).size(11)) .padding([2, 6]) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.strong.color)), border: Border { @@ -62,7 +62,7 @@ pub fn view(tools: &[ToolDef]) -> Option> { .width(Length::Fixed(BTN_SIZE)) .height(Length::Fixed(BTN_SIZE)) .style(|theme: &Theme, status| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let hovered = matches!( status, button::Status::Hovered | button::Status::Pressed @@ -85,7 +85,7 @@ pub fn view(tools: &[ToolDef]) -> Option> { } let panel = container(col).padding(4).style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.weak.color)), border: Border { diff --git a/src/ui/statusbar/mod.rs b/src/ui/statusbar/mod.rs index 1074800d..a6edc594 100644 --- a/src/ui/statusbar/mod.rs +++ b/src/ui/statusbar/mod.rs @@ -470,7 +470,7 @@ impl StatusBar { container(wrap) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.base.color)), border: Border { @@ -581,7 +581,7 @@ fn toggle_pill(icon: &'static [u8], active: bool, msg: Message) -> Element<'stat .style(move |theme: &Theme, status| { let mut style = button::subtle(theme, status); if active { - let palette = theme.extended_palette(); + let palette = theme.palette(); style.background = Some(Background::Color(match status { button::Status::Hovered => palette.primary.base.color, _ => palette.primary.weak.color, @@ -612,7 +612,7 @@ fn split_pill<'a>( ) -> Element<'a, Message> { container(row![main, caret].spacing(3).align_y(iced::Center)) .style(move |theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(if active { palette.primary.weak.color @@ -667,7 +667,7 @@ fn polar_pill<'a>( row![ polar_icon, text(angle).size(11).style(move |theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); text::Style { color: Some(if active { palette.primary.base.color @@ -796,7 +796,7 @@ fn space_tab<'a>( report_key_prefix: &'static str, ) -> Element<'a, Message> { let tab_style = move |theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let text_color = if !enabled { palette.background.base.text.scale_alpha(0.42) } else if is_active { @@ -912,7 +912,7 @@ fn space_mode_btn(current_layout: &str, in_mspace: bool) -> Element<'static, Mes .style(move |theme: &Theme, status| { let mut style = button::subtle(theme, status); if active { - let palette = theme.extended_palette(); + let palette = theme.palette(); style.background = Some(Background::Color(match status { button::Status::Hovered if clickable => palette.primary.base.color, _ => palette.primary.weak.color, diff --git a/src/ui/statusbar/status_menu.rs b/src/ui/statusbar/status_menu.rs index 48053ba6..66b96d1c 100644 --- a/src/ui/statusbar/status_menu.rs +++ b/src/ui/statusbar/status_menu.rs @@ -52,7 +52,7 @@ pub fn menu_bar<'a>( .close_on_background_click_global(true) .draw_path(DrawPath::Backdrop) .style(|theme: &Theme, _| { - let palette = theme.extended_palette(); + let palette = theme.palette(); iced_aw::style::menu_bar::Style { bar_background: Background::Color(Color::TRANSPARENT), bar_border: Border::default(), diff --git a/src/ui/statusbar/statusbar_menu.rs b/src/ui/statusbar/statusbar_menu.rs index 20777b08..25871e9b 100644 --- a/src/ui/statusbar/statusbar_menu.rs +++ b/src/ui/statusbar/statusbar_menu.rs @@ -37,7 +37,7 @@ fn layout_row<'a>(name: String, is_current: bool) -> Element<'a, Message> { .style(move |theme: &Theme, status| { let mut style = button::subtle(theme, status); if is_current && status == button::Status::Active { - let palette = theme.extended_palette(); + let palette = theme.palette(); style.background = Some(Background::Color(palette.primary.weak.color)); style.text_color = palette.primary.weak.text; } diff --git a/src/ui/style/anno_object_scale.rs b/src/ui/style/anno_object_scale.rs index 477e86fb..d069f476 100644 --- a/src/ui/style/anno_object_scale.rs +++ b/src/ui/style/anno_object_scale.rs @@ -9,18 +9,19 @@ use crate::app::Message; use crate::ui::style::style_manager::{hdivider, muted_text_style, tb_button}; use iced::widget::{column, container, mouse_area, row, scrollable, text, Space}; -use iced::{Background, Border, Element, Fill, Theme}; +use iced::{Background, Border, Element, Theme}; /// `scales` is `(name, "paper:drawing" ratio, is_member)`. Every label is cloned /// into the widget tree, so the returned element borrows nothing from the args. pub fn view_window( object_label: &str, scales: &[(String, String, bool)], + sizing: crate::ui::modal::ModalSizing, ) -> Element<'static, Message> { let toolbar = container( row![ text(format!("Object: {object_label}")).size(11), - Space::new().width(Fill), + Space::new().width(sizing.width), tb_button("Close", Message::CloseModal, true), ] .spacing(4) @@ -28,11 +29,11 @@ pub fn view_window( ) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.weak.color + theme.palette().background.weak.color )), ..Default::default() }) - .width(Fill) + .width(sizing.width) .padding([5, 8]); let rows: Vec> = scales @@ -41,23 +42,23 @@ pub fn view_window( let check = crate::ui::icons::themed_check_cell(*member); let label = row![ check, - text(name.clone()).size(11).width(Fill), + text(name.clone()).size(11).width(sizing.width), text(ratio.clone()).size(10).style(muted_text_style), ] .spacing(4) .align_y(iced::Center); let cell = container(label) .padding([4, 8]) - .width(Fill); + .width(sizing.width); mouse_area(cell) .on_press(Message::AnnoObjectScaleToggle(name.clone())) .into() }) .collect(); - let list = container(scrollable(column(rows).spacing(1)).height(Fill)) + let list = container(scrollable(column(rows).spacing(1)).height(sizing.height)) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.weak.color)), border: Border { @@ -68,8 +69,8 @@ pub fn view_window( ..Default::default() } }) - .width(Fill) - .height(Fill) + .width(sizing.width) + .height(sizing.height) .padding(2); let body = container( @@ -80,20 +81,20 @@ pub fn view_window( list, ] .spacing(6) - .height(Fill), + .height(sizing.height), ) - .width(Fill) - .height(Fill) + .width(sizing.width) + .height(sizing.height) .padding(12); - container(column![toolbar, hdivider(), body]) + container(column![toolbar, hdivider(sizing.width), body]) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.base.color + theme.palette().background.base.color )), ..Default::default() }) - .width(Fill) - .height(Fill) + .width(sizing.width) + .height(sizing.height) .into() } diff --git a/src/ui/style/dimstyle.rs b/src/ui/style/dimstyle.rs index 07c9ad11..bfa0a8a5 100644 --- a/src/ui/style/dimstyle.rs +++ b/src/ui/style/dimstyle.rs @@ -2,9 +2,9 @@ use crate::app::{ColorPickTarget, DsField, Message}; use iced::widget::{ - button, checkbox, column, container, pick_list, row, scrollable, text, text_input, Space, + button, checkbox, column, container, row, scrollable, text, text_input, Space, }; -use iced::{Background, Border, Element, Fill, Theme}; +use iced::{Background, Border, Element, Theme}; /// All DimStyle field values needed by the view. pub struct DimStyleValues<'a> { @@ -95,7 +95,7 @@ pub struct DimStyleValues<'a> { fn tab_btn_style(active: bool) -> impl Fn(&Theme, button::Status) -> button::Style { move |theme: &Theme, st| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = match (active, st) { (true, _) => palette.primary.strong, (false, button::Status::Hovered | button::Status::Pressed) => { @@ -117,7 +117,7 @@ fn tab_btn_style(active: bool) -> impl Fn(&Theme, button::Status) -> button::Sty } fn field_style(theme: &Theme, status: text_input::Status) -> text_input::Style { - let palette = theme.extended_palette(); + let palette = theme.palette(); let border = match status { text_input::Status::Focused { .. } => palette.primary.base.color, _ => palette.background.neutral.color, @@ -138,23 +138,23 @@ fn field_style(theme: &Theme, status: text_input::Status) -> text_input::Style { fn muted_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().background.base.text.scale_alpha(0.68)), + color: Some(theme.palette().background.base.text.scale_alpha(0.68)), } } fn primary_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().primary.base.color), + color: Some(theme.palette().primary.base.color), } } -fn hdivider<'a>() -> Element<'a, Message> { - container(Space::new().width(Fill).height(1)) - .width(Fill) +fn hdivider<'a>(width: iced::Length) -> Element<'a, Message> { + container(Space::new().width(width).height(1)) + .width(width) .height(1) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.neutral.color + theme.palette().background.neutral.color )), ..Default::default() }) @@ -169,6 +169,7 @@ pub fn view_window<'a>( vals: DimStyleValues<'a>, rename_active: Option<&'a str>, rename_buf: &'a str, + sizing: crate::ui::modal::ModalSizing, ) -> Element<'a, Message> { // ── Tab bar ─────────────────────────────────────────────────────────── let tabs = row![ @@ -234,7 +235,7 @@ pub fn view_window<'a>( .unwrap_or_else(|| val.to_string()); row![ lbl(label), - pick_list(labels, Some(cur), move |chosen| { + crate::ui::pick_list(labels, Some(cur), move |chosen| { let code = opts .iter() .find(|(_, l)| *l == chosen.as_str()) @@ -288,7 +289,7 @@ pub fn view_window<'a>( Message::DsColorMore(fld.clone()), Message::OpenColorWindow(ColorPickTarget::DimStyle(fld.clone())), ); - row![lbl(label), selector] + row![lbl(label), container(selector).width(150)] .spacing(8) .align_y(iced::Center) .into() @@ -303,7 +304,7 @@ pub fn view_window<'a>( -> Element<'a, Message> { row![ lbl(label), - pick_list(options, Some(selected), move |value| { + crate::ui::pick_list(options, Some(selected), move |value| { Message::DsSetHandle { field, value } }) .text_size(11) @@ -753,16 +754,16 @@ pub fn view_window<'a>( column![ text(format!("Editing: {selected}")).size(11).style(muted_style), tabs, - hdivider(), - scrollable(container(tab_content).padding([12, 12]).width(Fill)) - .width(Fill) - .height(Fill), + hdivider(sizing.width), + scrollable(container(tab_content).padding([12, 12]).width(sizing.width)) + .width(sizing.width) + .height(sizing.height), ] .spacing(6) - .height(Fill), + .height(sizing.height), ) - .height(Fill) - .width(Fill) + .height(sizing.height) + .width(sizing.width) .padding(iced::Padding { top: 12.0, right: 0.0, @@ -771,6 +772,7 @@ pub fn view_window<'a>( }); crate::ui::style::style_manager::view(crate::ui::style::style_manager::Scaffold { + sizing, kind: crate::app::StyleKind::Dim, styles: &styles, selected, diff --git a/src/ui/style/mleaderstyle.rs b/src/ui/style/mleaderstyle.rs index 22c908c3..ffb255d7 100644 --- a/src/ui/style/mleaderstyle.rs +++ b/src/ui/style/mleaderstyle.rs @@ -2,13 +2,13 @@ use crate::app::Message; use iced::widget::{ - button, checkbox, column, container, pick_list, row, scrollable, text, text_input, + button, checkbox, column, container, row, scrollable, text, text_input, }; -use iced::{Background, Border, Element, Fill, Theme}; +use iced::{Background, Border, Element, Theme}; fn btn_s(accent: bool) -> impl Fn(&Theme, button::Status) -> button::Style { move |theme: &Theme, st| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = match (accent, st) { (true, button::Status::Hovered | button::Status::Pressed) => palette.primary.strong, (false, button::Status::Hovered | button::Status::Pressed) => { @@ -32,13 +32,13 @@ fn btn_s(accent: bool) -> impl Fn(&Theme, button::Status) -> button::Style { fn muted_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().background.base.text.scale_alpha(0.68)), + color: Some(theme.palette().background.base.text.scale_alpha(0.68)), } } fn primary_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().primary.base.color), + color: Some(theme.palette().primary.base.color), } } @@ -142,7 +142,7 @@ fn enum_row<'a>( ) -> Element<'a, Message> { row![ text(label).size(11).style(muted_style).width(150), - pick_list(options, Some(selected), move |value| { + crate::ui::pick_list(options, Some(selected), move |value| { Message::MLeaderStyleSetEnum { field, value } }) .text_size(11) @@ -157,7 +157,7 @@ fn lineweight_row<'a>(selected: acadrust::types::LineWeight) -> Element<'a, Mess let selected = crate::ui::properties::LwItem(selected); row![ text("Line weight:").size(11).style(muted_style).width(150), - pick_list( + crate::ui::pick_list( crate::ui::properties::lw_options(), Some(selected), |item| Message::MLeaderStyleLineWeightChanged(item.0) @@ -199,7 +199,7 @@ fn handle_row<'a>( ) -> Element<'a, Message> { row![ text(label).size(11).style(muted_style).width(150), - pick_list(options, Some(selected), move |value| { + crate::ui::pick_list(options, Some(selected), move |value| { Message::MLeaderStyleSetHandle { field, value } }) .text_size(11) @@ -219,7 +219,10 @@ fn chk<'a>(label: &'static str, val: bool, field: &'static str) -> Element<'a, M .into() } -pub fn view_window<'a>(v: MLeaderStyleView<'a>) -> Element<'a, Message> { +pub fn view_window<'a>( + v: MLeaderStyleView<'a>, + sizing: crate::ui::modal::ModalSizing, +) -> Element<'a, Message> { // ── Right: Details panel ────────────────────────────────────────────── let details: Element<'_, Message> = if let Some(s) = v.style { scrollable( @@ -396,8 +399,8 @@ pub fn view_window<'a>(v: MLeaderStyleView<'a>) -> Element<'a, Message> { .spacing(6) .padding([12, 12]), ) - .width(Fill) - .height(Fill) + .width(sizing.width) + .height(sizing.height) .into() } else { container(text("Select a style to view details.").size(11).style(muted_style)) @@ -405,9 +408,12 @@ pub fn view_window<'a>(v: MLeaderStyleView<'a>) -> Element<'a, Message> { .into() }; - let right_panel = container(details).width(Fill).height(Fill); + let right_panel = container(details) + .width(sizing.width) + .height(sizing.height); crate::ui::style::style_manager::view(crate::ui::style::style_manager::Scaffold { + sizing, kind: crate::app::StyleKind::MLeader, styles: &v.styles, selected: v.selected, diff --git a/src/ui/style/mlstyle.rs b/src/ui/style/mlstyle.rs index 9a1c22d3..65aad3c6 100644 --- a/src/ui/style/mlstyle.rs +++ b/src/ui/style/mlstyle.rs @@ -2,11 +2,11 @@ use crate::app::Message; use iced::widget::{column, container, row, scrollable, text}; -use iced::{Element, Fill, Theme}; +use iced::{Element, Theme}; fn muted_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().background.base.text.scale_alpha(0.68)), + color: Some(theme.palette().background.base.text.scale_alpha(0.68)), } } @@ -17,6 +17,7 @@ pub fn view_window<'a>( current_style: String, rename_active: Option<&'a str>, rename_buf: &'a str, + sizing: crate::ui::modal::ModalSizing, ) -> Element<'a, Message> { // ── Right: Details panel ────────────────────────────────────────────── let info_row = |label: &'static str, val: String| -> Element<'_, Message> { @@ -69,7 +70,7 @@ pub fn view_window<'a>( ]; col_items.extend(elem_rows); scrollable(column(col_items).spacing(6).padding([12, 12])) - .height(Fill) + .height(sizing.height) .into() } else { container(text("Select a style to view details.").size(11).style(muted_style)) @@ -77,9 +78,12 @@ pub fn view_window<'a>( .into() }; - let right_panel = container(details).width(Fill).height(Fill); + let right_panel = container(details) + .width(sizing.width) + .height(sizing.height); crate::ui::style::style_manager::view(crate::ui::style::style_manager::Scaffold { + sizing, kind: crate::app::StyleKind::MLine, styles: &styles, selected, diff --git a/src/ui/style/plotstyle.rs b/src/ui/style/plotstyle.rs index 0a4ef8c7..564936bd 100644 --- a/src/ui/style/plotstyle.rs +++ b/src/ui/style/plotstyle.rs @@ -2,11 +2,11 @@ use crate::app::Message; use iced::widget::{button, column, container, row, scrollable, text, text_input, Space}; -use iced::{Background, Border, Element, Fill, Theme}; +use iced::{Background, Border, Element, Theme}; fn btn_s(accent: bool) -> impl Fn(&Theme, button::Status) -> button::Style { move |theme: &Theme, st| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = match (accent, st) { (true, button::Status::Hovered | button::Status::Pressed) => palette.primary.strong, (false, button::Status::Hovered | button::Status::Pressed) => { @@ -29,7 +29,7 @@ fn btn_s(accent: bool) -> impl Fn(&Theme, button::Status) -> button::Style { } fn field_style(theme: &Theme, status: text_input::Status) -> text_input::Style { - let palette = theme.extended_palette(); + let palette = theme.palette(); let border = match status { text_input::Status::Focused { .. } => palette.primary.base.color, _ => palette.background.neutral.color, @@ -50,30 +50,30 @@ fn field_style(theme: &Theme, status: text_input::Status) -> text_input::Style { fn muted_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().background.base.text.scale_alpha(0.68)), + color: Some(theme.palette().background.base.text.scale_alpha(0.68)), } } -fn hdivider<'a>() -> Element<'a, Message> { - container(Space::new().width(Fill).height(1)) - .width(Fill) +fn hdivider<'a>(width: iced::Length) -> Element<'a, Message> { + container(Space::new().width(width).height(1)) + .width(width) .height(1) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.neutral.color + theme.palette().background.neutral.color )), ..Default::default() }) .into() } -fn vsep<'a>() -> Element<'a, Message> { - container(Space::new().width(1).height(Fill)) +fn vsep<'a>(height: iced::Length) -> Element<'a, Message> { + container(Space::new().width(1).height(height)) .width(1) - .height(Fill) + .height(height) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.neutral.color + theme.palette().background.neutral.color )), ..Default::default() }) @@ -86,6 +86,7 @@ pub fn view_window<'a>( color_buf: &'a str, lw_buf: &'a str, screen_buf: &'a str, + sizing: crate::ui::modal::ModalSizing, ) -> Element<'a, Message> { let table_name = table .map(|t| t.name.as_str()) @@ -106,7 +107,7 @@ pub fn view_window<'a>( .on_press(Message::PlotStyleClear) .style(btn_s(false)) .padding([4, 10]), - Space::new().width(Fill), + Space::new().width(sizing.width), text(table_name).size(10).style(muted_style), ] .spacing(4) @@ -114,11 +115,11 @@ pub fn view_window<'a>( ) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.weak.color + theme.palette().background.weak.color )), ..Default::default() }) - .width(Fill) + .width(sizing.width) .padding([5, 8]); // ── Left: ACI list ──────────────────────────────────────────────────── @@ -153,7 +154,7 @@ pub fn view_window<'a>( button(text(label).size(10).font(iced::Font::MONOSPACE)) .on_press(Message::PlotStylePanelSelectAci(aci)) .style(move |theme: &Theme, st| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = match (is_sel, st) { (true, _) => Some(palette.primary.strong), (false, button::Status::Hovered | button::Status::Pressed) => { @@ -170,7 +171,7 @@ pub fn view_window<'a>( } }) .padding([2, 8]) - .width(Fill) + .width(sizing.width) .into() }) .collect(); @@ -178,9 +179,9 @@ pub fn view_window<'a>( let aci_list = container( column![ text("ACI Color Index").size(10).style(muted_style), - container(scrollable(column(aci_items).spacing(1)).height(Fill)) + container(scrollable(column(aci_items).spacing(1)).height(sizing.height)) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.weak.color)), border: Border { @@ -191,15 +192,15 @@ pub fn view_window<'a>( ..Default::default() } }) - .width(Fill) - .height(Fill) + .width(sizing.width) + .height(sizing.height) .padding(2), ] .spacing(4) - .height(Fill), + .height(sizing.height), ) .width(280) - .height(Fill) + .height(sizing.height) .padding(iced::Padding { top: 12.0, right: 8.0, @@ -261,29 +262,29 @@ pub fn view_window<'a>( text(format!(" Color: {cur_color}")).size(10), text(format!(" Lineweight: {cur_lw}")).size(10), text(format!(" Screening: {cur_scr}")).size(10), - Space::new().height(Fill), + Space::new().height(sizing.height), button(text("Apply to ACI").size(11)) .on_press(Message::PlotStylePanelApply) .style(btn_s(true)) .padding([5, 10]), ] .spacing(8) - .height(Fill), + .height(sizing.height), ) - .width(Fill) - .height(Fill) + .width(sizing.width) + .height(sizing.height) .padding([12, 12]); - let body = row![aci_list, vsep(), edit_panel].height(Fill); + let body = row![aci_list, vsep(sizing.height), edit_panel].height(sizing.height); - container(column![toolbar, hdivider(), body].spacing(0)) + container(column![toolbar, hdivider(sizing.width), body].spacing(0)) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.base.color + theme.palette().background.base.color )), ..Default::default() }) - .width(Fill) - .height(Fill) + .width(sizing.width) + .height(sizing.height) .into() } diff --git a/src/ui/style/point_style.rs b/src/ui/style/point_style.rs index 539b2317..05321070 100644 --- a/src/ui/style/point_style.rs +++ b/src/ui/style/point_style.rs @@ -34,7 +34,7 @@ impl canvas::Program for GlyphCanvas { _cursor: mouse::Cursor, ) -> Vec { let mut frame = canvas::Frame::new(renderer, bounds.size()); - let glyph = theme.extended_palette().background.base.text; + let glyph = theme.palette().background.base.text; let (cx, cy) = (bounds.width * 0.5, bounds.height * 0.5); let r = bounds.width.min(bounds.height) * 0.30; let stroke = canvas::Stroke { @@ -83,7 +83,7 @@ fn cell<'a>(value: i16, selected: bool) -> Element<'a, Message> { .padding(0) .on_press(Message::PointStyleSetMode(value)) .style(move |theme: &Theme, status| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = if selected { palette.primary.strong } else if matches!(status, button::Status::Hovered | button::Status::Pressed) { @@ -106,7 +106,7 @@ fn cell<'a>(value: i16, selected: bool) -> Element<'a, Message> { } fn field_style(theme: &Theme, status: text_input::Status) -> text_input::Style { - let palette = theme.extended_palette(); + let palette = theme.palette(); let border = match status { text_input::Status::Focused { .. } => palette.primary.base.color, _ => palette.background.neutral.color, @@ -127,11 +127,18 @@ fn field_style(theme: &Theme, status: text_input::Status) -> text_input::Style { fn muted_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().background.base.text.scale_alpha(0.68)), + color: Some(theme.palette().background.base.text.scale_alpha(0.68)), } } -pub fn view_window<'a>(pdmode: i16, relative: bool, size_buf: &str) -> Element<'a, Message> { +pub fn view_window<'a>( + pdmode: i16, + relative: bool, + size_buf: &str, + sizing: crate::ui::modal::ModalSizing, +) -> Element<'a, Message> { + let width = sizing.width; + let height = sizing.height; // Glyph grid: a row per enclosure, a cell per shape. let mut grid = column![].spacing(6); for enc in ENCLOSURES { @@ -192,16 +199,20 @@ pub fn view_window<'a>(pdmode: i16, relative: bool, size_buf: &str) -> Element<' Space::new().height(8), radios, Space::new().height(12), - row![Space::new().width(Length::Fill), ok], + row![Space::new().width(width), ok].width(width), ] .spacing(4) - .padding(20), + .padding(20) + .width(width) + .height(height), ) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.base.color + theme.palette().background.base.color )), ..Default::default() }) + .width(width) + .height(height) .into() } diff --git a/src/ui/style/scale_manager.rs b/src/ui/style/scale_manager.rs index fba9e389..b2065de1 100644 --- a/src/ui/style/scale_manager.rs +++ b/src/ui/style/scale_manager.rs @@ -12,7 +12,7 @@ use crate::ui::style::style_manager::{ use iced::widget::{ column, container, mouse_area, row, scrollable, text, text_input, Space, }; -use iced::{Background, Border, Element, Fill, Theme}; +use iced::{Background, Border, Element, Theme}; /// Inline-rename text-input id, so the rename-start handler can focus it. pub fn rename_input_id() -> iced::widget::Id { @@ -20,7 +20,7 @@ pub fn rename_input_id() -> iced::widget::Id { } fn field_style(theme: &Theme, status: text_input::Status) -> text_input::Style { - let palette = theme.extended_palette(); + let palette = theme.palette(); let border = match status { text_input::Status::Focused { .. } => palette.primary.base.color, _ => palette.background.neutral.color, @@ -49,6 +49,7 @@ pub fn view_window<'a, 'b>( rename_buf: &'a str, paper_buf: &'a str, drawing_buf: &'a str, + sizing: crate::ui::modal::ModalSizing, ) -> Element<'a, Message> { // ── Toolbar: New / Delete | Set Current / Apply ─────────────────────── let toolbar = container( @@ -56,7 +57,7 @@ pub fn view_window<'a, 'b>( tb_button("New", Message::ScaleManagerNew, false), tb_button("Copy", Message::ScaleManagerCopy, false), tb_button("Delete", Message::ScaleManagerDelete, false), - Space::new().width(Fill), + Space::new().width(sizing.width), tb_button("Set Current", Message::ScaleManagerSetCurrent, false), tb_button("Apply", Message::ScaleManagerApply, true), ] @@ -65,11 +66,11 @@ pub fn view_window<'a, 'b>( ) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.weak.color + theme.palette().background.weak.color )), ..Default::default() }) - .width(Fill) + .width(sizing.width) .padding([5, 8]); // ── Left: scale list ────────────────────────────────────────────────── @@ -85,7 +86,7 @@ pub fn view_window<'a, 'b>( .on_submit(Message::ScaleRenameCommit) .size(11) .padding([4, 8]) - .width(Fill) + .width(sizing.width) .into(); } let is_sel = name.as_str() == selected; @@ -93,16 +94,16 @@ pub fn view_window<'a, 'b>( let check = crate::ui::icons::themed_check_cell(is_cur); let label = row![ check, - text(name.clone()).size(11).width(Fill), + text(name.clone()).size(11).width(sizing.width), text(ratio.clone()).size(10).style(muted_text_style), ] .spacing(4) .align_y(iced::Center); let cell = container(label) .padding([4, 8]) - .width(Fill) + .width(sizing.width) .style(move |theme: &Theme| { - let pair = theme.extended_palette().primary.strong; + let pair = theme.palette().primary.strong; container::Style { background: is_sel.then_some(Background::Color(pair.color)), text_color: is_sel.then_some(pair.text), @@ -119,9 +120,9 @@ pub fn view_window<'a, 'b>( let list_panel = container( column![ text("Scales").size(10).style(muted_text_style), - container(scrollable(column(rows).spacing(1)).height(Fill)) + container(scrollable(column(rows).spacing(1)).height(sizing.height)) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.weak.color)), border: Border { @@ -132,15 +133,15 @@ pub fn view_window<'a, 'b>( ..Default::default() } }) - .width(Fill) - .height(Fill) + .width(sizing.width) + .height(sizing.height) .padding(2), ] .spacing(4) - .height(Fill), + .height(sizing.height), ) .width(190) - .height(Fill) + .height(sizing.height) .padding(iced::Padding { top: 12.0, right: 8.0, @@ -158,7 +159,7 @@ pub fn view_window<'a, 'b>( .style(field_style) .size(12) .padding([5, 8]) - .width(Fill), + .width(sizing.width), ] .align_y(iced::Center) .spacing(6) @@ -176,20 +177,20 @@ pub fn view_window<'a, 'b>( ] .spacing(8), ) - .width(Fill) - .height(Fill) + .width(sizing.width) + .height(sizing.height) .padding(12); - let body = row![list_panel, vsep(), editor].height(Fill); + let body = row![list_panel, vsep(sizing.height), editor].height(sizing.height); - container(column![toolbar, hdivider(), body]) + container(column![toolbar, hdivider(sizing.width), body]) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.base.color + theme.palette().background.base.color )), ..Default::default() }) - .width(Fill) - .height(Fill) + .width(sizing.width) + .height(sizing.height) .into() } diff --git a/src/ui/style/style_list.rs b/src/ui/style/style_list.rs index 14ab3ae9..b83afda7 100644 --- a/src/ui/style/style_list.rs +++ b/src/ui/style/style_list.rs @@ -49,7 +49,7 @@ pub fn item<'a>( .padding([4, 8]) .width(Fill) .style(move |theme: &Theme| { - let pair = theme.extended_palette().primary.strong; + let pair = theme.palette().primary.strong; container::Style { background: is_selected.then_some(Background::Color(pair.color)), text_color: is_selected.then_some(pair.text), diff --git a/src/ui/style/style_manager.rs b/src/ui/style/style_manager.rs index 499fe80d..3f656828 100644 --- a/src/ui/style/style_manager.rs +++ b/src/ui/style/style_manager.rs @@ -10,7 +10,7 @@ use crate::app::{Message, StyleKind}; use iced::widget::button::{Status, Style}; use iced::widget::{button, column, container, row, scrollable, text, Space}; -use iced::{Background, Border, Element, Fill, Theme}; +use iced::{Background, Border, Element, Theme}; /// Everything the shared frame needs. The per-manager `editor` element is the /// only bespoke part. @@ -24,6 +24,7 @@ use iced::{Background, Border, Element, Fill, Theme}; /// list data (`styles`, `selected`, …) that the frame only reads while building /// rows, so callers may pass a locally-built `Vec`. pub struct Scaffold<'a, 'b> { + pub sizing: crate::ui::modal::ModalSizing, pub kind: StyleKind, pub styles: &'b [String], pub selected: &'b str, @@ -46,12 +47,14 @@ pub struct Scaffold<'a, 'b> { } pub fn view<'a, 'b>(s: Scaffold<'a, 'b>) -> Element<'a, Message> { + let width = s.sizing.width; + let height = s.sizing.height; // ── Toolbar: New / Copy / Delete | … | Set Current / Apply ──────────── let bar = row![ tb_button("New", s.on_new, false), tb_button("Copy", s.on_copy, false), tb_button("Delete", s.on_delete, false), - Space::new().width(Fill), + Space::new().width(width), tb_button("Set Current", s.on_set_current, false), tb_button("Apply", s.on_apply, true), ] @@ -60,11 +63,11 @@ pub fn view<'a, 'b>(s: Scaffold<'a, 'b>) -> Element<'a, Message> { let toolbar = container(bar) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.weak.color + theme.palette().background.weak.color )), ..Default::default() }) - .width(Fill) + .width(width) .padding([5, 8]); // ── Left: style list (single click selects, double click renames) ───── @@ -89,9 +92,9 @@ pub fn view<'a, 'b>(s: Scaffold<'a, 'b>) -> Element<'a, Message> { let list_panel = container( column![ text("Styles").size(10).style(muted_text_style), - container(scrollable(column(rows).spacing(1)).height(Fill)) + container(scrollable(column(rows).spacing(1)).height(height)) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.weak.color)), border: Border { @@ -102,15 +105,15 @@ pub fn view<'a, 'b>(s: Scaffold<'a, 'b>) -> Element<'a, Message> { ..Default::default() } }) - .width(Fill) - .height(Fill) + .width(width) + .height(height) .padding(2), ] .spacing(4) - .height(Fill), + .height(height), ) .width(170) - .height(Fill) + .height(height) .padding(iced::Padding { top: 12.0, right: 8.0, @@ -118,17 +121,17 @@ pub fn view<'a, 'b>(s: Scaffold<'a, 'b>) -> Element<'a, Message> { left: 12.0, }); - let body = row![list_panel, vsep(), s.editor].height(Fill); + let body = row![list_panel, vsep(height), s.editor].height(height); - container(column![toolbar, hdivider(), body]) + container(column![toolbar, hdivider(width), body]) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.base.color + theme.palette().background.base.color )), ..Default::default() }) - .width(Fill) - .height(Fill) + .width(width) + .height(height) .into() } @@ -145,7 +148,7 @@ pub(crate) fn tb_button<'a>(label: &'a str, msg: Message, accent: bool) -> Eleme fn btn_s(accent: bool) -> impl Fn(&Theme, Status) -> Style { move |theme: &Theme, st| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = match (accent, st) { (true, Status::Hovered | Status::Pressed) => palette.primary.strong, (false, Status::Hovered | Status::Pressed) => palette.background.strong, @@ -167,30 +170,30 @@ fn btn_s(accent: bool) -> impl Fn(&Theme, Status) -> Style { pub(crate) fn muted_text_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().background.base.text.scale_alpha(0.68)), + color: Some(theme.palette().background.base.text.scale_alpha(0.68)), } } -pub(crate) fn hdivider<'a>() -> Element<'a, Message> { - container(Space::new().width(Fill).height(1)) - .width(Fill) +pub(crate) fn hdivider<'a>(width: iced::Length) -> Element<'a, Message> { + container(Space::new().width(width).height(1)) + .width(width) .height(1) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.neutral.color + theme.palette().background.neutral.color )), ..Default::default() }) .into() } -pub(crate) fn vsep<'a>() -> Element<'a, Message> { - container(Space::new().width(1).height(Fill)) +pub(crate) fn vsep<'a>(height: iced::Length) -> Element<'a, Message> { + container(Space::new().width(1).height(height)) .width(1) - .height(Fill) + .height(height) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.neutral.color + theme.palette().background.neutral.color )), ..Default::default() }) diff --git a/src/ui/style/tablestyle.rs b/src/ui/style/tablestyle.rs index b6750524..9f4c3265 100644 --- a/src/ui/style/tablestyle.rs +++ b/src/ui/style/tablestyle.rs @@ -2,13 +2,13 @@ use crate::app::Message; use iced::widget::{ - button, checkbox, column, container, pick_list, row, scrollable, text, text_input, Column, + button, checkbox, column, container, row, scrollable, text, text_input, Column, }; -use iced::{Background, Border, Element, Fill, Theme}; +use iced::{Background, Border, Element, Theme}; fn btn_s(accent: bool) -> impl Fn(&Theme, button::Status) -> button::Style { move |theme: &Theme, st| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = match (accent, st) { (true, button::Status::Hovered | button::Status::Pressed) => palette.primary.strong, (false, button::Status::Hovered | button::Status::Pressed) => { @@ -32,13 +32,13 @@ fn btn_s(accent: bool) -> impl Fn(&Theme, button::Status) -> button::Style { fn muted_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().background.base.text.scale_alpha(0.68)), + color: Some(theme.palette().background.base.text.scale_alpha(0.68)), } } fn primary_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().primary.base.color), + color: Some(theme.palette().primary.base.color), } } @@ -63,6 +63,7 @@ pub fn view_window<'a>( rename_active: Option<&'a str>, rename_buf: &'a str, color_open: Option<(u8, &'static str)>, + sizing: crate::ui::modal::ModalSizing, ) -> Element<'a, Message> { // ── Right: Details panel ────────────────────────────────────────────── let info_row = |label: &'static str, val: String| -> Element<'_, Message> { @@ -140,7 +141,7 @@ pub fn view_window<'a>( .push( row![ text(" Alignment:").size(11).style(muted_style).width(150), - pick_list( + crate::ui::pick_list( [ "TopLeft", "TopCenter", @@ -193,7 +194,7 @@ pub fn view_window<'a>( col = col.push( row![ text(format!(" {bname}")).size(11).style(muted_style).width(28), - pick_list( + crate::ui::pick_list( ["Single", "Double"] .iter() .map(|s| s.to_string()) @@ -273,7 +274,7 @@ pub fn view_window<'a>( .align_y(iced::Center), row![ text("Flow direction:").size(11).style(muted_style).width(160), - pick_list( + crate::ui::pick_list( ["Down", "Up"] .iter() .map(|s| s.to_string()) @@ -336,8 +337,8 @@ pub fn view_window<'a>( .spacing(6) .padding([12, 12]), ) - .width(Fill) - .height(Fill) + .width(sizing.width) + .height(sizing.height) .into() } else { container(text("Select a style to view details.").size(11).style(muted_style)) @@ -345,9 +346,12 @@ pub fn view_window<'a>( .into() }; - let right_panel = container(details).width(Fill).height(Fill); + let right_panel = container(details) + .width(sizing.width) + .height(sizing.height); crate::ui::style::style_manager::view(crate::ui::style::style_manager::Scaffold { + sizing, kind: crate::app::StyleKind::Table, styles: &styles, selected, diff --git a/src/ui/style/textstyle.rs b/src/ui/style/textstyle.rs index 9983f6fd..823ab571 100644 --- a/src/ui/style/textstyle.rs +++ b/src/ui/style/textstyle.rs @@ -5,7 +5,7 @@ use crate::app::StyleKind; use iced::widget::{ button, canvas, checkbox, column, container, row, scrollable, text, text_input, Space, }; -use iced::{mouse, Background, Border, Element, Fill, Length, Point, Rectangle, Theme}; +use iced::{mouse, Background, Border, Element, Length, Point, Rectangle, Theme}; /// View-model for the Text Style editor window. pub struct TextStyleView<'a> { @@ -36,7 +36,7 @@ const BUILTIN_FONTS: &[&str] = &[ fn list_item(active: bool) -> impl Fn(&Theme, button::Status) -> button::Style { move |theme: &Theme, st| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = match (active, st) { (true, _) => Some(palette.primary.strong), (false, button::Status::Hovered | button::Status::Pressed) => { @@ -53,7 +53,7 @@ fn list_item(active: bool) -> impl Fn(&Theme, button::Status) -> button::Style { } fn field_style(theme: &Theme, status: text_input::Status) -> text_input::Style { - let palette = theme.extended_palette(); + let palette = theme.palette(); let border = match status { text_input::Status::Focused { .. } => palette.primary.base.color, _ => palette.background.neutral.color, @@ -74,23 +74,23 @@ fn field_style(theme: &Theme, status: text_input::Status) -> text_input::Style { fn muted_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().background.base.text.scale_alpha(0.68)), + color: Some(theme.palette().background.base.text.scale_alpha(0.68)), } } fn primary_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().primary.base.color), + color: Some(theme.palette().primary.base.color), } } -fn vsep<'a>() -> Element<'a, Message> { - container(Space::new().width(1).height(Fill)) +fn vsep<'a>(height: Length) -> Element<'a, Message> { + container(Space::new().width(1).height(height)) .width(1) - .height(Fill) + .height(height) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.neutral.color + theme.palette().background.neutral.color )), ..Default::default() }) @@ -162,7 +162,7 @@ impl canvas::Program for TextPreviewCanvas { }; let stroke = canvas::Stroke { width: 1.4, - style: canvas::Style::Solid(theme.extended_palette().background.base.text), + style: canvas::Style::Solid(theme.palette().background.base.text), ..Default::default() }; for s in &strokes { @@ -181,7 +181,10 @@ impl canvas::Program for TextPreviewCanvas { } } -pub fn view_window<'a>(v: TextStyleView<'a>) -> Element<'a, Message> { +pub fn view_window<'a>( + v: TextStyleView<'a>, + sizing: crate::ui::modal::ModalSizing, +) -> Element<'a, Message> { let TextStyleView { styles, selected, @@ -207,7 +210,7 @@ pub fn view_window<'a>(v: TextStyleView<'a>) -> Element<'a, Message> { .on_press(Message::TextStyleFontPick(f.to_string())) .style(list_item(is_sel)) .padding([3, 8]) - .width(Fill) + .width(sizing.width) .into() }) .collect(); @@ -215,9 +218,9 @@ pub fn view_window<'a>(v: TextStyleView<'a>) -> Element<'a, Message> { let font_panel = container( column![ text("Font File").size(10).style(muted_style), - container(scrollable(column(font_items).spacing(1)).height(Fill)) + container(scrollable(column(font_items).spacing(1)).height(sizing.height)) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.weak.color)), border: Border { @@ -228,8 +231,8 @@ pub fn view_window<'a>(v: TextStyleView<'a>) -> Element<'a, Message> { ..Default::default() } }) - .width(Fill) - .height(Fill) + .width(sizing.width) + .height(sizing.height) .padding(2), text_input("font file…", font_buf) .on_input(|v| Message::TextStyleEdit { @@ -238,13 +241,13 @@ pub fn view_window<'a>(v: TextStyleView<'a>) -> Element<'a, Message> { }) .style(field_style) .size(11) - .width(Fill), + .width(sizing.width), ] .spacing(6) - .height(Fill), + .height(sizing.height), ) .width(190) - .height(Fill) + .height(sizing.height) .padding([12, 8]); // Labeled numeric/text field row → TextStyleEdit { field, value }. @@ -286,7 +289,7 @@ pub fn view_window<'a>(v: TextStyleView<'a>) -> Element<'a, Message> { oblique: oblique_buf.trim().parse::().unwrap_or(0.0).to_radians(), rotation: if upside_down { std::f32::consts::PI } else { 0.0 }, }) - .width(Fill) + .width(sizing.width) .height(Length::Fixed(56.0)); // ── Right: Properties ───────────────────────────────────────────────── @@ -320,7 +323,7 @@ pub fn view_window<'a>(v: TextStyleView<'a>) -> Element<'a, Message> { text("Preview").size(10).style(muted_style), container(preview) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.base.color)), border: Border { @@ -332,13 +335,13 @@ pub fn view_window<'a>(v: TextStyleView<'a>) -> Element<'a, Message> { } }) .padding(8) - .width(Fill), + .width(sizing.width), ] .spacing(10) - .height(Fill), + .height(sizing.height), ) - .width(Fill) - .height(Fill) + .width(sizing.width) + .height(sizing.height) .padding(iced::Padding { top: 12.0, right: 12.0, @@ -358,7 +361,7 @@ pub fn view_window<'a>(v: TextStyleView<'a>) -> Element<'a, Message> { }) .style(list_item(is_sel)) .padding([3, 8]) - .width(Fill) + .width(sizing.width) .into() }) .collect(); @@ -366,9 +369,9 @@ pub fn view_window<'a>(v: TextStyleView<'a>) -> Element<'a, Message> { let ttf_panel = container( column![ text("TrueType (system)").size(10).style(muted_style), - container(scrollable(column(ttf_items).spacing(1)).height(Fill)) + container(scrollable(column(ttf_items).spacing(1)).height(sizing.height)) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.weak.color)), border: Border { @@ -379,8 +382,8 @@ pub fn view_window<'a>(v: TextStyleView<'a>) -> Element<'a, Message> { ..Default::default() } }) - .width(Fill) - .height(Fill) + .width(sizing.width) + .height(sizing.height) .padding(2), text_input("TrueType font…", ttf_buf) .on_input(|v| Message::TextStyleEdit { @@ -389,18 +392,26 @@ pub fn view_window<'a>(v: TextStyleView<'a>) -> Element<'a, Message> { }) .style(field_style) .size(11) - .width(Fill), + .width(sizing.width), ] .spacing(6) - .height(Fill), + .height(sizing.height), ) .width(190) - .height(Fill) + .height(sizing.height) .padding([12, 8]); - let editor = row![font_panel, vsep(), ttf_panel, vsep(), props_panel].height(Fill); + let editor = row![ + font_panel, + vsep(sizing.height), + ttf_panel, + vsep(sizing.height), + props_panel + ] + .height(sizing.height); crate::ui::style::style_manager::view(crate::ui::style::style_manager::Scaffold { + sizing, kind: StyleKind::Text, styles: &styles, selected, diff --git a/src/ui/window/about.rs b/src/ui/window/about.rs index 53c3d431..d2ad33f3 100644 --- a/src/ui/window/about.rs +++ b/src/ui/window/about.rs @@ -7,7 +7,7 @@ fn info_row<'a>(label: &'static str, value: String) -> Element<'a, Message> { text(label) .size(11) .style(|theme: &Theme| iced::widget::text::Style { - color: Some(theme.extended_palette().background.base.text.scale_alpha(0.68)), + color: Some(theme.palette().background.base.text.scale_alpha(0.68)), }) .width(100), text(value).size(11), @@ -28,12 +28,12 @@ pub fn view_window<'a>() -> Element<'a, Message> { text("Open CAD Studio") .size(32) .style(|theme: &Theme| iced::widget::text::Style { - color: Some(theme.extended_palette().primary.base.color), + color: Some(theme.palette().primary.base.color), }), text("CAD application for Architecture & Engineering") .size(11) .style(|theme: &Theme| iced::widget::text::Style { - color: Some(theme.extended_palette().background.base.text.scale_alpha(0.68)), + color: Some(theme.palette().background.base.text.scale_alpha(0.68)), }), ] .spacing(4) @@ -84,7 +84,7 @@ pub fn view_window<'a>() -> Element<'a, Message> { ) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.base.color, + theme.palette().background.base.color, )), ..Default::default() }) diff --git a/src/ui/window/alias_editor.rs b/src/ui/window/alias_editor.rs index 6f453802..c361a050 100644 --- a/src/ui/window/alias_editor.rs +++ b/src/ui/window/alias_editor.rs @@ -6,7 +6,7 @@ use crate::app::Message; use iced::widget::{button, column, container, row, scrollable, text, text_input, Space}; -use iced::{Background, Element, Fill, Length, Theme}; +use iced::{Background, Element, Length, Theme}; /// Which column of an alias row a text edit targets. #[derive(Clone, Copy, Debug)] @@ -20,12 +20,15 @@ const GUTTER: f32 = 16.0; fn muted_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().background.base.text.scale_alpha(0.68)), + color: Some(theme.palette().background.base.text.scale_alpha(0.68)), } } /// Build the alias editor content. `rows` is the live working buffer. -pub fn view_window(rows: &[(String, String)]) -> Element<'_, Message> { +pub fn view_window( + rows: &[(String, String)], + sizing: crate::ui::modal::ModalSizing, +) -> Element<'_, Message> { let title = text("Command Aliases").size(15); let hint = text( "Type an alias and the command it runs (e.g. L → LINE). \ @@ -42,7 +45,7 @@ pub fn view_window(rows: &[(String, String)]) -> Element<'_, Message> { let head = container( row![ container(text("Alias").size(11).style(muted_style)).width(Length::Fixed(120.0)), - container(text("Command").size(11).style(muted_style)).width(Fill), + container(text("Command").size(11).style(muted_style)).width(sizing.width), Space::new().width(Length::Fixed(30.0)), ] .spacing(8), @@ -60,7 +63,7 @@ pub fn view_window(rows: &[(String, String)]) -> Element<'_, Message> { .on_input(move |v| Message::AliasEditorInput { idx, field: AliasField::Command, value: v }) .size(13) .padding([3, 6]) - .width(Fill); + .width(sizing.width); let del = button(crate::ui::icons::themed_danger(crate::ui::icons::CLOSE, 12.0)) .on_press(Message::AliasEditorRemove(idx)) .padding([2, 6]) @@ -89,20 +92,20 @@ pub fn view_window(rows: &[(String, String)]) -> Element<'_, Message> { hint, Space::new().height(6), head, - scrollable(container(list).padding(gutter)).height(Fill), + scrollable(container(list).padding(gutter)).height(sizing.height), Space::new().height(6), - row![add, Space::new().width(Fill), apply].align_y(iced::Center), + row![add, Space::new().width(sizing.width), apply].align_y(iced::Center), ] .spacing(6) - .width(Fill) - .height(Fill), + .width(sizing.width) + .height(sizing.height), ) .padding(12) - .width(Fill) - .height(Fill) + .width(sizing.width) + .height(sizing.height) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.base.color, + theme.palette().background.base.color, )), ..Default::default() }) diff --git a/src/ui/window/attribute_editor.rs b/src/ui/window/attribute_editor.rs index ba3c1915..7a807814 100644 --- a/src/ui/window/attribute_editor.rs +++ b/src/ui/window/attribute_editor.rs @@ -17,7 +17,7 @@ use crate::app::Message; use acadrust::entities::{HorizontalAlignment, VerticalAlignment}; use acadrust::types::{Color as AcadColor, LineWeight}; use iced::widget::{ - button, checkbox, column, container, pick_list, row, scrollable, text, text_input, Space, + button, checkbox, column, container, row, scrollable, text, text_input, Space, }; use iced::{Background, Border, Element, Length, Theme}; @@ -135,12 +135,12 @@ pub fn color_from_label(label: &str) -> Option { fn muted_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().background.base.text.scale_alpha(0.68)), + color: Some(theme.palette().background.base.text.scale_alpha(0.68)), } } fn field_style(theme: &Theme, status: text_input::Status) -> text_input::Style { - let palette = theme.extended_palette(); + let palette = theme.palette(); let border = match status { text_input::Status::Focused { .. } => palette.primary.base.color, _ => palette.background.neutral.color, @@ -156,13 +156,13 @@ fn field_style(theme: &Theme, status: text_input::Status) -> text_input::Style { } /// Horizontal 1px divider in the shared border colour (matches style windows). -fn hdivider<'a>() -> Element<'a, Message> { - container(Space::new().width(Length::Fill).height(1)) - .width(Length::Fill) +fn hdivider<'a>(width: Length) -> Element<'a, Message> { + container(Space::new().width(width).height(1)) + .width(width) .height(1) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.neutral.color + theme.palette().background.neutral.color )), ..Default::default() }) @@ -170,13 +170,18 @@ fn hdivider<'a>() -> Element<'a, Message> { } /// One `label : widget` row with a fixed-width label column. -fn field_row<'a>(label: &'a str, widget: Element<'a, Message>) -> Element<'a, Message> { +fn field_row<'a>( + label: &'a str, + widget: Element<'a, Message>, + width: Length, +) -> Element<'a, Message> { row![ container(text(label).size(12).style(muted_style)).width(LABEL_W), widget, ] .spacing(8) .align_y(iced::Center) + .width(width) .into() } @@ -185,6 +190,7 @@ fn edit_field<'a>( label: &'a str, value: &'a str, on_input: impl Fn(String) -> Message + 'a, + width: Length, ) -> Element<'a, Message> { let ti = text_input("", value) .on_input(on_input) @@ -192,8 +198,8 @@ fn edit_field<'a>( .style(field_style) .size(13) .padding([3, 6]) - .width(Length::Fill); - field_row(label, ti.into()) + .width(width); + field_row(label, ti.into(), width) } /// A labelled pick_list of owned string options. @@ -202,12 +208,13 @@ fn pick_field<'a>( options: Vec, selected: Option, on_select: impl Fn(String) -> Message + 'a, + width: Length, ) -> Element<'a, Message> { - let pl = pick_list(options, selected, on_select) + let pl = crate::ui::pick_list(options, selected, on_select) .text_size(13) .padding([3, 6]) - .width(Length::Fill); - field_row(label, pl.into()) + .width(width); + field_row(label, pl.into(), width) } fn tab_button<'a>(label: &'a str, this: AttrTab, active: AttrTab) -> Element<'a, Message> { @@ -216,7 +223,7 @@ fn tab_button<'a>(label: &'a str, this: AttrTab, active: AttrTab) -> Element<'a, .padding([4, 12]) .on_press(Message::AttrEditorTab(this)) .style(move |theme: &Theme, status| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = match (is_active, status) { (true, _) => palette.primary.strong, (false, button::Status::Hovered | button::Status::Pressed) => { @@ -250,7 +257,10 @@ pub fn view_window<'a>( layers: Vec, linetypes: Vec, styles: Vec, + sizing: crate::ui::modal::ModalSizing, ) -> Element<'a, Message> { + let width = sizing.width; + let height = sizing.height; // ── Top toolbar: block name on the left, Apply on the right ─────────── // Mirrors the style-manager windows (actions left, primary action right). let apply = button(text("Apply").size(11)) @@ -260,18 +270,18 @@ pub fn view_window<'a>( let toolbar = container( row![ text(format!("Block: {block}")).size(12).style(muted_style), - Space::new().width(Length::Fill), + Space::new().width(width), apply, ] .align_y(iced::Center), ) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.weak.color + theme.palette().background.weak.color )), ..Default::default() }) - .width(Length::Fill) + .width(width) .padding([5, 8]); let tabs = row![ @@ -285,36 +295,49 @@ pub fn view_window<'a>( text("This block has no attributes.").size(13).style(muted_style).into() } else { match tab { - AttrTab::Attribute => attribute_tab(rows, selected), - AttrTab::TextOptions => text_options_tab(&rows[selected.min(rows.len() - 1)], styles), + AttrTab::Attribute => attribute_tab(rows, selected, width, height), + AttrTab::TextOptions => { + text_options_tab(&rows[selected.min(rows.len() - 1)], styles, width, height) + } AttrTab::Properties => { - properties_tab(&rows[selected.min(rows.len() - 1)], layers, linetypes) + properties_tab( + &rows[selected.min(rows.len() - 1)], + layers, + linetypes, + width, + height, + ) } } }; let content = container(column![tabs, body].spacing(8)) - .width(Length::Fill) - .height(Length::Fill) + .width(width) + .height(height) .padding(12); - container(column![toolbar, hdivider(), content]) + container(column![toolbar, hdivider(width), content]) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.base.color + theme.palette().background.base.color )), ..Default::default() }) - .width(Length::Fill) - .height(Length::Fill) + .width(width) + .height(height) .into() } /// Attribute tab: tag / prompt / value list with row-select, plus a value box. -fn attribute_tab<'a>(rows: &'a [AttrRow], selected: usize) -> Element<'a, Message> { +fn attribute_tab<'a>( + rows: &'a [AttrRow], + selected: usize, + width: Length, + height: Length, +) -> Element<'a, Message> { let head = row![ container(text("Tag").size(11).style(muted_style)).width(130), - container(text("Prompt").size(11).style(muted_style)).width(Length::Fill), + container(text("Prompt").size(11).style(muted_style)).width(width), container(text("Value").size(11).style(muted_style)).width(140), ] .spacing(6); @@ -324,16 +347,16 @@ fn attribute_tab<'a>(rows: &'a [AttrRow], selected: usize) -> Element<'a, Messag let is_sel = idx == selected; let line = row![ container(text(r.tag.as_str()).size(12)).width(130), - container(text(r.prompt.as_str()).size(12).style(muted_style)).width(Length::Fill), + container(text(r.prompt.as_str()).size(12).style(muted_style)).width(width), container(text(r.value.as_str()).size(12)).width(140), ] .spacing(6); let btn = button(line) .on_press(Message::AttrEditorSelect(idx)) .padding([3, 4]) - .width(Length::Fill) + .width(width) .style(move |theme: &Theme, status| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let hovered = matches!(status, button::Status::Hovered); let pair = if is_sel { palette.primary.strong @@ -359,20 +382,27 @@ fn attribute_tab<'a>(rows: &'a [AttrRow], selected: usize) -> Element<'a, Messag .style(field_style) .size(13) .padding([4, 6]) - .width(Length::Fill); + .width(width); column![ head, - scrollable(list).height(Length::Fill), + scrollable(list).width(width).height(height), Space::new().height(8), - field_row("Value:", value_box.into()), + field_row("Value:", value_box.into(), width), ] .spacing(6) + .width(width) + .height(height) .into() } /// Text Options tab: the selected attribute's text formatting. -fn text_options_tab<'a>(r: &'a AttrRow, styles: Vec) -> Element<'a, Message> { +fn text_options_tab<'a>( + r: &'a AttrRow, + styles: Vec, + width: Length, + height: Length, +) -> Element<'a, Message> { let style_sel = if r.text_style.is_empty() { None } else { @@ -384,14 +414,14 @@ fn text_options_tab<'a>(r: &'a AttrRow, styles: Vec) -> Element<'a, Mess column![ pick_field("Text Style", styles, style_sel, |s| { Message::AttrEditorTextStyle(s) - }), + }, width), pick_field("Justification", justify_opts, justify_sel, |s| { Message::AttrEditorJustify(s) - }), - edit_field("Height", &r.height, Message::AttrEditorHeight), - edit_field("Rotation", &r.rotation, Message::AttrEditorRotation), - edit_field("Width Factor", &r.width_factor, Message::AttrEditorWidth), - edit_field("Oblique Angle", &r.oblique, Message::AttrEditorOblique), + }, width), + edit_field("Height", &r.height, Message::AttrEditorHeight, width), + edit_field("Rotation", &r.rotation, Message::AttrEditorRotation, width), + edit_field("Width Factor", &r.width_factor, Message::AttrEditorWidth, width), + edit_field("Oblique Angle", &r.oblique, Message::AttrEditorOblique, width), field_row( "", checkbox(r.backwards) @@ -400,6 +430,7 @@ fn text_options_tab<'a>(r: &'a AttrRow, styles: Vec) -> Element<'a, Mess .size(15) .text_size(12) .into(), + width, ), field_row( "", @@ -409,9 +440,12 @@ fn text_options_tab<'a>(r: &'a AttrRow, styles: Vec) -> Element<'a, Mess .size(15) .text_size(12) .into(), + width, ), ] .spacing(8) + .width(width) + .height(height) .into() } @@ -420,6 +454,8 @@ fn properties_tab<'a>( r: &'a AttrRow, layers: Vec, linetypes: Vec, + width: Length, + height: Length, ) -> Element<'a, Message> { let layer_sel = Some(r.layer.clone()); let lt_sel = Some(if r.linetype.is_empty() { @@ -439,21 +475,23 @@ fn properties_tab<'a>( let lw_opts = lw_options(); let lw_sel = LwItem(r.line_weight); - let lw = pick_list(lw_opts, Some(lw_sel), |it: LwItem| { + let lw = crate::ui::pick_list(lw_opts, Some(lw_sel), |it: LwItem| { Message::AttrEditorLineweight(it.0) }) .text_size(13) .padding([3, 6]) - .width(Length::Fill); + .width(width); column![ - pick_field("Layer", layers, layer_sel, Message::AttrEditorLayer), - pick_field("Linetype", linetypes, lt_sel, Message::AttrEditorLinetype), + pick_field("Layer", layers, layer_sel, Message::AttrEditorLayer, width), + pick_field("Linetype", linetypes, lt_sel, Message::AttrEditorLinetype, width), pick_field("Color", color_opts, color_sel, |s| { Message::AttrEditorColor(s) - }), - field_row("Lineweight", lw.into()), + }, width), + field_row("Lineweight", lw.into(), width), ] .spacing(8) + .width(width) + .height(height) .into() } diff --git a/src/ui/window/layer_state_manager.rs b/src/ui/window/layer_state_manager.rs index 04c2543f..f1a608c0 100644 --- a/src/ui/window/layer_state_manager.rs +++ b/src/ui/window/layer_state_manager.rs @@ -4,7 +4,7 @@ use crate::app::{LayerStateLayerFlag, LayerStateProperty, Message}; use crate::ui::properties::{lw_options, LwItem}; use acadrust::{LayerState, LayerStateMask}; use iced::widget::{ - button, checkbox, column, container, pick_list, row, scrollable, text, text_input, Space, + button, checkbox, column, container, row, scrollable, text, text_input, Space, }; use iced::{Background, Border, Element, Fill, Length, Theme}; use std::fmt; @@ -39,7 +39,7 @@ fn muted(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { color: Some( theme - .extended_palette() + .palette() .background .base .text @@ -68,13 +68,13 @@ fn list_style(selected: bool) -> impl Fn(&Theme, button::Status) -> button::Styl } } -fn divider<'a>() -> Element<'a, Message> { - container(Space::new().width(Fill).height(1)) - .width(Fill) +fn divider<'a>(width: Length) -> Element<'a, Message> { + container(Space::new().width(width).height(1)) + .width(width) .height(1) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.neutral.color, + theme.palette().background.neutral.color, )), ..Default::default() }) @@ -107,6 +107,7 @@ pub fn view_window<'a>( name: &'a str, description: &'a str, filter: &'a str, + sizing: crate::ui::modal::ModalSizing, ) -> Element<'a, Message> { let selected_state = selected.and_then(|selected| { states @@ -139,7 +140,7 @@ pub fn view_window<'a>( .on_press(Message::LayerStateManagerSelect(state.name.clone())) .style(list_style(is_selected)) .padding([6, 9]) - .width(Fill) + .width(sizing.width) .into() }) .collect(); @@ -165,7 +166,9 @@ pub fn view_window<'a>( let state_list: Element<'_, Message> = if rows.is_empty() { empty } else { - scrollable(column(rows).spacing(2)).height(Fill).into() + scrollable(column(rows).spacing(2)) + .height(sizing.height) + .into() }; let left = container( @@ -175,12 +178,12 @@ pub fn view_window<'a>( .size(11) .padding([5, 8]), container(state_list) - .width(Fill) - .height(Fill) + .width(sizing.width) + .height(sizing.height) .padding(3) .style(|theme: &Theme| container::Style { border: Border { - color: theme.extended_palette().background.neutral.color, + color: theme.palette().background.neutral.color, width: 1.0, radius: 3.0.into(), }, @@ -188,10 +191,10 @@ pub fn view_window<'a>( }), ] .spacing(8) - .height(Fill), + .height(sizing.height), ) .width(280) - .height(Fill) + .height(sizing.height) .padding(iced::Padding { top: 12.0, right: 8.0, @@ -261,13 +264,13 @@ pub fn view_window<'a>( .style(button_style(false)) .padding([5, 12]), edit.padding([5, 12]), - Space::new().width(Fill), + Space::new().width(sizing.width), restore.padding([5, 12]), delete.padding([5, 12]), ] .spacing(6) .align_y(iced::Center), - divider(), + divider(sizing.width), text("Name").size(10).style(muted), text_input("Layer state name", name) .on_input(Message::LayerStateManagerName) @@ -281,7 +284,7 @@ pub fn view_window<'a>( .padding([5, 8]), Space::new().height(8), details, - Space::new().height(Fill), + Space::new().height(sizing.height), text("Layer states are stored inside the drawing and remain available after reopening it.") .size(10) .style(muted), @@ -296,15 +299,15 @@ pub fn view_window<'a>( .padding([6, 14]), ] .spacing(7) - .height(Fill), + .height(sizing.height), ) - .width(Fill) - .height(Fill) + .width(sizing.width) + .height(sizing.height) .padding([12, 12]); - container(row![left, right].height(Fill)) - .width(Fill) - .height(Fill) + container(row![left, right].height(sizing.height)) + .width(sizing.width) + .height(sizing.height) .into() } @@ -380,10 +383,10 @@ fn editor_header<'a>() -> Element<'a, Message> { .padding([5, 8]) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.weak.color, + theme.palette().background.weak.color, )), border: Border { - color: theme.extended_palette().background.neutral.color, + color: theme.palette().background.neutral.color, width: 1.0, radius: 0.0.into(), }, @@ -429,13 +432,13 @@ fn editor_layer_row<'a>( 54.0 ), container(color).width(Length::Fixed(135.0)), - pick_list(linetypes, current_linetype, move |value| { + crate::ui::pick_list(linetypes, current_linetype, move |value| { Message::LayerStateEditorLayerLinetype(index, value) }) .text_size(11) .padding([3, 5]) .width(Length::Fixed(150.0)), - pick_list(lw_options(), current_lineweight, move |item: LwItem| { + crate::ui::pick_list(lw_options(), current_lineweight, move |item: LwItem| { Message::LayerStateEditorLayerLineweight(index, item.0) }) .text_size(11) @@ -446,7 +449,7 @@ fn editor_layer_row<'a>( .size(11) .padding([3, 5]) .width(Length::Fixed(135.0)), - pick_list( + crate::ui::pick_list( transparency_options(layer.transparency), Some(TransparencyItem(layer.transparency)), move |item| Message::LayerStateEditorLayerTransparency(index, item.0), @@ -461,7 +464,7 @@ fn editor_layer_row<'a>( .padding([3, 8]) .style(move |theme: &Theme| container::Style { background: (index % 2 == 1).then_some(Background::Color( - theme.extended_palette().background.weak.color, + theme.palette().background.weak.color, )), ..Default::default() }) @@ -474,6 +477,7 @@ pub fn view_editor<'a>( filter: &'a str, color_open: Option, linetypes: Vec, + sizing: crate::ui::modal::ModalSizing, ) -> Element<'a, Message> { let properties = [ ("On / Off", LayerStateProperty::On), @@ -529,7 +533,7 @@ pub fn view_editor<'a>( .on_input(Message::LayerStateEditorDescription) .size(11) .padding([3, 6]) - .width(Fill), + .width(sizing.width), ] .spacing(8) .align_y(iced::Center), @@ -537,9 +541,9 @@ pub fn view_editor<'a>( text(format!("{} saved layers", state.layers.len())) .size(10) .style(muted), - Space::new().width(Fill), + Space::new().width(sizing.width), text("Current layer").size(10).style(muted), - pick_list( + crate::ui::pick_list( layer_names, Some(state.current_layer.clone()), Message::LayerStateEditorCurrentLayer, @@ -550,12 +554,12 @@ pub fn view_editor<'a>( ] .spacing(8) .align_y(iced::Center), - divider(), + divider(sizing.width), text("Properties restored by this state").size(10).style(muted), mask_controls, row![ text("Saved layer values").size(12), - Space::new().width(Fill), + Space::new().width(sizing.width), text_input("Search layers…", filter) .on_input(Message::LayerStateEditorFilter) .size(11) @@ -564,14 +568,14 @@ pub fn view_editor<'a>( ] .align_y(iced::Center), container( - column![editor_header(), scrollable(rows).height(Fill)] + column![editor_header(), scrollable(rows).height(sizing.height)] .spacing(0) - .height(Fill), + .height(sizing.height), ) - .height(Fill) + .height(sizing.height) .style(|theme: &Theme| container::Style { border: Border { - color: theme.extended_palette().background.neutral.color, + color: theme.palette().background.neutral.color, width: 1.0, radius: 3.0.into(), }, @@ -581,7 +585,7 @@ pub fn view_editor<'a>( text("Changes affect the saved state only; the drawing is unchanged until Restore.") .size(10) .style(muted), - Space::new().width(Fill), + Space::new().width(sizing.width), button(text("Cancel").size(11)) .on_press(Message::LayerStateEditorCancel) .style(button_style(false)) @@ -595,10 +599,10 @@ pub fn view_editor<'a>( .align_y(iced::Center), ] .spacing(8) - .height(Fill), + .height(sizing.height), ) .padding(12) - .width(Fill) - .height(Fill) + .width(sizing.width) + .height(sizing.height) .into() } diff --git a/src/ui/window/layers.rs b/src/ui/window/layers.rs index 1f299488..1c637893 100644 --- a/src/ui/window/layers.rs +++ b/src/ui/window/layers.rs @@ -49,7 +49,7 @@ pub const LAYER_TABLE_SCROLL_ID: &str = "layer-manager-table-scroll"; fn muted_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().background.base.text.scale_alpha(0.68)), + color: Some(theme.palette().background.base.text.scale_alpha(0.68)), } } @@ -57,7 +57,7 @@ fn table_input_style( theme: &Theme, status: iced::widget::text_input::Status, ) -> iced::widget::text_input::Style { - let palette = theme.extended_palette(); + let palette = theme.palette(); let border = match status { iced::widget::text_input::Status::Focused { .. } => palette.primary.base.color, _ => palette.background.neutral.color, @@ -308,11 +308,19 @@ impl LayerPanel { } /// Render the layer panel as the full content of its own OS window. - pub fn view_window(&self, name_col_w: f32) -> Element<'_, Message> { - self.view_content(name_col_w) + pub fn view_window( + &self, + name_col_w: f32, + sizing: crate::ui::modal::ModalSizing, + ) -> Element<'_, Message> { + self.view_content(name_col_w, sizing) } - fn view_content(&self, name_col_w: f32) -> Element<'_, Message> { + fn view_content( + &self, + name_col_w: f32, + sizing: crate::ui::modal::ModalSizing, + ) -> Element<'_, Message> { let has_sel = self.selected.is_some(); let sel_is_zero = self .selected @@ -335,7 +343,7 @@ impl LayerPanel { Message::LayerSetCurrent, has_sel, ), - iced::widget::Space::new().width(Fill), + iced::widget::Space::new().width(sizing.width), // Search box: filters rows by name as the user types (#343). text_input("Search…", &self.filter) .on_input(Message::LayerManagerFilterChanged) @@ -349,11 +357,11 @@ impl LayerPanel { ) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.weak.color + theme.palette().background.weak.color )), ..Default::default() }) - .width(Fill) + .width(sizing.width) .padding([4, 8]); // ── Column header ───────────────────────────────────────────────── @@ -367,7 +375,7 @@ impl LayerPanel { container(iced::widget::Space::new().width(2).height(14)).style( |theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.neutral.color + theme.palette().background.neutral.color )), ..Default::default() }, @@ -390,7 +398,7 @@ impl LayerPanel { ), ] .spacing(4) - .width(Fill) + .width(sizing.width) .align_y(iced::Center); for vp in &self.vp_cols { @@ -404,7 +412,7 @@ impl LayerPanel { let col_header = container(header_row) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.weak.color)), border: Border { @@ -416,7 +424,7 @@ impl LayerPanel { } }) .padding([4, 8]) - .width(Fill); + .width(sizing.width); // ── Layer rows ──────────────────────────────────────────────────── let mut rows_col = column![].spacing(0); @@ -457,18 +465,18 @@ impl LayerPanel { let table = scrollable(rows_col) .id(iced::advanced::widget::Id::new(LAYER_TABLE_SCROLL_ID)) - .height(Fill); + .height(sizing.height.min(240.0)); // ── Full-window frame ───────────────────────────────────────────── container(column![toolbar, col_header, table].spacing(0)) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.base.color + theme.palette().background.base.color )), ..Default::default() }) - .width(Fill) - .height(Fill) + .width(sizing.width) + .height(sizing.height) .into() } } @@ -481,7 +489,7 @@ fn layer_cell_button_style( is_selected: bool, index: usize, ) -> button::Style { - let palette = theme.extended_palette(); + let palette = theme.palette(); let highlighted = matches!(status, button::Status::Hovered); let pair = if highlighted { palette.background.strong @@ -500,7 +508,7 @@ fn layer_cell_button_style( } fn layer_header_button_style(theme: &Theme, status: button::Status) -> button::Style { - let palette = theme.extended_palette(); + let palette = theme.palette(); let highlighted = matches!( status, button::Status::Hovered | button::Status::Pressed @@ -568,7 +576,7 @@ fn toolbar_btn<'a>(icon: &'static [u8], label: &'a str, msg: Message) -> Element ) .on_press(msg) .style(|theme: &Theme, status| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = match status { button::Status::Hovered | button::Status::Pressed => { palette.background.strong @@ -608,7 +616,7 @@ fn toolbar_btn_cond<'a>( } else { text(label).size(11).style(|theme: &Theme| iced::widget::text::Style { color: Some( - theme.extended_palette().background.base.text.scale_alpha(0.42) + theme.palette().background.base.text.scale_alpha(0.42) ), }) }, @@ -617,7 +625,7 @@ fn toolbar_btn_cond<'a>( .align_y(iced::Center), ) .style(move |theme: &Theme, status| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = match status { button::Status::Hovered if enabled => palette.background.strong, _ => palette.background.weak, @@ -657,7 +665,7 @@ fn name_tip<'a>(name: &'a str) -> Element<'a, Message> { right: 7.0, }) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.strong.color)), border: Border { @@ -891,7 +899,7 @@ fn layer_row<'a>( mouse_area( container(row_content) .style(move |theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = if is_selected { palette.primary.weak } else if index % 2 == 0 { diff --git a/src/ui/window/layout_manager.rs b/src/ui/window/layout_manager.rs index 28a616dc..0b52bdce 100644 --- a/src/ui/window/layout_manager.rs +++ b/src/ui/window/layout_manager.rs @@ -2,17 +2,17 @@ use crate::app::Message; use iced::widget::{button, column, container, row, scrollable, text, text_input, Space}; -use iced::{Background, Element, Fill, Theme}; +use iced::{Background, Element, Theme}; fn muted_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().background.base.text.scale_alpha(0.68)), + color: Some(theme.palette().background.base.text.scale_alpha(0.68)), } } fn primary_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().primary.base.color), + color: Some(theme.palette().primary.base.color), } } @@ -36,26 +36,26 @@ fn list_item(active: bool) -> impl Fn(&Theme, button::Status) -> button::Style { } } -fn hdivider<'a>() -> Element<'a, Message> { - container(Space::new().width(Fill).height(1)) - .width(Fill) +fn hdivider<'a>(width: iced::Length) -> Element<'a, Message> { + container(Space::new().width(width).height(1)) + .width(width) .height(1) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.neutral.color, + theme.palette().background.neutral.color, )), ..Default::default() }) .into() } -fn vsep<'a>() -> Element<'a, Message> { - container(Space::new().width(1).height(Fill)) +fn vsep<'a>(height: iced::Length) -> Element<'a, Message> { + container(Space::new().width(1).height(height)) .width(1) - .height(Fill) + .height(height) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.neutral.color, + theme.palette().background.neutral.color, )), ..Default::default() }) @@ -67,6 +67,7 @@ pub fn view_window<'a>( selected: &'a str, rename_buf: &'a str, current: String, + sizing: crate::ui::modal::ModalSizing, ) -> Element<'a, Message> { let is_model = selected == "Model"; @@ -87,7 +88,7 @@ pub fn view_window<'a>( } }) .padding([4, 10]), - Space::new().width(Fill), + Space::new().width(sizing.width), button( row![ crate::ui::icons::themed_arrow_left(9.0), @@ -120,11 +121,11 @@ pub fn view_window<'a>( ) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.weakest.color, + theme.palette().background.weakest.color, )), ..Default::default() }) - .width(Fill) + .width(sizing.width) .padding([5, 8]); // ── Left: Layout list ───────────────────────────────────────────────── @@ -143,7 +144,7 @@ pub fn view_window<'a>( .on_press(Message::LayoutManagerSelect(name.clone())) .style(list_item(is_sel)) .padding([5, 10]) - .width(Fill) + .width(sizing.width) .into() }) .collect(); @@ -151,17 +152,17 @@ pub fn view_window<'a>( let layout_list = container( column![ text("Layouts").size(10).style(muted_style), - container(scrollable(column(list_items).spacing(2)).height(Fill)) + container(scrollable(column(list_items).spacing(2)).height(sizing.height)) .style(container::bordered_box) - .width(Fill) - .height(Fill) + .width(sizing.width) + .height(sizing.height) .padding(2), ] .spacing(4) - .height(Fill), + .height(sizing.height), ) .width(220) - .height(Fill) + .height(sizing.height) .padding(iced::Padding { top: 12.0, right: 8.0, @@ -221,19 +222,19 @@ pub fn view_window<'a>( ] .spacing(8), ) - .width(Fill) + .width(sizing.width) .padding([12, 12]); - let body = row![layout_list, vsep(), details].height(Fill); + let body = row![layout_list, vsep(sizing.height), details].height(sizing.height); - container(column![toolbar, hdivider(), body].spacing(0)) + container(column![toolbar, hdivider(sizing.width), body].spacing(0)) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.base.color, + theme.palette().background.base.color, )), ..Default::default() }) - .width(Fill) - .height(Fill) + .width(sizing.width) + .height(sizing.height) .into() } diff --git a/src/ui/window/open_progress.rs b/src/ui/window/open_progress.rs index 661687ea..dfa10abf 100644 --- a/src/ui/window/open_progress.rs +++ b/src/ui/window/open_progress.rs @@ -62,7 +62,7 @@ pub fn view<'a>(progress: &'a OpenProgress, _now: Instant) -> Element<'a, Messag ) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().primary.base.color + theme.palette().primary.base.color )), border: Border { radius: 3.0.into(), @@ -85,7 +85,7 @@ pub fn view<'a>(progress: &'a OpenProgress, _now: Instant) -> Element<'a, Messag container(Space::new().width(Length::Fixed(BAR_TRACK_WIDTH)).height(Length::Fixed(BAR_TRACK_HEIGHT))) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.strong.color + theme.palette().background.strong.color )), border: Border { radius: 3.0.into(), @@ -110,7 +110,7 @@ pub fn view<'a>(progress: &'a OpenProgress, _now: Instant) -> Element<'a, Messag )) .size(13) .style(|theme: &Theme| iced::widget::text::Style { - color: Some(theme.extended_palette().background.base.text.scale_alpha(0.82)), + color: Some(theme.palette().background.base.text.scale_alpha(0.82)), }); let phase_line = text(format!( @@ -120,7 +120,7 @@ pub fn view<'a>(progress: &'a OpenProgress, _now: Instant) -> Element<'a, Messag )) .size(12) .style(|theme: &Theme| iced::widget::text::Style { - color: Some(theme.extended_palette().primary.base.color), + color: Some(theme.palette().primary.base.color), }); let cancel_btn: Element<'_, Message> = button(text("Cancel").size(12)) @@ -138,7 +138,7 @@ pub fn view<'a>(progress: &'a OpenProgress, _now: Instant) -> Element<'a, Messag ) .padding([18, 22]) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.weak.color)), border: Border { @@ -154,7 +154,7 @@ pub fn view<'a>(progress: &'a OpenProgress, _now: Instant) -> Element<'a, Messag let backdrop: Element<'_, Message> = container(Space::new().width(Fill).height(Fill)) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.strong.color.scale_alpha(0.72) + theme.palette().background.strong.color.scale_alpha(0.72) )), ..Default::default() }) diff --git a/src/ui/window/options.rs b/src/ui/window/options.rs index 15913622..5022c9c2 100644 --- a/src/ui/window/options.rs +++ b/src/ui/window/options.rs @@ -1,14 +1,15 @@ use crate::app::config::UiThemeConfig; use crate::app::Message; use iced::widget::{ - button, column, container, pick_list, row, scrollable, text, text_input, Space, + button, column, container, row, scrollable, text, text_input, Space, }; -use iced::{Background, Border, Element, Fill, Theme}; +use iced::{Background, Border, Element, Theme}; pub fn view_window<'a>( default_save_format: &'a str, ui_theme: &'a UiThemeConfig, theme_color_inputs: &'a [String; 6], + sizing: crate::ui::modal::ModalSizing, ) -> Element<'a, Message> { let selected_format = crate::io::SAVE_FORMAT_OPTIONS .iter() @@ -40,7 +41,7 @@ pub fn view_window<'a>( .style(move |theme: &Theme| container::Style { background: Some(Background::Color(color)), border: Border { - color: theme.extended_palette().background.strong.color, + color: theme.palette().background.strong.color, width: 1.0, radius: 3.0.into(), }, @@ -69,12 +70,12 @@ pub fn view_window<'a>( Space::new().height(10), row![ text("Default save format:").size(12).width(150), - pick_list( + crate::ui::pick_list( crate::io::SAVE_FORMAT_OPTIONS, selected_format, |format: &str| Message::DefaultSaveFormatChanged(format.to_string()) ) - .width(Fill), + .width(sizing.width), ] .spacing(12) .align_y(iced::Center), @@ -82,18 +83,19 @@ pub fn view_window<'a>( text( "Used for the first save of a new drawing. Existing drawings keep their file type and version." ) - .size(11), + .size(11) + .width(sizing.width), Space::new().height(22), text("Theme").size(15), Space::new().height(10), row![ text("Iced theme:").size(12).width(150), - pick_list( + crate::ui::pick_list( theme_options, selected_theme, Message::OptionsThemeChanged, ) - .width(Fill), + .width(sizing.width), ] .spacing(12) .align_y(iced::Center), @@ -101,25 +103,26 @@ pub fn view_window<'a>( text( "Changing a base colour switches to Custom. Iced generates every component shade from these six colours." ) - .size(11), + .size(11) + .width(sizing.width), Space::new().height(12), color_controls, ] .spacing(0) - .width(Fill); + .width(sizing.width); let body = column![ - scrollable(content).height(Fill), + scrollable(content).height(sizing.height), Space::new().height(12), - row![Space::new().width(Fill), close], + row![Space::new().width(sizing.width), close], ] - .width(Fill) - .height(Fill); + .width(sizing.width) + .height(sizing.height); container(body) .style(container::rounded_box) .padding([16, 18]) - .width(Fill) - .height(Fill) + .width(sizing.width) + .height(sizing.height) .into() } diff --git a/src/ui/window/plot.rs b/src/ui/window/plot.rs index 133fa54a..b7876da1 100644 --- a/src/ui/window/plot.rs +++ b/src/ui/window/plot.rs @@ -7,10 +7,10 @@ use crate::app::Message; use crate::io::paper_sizes::PaperSize; use iced::widget::{ - button, checkbox, column, container, mouse_area, pick_list, row, scrollable, text, text_input, + button, checkbox, column, container, mouse_area, row, scrollable, text, text_input, Space, }; -use iced::{Background, Border, Element, Fill, Length, Theme}; +use iced::{Background, Border, Element, Fit, Length, Theme}; /// Sentinel entries in the printer dropdown (not real printer names). pub const OUT_DEFAULT: &str = "System default printer"; @@ -212,7 +212,7 @@ impl PlotDialogState { fn btn(accent: bool) -> impl Fn(&Theme, button::Status) -> button::Style { move |theme: &Theme, st| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = match (accent, st) { (true, button::Status::Hovered | button::Status::Pressed) => palette.primary.strong, (false, button::Status::Hovered | button::Status::Pressed) => { @@ -236,7 +236,7 @@ fn btn(accent: bool) -> impl Fn(&Theme, button::Status) -> button::Style { } fn field_style(theme: &Theme, status: text_input::Status) -> text_input::Style { - let palette = theme.extended_palette(); + let palette = theme.palette(); let border = match status { text_input::Status::Focused { .. } => palette.primary.base.color, _ => palette.background.neutral.color, @@ -253,17 +253,17 @@ fn field_style(theme: &Theme, status: text_input::Status) -> text_input::Style { fn muted_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().background.base.text.scale_alpha(0.68)), + color: Some(theme.palette().background.base.text.scale_alpha(0.68)), } } -fn hdivider<'a>() -> Element<'a, Message> { - container(Space::new().width(Fill).height(1)) - .width(Fill) +fn hdivider<'a>(width: Length) -> Element<'a, Message> { + container(Space::new().width(width).height(1)) + .width(width) .height(1) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.neutral.color + theme.palette().background.neutral.color )), ..Default::default() }) @@ -274,13 +274,13 @@ fn section_label<'a>(s: &'static str) -> Element<'a, Message> { text(s).size(11).style(muted_style).into() } -fn vsep<'a>() -> Element<'a, Message> { - container(Space::new().width(1).height(Fill)) +fn vsep<'a>(height: Length) -> Element<'a, Message> { + container(Space::new().width(1).height(height)) .width(1) - .height(Fill) + .height(height) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.neutral.color + theme.palette().background.neutral.color )), ..Default::default() }) @@ -302,15 +302,15 @@ fn setup_row<'a>( .style(field_style) .size(11) .padding([4, 8]) - .width(Fill) + .width(Fit) .into(); } let is_sel = name == selected; let cell = container(text(name.to_string()).size(11)) .padding([4, 8]) - .width(Fill) + .width(Fit) .style(move |theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: is_sel.then_some(Background::Color(palette.primary.strong.color)), text_color: is_sel.then_some(palette.primary.strong.text), @@ -330,11 +330,12 @@ fn drop_row<'a>( options: Vec, selected: Option, ctor: fn(String) -> PlotDlgMsg, + width: Length, ) -> Element<'a, Message> { - let pl = pick_list(options, selected, move |s| Message::PlotDlg(ctor(s))) + let pl = crate::ui::pick_list(options, selected, move |s| Message::PlotDlg(ctor(s))) .text_size(12) .padding([3, 6]) - .width(Length::Fill); + .width(width); row![text(label).size(11).style(muted_style).width(92), pl] .spacing(8) .align_y(iced::Center) @@ -375,7 +376,12 @@ fn strs(items: &[&str]) -> Vec { items.iter().map(|s| s.to_string()).collect() } -pub fn view_window(s: &PlotDialogState) -> Element<'_, Message> { +pub fn view_window( + s: &PlotDialogState, + sizing: crate::ui::modal::ModalSizing, +) -> Element<'_, Message> { + let width = sizing.width; + let height = sizing.height; // ── Toolbar: Cancel … Preview Print/Export ────────────────────────── let action = if s.to_file { "Export PDF" } else { "Print" }; // `` / `` are pseudo-entries; layout rows are `*name*`. @@ -409,7 +415,7 @@ pub fn view_window(s: &PlotDialogState) -> Element<'_, Message> { let toolbar = container( row![ left_bar, - Space::new().width(Fill), + Space::new().width(width), button(text("Set current").size(11)) .on_press(Message::PlotDlg(PlotDlgMsg::SetCurrent)) .style(btn(false)) @@ -429,11 +435,11 @@ pub fn view_window(s: &PlotDialogState) -> Element<'_, Message> { ) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.weak.color + theme.palette().background.weak.color )), ..Default::default() }) - .width(Fill) + .width(width) .padding([5, 10]); // ── Printer dropdown: default + discovered printers + PDF sentinel ──── @@ -465,14 +471,14 @@ pub fn view_window(s: &PlotDialogState) -> Element<'_, Message> { .padding([6, 8]) .into() } else { - scrollable(column(rows).spacing(1)).height(Fill).into() + scrollable(column(rows).spacing(1)).height(height).into() }; let list_panel = container( column![ text("Page setups").size(10).style(muted_style), container(list_body) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.weak.color)), border: Border { @@ -483,15 +489,15 @@ pub fn view_window(s: &PlotDialogState) -> Element<'_, Message> { ..Default::default() } }) - .width(Fill) - .height(Fill) + .width(Length::Fill) + .height(height) .padding(2), ] .spacing(4) - .height(Fill), + .height(height), ) .width(160) - .height(Fill) + .height(height) .padding(iced::Padding { top: 12.0, right: 8.0, @@ -507,37 +513,39 @@ pub fn view_window(s: &PlotDialogState) -> Element<'_, Message> { }; let left = column![ section_label("Printer / plotter"), - drop_row("Output", printer_opts, printer_sel, PlotDlgMsg::Printer), + drop_row("Output", printer_opts, printer_sel, PlotDlgMsg::Printer, width), field_row("Copies", &s.copies, PlotDlgMsg::Copies, 60), - hdivider(), + hdivider(width), section_label("Paper"), - drop_row("Size", paper_opts, Some(s.paper.clone()), PlotDlgMsg::Paper), + drop_row("Size", paper_opts, Some(s.paper.clone()), PlotDlgMsg::Paper, width), drop_row( "Orientation", strs(&["Portrait", "Landscape"]), Some(s.orientation.clone()), PlotDlgMsg::Orientation, + width, ), drop_row( "Rotation", strs(&["0°", "90°", "180°", "270°"]), Some(s.rotation.clone()), PlotDlgMsg::Rotation, + width, ), - hdivider(), + hdivider(width), section_label("Plot area"), row![ container( - pick_list( + crate::ui::pick_list( strs(&["Layout", "Extents", "Display", "Window"]), Some(s.area.clone()), move |v| Message::PlotDlg(PlotDlgMsg::Area(v)), ) .text_size(12) .padding([3, 6]) - .width(Length::Fill) + .width(width) ) - .width(Fill), + .width(width), button(text("Pick…").size(11)) .on_press(Message::PlotDlg(PlotDlgMsg::PickWindow)) .style(btn(false)) @@ -554,7 +562,7 @@ pub fn view_window(s: &PlotDialogState) -> Element<'_, Message> { check("Center the plot", s.center, PlotFlag::Center), ] .spacing(9) - .width(Fill); + .width(width); // ── Right column ───────────────────────────────────────────────────── let right = column![ @@ -564,14 +572,15 @@ pub fn view_window(s: &PlotDialogState) -> Element<'_, Message> { strs(&["Fit", "1:1", "1:2", "1:5", "1:10", "1:20", "1:50", "1:100", "2:1"]), Some(s.scale.clone()), PlotDlgMsg::Scale, + width, ), check("Scale lineweights", s.scale_lw, PlotFlag::ScaleLw), - hdivider(), + hdivider(width), section_label("Plot style table (pen assignments)"), row![ container(text(style_label).size(12)) .style(|theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: Some(Background::Color(palette.background.base.color)), border: Border { @@ -583,7 +592,7 @@ pub fn view_window(s: &PlotDialogState) -> Element<'_, Message> { } }) .padding([4, 8]) - .width(Fill), + .width(width), button(text("Load…").size(11)) .on_press(Message::PlotDlg(PlotDlgMsg::LoadStyle)) .style(btn(false)) @@ -595,20 +604,20 @@ pub fn view_window(s: &PlotDialogState) -> Element<'_, Message> { ] .spacing(6) .align_y(iced::Center), - hdivider(), + hdivider(width), section_label("Quality"), row![ container( - pick_list( + crate::ui::pick_list( strs(&["Draft", "Normal", "High", "Maximum"]), Some(s.quality.clone()), move |v| Message::PlotDlg(PlotDlgMsg::Quality(v)), ) .text_size(12) .padding([3, 6]) - .width(Length::Fill) + .width(width) ) - .width(Fill), + .width(width), text("DPI").size(11).style(muted_style), text_input("", &s.dpi) .on_input(move |v| Message::PlotDlg(PlotDlgMsg::Dpi(v))) @@ -623,8 +632,9 @@ pub fn view_window(s: &PlotDialogState) -> Element<'_, Message> { strs(&["As displayed", "Wireframe", "Hidden", "Rendered"]), Some(s.shade.clone()), PlotDlgMsg::Shade, + width, ), - hdivider(), + hdivider(width), section_label("Plot options"), row![ column![ @@ -634,7 +644,7 @@ pub fn view_window(s: &PlotDialogState) -> Element<'_, Message> { check("Plot transparency", s.transparency, PlotFlag::Transparency), ] .spacing(6) - .width(Fill), + .width(width), column![ check("Paperspace last", s.paperspace_last, PlotFlag::PaperspaceLast), check("Hide paperspace", s.hide_paperspace, PlotFlag::HidePaperspace), @@ -642,28 +652,30 @@ pub fn view_window(s: &PlotDialogState) -> Element<'_, Message> { check("Save to layout", s.save_layout, PlotFlag::SaveLayout), ] .spacing(6) - .width(Fill), + .width(width), ] .spacing(10), ] .spacing(9) - .width(Fill); + .width(width); let detail = scrollable( - container(row![left, right].spacing(18).width(Fill)).padding(14), + container(row![left, right].spacing(18).width(width)).padding(14), ) - .width(Fill) - .height(Fill); - let body = row![list_panel, vsep(), detail].height(Fill); + .width(width) + .height(height); + let body = row![list_panel, vsep(height), detail] + .width(width) + .height(height); - container(column![toolbar, hdivider(), body].spacing(0)) + container(column![toolbar, hdivider(width), body].spacing(0)) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.base.color + theme.palette().background.base.color )), ..Default::default() }) - .width(Fill) - .height(Fill) + .width(width) + .height(height) .into() } diff --git a/src/ui/window/plugin_manager.rs b/src/ui/window/plugin_manager.rs index f6b505a7..3151cf7a 100644 --- a/src/ui/window/plugin_manager.rs +++ b/src/ui/window/plugin_manager.rs @@ -9,7 +9,7 @@ use crate::app::Message; use crate::plugin::external::{ExternalPlugin, RegistryEntry}; use iced::widget::{ - button, column, container, markdown, pick_list, row, rule, scrollable, text, text_input, Space, + button, column, container, markdown, row, rule, scrollable, text, text_input, Space, }; use iced::{Background, Border, Element, Fill, Length, Theme}; use rustc_hash::{FxHashMap, FxHashSet}; @@ -48,13 +48,13 @@ inventory::submit!(crate::command::CommandRegistration { fn muted_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().background.base.text.scale_alpha(0.68)), + color: Some(theme.palette().background.base.text.scale_alpha(0.68)), } } fn primary_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().primary.base.color), + color: Some(theme.palette().primary.base.color), } } @@ -62,7 +62,7 @@ fn badge<'a>(label: String) -> Element<'a, Message> { container(text(label).size(11)) .padding([2, 8]) .style(|theme: &Theme| { - let pair = theme.extended_palette().primary.weak; + let pair = theme.palette().primary.weak; container::Style { background: Some(Background::Color(pair.color)), text_color: Some(pair.text), @@ -105,7 +105,7 @@ fn status_badge<'a>(label: &str, kind: StatusKind) -> Element<'a, Message> { container(text(label.to_string()).size(11)) .padding([2, 8]) .style(move |theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = match kind { StatusKind::Muted => palette.background.weak, StatusKind::Success => palette.success.weak, @@ -126,7 +126,7 @@ fn status_badge<'a>(label: &str, kind: StatusKind) -> Element<'a, Message> { } fn card_style(theme: &Theme, selected: bool) -> container::Style { - let palette = theme.extended_palette(); + let palette = theme.palette(); container::Style { background: selected .then(|| Background::Color(palette.primary.weak.color.scale_alpha(0.18))), @@ -325,7 +325,7 @@ fn install_controls<'a>( text("no releases").size(11).style(muted_style).into() } else { let r = repo_s.clone(); - pick_list(tags, selected, move |tag| { + crate::ui::pick_list(tags, selected, move |tag| { Message::PluginReleaseSelect(r.clone(), tag) }) .text_size(12) @@ -490,7 +490,7 @@ fn registry_notice<'a>(m: &MarketView) -> Option> { container(body.padding([10, 12])) .width(Fill) .style(|theme: &Theme| { - let pair = theme.extended_palette().warning.weak; + let pair = theme.palette().warning.weak; container::Style { background: Some(Background::Color(pair.color.scale_alpha(0.16))), border: Border { @@ -638,7 +638,12 @@ fn resolve_readme_link(repo: &str, uri: &str) -> String { } } -fn readme_panel<'a>(market: &MarketView<'a>, theme: &Theme) -> Element<'a, Message> { +fn readme_panel<'a>( + market: &MarketView<'a>, + theme: &Theme, + width: Length, + height: Length, +) -> Element<'a, Message> { let Some(repo) = market.selected_repo else { return container( column![ @@ -649,9 +654,10 @@ fn readme_panel<'a>(market: &MarketView<'a>, theme: &Theme) -> Element<'a, Messa ] .spacing(8), ) - .center(Fill) - .width(Fill) - .height(Fill) + .center_x(width) + .center_y(height) + .width(width) + .height(height) .style(container::bordered_box) .into(); }; @@ -667,7 +673,7 @@ fn readme_panel<'a>(market: &MarketView<'a>, theme: &Theme) -> Element<'a, Messa text(repo.to_string()).size(11).style(primary_style), ] .spacing(3) - .width(Fill), + .width(width), pill_button( "View on GitHub", Message::OpenUrl(format!("https://github.com/{repo}")), @@ -686,9 +692,10 @@ fn readme_panel<'a>(market: &MarketView<'a>, theme: &Theme) -> Element<'a, Messa ] .spacing(6), ) - .center(Fill) - .width(Fill) - .height(Fill) + .center_x(width) + .center_y(height) + .width(width) + .height(height) .into() } else { match market.readmes.get(repo) { @@ -715,18 +722,20 @@ fn readme_panel<'a>(market: &MarketView<'a>, theme: &Theme) -> Element<'a, Messa ] .spacing(6), ) - .center(Fill) - .width(Fill) - .height(Fill) + .center_x(width) + .center_y(height) + .width(width) + .height(height) .into(), None => container( text("Select the plugin again to load its README.") .size(12) .style(muted_style), ) - .center(Fill) - .width(Fill) - .height(Fill) + .center_x(width) + .center_y(height) + .width(width) + .height(height) .into(), } }; @@ -738,18 +747,18 @@ fn readme_panel<'a>(market: &MarketView<'a>, theme: &Theme) -> Element<'a, Messa left: 0.0, }; let readme = scrollable(container(content).padding(gutter)) - .height(Fill) - .width(Fill); + .height(height) + .width(width); container( column![header, rule::horizontal(1), readme] .spacing(10) .padding([12, 14]) - .width(Fill) - .height(Fill), + .width(width) + .height(height), ) - .width(Fill) - .height(Fill) + .width(width) + .height(height) .style(container::bordered_box) .into() } @@ -760,7 +769,10 @@ pub fn view_window<'a>( loaded: &FxHashSet, market: MarketView<'a>, theme: &'a Theme, + sizing: crate::ui::modal::ModalSizing, ) -> Element<'a, Message> { + let width = sizing.width; + let height = sizing.height; let title = text("Plugins").size(20); let subtitle = text("Browse, install, and manage add-ons. Select one to view its README.") .size(12) @@ -813,40 +825,43 @@ pub fn view_window<'a>( left: 0.0, }; let catalog = scrollable(container(list.width(Fill)).padding(gutter)) - .height(Fill) + .height(height) .width(Fill); let search = text_input("Search plugins…", market.search) .on_input(Message::PluginSearchInput) .size(13) .padding([7, 10]) .width(Fill); - let catalog_pane = column![search, catalog].spacing(10).width(Fill).height(Fill); - let details = readme_panel(&market, theme); + let catalog_pane = column![search, catalog] + .spacing(10) + .width(Fill) + .height(height); + let details = readme_panel(&market, theme, width, height); let body = row![ container(catalog_pane) .width(Length::Fixed(410.0)) - .height(Fill), + .height(height), details, ] .spacing(14) - .height(Fill) - .width(Fill); + .height(height) + .width(width); container( column![title, subtitle, Space::new().height(12), body] .spacing(4) .padding(18) - .width(Fill) - .height(Fill), + .width(width) + .height(height), ) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.base.color, + theme.palette().background.base.color, )), ..Default::default() }) - .width(Fill) - .height(Fill) + .width(width) + .height(height) .into() } @@ -855,28 +870,58 @@ pub fn view_window<'a>( pub fn view_web_notice<'a>() -> Element<'a, Message> { let download = button(text("Download desktop app").size(13)) .on_press(Message::OpenUrl(DESKTOP_DOWNLOAD_URL.to_string())) - .padding([8, 16]) - .style(button::primary); + .padding([9, 18]) + .style(|theme: &Theme, status| { + let mut style = button::primary(theme, status); + style.text_color = iced::Color::WHITE; + style + }); - container( + let icon = container(crate::ui::icons::themed_primary( + crate::ui::icons::GEAR, + 26.0, + )) + .center(Length::Fixed(44.0)) + .style(|theme: &Theme| container::Style { + background: Some(Background::Color( + theme.palette().primary.weak.color, + )), + border: Border { + radius: 12.0.into(), + ..Default::default() + }, + ..Default::default() + }); + + let notice = container( column![ - text("Plugins require the desktop app").size(20), + icon, + text("Plugins are available in the desktop app") + .size(20) + .width(Length::Fit) + .align_x(iced::alignment::Horizontal::Center), text( "Open CAD Studio plugins are native packages and cannot run inside a browser. \ - Install the desktop app to browse, install, and use plugins.", + Download the desktop app to browse, install, and use plugins.", ) .size(13) + .width(Length::Fit) + .align_x(iced::alignment::Horizontal::Center) .style(muted_style), - Space::new().height(8), + Space::new().height(4), download, ] - .spacing(8) - .padding(24) - .width(Fill), + .spacing(10) + .align_x(iced::alignment::Horizontal::Center) + .width(Length::Fit), ) - .center(Fill) - .width(Fill) - .height(Fill) + .width(Length::Fit.max(380.0)); + + container(notice) + .center_x(Length::Fit) + .padding([16, 20]) + .width(Length::Fit) + .height(Length::Fit) .into() } diff --git a/src/ui/window/shortcuts.rs b/src/ui/window/shortcuts.rs index 2f5cdc69..8d02716f 100644 --- a/src/ui/window/shortcuts.rs +++ b/src/ui/window/shortcuts.rs @@ -2,7 +2,7 @@ use crate::app::Message; use iced::widget::{column, container, row, scrollable, text, Space}; -use iced::{Background, Element, Fill, Theme}; +use iced::{Background, Element, Theme}; use std::borrow::Cow; /// Display name of the primary accelerator modifier on this platform. @@ -16,23 +16,23 @@ const MOD: &str = "Ctrl"; fn muted_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().background.base.text.scale_alpha(0.68)), + color: Some(theme.palette().background.base.text.scale_alpha(0.68)), } } fn primary_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().primary.base.color), + color: Some(theme.palette().primary.base.color), } } -fn hdivider<'a>() -> Element<'a, Message> { - container(Space::new().width(Fill).height(1)) - .width(Fill) +fn hdivider<'a>(width: iced::Length) -> Element<'a, Message> { + container(Space::new().width(width).height(1)) + .width(width) .height(1) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.neutral.color, + theme.palette().background.neutral.color, )), ..Default::default() }) @@ -70,6 +70,7 @@ fn section<'a>(title: impl Into>) -> Element<'a, Message> { pub fn view_window<'a>( overrides: &'a rustc_hash::FxHashMap, + sizing: crate::ui::modal::ModalSizing, ) -> Element<'a, Message> { // ── Toolbar ─────────────────────────────────────────────────────────── let toolbar = container( @@ -82,11 +83,11 @@ pub fn view_window<'a>( ) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.weakest.color, + theme.palette().background.weakest.color, )), ..Default::default() }) - .width(Fill) + .width(sizing.width) .padding([5, 10]); // ── Shortcut entries ────────────────────────────────────────────────── @@ -152,8 +153,8 @@ pub fn view_window<'a>( // ── Section headers styled separately ──────────────────────────────── let content = scrollable(column(rows).spacing(3).padding([12, 16])) - .width(Fill) - .height(Fill); + .width(sizing.width) + .height(sizing.height); // ── Header row with accent ──────────────────────────────────────────── let header = container( @@ -166,20 +167,29 @@ pub fn view_window<'a>( ) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().primary.weak.color, + theme.palette().primary.weak.color, )), ..Default::default() }) - .width(Fill); + .width(sizing.width); - container(column![toolbar, hdivider(), header, hdivider(), content].spacing(0)) + container( + column![ + toolbar, + hdivider(sizing.width), + header, + hdivider(sizing.width), + content + ] + .spacing(0), + ) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.base.color, + theme.palette().background.base.color, )), ..Default::default() }) - .width(Fill) - .height(Fill) + .width(sizing.width) + .height(sizing.height) .into() } diff --git a/src/ui/window/update_notice.rs b/src/ui/window/update_notice.rs index 39d7c227..80394d8c 100644 --- a/src/ui/window/update_notice.rs +++ b/src/ui/window/update_notice.rs @@ -1,23 +1,28 @@ use crate::app::Message; use iced::widget::{button, column, container, row, scrollable, text, Space}; -use iced::{Background, Border, Element, Fill, Theme}; +use iced::{Background, Border, Element, Theme}; fn muted_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().background.base.text.scale_alpha(0.68)), + color: Some(theme.palette().background.base.text.scale_alpha(0.68)), } } fn primary_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { - color: Some(theme.extended_palette().primary.base.color), + color: Some(theme.palette().primary.base.color), } } /// Renders one of the two "Installed" / "Latest" cards. The `highlight` /// flag tints the border + label with the accent colour, making the new /// version the visual anchor of the row. -fn version_card<'a>(label: &'static str, value: String, highlight: bool) -> Element<'a, Message> { +fn version_card<'a>( + label: &'static str, + value: String, + highlight: bool, + width: iced::Length, +) -> Element<'a, Message> { container( column![ text(label) @@ -32,7 +37,7 @@ fn version_card<'a>(label: &'static str, value: String, highlight: bool) -> Elem .spacing(4) .align_x(iced::Center), ) - .width(Fill) + .width(width) .padding(iced::Padding { top: 14.0, right: 12.0, @@ -41,7 +46,7 @@ fn version_card<'a>(label: &'static str, value: String, highlight: bool) -> Elem }) .align_x(iced::Center) .style(move |theme: &Theme| { - let palette = theme.extended_palette(); + let palette = theme.palette(); let pair = if highlight { palette.primary.weak } else { @@ -120,7 +125,11 @@ fn strip_inline_md(s: &str) -> String { out } -pub fn view_window<'a>(latest: &'a str, body: &'a str) -> Element<'a, Message> { +pub fn view_window<'a>( + latest: &'a str, + body: &'a str, + sizing: crate::ui::modal::ModalSizing, +) -> Element<'a, Message> { let header = container( column![ text("New Release Available").size(20).style(primary_style), @@ -131,7 +140,7 @@ pub fn view_window<'a>(latest: &'a str, body: &'a str) -> Element<'a, Message> { .spacing(4) .align_x(iced::Center), ) - .width(Fill) + .width(sizing.width) .padding(iced::Padding { top: 14.0, right: 0.0, @@ -147,20 +156,21 @@ pub fn view_window<'a>(latest: &'a str, body: &'a str) -> Element<'a, Message> { "Installed", format!("v{}", env!("CARGO_PKG_VERSION")), false, + sizing.width, ); - let latest_card = version_card("Latest", format!("v{}", latest), true); + let latest_card = version_card("Latest", format!("v{}", latest), true, sizing.width); let arrow = container(crate::ui::icons::themed_secondary( crate::ui::icons::ARROW_LONG_RIGHT, 20.0, )) .width(iced::Length::Fixed(32.0)) - .height(Fill) + .height(sizing.height) .align_x(iced::Center) .align_y(iced::Center); let info_block = row![installed, arrow, latest_card] .spacing(0) .align_y(iced::Center) - .width(Fill); + .width(sizing.width); let later_btn = button(text("Later").size(11)) .on_press(Message::UpdateNoticeClose) @@ -172,7 +182,7 @@ pub fn view_window<'a>(latest: &'a str, body: &'a str) -> Element<'a, Message> { .style(button::primary) .padding([6, 16]); - let footer = row![Space::new().width(Fill), later_btn, open_btn] + let footer = row![Space::new().width(sizing.width), later_btn, open_btn] .spacing(8) .align_y(iced::Center) .padding(iced::Padding { @@ -203,27 +213,26 @@ pub fn view_window<'a>(latest: &'a str, body: &'a str) -> Element<'a, Message> { for line in body.lines() { col = col.push(render_notes_line(line)); } - scrollable(container(col).padding([10, 14])).height(Fill).into() + scrollable(container(col).padding([10, 14])) + .height(sizing.height) + .into() }; let notes_block = container(notes_body) - .width(Fill) - .height(Fill) + .width(sizing.width) + .height(sizing.height) .style(container::bordered_box); - // Wrap notes_block in a Fill-height container outside the column so it - // greedily claims every pixel left over after the fixed-height rows - // (header, version cards, heading, footer). Without this iced lets the - // notes panel shrink to its content height, leaving a gap above the - // footer. + // Keep the notes panel content-sized. The outer modal supplies the maximum + // height, so long release notes scroll instead of stretching the dialog. let notes_fill = container(notes_block) - .width(Fill) - .height(Fill); + .width(sizing.width) + .height(sizing.height); container( column![header, info_block, notes_heading, notes_fill, footer] .spacing(0) - .height(Fill) + .height(sizing.height) .padding(iced::Padding { top: 0.0, right: 20.0, @@ -233,11 +242,11 @@ pub fn view_window<'a>(latest: &'a str, body: &'a str) -> Element<'a, Message> { ) .style(|theme: &Theme| container::Style { background: Some(Background::Color( - theme.extended_palette().background.base.color, + theme.palette().background.base.color, )), ..Default::default() }) - .width(Fill) - .height(Fill) + .width(sizing.width) + .height(sizing.height) .into() } diff --git a/src/ui/wrap_bar.rs b/src/ui/wrap_bar.rs index f4ba6918..cb8cc760 100644 --- a/src/ui/wrap_bar.rs +++ b/src/ui/wrap_bar.rs @@ -23,7 +23,7 @@ use rustc_hash::FxHashMap; use iced::advanced::layout::{self, Layout}; use iced::advanced::widget::{self, tree, Widget}; -use iced::advanced::{mouse, overlay, renderer, Clipboard, Renderer as _, Shell}; +use iced::advanced::{mouse, overlay, renderer, Renderer as _, Shell}; use iced::{ Background, Border, Element, Event, Length, Point, Rectangle, Renderer, Shadow, Size, Theme, Vector, @@ -121,13 +121,9 @@ impl<'a> WrapBar<'a> { } impl<'a> Widget for WrapBar<'a> { - fn children(&self) -> Vec { - self.refs().iter().map(|e| widget::Tree::new(*e)).collect() - } - - fn diff(&self, tree: &mut widget::Tree) { - let refs: Vec<_> = self.refs().iter().map(|e| e.as_widget()).collect(); - tree.diff_children(&refs); + fn diff(&mut self, tree: &mut widget::Tree) { + let mut refs = self.refs_mut(); + tree.diff_children(&mut refs); } fn size(&self) -> Size { @@ -300,7 +296,6 @@ impl<'a> Widget for WrapBar<'a> { layout: Layout<'_>, cursor: mouse::Cursor, renderer: &Renderer, - clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, viewport: &Rectangle, ) { @@ -316,7 +311,6 @@ impl<'a> Widget for WrapBar<'a> { child_layout, cursor, renderer, - clipboard, shell, viewport, ); @@ -529,13 +523,8 @@ impl<'a> WrapFlow<'a> { } impl<'a> Widget for WrapFlow<'a> { - fn children(&self) -> Vec { - self.items.iter().map(widget::Tree::new).collect() - } - - fn diff(&self, tree: &mut widget::Tree) { - let refs: Vec<_> = self.items.iter().map(|e| e.as_widget()).collect(); - tree.diff_children(&refs); + fn diff(&mut self, tree: &mut widget::Tree) { + tree.diff_children(&mut self.items); } fn size(&self) -> Size { @@ -633,7 +622,6 @@ impl<'a> Widget for WrapFlow<'a> { layout: Layout<'_>, cursor: mouse::Cursor, renderer: &Renderer, - clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, viewport: &Rectangle, ) { @@ -649,7 +637,6 @@ impl<'a> Widget for WrapFlow<'a> { child_layout, cursor, renderer, - clipboard, shell, viewport, ); @@ -789,13 +776,8 @@ impl<'a> DensitySwap<'a> { } impl<'a> Widget for DensitySwap<'a> { - fn children(&self) -> Vec { - self.variants.iter().map(widget::Tree::new).collect() - } - - fn diff(&self, tree: &mut widget::Tree) { - let refs: Vec<_> = self.variants.iter().map(|e| e.as_widget()).collect(); - tree.diff_children(&refs); + fn diff(&mut self, tree: &mut widget::Tree) { + tree.diff_children(&mut self.variants); } fn size(&self) -> Size { @@ -847,7 +829,6 @@ impl<'a> Widget for DensitySwap<'a> { layout: Layout<'_>, cursor: mouse::Cursor, renderer: &Renderer, - clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, viewport: &Rectangle, ) { @@ -859,7 +840,6 @@ impl<'a> Widget for DensitySwap<'a> { child_layout, cursor, renderer, - clipboard, shell, viewport, ); @@ -980,22 +960,14 @@ impl<'a> PosReport<'a> { } impl<'a> Widget for PosReport<'a> { - fn children(&self) -> Vec { - vec![widget::Tree::new(&self.child)] - } - - fn diff(&self, tree: &mut widget::Tree) { - tree.diff_children(&[self.child.as_widget()]); + fn diff(&mut self, tree: &mut widget::Tree) { + tree.diff_children(std::slice::from_mut(&mut self.child)); } fn size(&self) -> Size { self.child.as_widget().size() } - fn size_hint(&self) -> Size { - self.child.as_widget().size_hint() - } - fn layout( &mut self, tree: &mut widget::Tree, @@ -1014,7 +986,6 @@ impl<'a> Widget for PosReport<'a> { layout: Layout<'_>, cursor: mouse::Cursor, renderer: &Renderer, - clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, viewport: &Rectangle, ) { @@ -1024,7 +995,6 @@ impl<'a> Widget for PosReport<'a> { layout, cursor, renderer, - clipboard, shell, viewport, ); @@ -1211,22 +1181,14 @@ impl<'a> Widget for ReorderTab<'a> { tree::State::new(ReorderState::default()) } - fn children(&self) -> Vec { - vec![widget::Tree::new(&self.child)] - } - - fn diff(&self, tree: &mut widget::Tree) { - tree.diff_children(&[self.child.as_widget()]); + fn diff(&mut self, tree: &mut widget::Tree) { + tree.diff_children(std::slice::from_mut(&mut self.child)); } fn size(&self) -> Size { self.child.as_widget().size() } - fn size_hint(&self) -> Size { - self.child.as_widget().size_hint() - } - fn layout( &mut self, tree: &mut widget::Tree, @@ -1245,7 +1207,6 @@ impl<'a> Widget for ReorderTab<'a> { layout: Layout<'_>, cursor: mouse::Cursor, renderer: &Renderer, - clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, viewport: &Rectangle, ) { @@ -1295,7 +1256,6 @@ impl<'a> Widget for ReorderTab<'a> { layout, cursor, renderer, - clipboard, shell, viewport, ); @@ -1374,7 +1334,7 @@ impl<'a> Widget for ReorderTab<'a> { shadow: Shadow::default(), snap: true, }, - Background::Color(theme.extended_palette().primary.base.color), + Background::Color(theme.palette().primary.base.color), ); } }