settings.rs
1 /* This file is part of DarkFi (https://dark.fi) 2 * 3 * Copyright (C) 2020-2024 Dyne.org foundation 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU Affero General Public License as 7 * published by the Free Software Foundation, either version 3 of the 8 * License, or (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU Affero General Public License for more details. 14 * 15 * You should have received a copy of the GNU Affero General Public License 16 * along with this program. If not, see <https://www.gnu.org/licenses/>. 17 */ 18 19 use crate::{ 20 app::{ 21 node::{create_button, create_editbox, create_layer, create_text, create_vector_art}, 22 App, 23 }, 24 expr::{self, Compiler}, 25 prop::{PropertyAtomicGuard, PropertyFloat32, PropertyStr, PropertyValue, Role}, 26 scene::{SceneNodePtr, Slot}, 27 shape, 28 ui::{Button, EditBox, Layer, ShapeVertex, Text, VectorArt, VectorShape}, 29 ExecutorPtr, 30 }; 31 32 use super::{ColorScheme, COLOR_SCHEME}; 33 34 use std::{ 35 collections::BTreeMap, 36 sync::{Arc, Mutex}, 37 }; 38 39 #[cfg(any(target_os = "android", feature = "emulate-android"))] 40 mod android_ui_consts { 41 pub const SETTING_LABEL_X: f32 = 40.; 42 pub const SETTING_LABEL_LINESPACE: f32 = 140.; 43 pub const SETTING_LABEL_BASELINE: f32 = 82.; 44 pub const SETTING_LABEL_FONTSIZE: f32 = 24.; 45 pub const SETTING_EDIT_FONTSIZE: f32 = 24.; 46 pub const SETTING_TITLE_X: f32 = 150.; 47 pub const SETTING_TITLE_FONTSIZE: f32 = 40.; 48 pub const SETTING_TITLE_BASELINE: f32 = 82.; 49 pub const SEARCH_PADDING_X: f32 = 120.; 50 pub const BORDER_RIGHT_SCALE: f32 = 5.; 51 pub const CURSOR_ASCENT: f32 = 50.; 52 pub const CURSOR_DESCENT: f32 = 20.; 53 pub const SELECT_ASCENT: f32 = 50.; 54 pub const SELECT_DESCENT: f32 = 20.; 55 56 pub const BACKARROW_SCALE: f32 = 30.; 57 pub const BACKARROW_X: f32 = 50.; 58 pub const BACKARROW_Y: f32 = 70.; 59 pub const BACKARROW_BG_W: f32 = 120.; 60 } 61 62 #[cfg(target_os = "android")] 63 mod ui_consts { 64 pub use super::android_ui_consts::*; 65 } 66 67 #[cfg(feature = "emulate-android")] 68 mod ui_consts { 69 pub use super::android_ui_consts::*; 70 } 71 72 #[cfg(all( 73 any(target_os = "linux", target_os = "macos", target_os = "windows"), 74 not(feature = "emulate-android") 75 ))] 76 mod ui_consts { 77 pub const SETTING_LABEL_X: f32 = 20.; 78 pub const SETTING_LABEL_LINESPACE: f32 = 60.; 79 pub const SETTING_LABEL_BASELINE: f32 = 37.; 80 pub const SETTING_LABEL_FONTSIZE: f32 = 14.; 81 pub const SETTING_EDIT_FONTSIZE: f32 = 14.; 82 pub const SETTING_TITLE_X: f32 = 100.; 83 pub const SETTING_TITLE_FONTSIZE: f32 = 20.; 84 pub const SETTING_TITLE_BASELINE: f32 = 37.; 85 pub const SEARCH_PADDING_X: f32 = 80.; 86 pub const BORDER_RIGHT_SCALE: f32 = 10.; 87 pub const CURSOR_ASCENT: f32 = 24.; 88 pub const CURSOR_DESCENT: f32 = 8.; 89 pub const SELECT_ASCENT: f32 = 30.; 90 pub const SELECT_DESCENT: f32 = 10.; 91 92 pub const BACKARROW_SCALE: f32 = 15.; 93 pub const BACKARROW_X: f32 = 38.; 94 pub const BACKARROW_Y: f32 = 26.; 95 pub const BACKARROW_BG_W: f32 = 80.; 96 } 97 98 use ui_consts::*; 99 100 #[derive(Clone)] 101 struct Setting { 102 name: String, 103 node: SceneNodePtr, 104 } 105 106 impl Setting { 107 fn value_as_string(&self) -> String { 108 match &self.node.get_property("value").unwrap().get_value(0).ok().unwrap() { 109 PropertyValue::Str(s) => s.clone(), 110 PropertyValue::Uint32(i) => i.to_string(), 111 PropertyValue::Bool(b) => { 112 if *b { 113 "TRUE".to_string() 114 } else { 115 "FALSE".to_string() 116 } 117 } 118 PropertyValue::Float32(fl) => fl.to_string(), 119 _ => "unknown".to_string(), 120 } 121 } 122 fn get_value(&self) -> PropertyValue { 123 self.node.get_property("value").unwrap().get_value(0).ok().unwrap() 124 } 125 fn get_default(&self) -> PropertyValue { 126 self.node.get_property("default").unwrap().get_value(0).ok().unwrap() 127 } 128 fn is_default(&self) -> bool { 129 self.get_value() == self.get_default() 130 } 131 fn reset(&self) { 132 let prop = self.node.get_property("value").unwrap(); 133 prop.set_raw_value(Role::App, 0, self.get_default()).unwrap(); 134 } 135 } 136 137 pub async fn make(app: &App, window: SceneNodePtr, _ex: ExecutorPtr) { 138 let mut cc = Compiler::new(); 139 cc.add_const_f32("BORDER_RIGHT_SCALE", BORDER_RIGHT_SCALE); 140 cc.add_const_f32("SEARCH_PADDING_X", SEARCH_PADDING_X); 141 cc.add_const_f32("X_RATIO", 1. / 2.); 142 let window_scale = PropertyFloat32::wrap( 143 &app.sg_root.lookup_node("/setting/scale").unwrap(), 144 Role::Internal, 145 "value", 146 0, 147 ) 148 .unwrap(); 149 let atom = &mut PropertyAtomicGuard::new(); 150 151 // Main view 152 let layer_node = create_layer("settings_layer"); 153 let prop = layer_node.get_property("rect").unwrap(); 154 prop.set_f32(atom, Role::App, 0, 0.).unwrap(); 155 prop.set_f32(atom, Role::App, 1, 0.).unwrap(); 156 prop.set_expr(atom, Role::App, 2, expr::load_var("w")).unwrap(); 157 prop.set_expr(atom, Role::App, 3, expr::load_var("h")).unwrap(); 158 layer_node.set_property_bool(atom, Role::App, "is_visible", true).unwrap(); 159 layer_node.set_property_u32(atom, Role::App, "z_index", 1).unwrap(); 160 let layer_node = 161 layer_node.setup(|me| Layer::new(me, app.render_api.clone(), app.ex.clone())).await; 162 window.link(layer_node.clone()); 163 164 let mut setting_y = 0.; 165 166 // Create the toolbar bg 167 let node = create_vector_art("toolbar_bg"); 168 let prop = node.get_property("rect").unwrap(); 169 prop.set_f32(atom, Role::App, 0, 0.).unwrap(); 170 prop.set_f32(atom, Role::App, 1, 0.).unwrap(); 171 prop.set_expr(atom, Role::App, 2, expr::load_var("w")).unwrap(); 172 prop.set_f32(atom, Role::App, 3, SETTING_LABEL_LINESPACE).unwrap(); 173 node.set_property_u32(atom, Role::App, "z_index", 2).unwrap(); 174 175 let (bg_color, sep_color) = match COLOR_SCHEME { 176 ColorScheme::DarkMode => ([0., 0.11, 0.11, 1.], [0.41, 0.6, 0.65, 1.]), 177 ColorScheme::PaperLight => ([1., 1., 1., 1.], [0., 0.6, 0.65, 1.]), 178 }; 179 let mut shape = VectorShape::new(); 180 shape.add_filled_box( 181 expr::const_f32(0.), 182 expr::const_f32(0.), 183 expr::const_f32(BACKARROW_BG_W), 184 expr::load_var("h"), 185 bg_color, 186 ); 187 shape.add_filled_box( 188 expr::const_f32(BACKARROW_BG_W), 189 expr::const_f32(0.), 190 expr::const_f32(BACKARROW_BG_W + 1.), 191 expr::load_var("h"), 192 sep_color, 193 ); 194 shape.add_filled_box( 195 expr::const_f32(0.), 196 expr::load_var("h"), 197 expr::load_var("w"), 198 cc.compile("h + 1").unwrap(), 199 sep_color, 200 ); 201 202 let node = 203 node.setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())).await; 204 layer_node.link(node); 205 206 // Create the back button 207 let node = create_vector_art("back_btn_bg"); 208 let prop = node.get_property("rect").unwrap(); 209 prop.set_f32(atom, Role::App, 0, BACKARROW_X).unwrap(); 210 prop.set_f32(atom, Role::App, 1, BACKARROW_Y).unwrap(); 211 prop.set_f32(atom, Role::App, 2, 0.).unwrap(); 212 prop.set_f32(atom, Role::App, 3, 0.).unwrap(); 213 node.set_property_u32(atom, Role::App, "z_index", 3).unwrap(); 214 215 let shape = shape::create_back_arrow().scaled(BACKARROW_SCALE); 216 let node = 217 node.setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())).await; 218 layer_node.link(node); 219 220 let node = create_button("back_btn"); 221 node.set_property_bool(atom, Role::App, "is_active", true).unwrap(); 222 let prop = node.get_property("rect").unwrap(); 223 prop.set_f32(atom, Role::App, 0, 0.).unwrap(); 224 prop.set_f32(atom, Role::App, 1, 0.).unwrap(); 225 prop.set_f32(atom, Role::App, 2, BACKARROW_BG_W).unwrap(); 226 prop.set_f32(atom, Role::App, 3, SETTING_LABEL_LINESPACE).unwrap(); 227 228 let sg_root = app.sg_root.clone(); 229 let goback = move || { 230 let atom = &mut PropertyAtomicGuard::new(); 231 232 // Disable visilibity of all relevant window nodes 233 // This is needed since for example all chats have a different node name. 234 let windows = sg_root.lookup_node("/window").unwrap().get_children(); 235 let target_substrings = vec!["_chat_layer", "menu_layer", "settings_layer"]; 236 237 for node in windows.iter() { 238 // Check if the node's name contains any of the target substrings 239 if target_substrings.iter().any(|&s| node.name.contains(s)) { 240 if let Err(e) = node.set_property_bool(atom, Role::App, "is_visible", false) { 241 debug!("Failed to set property 'is_visible' on node: {:?}", e); 242 } 243 } 244 } 245 246 // Go back to the dev channel 247 let menu_node = sg_root.lookup_node("/window/dev_chat_layer").unwrap(); 248 menu_node.set_property_bool(atom, Role::App, "is_visible", true).unwrap(); 249 }; 250 251 let (slot, recvr) = Slot::new("back_clicked"); 252 node.register("click", slot).unwrap(); 253 let goback2 = goback.clone(); 254 let listen_click = app.ex.spawn(async move { 255 while let Ok(_) = recvr.recv().await { 256 goback2(); 257 } 258 }); 259 app.tasks.lock().unwrap().push(listen_click); 260 261 let node = node.setup(|me| Button::new(me, app.ex.clone())).await; 262 layer_node.link(node.clone()); 263 264 // Label: "SETTINGS" title 265 let node = create_text("settings_label_fontsize"); 266 let prop = node.get_property("rect").unwrap(); 267 prop.set_f32(atom, Role::App, 0, SETTING_TITLE_X).unwrap(); 268 prop.set_f32(atom, Role::App, 1, 0.).unwrap(); 269 prop.set_f32(atom, Role::App, 2, 1000.).unwrap(); 270 prop.set_f32(atom, Role::App, 3, 200.).unwrap(); 271 node.set_property_u32(atom, Role::App, "z_index", 1).unwrap(); 272 node.set_property_f32(atom, Role::App, "baseline", SETTING_TITLE_BASELINE).unwrap(); 273 node.set_property_f32(atom, Role::App, "font_size", SETTING_TITLE_FONTSIZE).unwrap(); 274 node.set_property_str(atom, Role::App, "text", "SETTINGS").unwrap(); 275 node.set_property_f32_vec(atom, Role::App, "text_color", vec![1., 1., 1., 1.]).unwrap(); 276 node.set_property_u32(atom, Role::App, "z_index", 1).unwrap(); 277 278 let node = node 279 .setup(|me| { 280 Text::new( 281 me, 282 window_scale.clone(), 283 app.render_api.clone(), 284 app.text_shaper.clone(), 285 app.ex.clone(), 286 ) 287 }) 288 .await; 289 layer_node.link(node); 290 291 // Search Bar Background 292 let node = create_vector_art("emoji_picker_bg"); 293 let prop = node.get_property("rect").unwrap(); 294 prop.set_f32(atom, Role::App, 0, 0.).unwrap(); 295 let code = cc.compile("100").unwrap(); 296 prop.set_expr(atom, Role::App, 1, code).unwrap(); 297 prop.set_expr(atom, Role::App, 2, expr::load_var("w")).unwrap(); 298 prop.set_expr(atom, Role::App, 3, expr::load_var("50")).unwrap(); 299 //prop.add_depend(&emoji_dynamic_h_prop, 0, "dynamic_h"); 300 node.set_property_u32(atom, Role::App, "z_index", 4).unwrap(); 301 302 let mut shape = VectorShape::new(); 303 304 // Top line 305 shape.add_filled_box( 306 expr::const_f32(0.), 307 expr::const_f32(80.), 308 expr::load_var("w"), 309 expr::const_f32(1.), 310 [0.41, 0.6, 0.65, 1.], 311 ); 312 313 let node = 314 node.setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())).await; 315 layer_node.link(node); 316 317 // Search Bar Input 318 let editbox_node = create_editbox("search_input"); 319 editbox_node.set_property_bool(atom, Role::App, "is_active", true).unwrap(); 320 editbox_node.set_property_bool(atom, Role::App, "is_focused", true).unwrap(); 321 let prop = editbox_node.get_property("rect").unwrap(); 322 prop.set_f32(atom, Role::App, 0, SEARCH_PADDING_X).unwrap(); 323 prop.set_expr(atom, Role::App, 1, cc.compile("60 + 20").unwrap()).unwrap(); 324 prop.clone() 325 .set_expr(atom, Role::App, 2, cc.compile("w - SEARCH_PADDING_X*2").unwrap()) 326 .unwrap(); 327 prop.set_f32(atom, Role::App, 3, SETTING_LABEL_LINESPACE).unwrap(); 328 editbox_node.set_property_f32_vec(atom, Role::App, "text_color", vec![1., 1., 1., 1.]).unwrap(); 329 let prop = editbox_node.get_property("cursor_color").unwrap(); 330 prop.set_f32(atom, Role::App, 0, 0.5).unwrap(); 331 prop.set_f32(atom, Role::App, 1, 0.5).unwrap(); 332 prop.set_f32(atom, Role::App, 2, 0.5).unwrap(); 333 prop.set_f32(atom, Role::App, 3, 1.).unwrap(); 334 editbox_node.set_property_f32(atom, Role::App, "cursor_ascent", CURSOR_ASCENT).unwrap(); 335 editbox_node.set_property_f32(atom, Role::App, "cursor_descent", CURSOR_DESCENT).unwrap(); 336 editbox_node.set_property_f32(atom, Role::App, "select_ascent", SELECT_ASCENT).unwrap(); 337 editbox_node.set_property_f32(atom, Role::App, "select_descent", SELECT_DESCENT).unwrap(); 338 editbox_node 339 .set_property_f32_vec(atom, Role::App, "hi_bg_color", vec![0.5, 0.5, 0.5, 1.]) 340 .unwrap(); 341 let prop = editbox_node.get_property("selected").unwrap(); 342 prop.set_null(atom, Role::App, 0).unwrap(); 343 prop.set_null(atom, Role::App, 1).unwrap(); 344 editbox_node.set_property_u32(atom, Role::App, "z_index", 2).unwrap(); 345 editbox_node.set_property_bool(atom, Role::App, "is_active", true).unwrap(); 346 editbox_node.set_property_bool(atom, Role::App, "is_focused", true).unwrap(); 347 editbox_node.set_property_f32(atom, Role::App, "font_size", 16.).unwrap(); 348 editbox_node.set_property_f32(atom, Role::App, "baseline", 16.).unwrap(); 349 350 // Search icon 351 let node = create_vector_art("search_icon"); 352 let prop = node.get_property("rect").unwrap(); 353 prop.set_f32(atom, Role::App, 0, BACKARROW_X).unwrap(); 354 prop.clone() 355 .set_f32(atom, Role::App, 1, SETTING_LABEL_LINESPACE + SETTING_LABEL_LINESPACE / 2.) 356 .unwrap(); 357 prop.set_f32(atom, Role::App, 2, 0.).unwrap(); 358 prop.set_f32(atom, Role::App, 3, 0.).unwrap(); 359 node.set_property_u32(atom, Role::App, "z_index", 3).unwrap(); 360 361 let shape = shape::create_logo([1., 1., 1., 1.]).scaled(500.); 362 let node = 363 node.setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())).await; 364 layer_node.link(node); 365 366 // Search placeholder 367 let node = create_text("search_label"); 368 let prop = node.get_property("rect").unwrap(); 369 prop.set_f32(atom, Role::App, 0, SEARCH_PADDING_X).unwrap(); 370 prop.set_expr(atom, Role::App, 1, cc.compile("60 + 20").unwrap()).unwrap(); 371 prop.set_f32(atom, Role::App, 2, 1000.).unwrap(); 372 prop.set_f32(atom, Role::App, 3, 200.).unwrap(); 373 node.set_property_u32(atom, Role::App, "z_index", 1).unwrap(); 374 node.set_property_f32(atom, Role::App, "baseline", 16.).unwrap(); 375 node.set_property_f32(atom, Role::App, "font_size", 16.).unwrap(); 376 node.set_property_str(atom, Role::App, "text", "SEARCH...").unwrap(); 377 node.set_property_f32_vec(atom, Role::App, "text_color", vec![1., 1., 1., 0.45]).unwrap(); 378 node.set_property_u32(atom, Role::App, "z_index", 1).unwrap(); 379 380 let node = node 381 .setup(|me| { 382 Text::new( 383 me, 384 window_scale.clone(), 385 app.render_api.clone(), 386 app.text_shaper.clone(), 387 app.ex.clone(), 388 ) 389 }) 390 .await; 391 layer_node.link(node); 392 393 // Search settings counter 394 let node = create_text("search_count"); 395 let prop = node.get_property("rect").unwrap(); 396 prop.set_expr(atom, Role::App, 0, cc.compile("w - 50").unwrap()).unwrap(); 397 prop.set_expr(atom, Role::App, 1, cc.compile("60 + 20").unwrap()).unwrap(); 398 prop.set_f32(atom, Role::App, 2, 1000.).unwrap(); 399 prop.set_f32(atom, Role::App, 3, 200.).unwrap(); 400 node.set_property_u32(atom, Role::App, "z_index", 1).unwrap(); 401 node.set_property_f32(atom, Role::App, "baseline", 16.).unwrap(); 402 node.set_property_f32(atom, Role::App, "font_size", 16.).unwrap(); 403 node.set_property_str(atom, Role::App, "text", "").unwrap(); 404 node.set_property_f32_vec(atom, Role::App, "text_color", vec![0., 0.94, 1., 1.]).unwrap(); 405 node.set_property_u32(atom, Role::App, "z_index", 1).unwrap(); 406 407 let node = node 408 .setup(|me| { 409 Text::new( 410 me, 411 window_scale.clone(), 412 app.render_api.clone(), 413 app.text_shaper.clone(), 414 app.ex.clone(), 415 ) 416 }) 417 .await; 418 layer_node.link(node); 419 420 let sg_root3 = app.sg_root.clone(); 421 let search = move || { 422 let atom = &mut PropertyAtomicGuard::new(); 423 424 let path = "/window/settings_layer/search_input"; 425 let node = sg_root3.lookup_node(path.to_string()).unwrap(); 426 let search_string = node.get_property_str("text").unwrap(); 427 428 let path = "/window/settings_layer/search_label"; 429 let search_label_node = sg_root3.lookup_node(path.to_string()).unwrap(); 430 431 if search_string.len() > 0 { 432 let _ = search_label_node.set_property_f32(atom, Role::App, "font_size", 0.); 433 } else { 434 let _ = search_label_node.set_property_f32(atom, Role::App, "font_size", 16.); 435 } 436 437 let path = "/window/settings_layer/settings"; 438 let node = sg_root3.lookup_node(path.to_string()).unwrap(); 439 let setting_nodes = node.get_children(); 440 let mut found_nodes = Vec::new(); 441 442 // Iterate through the nodes 443 for node in setting_nodes.iter() { 444 // Hide all nodes initially, no matter what 445 let _ = node.set_property_bool(atom, Role::App, "is_visible", false); 446 447 // Check if the node matches the search string 448 if node.name.contains(&search_string.to_string()) { 449 found_nodes.push(node); // Store matching nodes 450 if let Err(e) = node.set_property_bool(atom, Role::App, "is_visible", true) { 451 debug!("Failed to set property 'is_visible' on node: {:?}", e); 452 } 453 } 454 } 455 456 // Set the `rect` property for each found node 457 for (i, node) in found_nodes.iter().enumerate() { 458 let prop = node.get_property("rect").unwrap(); 459 let y = i as f32 * 60. + 60. + 60.; 460 prop.set_f32(atom, Role::App, 1, y).unwrap(); 461 } 462 463 // Update the counter 464 let counter_text = found_nodes.len().to_string(); 465 let path = "/window/settings_layer/search_count"; 466 let node = sg_root3.lookup_node(path.to_string()).unwrap(); 467 let _ = node.set_property_str(atom, Role::App, "text", &counter_text).unwrap(); 468 }; 469 470 // Handle searching 471 let search_text = editbox_node.get_property("text").unwrap(); 472 let search_text_sub = search_text.subscribe_modify(); 473 let search2 = search.clone(); 474 let listen_search_text = app.ex.spawn(async move { 475 while let Ok(_) = search_text_sub.receive().await { 476 search2(); 477 } 478 }); 479 app.tasks.lock().unwrap().push(listen_search_text); 480 481 let node = editbox_node 482 .setup(|me| { 483 EditBox::new( 484 me, 485 window_scale.clone(), 486 app.render_api.clone(), 487 app.text_shaper.clone(), 488 app.ex.clone(), 489 ) 490 }) 491 .await; 492 layer_node.link(node); 493 494 // Search background 495 let node = create_vector_art("search_bg"); 496 let prop = node.get_property("rect").unwrap(); 497 prop.set_f32(atom, Role::App, 0, 0.).unwrap(); 498 prop.set_f32(atom, Role::App, 1, 60.).unwrap(); 499 prop.set_expr(atom, Role::App, 2, cc.compile("w * 100").unwrap()).unwrap(); 500 prop.set_f32(atom, Role::App, 3, SETTING_LABEL_LINESPACE).unwrap(); 501 node.set_property_u32(atom, Role::App, "z_index", 0).unwrap(); 502 503 let mut shape = VectorShape::new(); 504 505 let x1 = expr::const_f32(0.); 506 let y1 = expr::const_f32(0.); 507 let x2 = expr::load_var("w"); 508 let y2 = expr::const_f32(SETTING_LABEL_LINESPACE); 509 let (color1, color2) = match COLOR_SCHEME { 510 ColorScheme::DarkMode => ([0., 0.11, 0.11, 0.4], [0., 0.11, 0.11, 0.5]), 511 ColorScheme::PaperLight => ([1., 1., 1., 1.], [1., 1., 1., 1.]), 512 }; 513 let mut verts = vec![ 514 ShapeVertex::new(x1.clone(), y1.clone(), color1), 515 ShapeVertex::new(x2.clone(), y1.clone(), color1), 516 ShapeVertex::new(x1.clone(), y2.clone(), color2), 517 ShapeVertex::new(x2, y2, color2), 518 ]; 519 let mut indices = vec![0, 2, 1, 1, 2, 3]; 520 shape.verts.append(&mut verts); 521 shape.indices.append(&mut indices); 522 523 shape.add_filled_box( 524 expr::const_f32(0.), 525 expr::const_f32(SETTING_LABEL_LINESPACE - 1.), 526 expr::load_var("w"), 527 expr::const_f32(SETTING_LABEL_LINESPACE), 528 [0.15, 0.2, 0.19, 1.], 529 //[0., 0.11, 0.11, 0.4], 530 ); 531 532 let node = 533 node.setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())).await; 534 layer_node.link(node); 535 536 let node = create_vector_art("search_bg2"); 537 let prop = node.get_property("rect").unwrap(); 538 prop.set_f32(atom, Role::App, 0, 0.).unwrap(); 539 prop.set_f32(atom, Role::App, 1, 60.).unwrap(); 540 prop.set_expr(atom, Role::App, 2, cc.compile("w * 100").unwrap()).unwrap(); 541 prop.set_f32(atom, Role::App, 3, SETTING_LABEL_LINESPACE / 3.5).unwrap(); 542 node.set_property_u32(atom, Role::App, "z_index", 0).unwrap(); 543 544 let mut shape = VectorShape::new(); 545 546 let x1 = expr::const_f32(0.); 547 let y1 = expr::const_f32(0.); 548 let x2 = expr::load_var("w"); 549 let y2 = expr::const_f32(SETTING_LABEL_LINESPACE); 550 551 let (color1, color2) = match COLOR_SCHEME { 552 ColorScheme::DarkMode => ([0., 0.94, 1., 0.4], [0., 0.3, 0.25, 0.0]), 553 ColorScheme::PaperLight => ([0., 0.94, 1., 0.4], [0., 0.3, 0.25, 0.0]), 554 }; 555 556 let mut verts = vec![ 557 ShapeVertex::new(x1.clone(), y1.clone(), color1), 558 ShapeVertex::new(x2.clone(), y1.clone(), color1), 559 ShapeVertex::new(x1.clone(), y2.clone(), color2), 560 ShapeVertex::new(x2, y2, color2), 561 ]; 562 let mut indices = vec![0, 2, 1, 1, 2, 3]; 563 shape.verts.append(&mut verts); 564 shape.indices.append(&mut indices); 565 566 shape.add_filled_box( 567 expr::const_f32(0.), 568 expr::const_f32(SETTING_LABEL_LINESPACE - 1.), 569 expr::load_var("w"), 570 expr::const_f32(SETTING_LABEL_LINESPACE), 571 [0.15, 0.2, 0.19, 1.], 572 ); 573 574 let node = 575 node.setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())).await; 576 layer_node.link(node); 577 578 // Create a BTreeMap to store settings 579 let mut settings_map: BTreeMap<String, Arc<Setting>> = BTreeMap::new(); 580 581 // Get the app settings 582 let app_setting_root = app.sg_root.lookup_node("/setting").unwrap(); 583 for setting in app_setting_root.get_children().iter() { 584 let name = ["app", &setting.name.clone()].join("."); 585 settings_map.insert(name.clone(), Arc::new(Setting { name, node: setting.clone() })); 586 } 587 588 // Get the settings from all plugins 589 let sg_root_children = app.sg_root.clone().get_children(); 590 let plugin_node = sg_root_children.iter().find(|node| node.name == "plugin"); 591 if let Some(pnode) = plugin_node { 592 for plugin in pnode.get_children().iter() { 593 let plugin_children = plugin.get_children(); 594 let setting_root = plugin_children.iter().find(|node| node.name == "setting"); 595 if let Some(sroot) = setting_root { 596 for setting in sroot.get_children().iter() { 597 let name = [plugin.name.clone(), setting.name.clone()].join("."); 598 settings_map 599 .insert(name.clone(), Arc::new(Setting { name, node: setting.clone() })); 600 } 601 } 602 } 603 } 604 605 // Setting currently being edited 606 let active_setting: Arc<Mutex<Option<Arc<Setting>>>> = Arc::new(Mutex::new(None)); 607 608 // Setting Layer 609 // Contain a setting 610 let settings_layer_node = create_layer("settings"); 611 let prop = settings_layer_node.get_property("rect").unwrap(); 612 prop.set_f32(atom, Role::App, 0, 0.).unwrap(); 613 prop.set_f32(atom, Role::App, 1, 0.).unwrap(); 614 prop.set_expr(atom, Role::App, 2, expr::load_var("w")).unwrap(); 615 prop.set_f32(atom, Role::App, 3, 0.).unwrap(); 616 settings_layer_node.set_property_bool(atom, Role::App, "is_visible", true).unwrap(); 617 settings_layer_node.set_property_u32(atom, Role::App, "z_index", 0).unwrap(); 618 let settings_layer_node = settings_layer_node 619 .setup(|me| Layer::new(me, app.render_api.clone(), app.ex.clone())) 620 .await; 621 layer_node.link(settings_layer_node.clone()); 622 623 // Iterate over the map and process each setting 624 for setting in settings_map.values() { 625 let setting_clone = setting.clone(); 626 let setting_name = setting_clone.name.clone(); 627 let is_bool = matches!(setting_clone.get_value(), PropertyValue::Bool(_)); 628 629 setting_y += SETTING_LABEL_LINESPACE; 630 631 // Setting Layer 632 // Contain a setting 633 let setting_layer_node = create_layer(&setting_name.to_string()); 634 let prop = setting_layer_node.get_property("rect").unwrap(); 635 prop.set_f32(atom, Role::App, 0, 0.).unwrap(); 636 prop.set_f32(atom, Role::App, 1, setting_y + 60.).unwrap(); 637 prop.set_expr(atom, Role::App, 2, expr::load_var("w")).unwrap(); 638 prop.set_f32(atom, Role::App, 3, SETTING_LABEL_LINESPACE).unwrap(); 639 setting_layer_node.set_property_bool(atom, Role::App, "is_visible", true).unwrap(); 640 setting_layer_node.set_property_u32(atom, Role::App, "z_index", 0).unwrap(); 641 let setting_layer_node = setting_layer_node 642 .setup(|me| Layer::new(me, app.render_api.clone(), app.ex.clone())) 643 .await; 644 settings_layer_node.link(setting_layer_node.clone()); 645 646 // Background Label 647 let node = create_vector_art("key_bg"); 648 let prop = node.get_property("rect").unwrap(); 649 prop.set_f32(atom, Role::App, 0, 0.).unwrap(); 650 prop.set_f32(atom, Role::App, 1, 0.).unwrap(); 651 prop.clone() 652 .set_expr(atom, Role::App, 2, cc.compile("w * X_RATIO - BORDER_RIGHT_SCALE").unwrap()) 653 .unwrap(); 654 prop.set_f32(atom, Role::App, 3, SETTING_LABEL_LINESPACE).unwrap(); 655 node.set_property_u32(atom, Role::App, "z_index", 0).unwrap(); 656 657 let mut shape = VectorShape::new(); 658 659 let x1 = expr::const_f32(0.); 660 let y1 = expr::const_f32(0.); 661 let x2 = expr::load_var("w"); 662 let y2 = expr::const_f32(SETTING_LABEL_LINESPACE); 663 let (color1, color2) = match COLOR_SCHEME { 664 //ColorScheme::DarkMode => ([0., 0.11, 0.11, 1.], [0., 0.11, 0.11, 1.]), 665 ColorScheme::DarkMode => ([0., 0.11, 0.11, 0.4], [0., 0.11, 0.11, 0.5]), 666 ColorScheme::PaperLight => ([1., 1., 1., 1.], [1., 1., 1., 1.]), 667 }; 668 let mut verts = vec![ 669 ShapeVertex::new(x1.clone(), y1.clone(), color1), 670 ShapeVertex::new(x2.clone(), y1.clone(), color1), 671 ShapeVertex::new(x1.clone(), y2.clone(), color2), 672 ShapeVertex::new(x2, y2, color2), 673 ]; 674 let mut indices = vec![0, 2, 1, 1, 2, 3]; 675 shape.verts.append(&mut verts); 676 shape.indices.append(&mut indices); 677 678 shape.add_filled_box( 679 expr::const_f32(0.), 680 expr::const_f32(SETTING_LABEL_LINESPACE - 1.), 681 expr::load_var("w"), 682 expr::const_f32(SETTING_LABEL_LINESPACE), 683 [0.15, 0.2, 0.19, 1.], 684 ); 685 686 let node = node 687 .setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())) 688 .await; 689 setting_layer_node.link(node); 690 691 if is_bool { 692 // Background Value: Bool FALSE 693 let node = create_vector_art("value_bg_bool_false"); 694 let prop = node.get_property("rect").unwrap(); 695 prop.clone() 696 .set_expr( 697 atom, 698 Role::App, 699 0, 700 cc.compile("w * X_RATIO - BORDER_RIGHT_SCALE").unwrap(), 701 ) 702 .unwrap(); 703 prop.set_f32(atom, Role::App, 1, 0.).unwrap(); 704 prop.clone() 705 .set_expr( 706 atom, 707 Role::App, 708 2, 709 cc.compile("w * (1-X_RATIO) + BORDER_RIGHT_SCALE").unwrap(), 710 ) 711 .unwrap(); 712 prop.set_f32(atom, Role::App, 3, SETTING_LABEL_LINESPACE).unwrap(); 713 node.set_property_u32(atom, Role::App, "z_index", 0).unwrap(); 714 node.set_property_bool( 715 atom, 716 Role::App, 717 "is_visible", 718 matches!(setting_clone.get_value(), PropertyValue::Bool(false)), 719 ) 720 .unwrap(); 721 722 let mut shape = VectorShape::new(); 723 724 let x1 = expr::const_f32(0.); 725 let y1 = expr::const_f32(0.); 726 let x2 = expr::load_var("w"); 727 let y2 = expr::const_f32(SETTING_LABEL_LINESPACE); 728 729 let (color1, color2) = match COLOR_SCHEME { 730 //ColorScheme::DarkMode => ([0., 0.11, 0.11, 1.], [0., 0.11, 0.11, 1.]), 731 ColorScheme::DarkMode => ([0.0, 0.04, 0.04, 0.0], [0.7, 0.0, 0.0, 0.15]), 732 ColorScheme::PaperLight => ([0.0, 0.04, 0.04, 0.0], [0.7, 0.0, 0.0, 0.15]), 733 }; 734 735 let mut verts = vec![ 736 ShapeVertex::new(x1.clone(), y1.clone(), color1), 737 ShapeVertex::new(x2.clone(), y1.clone(), color1), 738 ShapeVertex::new(x1.clone(), y2.clone(), color2), 739 ShapeVertex::new(x2, y2, color2), 740 ]; 741 let mut indices = vec![0, 2, 1, 1, 2, 3]; 742 shape.verts.append(&mut verts); 743 shape.indices.append(&mut indices); 744 745 shape.add_filled_box( 746 expr::const_f32(0.), 747 expr::const_f32(SETTING_LABEL_LINESPACE - 1.), 748 expr::load_var("w"), 749 expr::const_f32(SETTING_LABEL_LINESPACE), 750 [0.15, 0.2, 0.19, 1.], 751 ); 752 753 let node = node 754 .setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())) 755 .await; 756 setting_layer_node.link(node); 757 758 // Background Value: Bool TRUE 759 let node = create_vector_art("value_bg_bool_true"); 760 let prop = node.get_property("rect").unwrap(); 761 prop.clone() 762 .set_expr( 763 atom, 764 Role::App, 765 0, 766 cc.compile("w * X_RATIO - BORDER_RIGHT_SCALE").unwrap(), 767 ) 768 .unwrap(); 769 prop.set_f32(atom, Role::App, 1, 0.).unwrap(); 770 prop.clone() 771 .set_expr( 772 atom, 773 Role::App, 774 2, 775 cc.compile("w * (1-X_RATIO) + BORDER_RIGHT_SCALE").unwrap(), 776 ) 777 .unwrap(); 778 prop.set_f32(atom, Role::App, 3, SETTING_LABEL_LINESPACE).unwrap(); 779 node.set_property_u32(atom, Role::App, "z_index", 0).unwrap(); 780 node.set_property_bool( 781 atom, 782 Role::App, 783 "is_visible", 784 matches!(setting_clone.get_value(), PropertyValue::Bool(true)), 785 ) 786 .unwrap(); 787 788 let mut shape = VectorShape::new(); 789 790 let x1 = expr::const_f32(0.); 791 let y1 = expr::const_f32(0.); 792 let x2 = expr::load_var("w"); 793 let y2 = expr::const_f32(SETTING_LABEL_LINESPACE); 794 795 let (color1, color2) = match COLOR_SCHEME { 796 //ColorScheme::DarkMode => ([0., 0.11, 0.11, 1.], [0., 0.11, 0.11, 1.]), 797 ColorScheme::DarkMode => ([0., 0.3, 0.25, 0.0], [0., 0.3, 0.25, 0.5]), 798 ColorScheme::PaperLight => ([0., 0.3, 0.25, 0.0], [0., 0.3, 0.25, 0.5]), 799 }; 800 801 let mut verts = vec![ 802 ShapeVertex::new(x1.clone(), y1.clone(), color1), 803 ShapeVertex::new(x2.clone(), y1.clone(), color1), 804 ShapeVertex::new(x1.clone(), y2.clone(), color2), 805 ShapeVertex::new(x2, y2, color2), 806 ]; 807 let mut indices = vec![0, 2, 1, 1, 2, 3]; 808 shape.verts.append(&mut verts); 809 shape.indices.append(&mut indices); 810 811 shape.add_filled_box( 812 expr::const_f32(0.), 813 expr::const_f32(SETTING_LABEL_LINESPACE - 1.), 814 expr::load_var("w"), 815 expr::const_f32(SETTING_LABEL_LINESPACE), 816 [0.15, 0.2, 0.19, 1.], 817 ); 818 819 let node = node 820 .setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())) 821 .await; 822 setting_layer_node.link(node); 823 } else { 824 // Background Value 825 let node = create_vector_art("value_bg"); 826 let prop = node.get_property("rect").unwrap(); 827 prop.clone() 828 .set_expr( 829 atom, 830 Role::App, 831 0, 832 cc.compile("w * X_RATIO - BORDER_RIGHT_SCALE").unwrap(), 833 ) 834 .unwrap(); 835 prop.set_f32(atom, Role::App, 1, 0.).unwrap(); 836 prop.clone() 837 .set_expr( 838 atom, 839 Role::App, 840 2, 841 cc.compile("w * (1-X_RATIO) + BORDER_RIGHT_SCALE").unwrap(), 842 ) 843 .unwrap(); 844 prop.set_f32(atom, Role::App, 3, SETTING_LABEL_LINESPACE).unwrap(); 845 node.set_property_u32(atom, Role::App, "z_index", 0).unwrap(); 846 node.set_property_bool(atom, Role::App, "is_visible", true).unwrap(); 847 848 let mut shape = VectorShape::new(); 849 850 let x1 = expr::const_f32(0.); 851 let y1 = expr::const_f32(0.); 852 let x2 = expr::load_var("w"); 853 let y2 = expr::const_f32(SETTING_LABEL_LINESPACE); 854 855 let (color1, color2) = match COLOR_SCHEME { 856 //ColorScheme::DarkMode => ([0., 0.11, 0.11, 1.], [0., 0.11, 0.11, 1.]), 857 ColorScheme::DarkMode => ([0., 0.02, 0.02, 0.5], [0., 0.04, 0.04, 0.7]), 858 ColorScheme::PaperLight => ([1., 1., 1., 1.], [1., 1., 1., 1.]), 859 }; 860 861 let mut verts = vec![ 862 ShapeVertex::new(x1.clone(), y1.clone(), color1), 863 ShapeVertex::new(x2.clone(), y1.clone(), color1), 864 ShapeVertex::new(x1.clone(), y2.clone(), color2), 865 ShapeVertex::new(x2, y2, color2), 866 ]; 867 let mut indices = vec![0, 2, 1, 1, 2, 3]; 868 shape.verts.append(&mut verts); 869 shape.indices.append(&mut indices); 870 871 shape.add_filled_box( 872 expr::const_f32(0.), 873 expr::const_f32(SETTING_LABEL_LINESPACE - 1.), 874 expr::load_var("w"), 875 expr::const_f32(SETTING_LABEL_LINESPACE), 876 [0.15, 0.2, 0.19, 1.], 877 ); 878 879 let node = node 880 .setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())) 881 .await; 882 setting_layer_node.link(node); 883 } 884 885 // Label Key 886 let label_value_node = create_text("key_label"); 887 let prop = label_value_node.get_property("rect").unwrap(); 888 prop.set_f32(atom, Role::App, 0, SETTING_LABEL_X).unwrap(); 889 prop.set_f32(atom, Role::App, 1, 0.).unwrap(); 890 prop.set_expr(atom, Role::App, 2, cc.compile("w * X_RATIO").unwrap()).unwrap(); 891 prop.set_f32(atom, Role::App, 3, 100.).unwrap(); 892 label_value_node.set_property_u32(atom, Role::App, "z_index", 1).unwrap(); 893 label_value_node 894 .set_property_f32(atom, Role::App, "baseline", SETTING_LABEL_BASELINE) 895 .unwrap(); 896 label_value_node 897 .set_property_f32(atom, Role::App, "font_size", SETTING_LABEL_FONTSIZE) 898 .unwrap(); 899 label_value_node.set_property_str(atom, Role::App, "text", setting_name.clone()).unwrap(); 900 if setting.is_default() { 901 label_value_node 902 .set_property_f32_vec(atom, Role::App, "text_color", vec![0.65, 0.87, 0.83, 1.]) 903 .unwrap(); 904 } else { 905 label_value_node 906 .set_property_f32_vec(atom, Role::App, "text_color", vec![1., 1., 1., 1.]) 907 .unwrap(); 908 } 909 label_value_node.set_property_u32(atom, Role::App, "z_index", 1).unwrap(); 910 911 let label_value_node = label_value_node 912 .setup(|me| { 913 Text::new( 914 me, 915 window_scale.clone(), 916 app.render_api.clone(), 917 app.text_shaper.clone(), 918 app.ex.clone(), 919 ) 920 }) 921 .await; 922 setting_layer_node.link(label_value_node); 923 924 // Text edit 925 let editbox_node = create_editbox("value_editbox"); 926 editbox_node.set_property_bool(atom, Role::App, "is_active", true).unwrap(); 927 editbox_node.set_property_bool(atom, Role::App, "is_focused", true).unwrap(); 928 let prop = editbox_node.get_property("rect").unwrap(); 929 prop.clone() 930 .set_expr(atom, Role::App, 0, cc.compile("w * X_RATIO + BORDER_RIGHT_SCALE").unwrap()) 931 .unwrap(); 932 prop.set_f32(atom, Role::App, 1, 0.).unwrap(); 933 prop.clone() 934 .set_expr(atom, Role::App, 2, cc.compile("w * X_RATIO - BORDER_RIGHT_SCALE").unwrap()) 935 .unwrap(); 936 prop.set_f32(atom, Role::App, 3, SETTING_LABEL_LINESPACE).unwrap(); 937 editbox_node.set_property_f32(atom, Role::App, "baseline", SETTING_LABEL_BASELINE).unwrap(); 938 editbox_node.set_property_f32(atom, Role::App, "font_size", SETTING_EDIT_FONTSIZE).unwrap(); 939 editbox_node.set_property_f32(atom, Role::App, "cursor_ascent", CURSOR_ASCENT).unwrap(); 940 editbox_node.set_property_f32(atom, Role::App, "cursor_descent", CURSOR_DESCENT).unwrap(); 941 editbox_node.set_property_f32(atom, Role::App, "select_ascent", SELECT_ASCENT).unwrap(); 942 editbox_node.set_property_f32(atom, Role::App, "select_descent", SELECT_DESCENT).unwrap(); 943 editbox_node 944 .set_property_f32_vec(atom, Role::App, "text_color", vec![0.7, 0.7, 0.7, 1.]) 945 .unwrap(); 946 editbox_node 947 .set_property_f32_vec(atom, Role::App, "cursor_color", vec![0.5, 0.5, 0.5, 1.]) 948 .unwrap(); 949 editbox_node 950 .set_property_f32_vec(atom, Role::App, "hi_bg_color", vec![0.5, 0.5, 0.5, 1.]) 951 .unwrap(); 952 let prop = editbox_node.get_property("selected").unwrap(); 953 prop.set_null(atom, Role::App, 0).unwrap(); 954 prop.set_null(atom, Role::App, 1).unwrap(); 955 editbox_node.set_property_u32(atom, Role::App, "z_index", 1).unwrap(); 956 editbox_node.set_property_bool(atom, Role::App, "is_active", false).unwrap(); 957 editbox_node.set_property_bool(atom, Role::App, "is_focused", false).unwrap(); 958 editbox_node.set_property_f32(atom, Role::App, "font_size", 0.).unwrap(); 959 960 let editz_text = PropertyStr::wrap(&editbox_node, Role::App, "text", 0).unwrap(); 961 962 // Handle enter pressed in the editbox 963 { 964 let (slot, recvr) = Slot::new("setting_enter_pressed"); 965 editbox_node.register("enter_pressed", slot).unwrap(); 966 let setting2 = setting.clone(); 967 let sg_root2 = setting_layer_node.clone(); 968 let active_setting2 = active_setting.clone(); 969 let editz_text2 = editz_text.clone(); 970 let listen_enter = app.ex.spawn(async move { 971 while let Ok(_) = recvr.recv().await { 972 update_setting( 973 setting2.clone(), 974 sg_root2.clone(), 975 active_setting2.clone(), 976 editz_text2.clone(), 977 ) 978 .await; 979 refresh_setting(setting2.clone(), sg_root2.clone()); 980 } 981 }); 982 app.tasks.lock().unwrap().push(listen_enter); 983 } 984 985 let node = editbox_node 986 .setup(|me| { 987 EditBox::new( 988 me, 989 window_scale.clone(), 990 app.render_api.clone(), 991 app.text_shaper.clone(), 992 app.ex.clone(), 993 ) 994 }) 995 .await; 996 setting_layer_node.link(node); 997 998 // Is this setting the one that is currently active 999 let cloned_active_setting = active_setting.clone(); 1000 let is_active_setting = match active_setting.lock().unwrap().as_ref() { 1001 Some(active) => Arc::ptr_eq(active, &setting), 1002 None => false, 1003 }; 1004 1005 if !is_active_setting { 1006 if is_bool { 1007 // Bool circle: FALSE 1008 let node = create_vector_art("bool_icon_bg_false"); 1009 let prop = node.get_property("rect").unwrap(); 1010 prop.clone() 1011 .set_expr( 1012 atom, 1013 Role::App, 1014 0, 1015 cc.compile("w * X_RATIO + BORDER_RIGHT_SCALE + 6").unwrap(), 1016 ) 1017 .unwrap(); 1018 prop.set_f32(atom, Role::App, 1, SETTING_LABEL_LINESPACE / 2.).unwrap(); 1019 prop.set_f32(atom, Role::App, 2, 0.).unwrap(); 1020 prop.set_f32(atom, Role::App, 3, 0.).unwrap(); 1021 node.set_property_u32(atom, Role::App, "z_index", 1).unwrap(); 1022 node.set_property_bool( 1023 atom, 1024 Role::App, 1025 "is_visible", 1026 matches!(setting_clone.get_value(), PropertyValue::Bool(false)), 1027 ) 1028 .unwrap(); 1029 1030 let shape = shape::create_circle([0.9, 0.4, 0.4, 0.7]).scaled(5.); 1031 let node = node 1032 .setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())) 1033 .await; 1034 setting_layer_node.link(node); 1035 1036 // Bool circle: TRUE 1037 let node = create_vector_art("bool_icon_bg_true"); 1038 let prop = node.get_property("rect").unwrap(); 1039 prop.clone() 1040 .set_expr( 1041 atom, 1042 Role::App, 1043 0, 1044 cc.compile("w * X_RATIO + BORDER_RIGHT_SCALE + 6").unwrap(), 1045 ) 1046 .unwrap(); 1047 prop.set_f32(atom, Role::App, 1, SETTING_LABEL_LINESPACE / 2.).unwrap(); 1048 prop.set_f32(atom, Role::App, 2, 0.).unwrap(); 1049 prop.set_f32(atom, Role::App, 3, 0.).unwrap(); 1050 node.set_property_u32(atom, Role::App, "z_index", 1).unwrap(); 1051 node.set_property_bool( 1052 atom, 1053 Role::App, 1054 "is_visible", 1055 matches!(setting_clone.get_value(), PropertyValue::Bool(true)), 1056 ) 1057 .unwrap(); 1058 1059 let shape = shape::create_circle([0., 0.94, 1., 1.]).scaled(5.); 1060 let node = node 1061 .setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())) 1062 .await; 1063 setting_layer_node.link(node); 1064 1065 // Label showing the setting's current value 1066 let value_node = create_text("value_label"); 1067 let prop = value_node.get_property("rect").unwrap(); 1068 prop.clone() 1069 .set_expr( 1070 atom, 1071 Role::App, 1072 0, 1073 cc.compile("w * X_RATIO + BORDER_RIGHT_SCALE + 20 + 6").unwrap(), 1074 ) 1075 .unwrap(); 1076 prop.set_f32(atom, Role::App, 1, 0.).unwrap(); 1077 prop.clone() 1078 .set_expr(atom, Role::App, 2, cc.compile("w * X_RATIO").unwrap()) 1079 .unwrap(); 1080 prop.set_f32(atom, Role::App, 3, 100.).unwrap(); 1081 value_node.set_property_u32(atom, Role::App, "z_index", 1).unwrap(); 1082 value_node 1083 .set_property_f32(atom, Role::App, "baseline", SETTING_LABEL_BASELINE) 1084 .unwrap(); 1085 value_node 1086 .set_property_f32(atom, Role::App, "font_size", SETTING_LABEL_FONTSIZE) 1087 .unwrap(); 1088 value_node 1089 .set_property_str(atom, Role::App, "text", setting_clone.value_as_string()) 1090 .unwrap(); 1091 if matches!(setting_clone.get_value(), PropertyValue::Bool(false)) { 1092 value_node 1093 .set_property_f32_vec( 1094 atom, 1095 Role::App, 1096 "text_color", 1097 vec![0.9, 0.4, 0.4, 1.], 1098 ) 1099 .unwrap(); 1100 } else { 1101 value_node 1102 .set_property_f32_vec( 1103 atom, 1104 Role::App, 1105 "text_color", 1106 vec![0.0, 0.94, 1., 1.], 1107 ) 1108 .unwrap(); 1109 } 1110 value_node.set_property_u32(atom, Role::App, "z_index", 2).unwrap(); 1111 1112 let node = value_node 1113 .setup(|me| { 1114 Text::new( 1115 me, 1116 window_scale.clone(), 1117 app.render_api.clone(), 1118 app.text_shaper.clone(), 1119 app.ex.clone(), 1120 ) 1121 }) 1122 .await; 1123 setting_layer_node.link(node); 1124 } else { 1125 // Label showing the setting's current value 1126 let value_node = create_text("value_label"); 1127 let prop = value_node.get_property("rect").unwrap(); 1128 prop.clone() 1129 .set_expr( 1130 atom, 1131 Role::App, 1132 0, 1133 cc.compile("w * X_RATIO + BORDER_RIGHT_SCALE").unwrap(), 1134 ) 1135 .unwrap(); 1136 prop.set_f32(atom, Role::App, 1, 0.).unwrap(); 1137 prop.clone() 1138 .set_expr(atom, Role::App, 2, cc.compile("w * X_RATIO").unwrap()) 1139 .unwrap(); 1140 prop.set_f32(atom, Role::App, 3, 100.).unwrap(); 1141 value_node.set_property_u32(atom, Role::App, "z_index", 1).unwrap(); 1142 value_node 1143 .set_property_f32(atom, Role::App, "baseline", SETTING_LABEL_BASELINE) 1144 .unwrap(); 1145 value_node 1146 .set_property_f32(atom, Role::App, "font_size", SETTING_LABEL_FONTSIZE) 1147 .unwrap(); 1148 value_node 1149 .set_property_str(atom, Role::App, "text", setting_clone.value_as_string()) 1150 .unwrap(); 1151 value_node 1152 .set_property_f32_vec(atom, Role::App, "text_color", vec![1., 1., 1., 1.]) 1153 .unwrap(); 1154 value_node.set_property_u32(atom, Role::App, "z_index", 2).unwrap(); 1155 1156 let node = value_node 1157 .setup(|me| { 1158 Text::new( 1159 me, 1160 window_scale.clone(), 1161 app.render_api.clone(), 1162 app.text_shaper.clone(), 1163 app.ex.clone(), 1164 ) 1165 }) 1166 .await; 1167 setting_layer_node.link(node); 1168 } 1169 1170 // A wide button useful to select the current setting 1171 let node = create_button("selector_btn"); 1172 node.set_property_bool(atom, Role::App, "is_active", true).unwrap(); 1173 let prop = node.get_property("rect").unwrap(); 1174 prop.set_expr(atom, Role::App, 0, cc.compile("w * X_RATIO").unwrap()).unwrap(); 1175 prop.set_f32(atom, Role::App, 1, 0.).unwrap(); 1176 prop.clone() 1177 .set_expr(atom, Role::App, 2, cc.compile("w * (1-X_RATIO)").unwrap()) 1178 .unwrap(); 1179 prop.set_f32(atom, Role::App, 3, SETTING_LABEL_LINESPACE).unwrap(); 1180 1181 let sg_root2 = app.sg_root.clone(); 1182 let setting_clone2 = setting_clone.clone(); 1183 let setting_root2 = setting_layer_node.clone(); 1184 let select = move || { 1185 let atom = &mut PropertyAtomicGuard::new(); 1186 let sg_root = sg_root2.clone(); 1187 let mut lock = cloned_active_setting.lock().unwrap(); 1188 1189 let path = "/window/settings_layer/search_input"; 1190 let node = sg_root.lookup_node(path).unwrap(); 1191 //node.set_property_bool(atom, Role::App, "is_active", false).unwrap(); 1192 node.set_property_bool(atom, Role::App, "is_focused", false).unwrap(); 1193 1194 let was_active = if let Some(s) = lock.as_ref() { 1195 let path = format!("/window/settings_layer/settings/{}", &s.name); 1196 let old_node = sg_root.lookup_node(&path).unwrap(); 1197 1198 let _was_active = s.name == setting_clone2.clone().name; 1199 1200 // Show the selected setting value label 1201 // of the selected setting, if there's one 1202 let node = old_node.lookup_node("/value_label").unwrap(); 1203 let text = PropertyStr::wrap(&node, Role::App, "text", 0).unwrap(); 1204 text.set(atom, &s.value_as_string()); 1205 1206 // Hide the selected setting editbox 1207 // of the selected setting, if there's one 1208 if !_was_active { 1209 let node = old_node.lookup_node("/value_editbox").unwrap(); 1210 node.set_property_bool(atom, Role::App, "is_active", false).unwrap(); 1211 node.set_property_bool(atom, Role::App, "is_focused", false).unwrap(); 1212 node.set_property_f32(atom, Role::App, "font_size", 0.).unwrap(); 1213 node.set_property_str(atom, Role::App, "text", "").unwrap(); 1214 } 1215 1216 // Hide conftrm button 1217 // (Bool settings don't have a confirm button so we have to check for it to 1218 // not panic) 1219 let is_bool = 1220 matches!(lock.clone().unwrap().get_value(), PropertyValue::Bool(_)); 1221 if !is_bool { 1222 old_node 1223 .lookup_node("/confirm_btn_bg") 1224 .unwrap() 1225 .set_property_bool(atom, Role::App, "is_visible", false) 1226 .unwrap(); 1227 } 1228 1229 _was_active 1230 } else { 1231 false 1232 }; 1233 1234 // Update what setting active_setting points to 1235 *lock = Some(setting_clone2.clone()); 1236 debug!("active setting set to: {}", lock.clone().unwrap().name); 1237 1238 if is_bool { 1239 // Hide the setting value label (set its text empty) 1240 let editbox = setting_root2.lookup_node("/value_label").unwrap(); 1241 let label_text = PropertyStr::wrap(&editbox, Role::App, "text", 0).unwrap(); 1242 1243 let value = setting_clone2.get_value(); 1244 1245 setting_root2 1246 .lookup_node("/value_bg_bool_true") 1247 .unwrap() 1248 .set_property_bool(atom, Role::App, "is_visible", false) 1249 .unwrap(); 1250 setting_root2 1251 .lookup_node("/value_bg_bool_false") 1252 .unwrap() 1253 .set_property_bool(atom, Role::App, "is_visible", false) 1254 .unwrap(); 1255 setting_root2 1256 .lookup_node("/bool_icon_bg_true") 1257 .unwrap() 1258 .set_property_bool(atom, Role::App, "is_visible", false) 1259 .unwrap(); 1260 setting_root2 1261 .lookup_node("/bool_icon_bg_false") 1262 .unwrap() 1263 .set_property_bool(atom, Role::App, "is_visible", false) 1264 .unwrap(); 1265 1266 if matches!(value, PropertyValue::Bool(false)) { 1267 setting_clone2 1268 .node 1269 .set_property_bool(atom, Role::User, "value", true) 1270 .unwrap(); 1271 label_text.set(atom, "TRUE"); 1272 1273 setting_root2 1274 .lookup_node("/value_bg_bool_true") 1275 .unwrap() 1276 .set_property_bool(atom, Role::App, "is_visible", true) 1277 .unwrap(); 1278 setting_root2 1279 .lookup_node("/bool_icon_bg_true") 1280 .unwrap() 1281 .set_property_bool(atom, Role::App, "is_visible", true) 1282 .unwrap(); 1283 1284 let node = setting_root2.lookup_node("/value_label").unwrap(); 1285 node.set_property_f32_vec( 1286 atom, 1287 Role::App, 1288 "text_color", 1289 vec![0., 0.94, 1., 1.], 1290 ) 1291 .unwrap(); 1292 } else { 1293 setting_clone2 1294 .node 1295 .set_property_bool(atom, Role::User, "value", false) 1296 .unwrap(); 1297 label_text.set(atom, "FALSE"); 1298 1299 setting_root2 1300 .lookup_node("/value_bg_bool_false") 1301 .unwrap() 1302 .set_property_bool(atom, Role::App, "is_visible", true) 1303 .unwrap(); 1304 setting_root2 1305 .lookup_node("/bool_icon_bg_false") 1306 .unwrap() 1307 .set_property_bool(atom, Role::App, "is_visible", true) 1308 .unwrap(); 1309 1310 let node = setting_root2.lookup_node("/value_label").unwrap(); 1311 node.set_property_f32_vec( 1312 atom, 1313 Role::App, 1314 "text_color", 1315 vec![0.9, 0.4, 0.4, 1.], 1316 ) 1317 .unwrap(); 1318 } 1319 } else { 1320 // Hide the setting value label (set its text empty) 1321 // TODO?: Visilibity property on labels 1322 let editbox = setting_root2.lookup_node("/value_label").unwrap(); 1323 let label_text = PropertyStr::wrap(&editbox, Role::App, "text", 0).unwrap(); 1324 label_text.set(atom, ""); 1325 1326 // Show the editbox 1327 let node = setting_root2.lookup_node("/value_editbox").unwrap(); 1328 node.set_property_bool(atom, Role::App, "is_active", true).unwrap(); 1329 node.set_property_bool(atom, Role::App, "is_focused", true).unwrap(); 1330 node.set_property_f32(atom, Role::App, "font_size", 16.).unwrap(); 1331 if !was_active { 1332 node.set_property_str( 1333 atom, 1334 Role::App, 1335 "text", 1336 setting_clone2.value_as_string(), 1337 ) 1338 .unwrap(); 1339 } 1340 1341 // Show confirm button 1342 setting_root2 1343 .lookup_node("/confirm_btn_bg") 1344 .unwrap() 1345 .set_property_bool(atom, Role::App, "is_visible", true) 1346 .unwrap(); 1347 } 1348 1349 refresh_setting(setting_clone2.clone(), setting_root2.clone()); 1350 }; 1351 1352 { 1353 let (slot, recvr) = Slot::new("select_clicked"); 1354 node.register("click", slot).unwrap(); 1355 let select2 = select.clone(); 1356 let listen_click = app.ex.spawn(async move { 1357 while let Ok(_) = recvr.recv().await { 1358 select2(); 1359 } 1360 }); 1361 app.tasks.lock().unwrap().push(listen_click); 1362 1363 let node = node.setup(|me| Button::new(me, app.ex.clone())).await; 1364 setting_layer_node.link(node.clone()); 1365 } 1366 } 1367 1368 if is_bool { 1369 // Switch icon 1370 let node = create_vector_art("switch_btn_bg"); 1371 let prop = node.get_property("rect").unwrap(); 1372 prop.set_expr(atom, Role::App, 0, cc.compile("w - 50").unwrap()).unwrap(); 1373 prop.set_f32(atom, Role::App, 1, SETTING_LABEL_LINESPACE / 2.).unwrap(); 1374 prop.set_f32(atom, Role::App, 2, 0.).unwrap(); 1375 prop.set_f32(atom, Role::App, 3, 0.).unwrap(); 1376 node.set_property_u32(atom, Role::App, "z_index", 1).unwrap(); 1377 1378 let shape = shape::create_switch([0., 0.94, 1., 1.]).scaled(10.); 1379 let node = node 1380 .setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())) 1381 .await; 1382 setting_layer_node.link(node); 1383 } else { 1384 // Confirm button 1385 let node = create_vector_art("confirm_btn_bg"); 1386 let prop = node.get_property("rect").unwrap(); 1387 prop.set_expr(atom, Role::App, 0, cc.compile("w - 50").unwrap()).unwrap(); 1388 prop.set_f32(atom, Role::App, 1, SETTING_LABEL_LINESPACE / 2.).unwrap(); 1389 prop.set_f32(atom, Role::App, 2, 0.).unwrap(); 1390 prop.set_f32(atom, Role::App, 3, 0.).unwrap(); 1391 node.set_property_u32(atom, Role::App, "z_index", 3).unwrap(); 1392 node.set_property_bool(atom, Role::App, "is_visible", false).unwrap(); 1393 1394 let shape = shape::create_confirm([0., 0.94, 1., 1.]).scaled(10.); 1395 let node = node 1396 .setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())) 1397 .await; 1398 setting_layer_node.link(node.clone()); 1399 1400 let node = create_button("confirm_btn"); 1401 node.set_property_bool(atom, Role::App, "is_active", true).unwrap(); 1402 let prop = node.get_property("rect").unwrap(); 1403 prop.set_expr(atom, Role::App, 0, cc.compile("w - 100").unwrap()).unwrap(); 1404 prop.set_f32(atom, Role::App, 1, 0.).unwrap(); 1405 prop.set_f32(atom, Role::App, 2, 100.).unwrap(); 1406 prop.set_f32(atom, Role::App, 3, SETTING_LABEL_LINESPACE).unwrap(); 1407 node.set_property_u32(atom, Role::App, "z_index", 3).unwrap(); 1408 1409 let node = node.setup(|me| Button::new(me, app.ex.clone())).await; 1410 setting_layer_node.link(node.clone()); 1411 1412 // Handle confirm button click 1413 { 1414 let (slot, recvr) = Slot::new("confirm_clicked"); 1415 node.register("click", slot).unwrap(); 1416 let setting2 = setting.clone(); 1417 let sg_root2 = setting_layer_node.clone(); 1418 let active_setting2 = active_setting.clone(); 1419 let editz_text2 = editz_text.clone(); 1420 let listen_click = app.ex.spawn(async move { 1421 while let Ok(_) = recvr.recv().await { 1422 info!("confirm clicked"); 1423 update_setting( 1424 setting2.clone(), 1425 sg_root2.clone(), 1426 active_setting2.clone(), 1427 editz_text2.clone(), 1428 ) 1429 .await; 1430 refresh_setting(setting2.clone(), sg_root2.clone()); 1431 } 1432 }); 1433 app.tasks.lock().unwrap().push(listen_click); 1434 } 1435 } 1436 1437 // Reset icon 1438 let node = create_vector_art("reset_btn_bg"); 1439 let prop = node.get_property("rect").unwrap(); 1440 prop.set_expr(atom, Role::App, 0, cc.compile("w - 100").unwrap()).unwrap(); 1441 prop.set_f32(atom, Role::App, 1, SETTING_LABEL_LINESPACE / 2.).unwrap(); 1442 prop.set_f32(atom, Role::App, 2, 0.).unwrap(); 1443 prop.set_f32(atom, Role::App, 3, 0.).unwrap(); 1444 node.set_property_bool(atom, Role::App, "is_visible", !setting.is_default()).unwrap(); 1445 node.set_property_u32(atom, Role::App, "z_index", 1).unwrap(); 1446 1447 let shape = shape::create_reset([0., 0.94, 1., 1.]).scaled(15.); 1448 let node = node 1449 .setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())) 1450 .await; 1451 setting_layer_node.link(node); 1452 1453 let node = create_button("reset_btn"); 1454 node.set_property_bool(atom, Role::App, "is_active", !setting.is_default()).unwrap(); 1455 let prop = node.get_property("rect").unwrap(); 1456 prop.set_expr(atom, Role::App, 0, cc.compile("w - 115").unwrap()).unwrap(); 1457 prop.set_f32(atom, Role::App, 1, 0.).unwrap(); 1458 prop.set_f32(atom, Role::App, 2, 50.).unwrap(); 1459 prop.set_f32(atom, Role::App, 3, SETTING_LABEL_LINESPACE).unwrap(); 1460 node.set_property_u32(atom, Role::App, "z_index", 3).unwrap(); 1461 1462 let node = node.setup(|me| Button::new(me, app.ex.clone())).await; 1463 setting_layer_node.link(node.clone()); 1464 1465 // Handle reset button click 1466 { 1467 let (slot, recvr) = Slot::new("reset_clicked"); 1468 node.register("click", slot).unwrap(); 1469 let setting2 = setting.clone(); 1470 let sg_root2 = setting_layer_node.clone(); 1471 let active_setting2 = active_setting.clone(); 1472 let editz_text2 = editz_text.clone(); 1473 let listen_click = app.ex.spawn(async move { 1474 while let Ok(_) = recvr.recv().await { 1475 info!("reset clicked"); 1476 setting2.reset(); 1477 1478 let atom = &mut PropertyAtomicGuard::new(); 1479 1480 // Show the selected setting value label (set its text empty) 1481 // of the selected setting, if there's one 1482 let node = sg_root2.lookup_node("/value_label").unwrap(); 1483 let text = PropertyStr::wrap(&node, Role::App, "text", 0).unwrap(); 1484 text.set(atom, setting2.value_as_string()); 1485 1486 let node = sg_root2.lookup_node("/value_editbox").unwrap(); 1487 node.set_property_str(atom, Role::App, "text", setting2.value_as_string()) 1488 .unwrap(); 1489 1490 update_setting( 1491 setting2.clone(), 1492 sg_root2.clone(), 1493 active_setting2.clone(), 1494 editz_text2.clone(), 1495 ) 1496 .await; 1497 refresh_setting(setting2.clone(), sg_root2.clone()); 1498 } 1499 }); 1500 app.tasks.lock().unwrap().push(listen_click); 1501 } 1502 } 1503 1504 let settings_node = app.sg_root.lookup_node("/window/settings_layer").unwrap(); 1505 settings_node.set_property_bool(atom, Role::App, "is_visible", false).unwrap(); 1506 1507 // Searchbar results count 1508 let node = app.sg_root.lookup_node("/window/settings_layer/settings").unwrap(); 1509 let counter_text = node.get_children().len().to_string(); 1510 let node = app.sg_root.lookup_node("/window/settings_layer/search_count").unwrap(); 1511 node.set_property_str(atom, Role::App, "text", &counter_text).unwrap(); 1512 } 1513 1514 fn refresh_setting(setting: Arc<Setting>, sn: SceneNodePtr) { 1515 let atom = &mut PropertyAtomicGuard::new(); 1516 let is_bool = matches!(setting.get_value(), PropertyValue::Bool(_)); 1517 1518 let node = sn.lookup_node("/key_label").unwrap(); 1519 if setting.clone().is_default() { 1520 node.set_property_f32_vec(atom, Role::App, "text_color", vec![0.65, 0.87, 0.83, 1.]) 1521 .unwrap(); 1522 } else { 1523 node.set_property_f32_vec(atom, Role::App, "text_color", vec![1., 1., 1., 1.]).unwrap(); 1524 } 1525 1526 let node = sn.lookup_node("/reset_btn_bg").unwrap(); 1527 node.set_property_bool(atom, Role::App, "is_visible", !setting.clone().is_default()).unwrap(); 1528 let node = sn.lookup_node("/reset_btn").unwrap(); 1529 node.set_property_bool(atom, Role::App, "is_active", !is_bool && !setting.clone().is_default()) 1530 .unwrap(); 1531 } 1532 1533 async fn update_setting( 1534 setting: Arc<Setting>, 1535 sn: SceneNodePtr, 1536 active_setting: Arc<Mutex<Option<Arc<Setting>>>>, 1537 editz_text: PropertyStr, 1538 ) { 1539 let atom = &mut PropertyAtomicGuard::new(); 1540 1541 if let Some(node) = sn.lookup_node("/value_editbox") { 1542 node.set_property_f32(atom, Role::App, "font_size", 0.).unwrap(); 1543 node.set_property_bool(atom, Role::App, "is_active", false).unwrap(); 1544 node.set_property_bool(atom, Role::App, "is_focused", false).unwrap(); 1545 } 1546 1547 match &setting.get_value() { 1548 PropertyValue::Uint32(_) => { 1549 let value_str = editz_text.get(); 1550 let parsed = value_str.parse::<u32>(); 1551 if let Ok(value) = parsed { 1552 if let Some(node) = sn.lookup_node("/value_label") { 1553 node.set_property_str(atom, Role::App, "text", value_str).unwrap(); 1554 } 1555 if let Some(node) = sn.lookup_node("/confirm_btn_bg") { 1556 node.set_property_bool(atom, Role::App, "is_visible", false).unwrap(); 1557 } 1558 setting.node.set_property_u32(atom, Role::User, "value", value).unwrap(); 1559 let mut active_setting_value = active_setting.lock().unwrap(); 1560 *active_setting_value = None; 1561 } 1562 } 1563 PropertyValue::Float32(_) => { 1564 let value_str = editz_text.get(); 1565 let parsed = value_str.parse::<f32>(); 1566 if let Ok(value) = parsed { 1567 if let Some(node) = sn.lookup_node("/value_label") { 1568 node.set_property_str(atom, Role::App, "text", value_str).unwrap(); 1569 } 1570 if let Some(node) = sn.lookup_node("/confirm_btn_bg") { 1571 node.set_property_bool(atom, Role::App, "is_visible", false).unwrap(); 1572 } 1573 setting.node.set_property_f32(atom, Role::User, "value", value).unwrap(); 1574 let mut active_setting_value = active_setting.lock().unwrap(); 1575 *active_setting_value = None; 1576 } 1577 } 1578 PropertyValue::Str(_) => { 1579 let value_str = editz_text.get(); 1580 if let Some(node) = sn.lookup_node("/value_label") { 1581 node.set_property_str(atom, Role::App, "text", &value_str).unwrap(); 1582 } 1583 if let Some(node) = sn.lookup_node("/confirm_btn_bg") { 1584 node.set_property_bool(atom, Role::App, "is_visible", false).unwrap(); 1585 } 1586 setting.node.set_property_str(atom, Role::User, "value", value_str).unwrap(); 1587 let mut active_setting_value = active_setting.lock().unwrap(); 1588 *active_setting_value = None; 1589 } 1590 _ => {} 1591 }; 1592 }