git.lucas.co / cce-designer
graphic design tool
git clone https://git.lucas.co/cce-designer.git

commit005d9297edff56a51d42311e3bf3fd7fdf8af6e4
parentd7584553c2
authorLucas Galante <[email protected]>
date2026-05-28 15:41
Update system configuration and interface modules

 Cargo.lock  | 290 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/main.rs |  97 +++++++++++++++++++-
 2 files changed, 381 insertions(+), 6 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 2a97160..7399529 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2,6 +2,12 @@
 # It is not intended for manual editing.
 version = 4
 
+[[package]]
+name = "adler2"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
+
 [[package]]
 name = "android_system_properties"
 version = "0.1.5"
@@ -11,6 +17,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"
@@ -32,6 +44,12 @@ version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
 
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
 [[package]]
 name = "bit-set"
 version = "0.8.0"
@@ -191,6 +209,7 @@ dependencies = [
  "glyphon",
  "pollster",
  "raw-window-handle",
+ "resvg",
  "smithay-client-toolkit",
  "tokio",
  "wayland-client",
@@ -208,6 +227,12 @@ dependencies = [
  "unicode-width",
 ]
 
+[[package]]
+name = "color_quant"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
+
 [[package]]
 name = "concurrent-queue"
 version = "2.5.0"
@@ -256,7 +281,7 @@ dependencies = [
  "rangemap",
  "rayon",
  "rustc-hash 1.1.0",
- "rustybuzz",
+ "rustybuzz 0.14.1",
  "self_cell",
  "swash",
  "sys-locale",
@@ -267,6 +292,15 @@ dependencies = [
  "unicode-segmentation",
 ]
 
+[[package]]
+name = "crc32fast"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
+dependencies = [
+ "cfg-if",
+]
+
 [[package]]
 name = "crossbeam-deque"
 version = "0.8.6"
@@ -298,6 +332,12 @@ version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f"
 
+[[package]]
+name = "data-url"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376"
+
 [[package]]
 name = "dlib"
 version = "0.5.3"
@@ -363,12 +403,37 @@ dependencies = [
  "num-traits",
 ]
 
+[[package]]
+name = "fdeflate"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
+dependencies = [
+ "simd-adler32",
+]
+
 [[package]]
 name = "find-msvc-tools"
 version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
 
+[[package]]
+name = "flate2"
+version = "1.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
+dependencies = [
+ "crc32fast",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "float-cmp"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
+
 [[package]]
 name = "foldhash"
 version = "0.1.5"
@@ -390,7 +455,7 @@ version = "0.5.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646"
 dependencies = [
- "roxmltree",
+ "roxmltree 0.20.0",
 ]
 
 [[package]]
@@ -458,6 +523,16 @@ dependencies = [
  "slab",
 ]
 
+[[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 = "gl_generator"
 version = "0.14.0"
@@ -593,6 +668,12 @@ version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
 
+[[package]]
+name = "imagesize"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284"
+
 [[package]]
 name = "indexmap"
 version = "2.14.0"
@@ -637,6 +718,12 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "jpeg-decoder"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07"
+
 [[package]]
 name = "js-sys"
 version = "0.3.98"
@@ -666,6 +753,17 @@ version = "3.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
 
+[[package]]
+name = "kurbo"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62"
+dependencies = [
+ "arrayvec",
+ "euclid",
+ "smallvec",
+]
+
 [[package]]
 name = "libc"
 version = "0.2.186"
@@ -775,6 +873,16 @@ dependencies = [
  "paste",
 ]
 
+[[package]]
+name = "miniz_oxide"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
+dependencies = [
+ "adler2",
+ "simd-adler32",
+]
+
 [[package]]
 name = "mio"
 version = "1.2.0"
@@ -898,6 +1006,12 @@ version = "1.0.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
 
+[[package]]
+name = "pico-args"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
+
 [[package]]
 name = "pin-project-lite"
 version = "0.2.17"
@@ -910,6 +1024,19 @@ version = "0.3.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
 
+[[package]]
+name = "png"
+version = "0.17.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
+dependencies = [
+ "bitflags 1.3.2",
+ "crc32fast",
+ "fdeflate",
+ "flate2",
+ "miniz_oxide",
+]
+
 [[package]]
 name = "polling"
 version = "3.11.0"
@@ -1032,6 +1159,38 @@ version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
 
+[[package]]
+name = "resvg"
+version = "0.41.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2327ced609dadeed3e9702fec3e6b2ddd208758a9268d13e06566c6101ba533"
+dependencies = [
+ "gif",
+ "jpeg-decoder",
+ "log",
+ "pico-args",
+ "png",
+ "rgb",
+ "svgtypes",
+ "tiny-skia",
+ "usvg",
+]
+
+[[package]]
+name = "rgb"
+version = "0.8.53"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
+name = "roxmltree"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f"
+
 [[package]]
 name = "roxmltree"
 version = "0.20.0"
@@ -1082,6 +1241,22 @@ version = "1.0.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
 
+[[package]]
+name = "rustybuzz"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88117946aa1bfb53c2ae0643ceac6506337f44887f8c9fbfb43587b1cc52ba49"
+dependencies = [
+ "bitflags 2.11.1",
+ "bytemuck",
+ "smallvec",
+ "ttf-parser 0.20.0",
+ "unicode-bidi-mirroring",
+ "unicode-ccc",
+ "unicode-properties",
+ "unicode-script",
+]
+
 [[package]]
 name = "rustybuzz"
 version = "0.14.1"
@@ -1176,6 +1351,27 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "simd-adler32"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
+
+[[package]]
+name = "simplecss"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c"
+dependencies = [
+ "log",
+]
+
+[[package]]
+name = "siphasher"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649"
+
 [[package]]
 name = "skrifa"
 version = "0.22.3"
@@ -1260,6 +1456,15 @@ 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"
+dependencies = [
+ "float-cmp",
+]
+
 [[package]]
 name = "strum"
 version = "0.26.3"
@@ -1288,6 +1493,16 @@ version = "0.4.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb"
 
+[[package]]
+name = "svgtypes"
+version = "0.15.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc"
+dependencies = [
+ "kurbo",
+ "siphasher",
+]
+
 [[package]]
 name = "swash"
 version = "0.1.19"
@@ -1368,6 +1583,32 @@ 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",
+ "png",
+ "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"
@@ -1471,6 +1712,12 @@ version = "1.13.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c"
 
+[[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.1.14"
@@ -1483,6 +1730,33 @@ version = "0.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
 
+[[package]]
+name = "usvg"
+version = "0.41.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c704361d822337cfc00387672c7b59eaa72a1f0744f62b2a68aa228a0c6927d"
+dependencies = [
+ "base64",
+ "data-url",
+ "flate2",
+ "fontdb",
+ "imagesize",
+ "kurbo",
+ "log",
+ "pico-args",
+ "roxmltree 0.19.0",
+ "rustybuzz 0.13.0",
+ "simplecss",
+ "siphasher",
+ "strict-num",
+ "svgtypes",
+ "tiny-skia-path",
+ "unicode-bidi",
+ "unicode-script",
+ "unicode-vo",
+ "xmlwriter",
+]
+
 [[package]]
 name = "version_check"
 version = "0.9.5"
@@ -1655,6 +1929,12 @@ dependencies = [
  "wasm-bindgen",
 ]
 
+[[package]]
+name = "weezl"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
+
 [[package]]
 name = "wgpu"
 version = "24.0.5"
@@ -1957,6 +2237,12 @@ version = "0.8.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f"
 
+[[package]]
+name = "xmlwriter"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
+
 [[package]]
 name = "yazi"
 version = "0.1.6"
diff --git a/src/main.rs b/src/main.rs
index 0d5f513..914ec31 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -4331,7 +4331,7 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
                                     if prev == i && t.elapsed() < std::time::Duration::from_millis(500) {
                                         let dir_idx = self.node_slots.iter().position(|&x| x == i).unwrap();
                                         let dir = self.current_dir();
-                                        if dir_idx < dir.children.len() && !dir.children[dir_idx].children.is_empty() {
+                                        if dir_idx < dir.children.len() && (dir.children[dir_idx].node_type == "node" || dir.children[dir_idx].node_type == "opencl" || !dir.children[dir_idx].children.is_empty()) {
                                             self.current_path.push(dir_idx);
                                             self.on_path_changed();
                                             changed = true;
@@ -4543,8 +4543,8 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
                                                 if let Some(focused) = self.focused_widget {
                                                     if let Some(slot_idx) = self.node_slots.iter().position(|&x| x == focused) {
                                                         let dir = self.current_dir();
-                                                        if slot_idx < dir.children.len() && !dir.children[slot_idx].children.is_empty() {
-                                                            self.current_path.push(slot_idx);
+                                                         if slot_idx < dir.children.len() && (dir.children[slot_idx].node_type == "node" || dir.children[slot_idx].node_type == "opencl" || !dir.children[slot_idx].children.is_empty()) {
+                                                             self.current_path.push(slot_idx);
                                                             self.on_path_changed();
                                                             changed = true;
                                                         }
@@ -4560,6 +4560,95 @@ fn geometry_to_spreadsheet_data(geom: &Geometry) -> (Vec<String>, Vec<Vec<String
                                             self.zoom(1.15, None);
                                             changed = true;
                                         }
+                                        "f" | "F" => {
+                                            if self.focused_pane == LEFT_MENUBAR_IDX {
+                                                let active_nodes = self.current_dir().children.len().min(self.node_slots.len());
+                                                if active_nodes == 0 {
+                                                    self.grid_size_x = 80.0;
+                                                    self.grid_size_y = 40.0;
+                                                    self.skipped_col_w = 20.0;
+                                                    self.skipped_row_h = 20.0;
+                                                    self.pan_x = 20.0;
+                                                    self.pan_y = 20.0;
+                                                } else {
+                                                    let base_gx = 80.0;
+                                                    let base_gy = 40.0;
+                                                    let base_col_w = 20.0;
+                                                    let base_row_h = 20.0;
+
+                                                    let mut b_xmin = f32::MAX;
+                                                    let mut b_xmax = f32::MIN;
+                                                    let mut b_ymin = f32::MAX;
+                                                    let mut b_ymax = f32::MIN;
+
+                                                    for slot_idx in 0..active_nodes {
+                                                        let (col, row) = self.left_offsets[slot_idx];
+                                                        let x_min = col * (base_gx + base_col_w);
+                                                        let x_max = x_min + base_gx;
+                                                        let y_min = row * (base_gy + base_row_h);
+                                                        let y_max = y_min + base_gy;
+
+                                                        if x_min < b_xmin { b_xmin = x_min; }
+                                                        if x_max > b_xmax { b_xmax = x_max; }
+                                                        if y_min < b_ymin { b_ymin = y_min; }
+                                                        if y_max > b_ymax { b_ymax = y_max; }
+                                                    }
+
+                                                    let w_base = b_xmax - b_xmin;
+                                                    let h_base = b_ymax - b_ymin;
+
+                                                    let viewport_w = self.content_left_w();
+                                                    let body_h = self.body_h();
+                                                    let viewport_h = body_h - MENUBAR_H - BREADCRUMB_H;
+
+                                                    let padding = 40.0;
+                                                    let padded_w = (viewport_w - 2.0 * padding).max(10.0);
+                                                    let padded_h = (viewport_h - 2.0 * padding).max(10.0);
+
+                                                    let fx = padded_w / w_base;
+                                                    let fy = padded_h / h_base;
+                                                    let mut f = fx.min(fy);
+
+                                                    f = f.min(1.0).max(30.0 / base_gx);
+
+                                                    self.grid_size_x = (base_gx * f).clamp(30.0, 500.0);
+                                                    self.grid_size_y = (base_gy * f).clamp(15.0, 250.0);
+                                                    self.skipped_col_w = base_col_w * f;
+                                                    self.skipped_row_h = base_row_h * f;
+
+                                                    let mut actual_xmin = f32::MAX;
+                                                    let mut actual_xmax = f32::MIN;
+                                                    let mut actual_ymin = f32::MAX;
+                                                    let mut actual_ymax = f32::MIN;
+
+                                                    for slot_idx in 0..active_nodes {
+                                                        let (col, row) = self.left_offsets[slot_idx];
+                                                        let x_min = col * (self.grid_size_x + self.skipped_col_w);
+                                                        let x_max = x_min + self.grid_size_x;
+                                                        let y_min = row * (self.grid_size_y + self.skipped_row_h);
+                                                        let y_max = y_min + self.grid_size_y;
+
+                                                        if x_min < actual_xmin { actual_xmin = x_min; }
+                                                        if x_max > actual_xmax { actual_xmax = x_max; }
+                                                        if y_min < actual_ymin { actual_ymin = y_min; }
+                                                        if y_max > actual_ymax { actual_ymax = y_max; }
+                                                    }
+
+                                                    let actual_w = actual_xmax - actual_xmin;
+                                                    let actual_h = actual_ymax - actual_ymin;
+
+                                                    self.pan_x = (viewport_w - actual_w) / 2.0 - actual_xmin;
+                                                    self.pan_y = (viewport_h - actual_h) / 2.0 - actual_ymin;
+                                                }
+
+                                                self.sync_grid_settings();
+                                                self.rebuild_positions();
+                                                self.apply_layout();
+                                                self.update_panel_bounds();
+                                                self.upload_vertices();
+                                                changed = true;
+                                            }
+                                        }
                                         _ => {}
                                     }
                                 }
@@ -5628,7 +5717,7 @@ impl AppState {
                         }
                         HttpAction::Enter { slot } => {
                             let dir = state.current_dir();
-                            if slot < dir.children.len() && !dir.children[slot].children.is_empty() {
+                            if slot < dir.children.len() && (dir.children[slot].node_type == "node" || dir.children[slot].node_type == "opencl" || !dir.children[slot].children.is_empty()) {
                                 state.current_path.push(slot);
                                 state.on_path_changed();
                                 needs_redraw = true;