graphic design tool
git clone https://git.lucas.co/cce-designer.git
Migrate from winit to smithay-client-toolkit and calloop
Cargo.lock | 896 ++--------------------------------------
Cargo.toml | 7 +-
nodes/node_1.json | 21 -
nodes/node_2.json | 7 -
src/main.rs | 1178 +++++++++++++++++++++++++++++++++++++++--------------
5 files changed, 906 insertions(+), 1203 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index c98e3a8..2a97160 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2,60 +2,6 @@
# It is not intended for manual editing.
version = 4
-[[package]]
-name = "ab_glyph"
-version = "0.2.32"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2"
-dependencies = [
- "ab_glyph_rasterizer",
- "owned_ttf_parser",
-]
-
-[[package]]
-name = "ab_glyph_rasterizer"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
-
-[[package]]
-name = "ahash"
-version = "0.8.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
-dependencies = [
- "cfg-if",
- "getrandom",
- "once_cell",
- "version_check",
- "zerocopy",
-]
-
-[[package]]
-name = "android-activity"
-version = "0.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f2a1bb052857d5dd49572219344a7332b31b76405648eabac5bc68978251bcd"
-dependencies = [
- "android-properties",
- "bitflags 2.11.1",
- "cc",
- "jni",
- "libc",
- "log",
- "ndk",
- "ndk-context",
- "ndk-sys 0.6.0+11769913",
- "num_enum",
- "thiserror 2.0.18",
-]
-
-[[package]]
-name = "android-properties"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04"
-
[[package]]
name = "android_system_properties"
version = "0.1.5"
@@ -65,24 +11,12 @@ dependencies = [
"libc",
]
-[[package]]
-name = "arrayref"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
-
[[package]]
name = "arrayvec"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
-[[package]]
-name = "as-raw-xcb-connection"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b"
-
[[package]]
name = "ash"
version = "0.38.0+1.3.281"
@@ -92,12 +26,6 @@ dependencies = [
"libloading",
]
-[[package]]
-name = "atomic-waker"
-version = "1.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
-
[[package]]
name = "autocfg"
version = "1.5.0"
@@ -140,15 +68,6 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
-[[package]]
-name = "block2"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f"
-dependencies = [
- "objc2",
-]
-
[[package]]
name = "bumpalo"
version = "3.20.2"
@@ -214,8 +133,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98"
dependencies = [
"find-msvc-tools",
- "jobserver",
- "libc",
"shlex",
]
@@ -247,16 +164,21 @@ name = "clear-design-interface"
version = "0.1.0"
dependencies = [
"bytemuck",
+ "calloop",
+ "calloop-wayland-source",
"clear-ui",
"glam",
"glyphon",
"opencl3",
"pollster",
+ "raw-window-handle",
"serde",
"serde_json",
+ "smithay-client-toolkit",
"tokio",
+ "wayland-client",
"wgpu",
- "winit",
+ "xkeysym",
]
[[package]]
@@ -264,11 +186,16 @@ name = "clear-ui"
version = "0.1.0"
dependencies = [
"bytemuck",
+ "calloop",
+ "calloop-wayland-source",
"glyphon",
"pollster",
+ "raw-window-handle",
+ "smithay-client-toolkit",
"tokio",
+ "wayland-client",
"wgpu",
- "winit",
+ "xkeysym",
]
[[package]]
@@ -281,16 +208,6 @@ dependencies = [
"unicode-width",
]
-[[package]]
-name = "combine"
-version = "4.6.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
-dependencies = [
- "bytes",
- "memchr",
-]
-
[[package]]
name = "concurrent-queue"
version = "2.5.0"
@@ -316,19 +233,6 @@ version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
-[[package]]
-name = "core-graphics"
-version = "0.23.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081"
-dependencies = [
- "bitflags 1.3.2",
- "core-foundation",
- "core-graphics-types",
- "foreign-types",
- "libc",
-]
-
[[package]]
name = "core-graphics-types"
version = "0.1.3"
@@ -394,12 +298,6 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f"
-[[package]]
-name = "dispatch"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
-
[[package]]
name = "dlib"
version = "0.5.3"
@@ -424,12 +322,6 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
-[[package]]
-name = "dpi"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76"
-
[[package]]
name = "either"
version = "1.15.0"
@@ -509,7 +401,7 @@ checksum = "b0299020c3ef3f60f526a4f64ab4a3d4ce116b1acbf24cdd22da0068e5d81dc3"
dependencies = [
"fontconfig-parser",
"log",
- "memmap2",
+ "memmap2 0.9.10",
"slotmap",
"tinyvec",
"ttf-parser 0.20.0",
@@ -566,28 +458,6 @@ dependencies = [
"slab",
]
-[[package]]
-name = "gethostname"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8"
-dependencies = [
- "rustix 1.1.4",
- "windows-link",
-]
-
-[[package]]
-name = "getrandom"
-version = "0.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
-dependencies = [
- "cfg-if",
- "libc",
- "r-efi",
- "wasip2",
-]
-
[[package]]
name = "gl_generator"
version = "0.14.0"
@@ -739,36 +609,6 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
-[[package]]
-name = "jni"
-version = "0.22.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498"
-dependencies = [
- "cfg-if",
- "combine",
- "jni-macros",
- "jni-sys 0.4.1",
- "log",
- "simd_cesu8",
- "thiserror 2.0.18",
- "walkdir",
- "windows-link",
-]
-
-[[package]]
-name = "jni-macros"
-version = "0.22.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3"
-dependencies = [
- "proc-macro2",
- "quote",
- "rustc_version",
- "simd_cesu8",
- "syn",
-]
-
[[package]]
name = "jni-sys"
version = "0.3.1"
@@ -797,16 +637,6 @@ dependencies = [
"syn",
]
-[[package]]
-name = "jobserver"
-version = "0.1.34"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
-dependencies = [
- "getrandom",
- "libc",
-]
-
[[package]]
name = "js-sys"
version = "0.3.98"
@@ -858,18 +688,6 @@ version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
-[[package]]
-name = "libredox"
-version = "0.1.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c"
-dependencies = [
- "bitflags 2.11.1",
- "libc",
- "plain",
- "redox_syscall 0.7.5",
-]
-
[[package]]
name = "linux-raw-sys"
version = "0.4.15"
@@ -924,6 +742,15 @@ version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
+[[package]]
+name = "memmap2"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "memmap2"
version = "0.9.10"
@@ -981,27 +808,6 @@ dependencies = [
"unicode-xid",
]
-[[package]]
-name = "ndk"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4"
-dependencies = [
- "bitflags 2.11.1",
- "jni-sys 0.3.1",
- "log",
- "ndk-sys 0.6.0+11769913",
- "num_enum",
- "raw-window-handle",
- "thiserror 1.0.69",
-]
-
-[[package]]
-name = "ndk-context"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
-
[[package]]
name = "ndk-sys"
version = "0.5.0+25.2.9519653"
@@ -1011,15 +817,6 @@ dependencies = [
"jni-sys 0.3.1",
]
-[[package]]
-name = "ndk-sys"
-version = "0.6.0+11769913"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873"
-dependencies = [
- "jni-sys 0.3.1",
-]
-
[[package]]
name = "num-traits"
version = "0.2.19"
@@ -1029,28 +826,6 @@ dependencies = [
"autocfg",
]
-[[package]]
-name = "num_enum"
-version = "0.7.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26"
-dependencies = [
- "num_enum_derive",
- "rustversion",
-]
-
-[[package]]
-name = "num_enum_derive"
-version = "0.7.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8"
-dependencies = [
- "proc-macro-crate",
- "proc-macro2",
- "quote",
- "syn",
-]
-
[[package]]
name = "objc"
version = "0.2.7"
@@ -1060,209 +835,6 @@ dependencies = [
"malloc_buf",
]
-[[package]]
-name = "objc-sys"
-version = "0.3.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310"
-
-[[package]]
-name = "objc2"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804"
-dependencies = [
- "objc-sys",
- "objc2-encode",
-]
-
-[[package]]
-name = "objc2-app-kit"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff"
-dependencies = [
- "bitflags 2.11.1",
- "block2",
- "libc",
- "objc2",
- "objc2-core-data",
- "objc2-core-image",
- "objc2-foundation",
- "objc2-quartz-core",
-]
-
-[[package]]
-name = "objc2-cloud-kit"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009"
-dependencies = [
- "bitflags 2.11.1",
- "block2",
- "objc2",
- "objc2-core-location",
- "objc2-foundation",
-]
-
-[[package]]
-name = "objc2-contacts"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889"
-dependencies = [
- "block2",
- "objc2",
- "objc2-foundation",
-]
-
-[[package]]
-name = "objc2-core-data"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef"
-dependencies = [
- "bitflags 2.11.1",
- "block2",
- "objc2",
- "objc2-foundation",
-]
-
-[[package]]
-name = "objc2-core-image"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80"
-dependencies = [
- "block2",
- "objc2",
- "objc2-foundation",
- "objc2-metal",
-]
-
-[[package]]
-name = "objc2-core-location"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781"
-dependencies = [
- "block2",
- "objc2",
- "objc2-contacts",
- "objc2-foundation",
-]
-
-[[package]]
-name = "objc2-encode"
-version = "4.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
-
-[[package]]
-name = "objc2-foundation"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
-dependencies = [
- "bitflags 2.11.1",
- "block2",
- "dispatch",
- "libc",
- "objc2",
-]
-
-[[package]]
-name = "objc2-link-presentation"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398"
-dependencies = [
- "block2",
- "objc2",
- "objc2-app-kit",
- "objc2-foundation",
-]
-
-[[package]]
-name = "objc2-metal"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6"
-dependencies = [
- "bitflags 2.11.1",
- "block2",
- "objc2",
- "objc2-foundation",
-]
-
-[[package]]
-name = "objc2-quartz-core"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a"
-dependencies = [
- "bitflags 2.11.1",
- "block2",
- "objc2",
- "objc2-foundation",
- "objc2-metal",
-]
-
-[[package]]
-name = "objc2-symbols"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc"
-dependencies = [
- "objc2",
- "objc2-foundation",
-]
-
-[[package]]
-name = "objc2-ui-kit"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f"
-dependencies = [
- "bitflags 2.11.1",
- "block2",
- "objc2",
- "objc2-cloud-kit",
- "objc2-core-data",
- "objc2-core-image",
- "objc2-core-location",
- "objc2-foundation",
- "objc2-link-presentation",
- "objc2-quartz-core",
- "objc2-symbols",
- "objc2-uniform-type-identifiers",
- "objc2-user-notifications",
-]
-
-[[package]]
-name = "objc2-uniform-type-identifiers"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe"
-dependencies = [
- "block2",
- "objc2",
- "objc2-foundation",
-]
-
-[[package]]
-name = "objc2-user-notifications"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3"
-dependencies = [
- "bitflags 2.11.1",
- "block2",
- "objc2",
- "objc2-core-location",
- "objc2-foundation",
-]
-
[[package]]
name = "once_cell"
version = "1.21.4"
@@ -1288,16 +860,6 @@ dependencies = [
"libc",
]
-[[package]]
-name = "orbclient"
-version = "0.3.54"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a570f6bca41d29acb2139229a7c873ec99bc9a313bd10804081d89bfac8ff329"
-dependencies = [
- "libc",
- "libredox",
-]
-
[[package]]
name = "ordered-float"
version = "4.6.0"
@@ -1307,15 +869,6 @@ dependencies = [
"num-traits",
]
-[[package]]
-name = "owned_ttf_parser"
-version = "0.25.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b"
-dependencies = [
- "ttf-parser 0.25.1",
-]
-
[[package]]
name = "parking_lot"
version = "0.12.5"
@@ -1334,7 +887,7 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
dependencies = [
"cfg-if",
"libc",
- "redox_syscall 0.5.18",
+ "redox_syscall",
"smallvec",
"windows-link",
]
@@ -1345,32 +898,6 @@ version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
-[[package]]
-name = "percent-encoding"
-version = "2.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
-
-[[package]]
-name = "pin-project"
-version = "1.1.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924"
-dependencies = [
- "pin-project-internal",
-]
-
-[[package]]
-name = "pin-project-internal"
-version = "1.1.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
[[package]]
name = "pin-project-lite"
version = "0.2.17"
@@ -1383,12 +910,6 @@ version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
-[[package]]
-name = "plain"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
-
[[package]]
name = "polling"
version = "3.11.0"
@@ -1415,15 +936,6 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa"
-[[package]]
-name = "proc-macro-crate"
-version = "3.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f"
-dependencies = [
- "toml_edit",
-]
-
[[package]]
name = "proc-macro2"
version = "1.0.106"
@@ -1457,12 +969,6 @@ dependencies = [
"proc-macro2",
]
-[[package]]
-name = "r-efi"
-version = "5.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
-
[[package]]
name = "range-alloc"
version = "0.1.5"
@@ -1511,15 +1017,6 @@ dependencies = [
"font-types",
]
-[[package]]
-name = "redox_syscall"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
-dependencies = [
- "bitflags 1.3.2",
-]
-
[[package]]
name = "redox_syscall"
version = "0.5.18"
@@ -1529,15 +1026,6 @@ dependencies = [
"bitflags 2.11.1",
]
-[[package]]
-name = "redox_syscall"
-version = "0.7.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4666a1a60d8412eab19d94f6d13dcc9cea0a5ef4fdf6a5db306537413c661b1b"
-dependencies = [
- "bitflags 2.11.1",
-]
-
[[package]]
name = "renderdoc-sys"
version = "1.1.0"
@@ -1562,15 +1050,6 @@ version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe"
-[[package]]
-name = "rustc_version"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
-dependencies = [
- "semver",
-]
-
[[package]]
name = "rustix"
version = "0.38.44"
@@ -1620,15 +1099,6 @@ dependencies = [
"unicode-script",
]
-[[package]]
-name = "same-file"
-version = "1.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
-dependencies = [
- "winapi-util",
-]
-
[[package]]
name = "scoped-tls"
version = "1.0.1"
@@ -1641,31 +1111,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
-[[package]]
-name = "sctk-adwaita"
-version = "0.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec"
-dependencies = [
- "ab_glyph",
- "log",
- "memmap2",
- "smithay-client-toolkit",
- "tiny-skia",
-]
-
[[package]]
name = "self_cell"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89"
-[[package]]
-name = "semver"
-version = "1.0.28"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
-
[[package]]
name = "serde"
version = "1.0.228"
@@ -1725,22 +1176,6 @@ dependencies = [
"libc",
]
-[[package]]
-name = "simd_cesu8"
-version = "1.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33"
-dependencies = [
- "rustc_version",
- "simdutf8",
-]
-
-[[package]]
-name = "simdutf8"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
-
[[package]]
name = "skrifa"
version = "0.22.3"
@@ -1779,12 +1214,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016"
dependencies = [
"bitflags 2.11.1",
+ "bytemuck",
"calloop",
"calloop-wayland-source",
"cursor-icon",
"libc",
"log",
- "memmap2",
+ "memmap2 0.9.10",
+ "pkg-config",
"rustix 0.38.44",
"thiserror 1.0.69",
"wayland-backend",
@@ -1794,18 +1231,10 @@ dependencies = [
"wayland-protocols",
"wayland-protocols-wlr",
"wayland-scanner",
+ "xkbcommon",
"xkeysym",
]
-[[package]]
-name = "smol_str"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead"
-dependencies = [
- "serde",
-]
-
[[package]]
name = "socket2"
version = "0.6.3"
@@ -1831,12 +1260,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
-[[package]]
-name = "strict-num"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
-
[[package]]
name = "strum"
version = "0.26.3"
@@ -1945,31 +1368,6 @@ dependencies = [
"syn",
]
-[[package]]
-name = "tiny-skia"
-version = "0.11.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab"
-dependencies = [
- "arrayref",
- "arrayvec",
- "bytemuck",
- "cfg-if",
- "log",
- "tiny-skia-path",
-]
-
-[[package]]
-name = "tiny-skia-path"
-version = "0.11.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93"
-dependencies = [
- "arrayref",
- "bytemuck",
- "strict-num",
-]
-
[[package]]
name = "tinyvec"
version = "1.11.0"
@@ -2013,52 +1411,6 @@ dependencies = [
"syn",
]
-[[package]]
-name = "toml_datetime"
-version = "1.1.1+spec-1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
-dependencies = [
- "serde_core",
-]
-
-[[package]]
-name = "toml_edit"
-version = "0.25.11+spec-1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b"
-dependencies = [
- "indexmap",
- "toml_datetime",
- "toml_parser",
- "winnow",
-]
-
-[[package]]
-name = "toml_parser"
-version = "1.1.2+spec-1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
-dependencies = [
- "winnow",
-]
-
-[[package]]
-name = "tracing"
-version = "0.1.44"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
-dependencies = [
- "pin-project-lite",
- "tracing-core",
-]
-
-[[package]]
-name = "tracing-core"
-version = "0.1.36"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
-
[[package]]
name = "ttf-parser"
version = "0.20.0"
@@ -2071,12 +1423,6 @@ version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8"
-[[package]]
-name = "ttf-parser"
-version = "0.25.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
-
[[package]]
name = "unicode-bidi"
version = "0.3.18"
@@ -2143,31 +1489,12 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
-[[package]]
-name = "walkdir"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
-dependencies = [
- "same-file",
- "winapi-util",
-]
-
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
-[[package]]
-name = "wasip2"
-version = "1.0.3+wasi-0.2.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6"
-dependencies = [
- "wit-bindgen",
-]
-
[[package]]
name = "wasm-bindgen"
version = "0.2.121"
@@ -2283,19 +1610,6 @@ dependencies = [
"wayland-scanner",
]
-[[package]]
-name = "wayland-protocols-plasma"
-version = "0.3.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b6d8cf1eb2c1c31ed1f5643c88a6e53538129d4af80030c8cabd1f9fa884d91"
-dependencies = [
- "bitflags 2.11.1",
- "wayland-backend",
- "wayland-client",
- "wayland-protocols",
- "wayland-scanner",
-]
-
[[package]]
name = "wayland-protocols-wlr"
version = "0.3.12"
@@ -2328,7 +1642,6 @@ checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be"
dependencies = [
"dlib",
"log",
- "once_cell",
"pkg-config",
]
@@ -2342,16 +1655,6 @@ dependencies = [
"wasm-bindgen",
]
-[[package]]
-name = "web-time"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
-dependencies = [
- "js-sys",
- "wasm-bindgen",
-]
-
[[package]]
name = "wgpu"
version = "24.0.5"
@@ -2430,7 +1733,7 @@ dependencies = [
"log",
"metal",
"naga",
- "ndk-sys 0.5.0+25.2.9519653",
+ "ndk-sys",
"objc",
"once_cell",
"ordered-float",
@@ -2540,15 +1843,6 @@ dependencies = [
"windows-targets",
]
-[[package]]
-name = "windows-sys"
-version = "0.52.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
-dependencies = [
- "windows-targets",
-]
-
[[package]]
name = "windows-sys"
version = "0.59.0"
@@ -2631,105 +1925,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
-[[package]]
-name = "winit"
-version = "0.30.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a6755fa58a9f8350bd1e472d4c3fcc25f824ec358933bba33306d0b63df5978d"
-dependencies = [
- "ahash",
- "android-activity",
- "atomic-waker",
- "bitflags 2.11.1",
- "block2",
- "bytemuck",
- "calloop",
- "cfg_aliases",
- "concurrent-queue",
- "core-foundation",
- "core-graphics",
- "cursor-icon",
- "dpi",
- "js-sys",
- "libc",
- "memmap2",
- "ndk",
- "objc2",
- "objc2-app-kit",
- "objc2-foundation",
- "objc2-ui-kit",
- "orbclient",
- "percent-encoding",
- "pin-project",
- "raw-window-handle",
- "redox_syscall 0.4.1",
- "rustix 0.38.44",
- "sctk-adwaita",
- "smithay-client-toolkit",
- "smol_str",
- "tracing",
- "unicode-segmentation",
- "wasm-bindgen",
- "wasm-bindgen-futures",
- "wayland-backend",
- "wayland-client",
- "wayland-protocols",
- "wayland-protocols-plasma",
- "web-sys",
- "web-time",
- "windows-sys 0.52.0",
- "x11-dl",
- "x11rb",
- "xkbcommon-dl",
-]
-
-[[package]]
-name = "winnow"
-version = "1.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "wit-bindgen"
-version = "0.57.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
-
-[[package]]
-name = "x11-dl"
-version = "2.21.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f"
-dependencies = [
- "libc",
- "once_cell",
- "pkg-config",
-]
-
-[[package]]
-name = "x11rb"
-version = "0.13.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414"
-dependencies = [
- "as-raw-xcb-connection",
- "gethostname",
- "libc",
- "libloading",
- "once_cell",
- "rustix 1.1.4",
- "x11rb-protocol",
-]
-
-[[package]]
-name = "x11rb-protocol"
-version = "0.13.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
-
[[package]]
name = "xcursor"
version = "0.3.10"
@@ -2737,15 +1932,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b"
[[package]]
-name = "xkbcommon-dl"
-version = "0.4.2"
+name = "xkbcommon"
+version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5"
+checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e"
dependencies = [
- "bitflags 2.11.1",
- "dlib",
- "log",
- "once_cell",
+ "libc",
+ "memmap2 0.8.0",
"xkeysym",
]
@@ -2754,6 +1947,9 @@ name = "xkeysym"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
+dependencies = [
+ "bytemuck",
+]
[[package]]
name = "xml-rs"
@@ -2773,26 +1969,6 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd15f8e0dbb966fd9245e7498c7e9e5055d9e5c8b676b95bd67091cd11a1e697"
-[[package]]
-name = "zerocopy"
-version = "0.8.48"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9"
-dependencies = [
- "zerocopy-derive",
-]
-
-[[package]]
-name = "zerocopy-derive"
-version = "0.8.48"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
[[package]]
name = "zmij"
version = "1.0.21"
diff --git a/Cargo.toml b/Cargo.toml
index d966ab5..5d36f14 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,7 +5,12 @@ edition = "2021"
[dependencies]
clear-ui = { path = "../clear-ui" }
-winit = "0.30"
+smithay-client-toolkit = "0.19.2"
+calloop = "0.13.0"
+calloop-wayland-source = "0.3.0"
+wayland-client = { version = "0.31", features = ["system"] }
+xkeysym = "0.2"
+raw-window-handle = "0.6"
wgpu = "24"
bytemuck = { version = "1", features = ["derive"] }
pollster = "0.4"
diff --git a/nodes/node_1.json b/nodes/node_1.json
deleted file mode 100644
index 83a7ed1..0000000
--- a/nodes/node_1.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "name": "Node 1",
- "children": [
- {
- "name": "Child A",
- "params": [
- { "name": "Type", "default": "Subnet" }
- ]
- },
- {
- "name": "Child B",
- "params": [
- { "name": "Type", "default": "Subnet" }
- ]
- }
- ],
- "params": [
- { "name": "IP", "label": "IP Address", "type": "string", "default": "192.168.1.1" },
- { "name": "Port", "type": "string", "default": "8080" }
- ]
-}
diff --git a/nodes/node_2.json b/nodes/node_2.json
deleted file mode 100644
index dc66872..0000000
--- a/nodes/node_2.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "name": "Node 2",
- "params": [
- { "name": "IP", "default": "10.0.0.1" },
- { "name": "Port", "default": "443" }
- ]
-}
diff --git a/src/main.rs b/src/main.rs
index 13b246a..2c237ee 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -16,11 +16,73 @@ use opencl3::kernel::{Kernel, ExecuteKernel};
use opencl3::memory::{Buffer as ClBuffer, CL_MEM_READ_WRITE};
use opencl3::types::{cl_float, cl_int, CL_TRUE};
-use winit::application::ApplicationHandler;
-use winit::event::{ElementState, KeyEvent, MouseButton, WindowEvent, MouseScrollDelta};
-use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
-use winit::keyboard::{Key, ModifiersState, NamedKey};
-use winit::window::{Window, WindowAttributes};
+use clear_ui::widget::{ElementState, MouseButton, MouseScrollDelta, KeyEvent, Key, NamedKey, Position};
+
+use smithay_client_toolkit::{
+ compositor::{CompositorHandler, CompositorState},
+ delegate_compositor, delegate_keyboard, delegate_pointer, delegate_registry,
+ delegate_seat, delegate_shm, delegate_xdg_shell, delegate_xdg_window, delegate_output,
+ registry::{ProvidesRegistryState, RegistryState},
+ output::{OutputHandler, OutputState},
+ seat::{
+ keyboard::KeyboardHandler,
+ pointer::PointerHandler,
+ Capability, SeatHandler, SeatState,
+ },
+ shell::{
+ xdg::{
+ window::{Window as XdgWindow, WindowConfigure, WindowHandler, WindowDecorations},
+ XdgShell,
+ },
+ WaylandSurface,
+ },
+ shm::{Shm, ShmHandler},
+};
+use wayland_client::{
+ globals::registry_queue_init,
+ protocol::{wl_keyboard, wl_output, wl_pointer, wl_seat, wl_shm, wl_surface},
+ Connection, QueueHandle, Proxy,
+};
+use calloop_wayland_source::WaylandSource;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum TouchPhase {
+ Started,
+ Moved,
+ Ended,
+ Cancelled,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
+struct ModifiersState {
+ ctrl: bool,
+ alt: bool,
+ shift: bool,
+ logo: bool,
+}
+
+impl ModifiersState {
+ fn control_key(&self) -> bool { self.ctrl }
+ fn alt_key(&self) -> bool { self.alt }
+ fn shift_key(&self) -> bool { self.shift }
+ fn super_key(&self) -> bool { self.logo }
+ fn state(&self) -> Self { *self }
+}
+
+#[derive(Debug, Clone, Copy)]
+struct LocalPosition {
+ x: f64,
+ y: f64,
+}
+
+enum WindowEvent {
+ MouseWheel { delta: clear_ui::widget::MouseScrollDelta, phase: TouchPhase },
+ PinchGesture { delta: f64 },
+ CursorMoved { position: LocalPosition },
+ MouseInput { state: clear_ui::widget::ElementState, button: clear_ui::widget::MouseButton },
+ ModifiersChanged(ModifiersState),
+ KeyboardInput { event: clear_ui::widget::KeyEvent },
+}
use wgpu::util::DeviceExt;
@@ -2101,8 +2163,25 @@ fn make_text_buffer(font_system: &mut FontSystem, text: &str, size: f32) -> Buff
buffer
}
+fn get_next_visible_pane(current_pane: usize, show_spreadsheet: bool, shift_pressed: bool) -> usize {
+ let visible_panes = if show_spreadsheet {
+ vec![LEFT_MENUBAR_IDX, RIGHT_MENUBAR_IDX, PARAM_MENUBAR_IDX, SPREADSHEET_MENUBAR_IDX]
+ } else {
+ vec![LEFT_MENUBAR_IDX, RIGHT_MENUBAR_IDX, PARAM_MENUBAR_IDX]
+ };
+
+ let current_pos = visible_panes.iter().position(|&x| x == current_pane).unwrap_or(0);
+ let next_pos = if shift_pressed {
+ (current_pos + visible_panes.len() - 1) % visible_panes.len()
+ } else {
+ (current_pos + 1) % visible_panes.len()
+ };
+ visible_panes[next_pos]
+}
+
struct State {
- window: Arc<Window>,
+ window: XdgWindow,
+ wl_surface: wl_surface::WlSurface,
surface: wgpu::Surface<'static>,
device: wgpu::Device,
queue: wgpu::Queue,
@@ -2349,18 +2428,16 @@ impl State {
self.upload_vertices();
}
- fn next_node_offset(&self, _idx: usize) -> (f32, f32) {
- (self.grid_cursor_col as f32, self.grid_cursor_row as f32)
- }
fn place_selected_node(&mut self) -> bool {
let Some(&template_idx) = self.node_palette_filtered.get(self.node_palette_selected) else { return false; };
let child_idx = self.current_dir().children.len();
if child_idx >= self.node_slots.len() { return false; }
let mut node = self.node_templates[template_idx].node.clone();
- node.position = (self.grid_cursor_col as f32, self.grid_cursor_row as f32);
+ let (nx, ny) = self.find_empty_cell(self.grid_cursor_col as f32, self.grid_cursor_row as f32, None);
+ node.position = (nx, ny);
self.current_dir_mut().children.push(node);
- self.left_offsets[child_idx] = self.next_node_offset(child_idx);
+ self.left_offsets[child_idx] = (nx, ny);
self.close_node_palette();
self.sync_nodes();
self.rebuild_positions();
@@ -2538,6 +2615,27 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
self.upload_vertices();
}
+ fn find_empty_cell(&self, start_x: f32, start_y: f32, skip_idx: Option<usize>) -> (f32, f32) {
+ let x = start_x;
+ let mut y = start_y;
+ let dir = self.current_dir();
+ loop {
+ let occupied = dir.children.iter().enumerate().any(|(idx, c)| {
+ if Some(idx) == skip_idx {
+ false
+ } else {
+ (c.position.0 - x).abs() < 0.01 && (c.position.1 - y).abs() < 0.01
+ }
+ });
+ if occupied {
+ y += 1.0;
+ } else {
+ break;
+ }
+ }
+ (x, y)
+ }
+
fn sync_nodes(&mut self) {
let node_infos: Vec<Option<(String, (f32, f32))>> = {
let dir = self.current_dir();
@@ -2747,23 +2845,39 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
(tex, view)
}
- async fn new(window: Arc<Window>) -> Self {
+ async fn new(
+ conn: &Connection,
+ qh: &QueueHandle<AppState>,
+ compositor_state: &CompositorState,
+ xdg_shell_state: &XdgShell,
+ pw: u32,
+ ph: u32,
+ ) -> Self {
let settings = DesignSettings::load();
- let scale = window.scale_factor();
- let physical_size = window.inner_size();
- let pw = physical_size.width.max(1);
- let ph = physical_size.height.max(1);
+ let scale = 2.0f64; // Default to 2.0 (high-DPI)
let lw = pw as f32 / scale as f32;
let lh = ph as f32 / scale as f32;
let sw = lw;
+ let wl_surface = compositor_state.create_surface(qh);
+ let window = xdg_shell_state.create_window(wl_surface.clone(), WindowDecorations::None, qh);
+ window.set_title("Clear Design Interface");
+ window.set_app_id("clear-design-interface");
+ window.set_min_size(Some((pw, ph)));
+ window.commit();
+
+ let wayland_handle = Box::leak(Box::new(clear_ui::wayland::WaylandSurfaceHandle {
+ display_ptr: conn.backend().display_id().as_ptr() as *mut std::ffi::c_void,
+ surface_ptr: wl_surface.id().as_ptr() as *mut std::ffi::c_void,
+ }));
+
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
backends: wgpu::Backends::VULKAN,
..Default::default()
});
let surface = instance
- .create_surface(window.clone())
+ .create_surface(wayland_handle)
.expect("Failed to create surface");
let adapter = instance
@@ -3114,6 +3228,7 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
let mut state = Self {
window,
+ wl_surface,
surface,
device,
queue,
@@ -3750,14 +3865,14 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
text_renderer.prepare(device, queue, font_system, text_atlas, text_viewport, areas, swash_cache).unwrap();
}
- fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
- if new_size.width > 0 && new_size.height > 0 {
- self.physical_width = new_size.width;
- self.physical_height = new_size.height;
- self.width = new_size.width as f32 / self.scale as f32;
- self.height = new_size.height as f32 / self.scale as f32;
- self.config.width = new_size.width;
- self.config.height = new_size.height;
+ fn resize(&mut self, width: u32, height: u32) {
+ if width > 0 && height > 0 {
+ self.physical_width = width;
+ self.physical_height = height;
+ self.width = width as f32 / self.scale as f32;
+ self.height = height as f32 / self.scale as f32;
+ self.config.width = width;
+ self.config.height = height;
self.surface.configure(&self.device, &self.config);
let (tex, view) = self.create_depth_texture();
@@ -3860,10 +3975,10 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
} else if !dialog_open && in_network_pane {
if self.modifiers.control_key() {
let factor = match delta {
- winit::event::MouseScrollDelta::LineDelta(_x, y) => {
+ MouseScrollDelta::LineDelta(_x, y) => {
if *y > 0.0 { 1.15 } else if *y < 0.0 { 1.0 / 1.15 } else { 1.0 }
}
- winit::event::MouseScrollDelta::PixelDelta(pos) => {
+ MouseScrollDelta::PixelDelta(pos) => {
let dy = pos.y as f32;
(1.0 + dy / 100.0).clamp(0.8, 1.25)
}
@@ -3876,7 +3991,7 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
}
} else {
match delta {
- winit::event::MouseScrollDelta::LineDelta(x, y) => {
+ MouseScrollDelta::LineDelta(x, y) => {
let dx = *x * 30.0;
let dy = *y * 30.0;
self.pan_x -= dx;
@@ -3890,14 +4005,14 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
self.sync_grid_settings();
true
}
- winit::event::MouseScrollDelta::PixelDelta(pos) => {
+ MouseScrollDelta::PixelDelta(pos) => {
let dx = pos.x as f32 / self.scale as f32;
let dy = pos.y as f32 / self.scale as f32;
self.pan_x -= dx;
self.pan_y -= dy;
self.is_scrolling_trackpad = match phase {
- winit::event::TouchPhase::Started | winit::event::TouchPhase::Moved => true,
- winit::event::TouchPhase::Ended | winit::event::TouchPhase::Cancelled => false,
+ TouchPhase::Started | TouchPhase::Moved => true,
+ TouchPhase::Ended | TouchPhase::Cancelled => false,
};
self.last_scroll_time = Instant::now();
self.scroll_accum_x += dx;
@@ -3910,7 +4025,7 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
} else if !dialog_open && in_viewport {
if self.modifiers.control_key() {
match delta {
- winit::event::MouseScrollDelta::LineDelta(_x, y) => {
+ MouseScrollDelta::LineDelta(_x, y) => {
let dy = *y * 0.15;
self.viewport_zoom *= (-dy).exp();
self.viewport_zoom = self.viewport_zoom.clamp(0.05, 20.0);
@@ -3922,14 +4037,14 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
true
}
- winit::event::MouseScrollDelta::PixelDelta(pos) => {
+ MouseScrollDelta::PixelDelta(pos) => {
let dy = (pos.y as f32 / self.scale as f32) * 0.005;
self.viewport_zoom *= (-dy).exp();
self.viewport_zoom = self.viewport_zoom.clamp(0.05, 20.0);
self.is_zooming_viewport = match phase {
- winit::event::TouchPhase::Started | winit::event::TouchPhase::Moved => true,
- winit::event::TouchPhase::Ended | winit::event::TouchPhase::Cancelled => false,
+ TouchPhase::Started | TouchPhase::Moved => true,
+ TouchPhase::Ended | TouchPhase::Cancelled => false,
};
self.last_zoom_time = Instant::now();
self.zoom_accum += dy;
@@ -3939,7 +4054,7 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
}
} else {
match delta {
- winit::event::MouseScrollDelta::LineDelta(x, y) => {
+ MouseScrollDelta::LineDelta(x, y) => {
self.scroll_lock = 0;
let dx = *x * 0.05;
let dy = *y * 0.05;
@@ -3955,11 +4070,11 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
true
}
- winit::event::MouseScrollDelta::PixelDelta(pos) => {
+ MouseScrollDelta::PixelDelta(pos) => {
let mut dx = (pos.x as f32 / self.scale as f32) * 0.005;
let mut dy = (pos.y as f32 / self.scale as f32) * 0.005;
- if *phase == winit::event::TouchPhase::Started {
+ if *phase == TouchPhase::Started {
self.scroll_lock = 0;
self.rotate_accum_yaw = 0.0;
self.rotate_accum_pitch = 0.0;
@@ -3989,8 +4104,8 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
self.rotation_x -= dy;
self.is_rotating_viewport = match phase {
- winit::event::TouchPhase::Started | winit::event::TouchPhase::Moved => true,
- winit::event::TouchPhase::Ended | winit::event::TouchPhase::Cancelled => {
+ TouchPhase::Started | TouchPhase::Moved => true,
+ TouchPhase::Ended | TouchPhase::Cancelled => {
self.scroll_lock = 0;
false
}
@@ -4232,6 +4347,18 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
ElementState::Released => {
if self.drag_widget.is_some() {
let idx = self.drag_widget.unwrap();
+ if self.node_slots.contains(&idx) {
+ if let Some(slot_idx) = self.node_slots.iter().position(|&x| x == idx) {
+ let pos = self.current_dir().children[slot_idx].position;
+ let (nx, ny) = self.find_empty_cell(pos.0, pos.1, Some(slot_idx));
+ self.current_dir_mut().children[slot_idx].position = (nx, ny);
+ self.left_offsets[slot_idx] = (nx, ny);
+ self.rebuild_positions();
+ self.apply_layout();
+ self.update_panel_bounds();
+ self.upload_vertices();
+ }
+ }
self.widgets[idx].drag_end();
self.drag_widget = None;
changed = true;
@@ -4272,6 +4399,14 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
self.space_pressed = event.state == ElementState::Pressed;
}
+ if event.state == ElementState::Pressed {
+ if event.logical_key == Key::Named(NamedKey::Tab) && self.modifiers.control_key() && !self.modifiers.alt_key() && !self.modifiers.super_key() {
+ self.focused_pane = get_next_visible_pane(self.focused_pane, self.show_spreadsheet, self.modifiers.shift_key());
+ self.sync_pane_focus();
+ return true;
+ }
+ }
+
if self.widgets[PARAM_IDX].keyboard_input(event) {
if let Some(focused) = self.focused_widget {
if self.node_slots.contains(&focused) {
@@ -4458,7 +4593,7 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
}
}
- fn render(&mut self) {
+ fn render(&mut self) -> bool {
let now = Instant::now();
let dt = now.duration_since(self.last_frame).as_secs_f32().min(0.1);
self.last_frame = now;
@@ -4627,7 +4762,6 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
self.sync_layout();
self.read_panel_offsets();
self.upload_vertices();
- self.window.request_redraw();
}
self.prepare_text();
@@ -4636,10 +4770,10 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
Ok(t) => t,
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
self.surface.configure(&self.device, &self.config);
- return;
+ return false;
}
- Err(wgpu::SurfaceError::Timeout) => return,
- Err(e) => { eprintln!("Surface error: {e:?}"); return; }
+ Err(wgpu::SurfaceError::Timeout) => return false,
+ Err(e) => { eprintln!("Surface error: {e:?}"); return false; }
};
let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
@@ -4795,309 +4929,632 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
}
self.queue.submit(std::iter::once(encoder.finish()));
- self.window.pre_present_notify();
output.present();
+ tick_changed || panned
}
}
-struct App { state: Option<State> }
+struct AppState {
+ registry_state: RegistryState,
+ compositor_state: CompositorState,
+ xdg_shell_state: XdgShell,
+ shm_state: Shm,
+ seat_state: SeatState,
+ output_state: OutputState,
-impl App { fn new() -> Self { Self { state: None } } }
+ seats: Vec<wl_seat::WlSeat>,
+ pointer: Option<wl_pointer::WlPointer>,
+ keyboard: Option<wl_keyboard::WlKeyboard>,
-impl ApplicationHandler<CustomEvent> for App {
- fn resumed(&mut self, event_loop: &ActiveEventLoop) {
- if self.state.is_some() { return; }
+ window: XdgWindow,
+ surface: wl_surface::WlSurface,
- let window = Arc::new(
- event_loop.create_window(
- WindowAttributes::default()
- .with_title("Clear Design Interface")
- .with_inner_size(winit::dpi::LogicalSize::new(1280, 800)),
- ).unwrap(),
- );
+ state: Option<State>,
+ exit: bool,
+ redraw: bool,
+}
+
+impl CompositorHandler for AppState {
+ fn scale_factor_changed(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _surface: &wl_surface::WlSurface,
+ scale_factor: i32,
+ ) {
+ if let Some(state) = &mut self.state {
+ state.scale = scale_factor as f64;
+ let pw = (state.width as f64 * state.scale) as u32;
+ let ph = (state.height as f64 * state.scale) as u32;
+ state.resize(pw, ph);
+ self.redraw = true;
+ }
+ }
+
+ fn transform_changed(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _surface: &wl_surface::WlSurface,
+ _new_transform: wl_output::Transform,
+ ) {}
+
+ fn frame(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _surface: &wl_surface::WlSurface,
+ _time: u32,
+ ) {}
+
+ fn surface_enter(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _surface: &wl_surface::WlSurface,
+ _output: &wl_output::WlOutput,
+ ) {}
+
+ fn surface_leave(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _surface: &wl_surface::WlSurface,
+ _output: &wl_output::WlOutput,
+ ) {}
+}
- let state = pollster::block_on(State::new(window));
- self.state = Some(state);
- self.state.as_ref().unwrap().window.request_redraw();
+impl OutputHandler for AppState {
+ fn output_state(&mut self) -> &mut OutputState {
+ &mut self.output_state
}
- fn window_event(
- &mut self, event_loop: &ActiveEventLoop,
- _window_id: winit::window::WindowId, event: WindowEvent,
+ fn new_output(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _output: wl_output::WlOutput,
+ ) {}
+
+ fn update_output(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _output: wl_output::WlOutput,
+ ) {}
+
+ fn output_destroyed(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _output: wl_output::WlOutput,
+ ) {}
+}
+
+impl SeatHandler for AppState {
+ fn seat_state(&mut self) -> &mut SeatState {
+ &mut self.seat_state
+ }
+
+ fn new_seat(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, seat: wl_seat::WlSeat) {
+ self.seats.push(seat);
+ }
+
+ fn new_capability(
+ &mut self,
+ _conn: &Connection,
+ qh: &QueueHandle<Self>,
+ seat: wl_seat::WlSeat,
+ capability: Capability,
) {
- let needs_redraw = match event {
- WindowEvent::CloseRequested => { event_loop.exit(); true }
- WindowEvent::Resized(size) => {
- if let Some(state) = &mut self.state { state.resize(size); }
- true
+ if capability == Capability::Pointer && self.pointer.is_none() {
+ let pointer = self.seat_state.get_pointer(qh, &seat).unwrap();
+ self.pointer = Some(pointer);
+ }
+ if capability == Capability::Keyboard && self.keyboard.is_none() {
+ let keyboard = self
+ .seat_state
+ .get_keyboard(qh, &seat, None)
+ .unwrap();
+ self.keyboard = Some(keyboard);
+ }
+ }
+
+ fn remove_capability(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _seat: wl_seat::WlSeat,
+ capability: Capability,
+ ) {
+ if capability == Capability::Pointer {
+ self.pointer = None;
+ }
+ if capability == Capability::Keyboard {
+ self.keyboard = None;
+ }
+ }
+
+ fn remove_seat(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, seat: wl_seat::WlSeat) {
+ self.seats.retain(|s| s != &seat);
+ }
+}
+
+impl ShmHandler for AppState {
+ fn shm_state(&mut self) -> &mut Shm {
+ &mut self.shm_state
+ }
+}
+
+impl PointerHandler for AppState {
+ fn pointer_frame(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _pointer: &wl_pointer::WlPointer,
+ events: &[smithay_client_toolkit::seat::pointer::PointerEvent],
+ ) {
+ use smithay_client_toolkit::seat::pointer::PointerEventKind;
+ for event in events {
+ let (x, y) = event.position;
+ if let Some(st) = &mut self.state {
+ let scale = st.scale;
+ match &event.kind {
+ PointerEventKind::Motion { .. } => {
+ let ev = WindowEvent::CursorMoved {
+ position: LocalPosition {
+ x: x * scale,
+ y: y * scale,
+ },
+ };
+ self.process_event(ev);
+ }
+ PointerEventKind::Press { button, .. } => {
+ let btn = match *button {
+ 272 => clear_ui::widget::MouseButton::Left,
+ 273 => clear_ui::widget::MouseButton::Right,
+ 274 => clear_ui::widget::MouseButton::Middle,
+ _ => continue,
+ };
+ let ev = WindowEvent::MouseInput {
+ state: clear_ui::widget::ElementState::Pressed,
+ button: btn,
+ };
+ self.process_event(ev);
+ }
+ PointerEventKind::Release { button, .. } => {
+ let btn = match *button {
+ 272 => clear_ui::widget::MouseButton::Left,
+ 273 => clear_ui::widget::MouseButton::Right,
+ 274 => clear_ui::widget::MouseButton::Middle,
+ _ => continue,
+ };
+ let ev = WindowEvent::MouseInput {
+ state: clear_ui::widget::ElementState::Released,
+ button: btn,
+ };
+ self.process_event(ev);
+ }
+ PointerEventKind::Axis { horizontal, vertical, .. } => {
+ let h_val = horizontal.absolute as f32;
+ let v_val = vertical.absolute as f32;
+ let ev = WindowEvent::MouseWheel {
+ delta: clear_ui::widget::MouseScrollDelta::LineDelta(-h_val / 10.0, -v_val / 10.0),
+ phase: TouchPhase::Moved,
+ };
+ self.process_event(ev);
+ }
+ _ => {}
+ }
+ }
+ }
+ }
+}
+
+impl KeyboardHandler for AppState {
+ fn enter(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _keyboard: &wl_keyboard::WlKeyboard,
+ _surface: &wl_surface::WlSurface,
+ _serial: u32,
+ _raw_modifiers: &[u32],
+ _keysyms: &[xkeysym::Keysym],
+ ) {}
+
+ fn leave(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _keyboard: &wl_keyboard::WlKeyboard,
+ _surface: &wl_surface::WlSurface,
+ _serial: u32,
+ ) {}
+
+ fn press_key(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _keyboard: &wl_keyboard::WlKeyboard,
+ _serial: u32,
+ event: smithay_client_toolkit::seat::keyboard::KeyEvent,
+ ) {
+ self.handle_key(event, clear_ui::widget::ElementState::Pressed);
+ }
+
+ fn release_key(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _keyboard: &wl_keyboard::WlKeyboard,
+ _serial: u32,
+ event: smithay_client_toolkit::seat::keyboard::KeyEvent,
+ ) {
+ self.handle_key(event, clear_ui::widget::ElementState::Released);
+ }
+
+ fn update_modifiers(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _keyboard: &wl_keyboard::WlKeyboard,
+ _serial: u32,
+ _modifiers: smithay_client_toolkit::seat::keyboard::Modifiers,
+ _layout: u32,
+ ) {}
+}
+
+impl WindowHandler for AppState {
+ fn configure(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _window: &XdgWindow,
+ configure: WindowConfigure,
+ _serial: u32,
+ ) {
+ let (w, h) = configure.new_size;
+ if let (Some(w), Some(h)) = (w, h) {
+ let width = w.get();
+ let height = h.get();
+ if let Some(state) = &mut self.state {
+ state.resize(width, height);
}
- WindowEvent::RedrawRequested => {
- if let Some(state) = &mut self.state {
- state.render();
- state.window.request_redraw();
+ }
+ self.redraw = true;
+ }
+
+ fn request_close(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, _window: &XdgWindow) {
+ self.exit = true;
+ }
+}
+
+impl ProvidesRegistryState for AppState {
+ fn registry(&mut self) -> &mut RegistryState {
+ &mut self.registry_state
+ }
+
+ fn runtime_add_global(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _name: u32,
+ _interface: &str,
+ _version: u32,
+ ) {}
+
+ fn runtime_remove_global(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _name: u32,
+ _interface: &str,
+ ) {}
+}
+
+delegate_compositor!(AppState);
+delegate_xdg_shell!(AppState);
+delegate_xdg_window!(AppState);
+delegate_shm!(AppState);
+delegate_seat!(AppState);
+delegate_pointer!(AppState);
+delegate_keyboard!(AppState);
+delegate_registry!(AppState);
+delegate_output!(AppState);
+
+impl AppState {
+ fn handle_key(&mut self, event: smithay_client_toolkit::seat::keyboard::KeyEvent, state: clear_ui::widget::ElementState) {
+ use clear_ui::widget::{Key, KeyEvent, NamedKey};
+ let logical_key = match event.keysym {
+ xkeysym::Keysym::Escape => Key::Named(NamedKey::Escape),
+ xkeysym::Keysym::Return => Key::Named(NamedKey::Enter),
+ xkeysym::Keysym::BackSpace => Key::Named(NamedKey::Backspace),
+ xkeysym::Keysym::Down => Key::Named(NamedKey::ArrowDown),
+ xkeysym::Keysym::Up => Key::Named(NamedKey::ArrowUp),
+ xkeysym::Keysym::Left => Key::Named(NamedKey::ArrowLeft),
+ xkeysym::Keysym::Right => Key::Named(NamedKey::ArrowRight),
+ xkeysym::Keysym::Tab => Key::Named(NamedKey::Tab),
+ xkeysym::Keysym::Delete => Key::Named(NamedKey::Delete),
+ xkeysym::Keysym::space => Key::Named(NamedKey::Space),
+ _ => {
+ if let Some(ref text) = event.utf8 {
+ Key::Character(text.clone())
+ } else {
+ return;
}
- true
}
- WindowEvent::ScaleFactorChanged { scale_factor, mut inner_size_writer } => {
- if let Some(state) = &mut self.state {
- let new_physical = winit::dpi::PhysicalSize::new(
- (state.width as f64 * scale_factor) as u32,
- (state.height as f64 * scale_factor) as u32,
- );
- let _ = inner_size_writer.request_inner_size(new_physical);
- state.scale = scale_factor;
- state.resize(new_physical);
- state.window.request_redraw();
+ };
+
+ if let Some(st) = &mut self.state {
+ match event.keysym {
+ xkeysym::Keysym::Control_L | xkeysym::Keysym::Control_R => {
+ st.modifiers.ctrl = state == clear_ui::widget::ElementState::Pressed;
}
- true
+ xkeysym::Keysym::Alt_L | xkeysym::Keysym::Alt_R => {
+ st.modifiers.alt = state == clear_ui::widget::ElementState::Pressed;
+ }
+ xkeysym::Keysym::Shift_L | xkeysym::Keysym::Shift_R => {
+ st.modifiers.shift = state == clear_ui::widget::ElementState::Pressed;
+ }
+ xkeysym::Keysym::Super_L | xkeysym::Keysym::Super_R => {
+ st.modifiers.logo = state == clear_ui::widget::ElementState::Pressed;
+ }
+ _ => {}
}
- _ => {
- if let Some(state) = &mut self.state {
- let mut changed = state.handle_event(&event);
- if let Some(seg) = state.widgets[BREADCRUMB_IDX].path_click() {
- if seg < state.current_path.len() {
- state.current_path.truncate(seg);
- state.on_path_changed();
- changed = true;
- }
- }
+ let custom_event = KeyEvent {
+ state,
+ logical_key,
+ text: event.utf8.clone(),
+ repeat: false,
+ };
- if let Some(action) = state.pending_action.take() {
- state.execute_action(action);
- changed = true;
- }
+ let ev = WindowEvent::KeyboardInput { event: custom_event };
+ self.process_event(ev);
+ }
+ }
- if let Some((menu_idx, item_idx)) = state.widgets[HEADER_IDX].menu_click() {
- if menu_idx == 0 { // File
- match item_idx {
- 0 => { // New Project
- state.new_project();
- changed = true;
- }
- 1 => { // Open
- let proj_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("project.json");
- if let Err(e) = state.load_from_file(&proj_path) {
- eprintln!("Failed to load project: {:?}", e);
- }
- changed = true;
- }
- 2 => { // Save
- let proj_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("project.json");
- if let Err(e) = state.save_to_file(&proj_path) {
- eprintln!("Failed to save project: {:?}", e);
- }
- changed = true;
- }
- 3 => { // Configure
- state.execute_action(Action::ToggleConfigure);
- changed = true;
- }
- 4 => { // Exit
- state.exit_requested = true;
- }
- _ => {}
+ fn process_event(&mut self, ev: WindowEvent) {
+ if let Some(state) = &mut self.state {
+ let mut changed = state.handle_event(&ev);
+
+ if let Some(seg) = state.widgets[BREADCRUMB_IDX].path_click() {
+ if seg < state.current_path.len() {
+ state.current_path.truncate(seg);
+ state.on_path_changed();
+ changed = true;
+ }
+ }
+
+ if let Some(action) = state.pending_action.take() {
+ state.execute_action(action);
+ changed = true;
+ }
+
+ if let Some((menu_idx, item_idx)) = state.widgets[HEADER_IDX].menu_click() {
+ if menu_idx == 0 { // File
+ match item_idx {
+ 0 => { // New Project
+ state.new_project();
+ changed = true;
+ }
+ 1 => { // Open
+ let proj_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("project.json");
+ if let Err(e) = state.load_from_file(&proj_path) {
+ eprintln!("Failed to load project: {:?}", e);
}
+ changed = true;
}
- }
-
- if let Some((menu_idx, item_idx)) = state.widgets[LEFT_MENUBAR_IDX].menu_click() {
- if menu_idx == 0 { // File
- match item_idx {
- 0 => { // New
- state.new_project();
- changed = true;
- }
- 1 => { // Open
- let proj_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("project.json");
- if let Err(e) = state.load_from_file(&proj_path) {
- eprintln!("Failed to load project: {:?}", e);
- }
- changed = true;
- }
- 2 => { // Save
- let proj_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("project.json");
- if let Err(e) = state.save_to_file(&proj_path) {
- eprintln!("Failed to save project: {:?}", e);
- }
- changed = true;
- }
- _ => {}
+ 2 => { // Save
+ let proj_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("project.json");
+ if let Err(e) = state.save_to_file(&proj_path) {
+ eprintln!("Failed to save project: {:?}", e);
}
+ changed = true;
+ }
+ 3 => { // Configure
+ state.execute_action(Action::ToggleConfigure);
+ changed = true;
}
+ 4 => { // Exit
+ state.exit_requested = true;
+ }
+ _ => {}
}
+ }
+ }
- if let Some((menu_idx, item_idx)) = state.widgets[RIGHT_MENUBAR_IDX].menu_click() {
- if menu_idx == 0 {
- let camera_nodes: Vec<String> = state.current_dir().children.iter()
- .filter(|c| c.node_type == "camera")
- .map(|c| c.name.clone())
- .collect();
- let mut items = vec!["Default Camera".to_string()];
- items.extend(camera_nodes);
- if item_idx < items.len() {
- state.active_camera = items[item_idx].clone();
- for (i, item) in items.iter().enumerate() {
- state.widgets[RIGHT_MENUBAR_IDX].set_item_checked(0, i, item == &state.active_camera);
- }
- changed = true;
+ if let Some((menu_idx, item_idx)) = state.widgets[LEFT_MENUBAR_IDX].menu_click() {
+ if menu_idx == 0 { // File
+ match item_idx {
+ 0 => { // New
+ state.new_project();
+ changed = true;
+ }
+ 1 => { // Open
+ let proj_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("project.json");
+ if let Err(e) = state.load_from_file(&proj_path) {
+ eprintln!("Failed to load project: {:?}", e);
}
- } else {
- let action = if menu_idx == 1 {
- Some(Action::ToggleSquareViewport)
- } else if menu_idx == 2 {
- match item_idx {
- 0 => Some(Action::ToggleGrid),
- 1 => Some(Action::ToggleCube),
- 2 => Some(Action::ToggleOrigin),
- 3 => Some(Action::ToggleCameraPivot),
- _ => None,
- }
- } else { None };
- if let Some(a) = action {
- state.execute_action(a);
- changed = true;
+ changed = true;
+ }
+ 2 => { // Save
+ let proj_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("project.json");
+ if let Err(e) = state.save_to_file(&proj_path) {
+ eprintln!("Failed to save project: {:?}", e);
}
+ changed = true;
}
+ _ => {}
}
+ }
+ }
- let mut settings_changed = false;
- while let Some((id, val)) = state.widgets[CONFIG_DIALOG_IDX].take_config_toggle() {
- match id {
- 0 => {
- state.grid_snap_enabled = val;
- for &i in &state.node_slots {
- let gx = if val { state.grid_size_x + state.skipped_col_w } else { 0.0 };
- let gy = if val { state.grid_size_y + state.skipped_row_h } else { 0.0 };
- state.widgets[i].set_grid_snap(gx, gy);
- }
- }
- 1 => {
- state.network_grid_visible = val;
- state.widgets[CONTENT_IDX].set_show_network_grid(val);
- }
- 2 => {
- state.show_grid = val;
- state.widgets[RIGHT_MENUBAR_IDX].set_item_checked(2, 0, val);
- }
- 3 => {
- state.show_cube = val;
- state.widgets[RIGHT_MENUBAR_IDX].set_item_checked(2, 1, val);
- }
- 4 => {
- state.show_origin = val;
- state.widgets[RIGHT_MENUBAR_IDX].set_item_checked(2, 2, val);
- }
- 5 => {
- state.show_camera_pivot = val;
- state.widgets[RIGHT_MENUBAR_IDX].set_item_checked(2, 3, val);
- }
- _ => {}
+ if let Some((menu_idx, item_idx)) = state.widgets[RIGHT_MENUBAR_IDX].menu_click() {
+ if menu_idx == 0 {
+ let camera_nodes: Vec<String> = state.current_dir().children.iter()
+ .filter(|c| c.node_type == "camera")
+ .map(|c| c.name.clone())
+ .collect();
+ let mut items = vec!["Default Camera".to_string()];
+ items.extend(camera_nodes);
+ if item_idx < items.len() {
+ state.active_camera = items[item_idx].clone();
+ for (i, item) in items.iter().enumerate() {
+ state.widgets[RIGHT_MENUBAR_IDX].set_item_checked(0, i, item == &state.active_camera);
}
changed = true;
- settings_changed = true;
}
-
- while let Some((id, val)) = state.widgets[CONFIG_DIALOG_IDX].take_config_spin() {
- match id {
- 0 => state.grid_size_x = val,
- 1 => state.grid_size_y = val,
- 2 => state.skipped_row_h = val,
- 3 => state.skipped_col_w = val,
- 4 => state.viewport_bg_color[0] = val,
- 5 => state.viewport_bg_color[1] = val,
- 6 => state.viewport_bg_color[2] = val,
- 7 => {
- state.origin_size = val;
- state.update_origin_geometry();
- }
- 8 => {
- state.grid_thickness = val;
- state.update_grid_geometry();
- }
- 9 => {
- state.camera_pivot_size = val;
- state.update_pivot_geometry();
- }
- _ => {}
+ } else {
+ let action = if menu_idx == 1 {
+ Some(Action::ToggleSquareViewport)
+ } else if menu_idx == 2 {
+ match item_idx {
+ 0 => Some(Action::ToggleGrid),
+ 1 => Some(Action::ToggleCube),
+ 2 => Some(Action::ToggleOrigin),
+ 3 => Some(Action::ToggleCameraPivot),
+ _ => None,
}
+ } else { None };
+ if let Some(a) = action {
+ state.execute_action(a);
+ changed = true;
+ }
+ }
+ }
+
+ let mut settings_changed = false;
+ while let Some((id, val)) = state.widgets[CONFIG_DIALOG_IDX].take_config_toggle() {
+ match id {
+ 0 => {
+ state.grid_snap_enabled = val;
for &i in &state.node_slots {
- let (x, y, _, _) = state.widgets[i].rect();
- state.widgets[i].set_rect(x, y, state.grid_size_x, state.grid_size_y);
+ let gx = if val { state.grid_size_x + state.skipped_col_w } else { 0.0 };
+ let gy = if val { state.grid_size_y + state.skipped_row_h } else { 0.0 };
+ state.widgets[i].set_grid_snap(gx, gy);
}
- state.sync_grid_settings();
- changed = true;
- settings_changed = true;
}
+ 1 => {
+ state.network_grid_visible = val;
+ state.widgets[CONTENT_IDX].set_show_network_grid(val);
+ }
+ 2 => {
+ state.show_grid = val;
+ state.widgets[RIGHT_MENUBAR_IDX].set_item_checked(2, 0, val);
+ }
+ 3 => {
+ state.show_cube = val;
+ state.widgets[RIGHT_MENUBAR_IDX].set_item_checked(2, 1, val);
+ }
+ 4 => {
+ state.show_origin = val;
+ state.widgets[RIGHT_MENUBAR_IDX].set_item_checked(2, 2, val);
+ }
+ 5 => {
+ state.show_camera_pivot = val;
+ state.widgets[RIGHT_MENUBAR_IDX].set_item_checked(2, 3, val);
+ }
+ _ => {}
+ }
+ changed = true;
+ settings_changed = true;
+ }
- if settings_changed {
- state.save_settings();
+ while let Some((id, val)) = state.widgets[CONFIG_DIALOG_IDX].take_config_spin() {
+ match id {
+ 0 => state.grid_size_x = val,
+ 1 => state.grid_size_y = val,
+ 2 => state.skipped_row_h = val,
+ 3 => state.skipped_col_w = val,
+ 4 => state.viewport_bg_color[0] = val,
+ 5 => state.viewport_bg_color[1] = val,
+ 6 => state.viewport_bg_color[2] = val,
+ 7 => {
+ state.origin_size = val;
+ state.update_origin_geometry();
+ }
+ 8 => {
+ state.grid_thickness = val;
+ state.update_grid_geometry();
+ }
+ 9 => {
+ state.camera_pivot_size = val;
+ state.update_pivot_geometry();
}
+ _ => {}
+ }
+ for &i in &state.node_slots {
+ let (x, y, _, _) = state.widgets[i].rect();
+ state.widgets[i].set_rect(x, y, state.grid_size_x, state.grid_size_y);
+ }
+ state.sync_grid_settings();
+ changed = true;
+ settings_changed = true;
+ }
- if changed {
- state.sync_layout();
- state.read_panel_offsets();
- state.sync_cursor_and_selection();
-
- if state.drag_widget == Some(PARAM_IDX) && state.widgets[PARAM_IDX].is_dragging() {
- if let Some(focused) = state.focused_widget {
- if state.node_slots.contains(&focused) {
- if let Some(slot_idx) = state.node_slots.iter().position(|&x| x == focused) {
- let updated_params = state.widgets[PARAM_IDX].node_params();
- let dir = state.current_dir_mut();
- if let Some(child) = dir.children.get_mut(slot_idx) {
- let mut param_changed = false;
- for (u_name, u_val, _type) in &updated_params {
- if let Some(p) = child.params.iter_mut().find(|p| p.name == *u_name) {
- if p.default != *u_val {
- p.default = u_val.clone();
- param_changed = true;
- }
- }
- }
- if param_changed {
- state.rebuild_scene_geometry();
+ if settings_changed {
+ state.save_settings();
+ }
+
+ if changed {
+ state.sync_layout();
+ state.read_panel_offsets();
+ state.sync_cursor_and_selection();
+
+ if state.drag_widget == Some(PARAM_IDX) && state.widgets[PARAM_IDX].is_dragging() {
+ if let Some(focused) = state.focused_widget {
+ if state.node_slots.contains(&focused) {
+ if let Some(slot_idx) = state.node_slots.iter().position(|&x| x == focused) {
+ let updated_params = state.widgets[PARAM_IDX].node_params();
+ let dir = state.current_dir_mut();
+ if let Some(child) = dir.children.get_mut(slot_idx) {
+ let mut param_changed = false;
+ for (u_name, u_val, _type) in &updated_params {
+ if let Some(p) = child.params.iter_mut().find(|p| p.name == *u_name) {
+ if p.default != *u_val {
+ p.default = u_val.clone();
+ param_changed = true;
}
}
}
+ if param_changed {
+ state.rebuild_scene_geometry();
+ }
}
}
}
+ }
+ }
- state.sync_nodes();
-
- // Sync Parameters pane with selected node
- let params = state.focused_widget.and_then(|f| {
- if state.node_slots.contains(&f) { Some(f) } else { None }
- }).map(|f| state.widgets[f].node_params()).unwrap_or_default();
- state.widgets[PARAM_IDX].set_display_params(¶ms);
+ state.sync_nodes();
- state.upload_vertices();
- }
+ // Sync Parameters pane with selected node
+ let params = state.focused_widget.and_then(|f| {
+ if state.node_slots.contains(&f) { Some(f) } else { None }
+ }).map(|f| state.widgets[f].node_params()).unwrap_or_default();
+ state.widgets[PARAM_IDX].set_display_params(¶ms);
- state.update_status_text(&format!(
- "col: {:.0} vp: {:.0} params: {:.0}",
- state.content_left_w(), state.viewport_w(), state.param_w(),
- ));
- changed
- } else { false }
+ state.upload_vertices();
}
- };
- if needs_redraw {
- if let Some(state) = &mut self.state { state.window.request_redraw(); }
- }
- if let Some(state) = &self.state {
- if state.exit_requested {
- event_loop.exit();
+
+ state.update_status_text(&format!(
+ "col: {:.0} vp: {:.0} params: {:.0}",
+ state.content_left_w(), state.viewport_w(), state.param_w(),
+ ));
+
+ if changed {
+ self.redraw = true;
}
}
}
- fn user_event(&mut self, _event_loop: &ActiveEventLoop, event: CustomEvent) {
+ fn handle_user_event(&mut self, event: CustomEvent) {
let mut needs_redraw = false;
- match event {
- CustomEvent::GetState(tx) => {
- if let Some(state) = &self.state {
+ if let Some(state) = &mut self.state {
+ match event {
+ CustomEvent::GetState(tx) => {
let proj = Project {
name: "Project".to_string(),
root: state.fs_root.clone(),
@@ -5109,12 +5566,8 @@ impl ApplicationHandler<CustomEvent> for App {
};
let json = serde_json::to_string_pretty(&proj).unwrap_or_default();
let _ = tx.send(json);
- } else {
- let _ = tx.send("null".to_string());
}
- }
- CustomEvent::PostAction(action, tx) => {
- if let Some(state) = &mut self.state {
+ CustomEvent::PostAction(action, tx) => {
let res = match action {
HttpAction::Up => {
if !state.current_path.is_empty() {
@@ -5208,12 +5661,13 @@ impl ApplicationHandler<CustomEvent> for App {
});
if let Some(idx) = template_idx {
let mut node = state.node_templates[idx].node.clone();
- node.position = (x, y);
+ let (nx, ny) = state.find_empty_cell(x, y, None);
+ node.position = (nx, ny);
if let Some(n) = name {
node.name = n;
}
state.current_dir_mut().children.push(node);
- state.left_offsets[child_idx] = (x, y);
+ state.left_offsets[child_idx] = (nx, ny);
state.sync_nodes();
state.rebuild_positions();
state.apply_layout();
@@ -5263,9 +5717,10 @@ impl ApplicationHandler<CustomEvent> for App {
HttpAction::MoveNode { slot, x, y } => {
let len = state.current_dir().children.len();
if slot < len {
- state.current_dir_mut().children[slot].position = (x, y);
+ let (nx, ny) = state.find_empty_cell(x, y, Some(slot));
+ state.current_dir_mut().children[slot].position = (nx, ny);
if slot < state.left_offsets.len() {
- state.left_offsets[slot] = (x, y);
+ state.left_offsets[slot] = (nx, ny);
}
state.sync_nodes();
state.rebuild_positions();
@@ -5316,24 +5771,63 @@ impl ApplicationHandler<CustomEvent> for App {
}
};
let _ = tx.send(res);
- } else {
+ }
+ }
+ } else {
+ match event {
+ CustomEvent::GetState(tx) => {
+ let _ = tx.send("null".to_string());
+ }
+ CustomEvent::PostAction(_, tx) => {
let _ = tx.send(Err("State not initialized".to_string()));
}
}
}
if needs_redraw {
- if let Some(state) = &mut self.state {
- state.window.request_redraw();
- }
+ self.redraw = true;
}
}
}
fn main() {
- let event_loop = EventLoop::<CustomEvent>::with_user_event().build().unwrap();
- event_loop.set_control_flow(ControlFlow::Poll);
+ let conn = Connection::connect_to_env().unwrap();
+ let (globals, mut event_queue) = registry_queue_init(&conn).unwrap();
+ let qh = event_queue.handle();
+
+ let compositor_state = CompositorState::bind(&globals, &qh).unwrap();
+ let xdg_shell_state = XdgShell::bind(&globals, &qh).unwrap();
+ let shm_state = Shm::bind(&globals, &qh).unwrap();
+ let seat_state = SeatState::new(&globals, &qh);
+ let output_state = OutputState::new(&globals, &qh);
+
+ let state = pollster::block_on(State::new(
+ &conn,
+ &qh,
+ &compositor_state,
+ &xdg_shell_state,
+ 1280, 800
+ ));
+
+ let mut app = AppState {
+ registry_state: RegistryState::new(&globals),
+ compositor_state,
+ xdg_shell_state,
+ shm_state,
+ seat_state,
+ output_state,
+ seats: Vec::new(),
+ pointer: None,
+ keyboard: None,
+ window: state.window.clone(),
+ surface: state.wl_surface.clone(),
+ state: Some(state),
+ exit: false,
+ redraw: true,
+ };
- let proxy = event_loop.create_proxy();
+ let (sender, channel) = calloop::channel::channel::<CustomEvent>();
+
+ let server_sender = sender.clone();
std::thread::spawn(move || {
let listener = match TcpListener::bind("127.0.0.1:3000") {
Ok(l) => l,
@@ -5350,7 +5844,7 @@ fn main() {
Err(_) => continue,
};
- let proxy = proxy.clone();
+ let server_sender = server_sender.clone();
std::thread::spawn(move || {
let mut write_stream = match stream.try_clone() {
Ok(s) => s,
@@ -5364,7 +5858,7 @@ fn main() {
if request_line.starts_with("GET /state") {
let (tx, rx) = std::sync::mpsc::channel();
- if proxy.send_event(CustomEvent::GetState(tx)).is_ok() {
+ if server_sender.send(CustomEvent::GetState(tx)).is_ok() {
let response_body = rx.recv().unwrap_or_else(|_| "null".to_string());
let response = format!(
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
@@ -5403,7 +5897,7 @@ fn main() {
if let Ok(body_str) = String::from_utf8(body_bytes) {
if let Ok(action) = serde_json::from_str::<HttpAction>(&body_str) {
let (tx, rx) = std::sync::mpsc::channel();
- if proxy.send_event(CustomEvent::PostAction(action, tx)).is_ok() {
+ if server_sender.send(CustomEvent::PostAction(action, tx)).is_ok() {
match rx.recv() {
Ok(Ok(msg)) => {
let body = format!("{{\"status\":\"success\",\"message\":\"{}\"}}", msg);
@@ -5483,8 +5977,38 @@ fn main() {
}
});
- let mut app = App::new();
- event_loop.run_app(&mut app).unwrap();
+ let mut event_loop = calloop::EventLoop::try_new().unwrap();
+ let loop_handle = event_loop.handle();
+
+ WaylandSource::new(conn, event_queue).insert(loop_handle.clone()).unwrap();
+
+ loop_handle.insert_source(channel, |event, _metadata, app_state: &mut AppState| {
+ if let calloop::channel::Event::Msg(msg) = event {
+ app_state.handle_user_event(msg);
+ }
+ }).unwrap();
+
+ loop {
+ let timeout = if app.redraw {
+ std::time::Duration::from_millis(0)
+ } else {
+ std::time::Duration::from_millis(16)
+ };
+ event_loop.dispatch(timeout, &mut app).unwrap();
+
+ if app.exit || app.state.as_ref().map(|s| s.exit_requested).unwrap_or(false) {
+ break;
+ }
+
+ if app.redraw {
+ app.redraw = false;
+ if let Some(state) = &mut app.state {
+ if state.render() {
+ app.redraw = true;
+ }
+ }
+ }
+ }
}
#[cfg(test)]
@@ -5683,4 +6207,30 @@ mod tests {
_ => panic!("Expected AddNode"),
}
}
+
+ #[test]
+ fn test_get_next_visible_pane() {
+ // Without spreadsheet (3 panes: LEFT, RIGHT, PARAM)
+ assert_eq!(get_next_visible_pane(LEFT_MENUBAR_IDX, false, false), RIGHT_MENUBAR_IDX);
+ assert_eq!(get_next_visible_pane(RIGHT_MENUBAR_IDX, false, false), PARAM_MENUBAR_IDX);
+ assert_eq!(get_next_visible_pane(PARAM_MENUBAR_IDX, false, false), LEFT_MENUBAR_IDX);
+
+ // With spreadsheet (4 panes: LEFT, RIGHT, PARAM, SPREADSHEET)
+ assert_eq!(get_next_visible_pane(LEFT_MENUBAR_IDX, true, false), RIGHT_MENUBAR_IDX);
+ assert_eq!(get_next_visible_pane(RIGHT_MENUBAR_IDX, true, false), PARAM_MENUBAR_IDX);
+ assert_eq!(get_next_visible_pane(PARAM_MENUBAR_IDX, true, false), SPREADSHEET_MENUBAR_IDX);
+ assert_eq!(get_next_visible_pane(SPREADSHEET_MENUBAR_IDX, true, false), LEFT_MENUBAR_IDX);
+
+ // Reverse cycling with shift key (without spreadsheet)
+ assert_eq!(get_next_visible_pane(LEFT_MENUBAR_IDX, false, true), PARAM_MENUBAR_IDX);
+ assert_eq!(get_next_visible_pane(PARAM_MENUBAR_IDX, false, true), RIGHT_MENUBAR_IDX);
+ assert_eq!(get_next_visible_pane(RIGHT_MENUBAR_IDX, false, true), LEFT_MENUBAR_IDX);
+
+ // Reverse cycling with shift key (with spreadsheet)
+ assert_eq!(get_next_visible_pane(LEFT_MENUBAR_IDX, true, true), SPREADSHEET_MENUBAR_IDX);
+ assert_eq!(get_next_visible_pane(SPREADSHEET_MENUBAR_IDX, true, true), PARAM_MENUBAR_IDX);
+ assert_eq!(get_next_visible_pane(PARAM_MENUBAR_IDX, true, true), RIGHT_MENUBAR_IDX);
+ assert_eq!(get_next_visible_pane(RIGHT_MENUBAR_IDX, true, true), LEFT_MENUBAR_IDX);
+ }
}
+