feat: implmented half working dynamic update system
This commit is contained in:
81
Cargo.lock
generated
81
Cargo.lock
generated
@@ -19,16 +19,16 @@ checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.11"
|
||||
version = "0.8.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"const-random",
|
||||
"getrandom",
|
||||
"getrandom 0.3.3",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
"zerocopy 0.8.26",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -556,7 +556,7 @@ version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"getrandom 0.2.15",
|
||||
"once_cell",
|
||||
"tiny-keccak",
|
||||
]
|
||||
@@ -1166,10 +1166,22 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasi 0.14.2+wasi-0.2.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.31.0"
|
||||
@@ -1273,7 +1285,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7a68216437ef68f0738e48d6c7bb9e6e6a92237e001b03d838314b068f33c94"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"getrandom",
|
||||
"getrandom 0.2.15",
|
||||
"grass_compiler",
|
||||
]
|
||||
|
||||
@@ -1444,6 +1456,7 @@ dependencies = [
|
||||
name = "iirhai"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"anyhow",
|
||||
"chrono",
|
||||
"colored",
|
||||
@@ -1679,7 +1692,7 @@ checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
@@ -1691,7 +1704,7 @@ checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec"
|
||||
dependencies = [
|
||||
"hermit-abi 0.3.9",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
@@ -1966,7 +1979,7 @@ version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
"zerocopy 0.7.35",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2066,6 +2079,12 @@ 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 = "rand"
|
||||
version = "0.8.5"
|
||||
@@ -2093,7 +2112,7 @@ version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"getrandom 0.2.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2807,6 +2826,15 @@ version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.14.2+wasi-0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.93"
|
||||
@@ -3118,6 +3146,15 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||
dependencies = [
|
||||
"bitflags 2.6.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x11"
|
||||
version = "2.21.0"
|
||||
@@ -3229,7 +3266,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
"zerocopy-derive 0.7.35",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
|
||||
dependencies = [
|
||||
"zerocopy-derive 0.8.26",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3243,6 +3289,17 @@ dependencies = [
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.87",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant"
|
||||
version = "3.15.2"
|
||||
|
||||
@@ -9,6 +9,7 @@ iirhai = { version = "0.1.0", path = "crates/iirhai" }
|
||||
notifier_host = { version = "0.1.0", path = "crates/notifier_host" }
|
||||
|
||||
anyhow = "1.0.86"
|
||||
ahash = "0.8.12"
|
||||
bincode = "1.3.3"
|
||||
bytesize = "2.0.1"
|
||||
cached = "0.53.1"
|
||||
|
||||
@@ -7,7 +7,11 @@ use crate::{
|
||||
paths::EwwPaths,
|
||||
widgets::window::Window,
|
||||
// dynval::DynVal,
|
||||
widgets::{build_widget::build_gtk_widget, build_widget::WidgetInput},
|
||||
widgets::{
|
||||
build_widget::build_gtk_widget,
|
||||
build_widget::WidgetInput,
|
||||
widget_definitions::WidgetRegistry,
|
||||
},
|
||||
window::{
|
||||
coords::Coords,
|
||||
monitor::MonitorIdentifier,
|
||||
@@ -23,7 +27,7 @@ use ewwii_shared_util::Span;
|
||||
use gdk::Monitor;
|
||||
use glib::ObjectExt;
|
||||
use gtk::{gdk, glib};
|
||||
use iirhai::widgetnode::WidgetNode;
|
||||
use iirhai::widgetnode::{WidgetNode, get_id_to_props_map};
|
||||
use itertools::Itertools;
|
||||
use once_cell::sync::Lazy;
|
||||
use rhai::{Dynamic, Scope};
|
||||
@@ -78,7 +82,6 @@ pub enum DaemonCommand {
|
||||
pub struct EwwiiWindow {
|
||||
pub name: String,
|
||||
pub gtk_window: Window,
|
||||
pub content_box: gtk::Box,
|
||||
pub destroy_event_handler_id: Option<glib::SignalHandlerId>,
|
||||
}
|
||||
|
||||
@@ -322,7 +325,15 @@ impl<B: DisplayBackend> App<B> {
|
||||
|
||||
let initiator = WindowInitiator::new(&window_def, window_args)?;
|
||||
|
||||
let root_widget = build_gtk_widget(WidgetInput::Window(window_def))?;
|
||||
/// Holds the id and the props of a widget
|
||||
/// It is crutual for supporting dynamic updates
|
||||
let mut widget_reg_store = WidgetRegistry::new();
|
||||
// note for future me: ^ this might need cloning.
|
||||
|
||||
let root_widget = build_gtk_widget(
|
||||
WidgetInput::Window(window_def),
|
||||
&mut widget_reg_store
|
||||
)?;
|
||||
|
||||
root_widget.style_context().add_class(window_name);
|
||||
|
||||
@@ -367,25 +378,32 @@ impl<B: DisplayBackend> App<B> {
|
||||
}
|
||||
}));
|
||||
|
||||
let window_for_task = ewwii_window.gtk_window.clone();
|
||||
let container_for_task = ewwii_window.content_box.clone();
|
||||
|
||||
update_receiver.attach(None, move |new_root_widget| {
|
||||
for child in container_for_task.children() {
|
||||
container_for_task.remove(&child);
|
||||
}
|
||||
let id_to_prop = get_id_to_props_map(&new_root_widget.expect(
|
||||
"Failed to hash id's and props"
|
||||
));
|
||||
|
||||
match new_root_widget {
|
||||
Ok(node) => {
|
||||
let gtk_widget: gtk::Widget =
|
||||
build_gtk_widget(WidgetInput::Node(node)).expect("Unable to create the gtk widget.");
|
||||
container_for_task.add(>k_widget);
|
||||
container_for_task.show_all();
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("Widget render failed: {:?}", err);
|
||||
}
|
||||
}
|
||||
println!("RECEIVED UPDATE!");
|
||||
|
||||
widget_reg_store.update_prop_changes(id_to_prop.expect(
|
||||
"Failed to update property changes."
|
||||
));
|
||||
|
||||
// for child in container_for_task.children() {
|
||||
// container_for_task.remove(&child);
|
||||
// }
|
||||
|
||||
// match new_root_widget {
|
||||
// Ok(node) => {
|
||||
// let gtk_widget: gtk::Widget =
|
||||
// build_gtk_widget(WidgetInput::Node(node)).expect("Unable to create the gtk widget.");
|
||||
// container_for_task.add(>k_widget);
|
||||
// container_for_task.show_all();
|
||||
// }
|
||||
// Err(err) => {
|
||||
// eprintln!("Widget render failed: {:?}", err);
|
||||
// }
|
||||
// }
|
||||
|
||||
glib::ControlFlow::Continue
|
||||
});
|
||||
@@ -503,23 +521,7 @@ fn initialize_window<B: DisplayBackend>(
|
||||
on_screen_changed(&window, None);
|
||||
window.connect_screen_changed(on_screen_changed);
|
||||
|
||||
// crate container that will be replaced on rerender
|
||||
|
||||
// !FIXME
|
||||
// The following is the layout of ewwii. Due to the usage of a container during creation,
|
||||
// the css styling has an issue where it doesnt apply correctly.
|
||||
// I have to removing it and add a smarter system to update the gtk widgets
|
||||
// instead of relying on replacing a container every time which resets the state.
|
||||
// GtkWindow
|
||||
// └── GtkBox (vertical)
|
||||
// └── GtkBox (centerbox for example, with window_name as class)
|
||||
|
||||
let container = gtk::Box::new(gtk::Orientation::Vertical, 0);
|
||||
container.set_hexpand(true);
|
||||
container.set_vexpand(true);
|
||||
|
||||
container.add(&root_widget);
|
||||
window.add(&container);
|
||||
window.add(&root_widget);
|
||||
|
||||
window.realize();
|
||||
|
||||
@@ -551,7 +553,11 @@ fn initialize_window<B: DisplayBackend>(
|
||||
|
||||
window.show_all();
|
||||
|
||||
Ok(EwwiiWindow { name: window_init.name.clone(), gtk_window: window, content_box: container, destroy_event_handler_id: None })
|
||||
Ok(EwwiiWindow {
|
||||
name: window_init.name.clone(),
|
||||
gtk_window: window,
|
||||
destroy_event_handler_id: None
|
||||
})
|
||||
}
|
||||
|
||||
async fn generate_new_widgetnode(all_vars: &HashMap<String, String>, code_path: &Path) -> Result<WidgetNode> {
|
||||
|
||||
@@ -1,19 +1,10 @@
|
||||
use anyhow::Result;
|
||||
// use codespan_reporting::diagnostic::Severity;
|
||||
// use ewwii_shared_util::{AttrName, Spanned};
|
||||
use gtk::{
|
||||
gdk::prelude::Cast,
|
||||
// prelude::{BoxExt, ContainerExt, WidgetExt},
|
||||
// Orientation,
|
||||
};
|
||||
// use maplit::hashmap;
|
||||
// use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||
|
||||
use crate::{
|
||||
config::WindowDefinition,
|
||||
// gen_diagnostic_macro,
|
||||
// error_handling_ctx,
|
||||
// dynval::DynVal,
|
||||
widgets::widget_definitions::*,
|
||||
};
|
||||
|
||||
@@ -27,42 +18,64 @@ pub enum WidgetInput {
|
||||
Window(WindowDefinition),
|
||||
}
|
||||
|
||||
pub fn build_gtk_widget(input: WidgetInput) -> Result<gtk::Widget> {
|
||||
pub fn build_gtk_widget(input: WidgetInput, widget_reg: &mut WidgetRegistry) -> Result<gtk::Widget> {
|
||||
let node = match input {
|
||||
WidgetInput::Node(n) => n,
|
||||
WidgetInput::Window(w) => w.root_widget,
|
||||
};
|
||||
build_gtk_widget_from_node(node)
|
||||
build_gtk_widget_from_node(node, widget_reg)
|
||||
}
|
||||
|
||||
// TODO: implement the commented lines
|
||||
fn build_gtk_widget_from_node(root_node: WidgetNode) -> Result<gtk::Widget> {
|
||||
fn build_gtk_widget_from_node(root_node: WidgetNode, widget_reg: &mut WidgetRegistry) -> Result<gtk::Widget> {
|
||||
let root_node2 = root_node.clone();
|
||||
let gtk_widget = match root_node {
|
||||
WidgetNode::Box { props, children } => build_gtk_box(props, children)?.upcast(),
|
||||
WidgetNode::CenterBox { props, children } => build_center_box(props, children)?.upcast(),
|
||||
WidgetNode::EventBox { props, children } => build_gtk_event_box(props, children)?.upcast(),
|
||||
WidgetNode::ToolTip { children } => build_tooltip(children)?.upcast(),
|
||||
WidgetNode::CircularProgress { props } => build_circular_progress_bar(props)?.upcast(),
|
||||
WidgetNode::Graph { props } => build_graph(props)?.upcast(),
|
||||
WidgetNode::Transform { props } => build_transform(props)?.upcast(),
|
||||
WidgetNode::Slider { props } => build_gtk_scale(props)?.upcast(),
|
||||
WidgetNode::Progress { props } => build_gtk_progress(props)?.upcast(),
|
||||
WidgetNode::Image { props } => build_gtk_image(props)?.upcast(),
|
||||
WidgetNode::Button { props } => build_gtk_button(props)?.upcast(),
|
||||
WidgetNode::Label { props } => build_gtk_label(props)?.upcast(),
|
||||
WidgetNode::Box { props, children } =>
|
||||
build_gtk_box(props, children, widget_reg)?.upcast(),
|
||||
WidgetNode::CenterBox { props, children } =>
|
||||
build_center_box(props, children, widget_reg)?.upcast(),
|
||||
WidgetNode::EventBox { props, children } =>
|
||||
build_gtk_event_box(props, children, widget_reg)?.upcast(),
|
||||
WidgetNode::ToolTip { children } => build_tooltip(children, widget_reg)?.upcast(),
|
||||
WidgetNode::CircularProgress { props } =>
|
||||
build_circular_progress_bar(props, widget_reg)?.upcast(),
|
||||
WidgetNode::Graph { props } =>
|
||||
build_graph(props, widget_reg)?.upcast(),
|
||||
WidgetNode::Transform { props } =>
|
||||
build_transform(props, widget_reg)?.upcast(),
|
||||
WidgetNode::Slider { props } =>
|
||||
build_gtk_scale(props, widget_reg)?.upcast(),
|
||||
WidgetNode::Progress { props } =>
|
||||
build_gtk_progress(props, widget_reg)?.upcast(),
|
||||
WidgetNode::Image { props } =>
|
||||
build_gtk_image(props, widget_reg)?.upcast(),
|
||||
WidgetNode::Button { props } =>
|
||||
build_gtk_button(props, widget_reg)?.upcast(),
|
||||
WidgetNode::Label { props } =>
|
||||
build_gtk_label(props, widget_reg)?.upcast(),
|
||||
// WIDGET_NAME_LITERAL => build_gtk_literal(node)?.upcast(),
|
||||
WidgetNode::Input { props } => build_gtk_input(props)?.upcast(),
|
||||
WidgetNode::Calendar { props } => build_gtk_calendar(props)?.upcast(),
|
||||
WidgetNode::ColorButton { props } => build_gtk_color_button(props)?.upcast(),
|
||||
WidgetNode::Expander { props, children } => build_gtk_expander(props, children)?.upcast(),
|
||||
WidgetNode::ColorChooser { props } => build_gtk_color_chooser(props)?.upcast(),
|
||||
WidgetNode::ComboBoxText { props } => build_gtk_combo_box_text(props)?.upcast(),
|
||||
WidgetNode::Checkbox { props } => build_gtk_checkbox(props)?.upcast(),
|
||||
WidgetNode::Revealer { props, children } => build_gtk_revealer(props, children)?.upcast(),
|
||||
WidgetNode::Scroll { props, children } => build_gtk_scrolledwindow(props, children)?.upcast(),
|
||||
WidgetNode::OverLay { children } => build_gtk_overlay(children)?.upcast(),
|
||||
WidgetNode::Stack { props, children } => build_gtk_stack(props, children)?.upcast(),
|
||||
WidgetNode::Input { props } =>
|
||||
build_gtk_input(props, widget_reg)?.upcast(),
|
||||
WidgetNode::Calendar { props } =>
|
||||
build_gtk_calendar(props, widget_reg)?.upcast(),
|
||||
WidgetNode::ColorButton { props } =>
|
||||
build_gtk_color_button(props, widget_reg)?.upcast(),
|
||||
WidgetNode::Expander { props, children } =>
|
||||
build_gtk_expander(props, children, widget_reg)?.upcast(),
|
||||
WidgetNode::ColorChooser { props } =>
|
||||
build_gtk_color_chooser(props, widget_reg)?.upcast(),
|
||||
WidgetNode::ComboBoxText { props } =>
|
||||
build_gtk_combo_box_text(props, widget_reg)?.upcast(),
|
||||
WidgetNode::Checkbox { props } =>
|
||||
build_gtk_checkbox(props, widget_reg)?.upcast(),
|
||||
WidgetNode::Revealer { props, children } =>
|
||||
build_gtk_revealer(props, children, widget_reg)?.upcast(),
|
||||
WidgetNode::Scroll { props, children } =>
|
||||
build_gtk_scrolledwindow(props, children, widget_reg)?.upcast(),
|
||||
WidgetNode::OverLay { children } =>
|
||||
build_gtk_overlay(children, widget_reg)?.upcast(),
|
||||
WidgetNode::Stack { props, children } =>
|
||||
build_gtk_stack(props, children, widget_reg)?.upcast(),
|
||||
// WIDGET_NAME_SYSTRAY => build_systray(node)?.upcast(),
|
||||
unknown => {
|
||||
return Err(anyhow::anyhow!("Cannot build GTK widget from node: {:?}", unknown));
|
||||
|
||||
@@ -8,7 +8,7 @@ use anyhow::{anyhow, bail, Result};
|
||||
use gdk::{ModifierType, NotifyType};
|
||||
use gtk::{self, prelude::*, DestDefaults, TargetEntry, TargetList};
|
||||
use gtk::{gdk, glib, pango};
|
||||
use iirhai::widgetnode::WidgetNode;
|
||||
use iirhai::widgetnode::{WidgetNode, hash_props_and_type};
|
||||
use once_cell::sync::Lazy;
|
||||
use rhai::Map;
|
||||
|
||||
@@ -20,6 +20,7 @@ use std::{
|
||||
collections::HashSet,
|
||||
rc::Rc,
|
||||
time::Duration,
|
||||
collections::HashMap,
|
||||
};
|
||||
|
||||
// custom widgets
|
||||
@@ -49,7 +50,49 @@ macro_rules! connect_signal_handler {
|
||||
}};
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_box(props: Map, children: Vec<WidgetNode>) -> Result<gtk::Box> {
|
||||
pub type UpdateFn = Box<dyn Fn(&Map)>;
|
||||
|
||||
pub struct WidgetEntry {
|
||||
// pub widget: gtk::Widget, // not needed now
|
||||
pub update_fn: UpdateFn,
|
||||
}
|
||||
|
||||
pub struct WidgetRegistry {
|
||||
widgets: HashMap<u64, WidgetEntry>,
|
||||
}
|
||||
|
||||
impl WidgetRegistry {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
widgets: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_prop_changes(&self, id_to_props: HashMap<u64, Map>) {
|
||||
println!("--- id_to_props keys ---");
|
||||
for id in id_to_props.keys() {
|
||||
println!("{}", id);
|
||||
}
|
||||
|
||||
println!("--- self.widgets keys ---");
|
||||
for id in self.widgets.keys() {
|
||||
println!("{}", id);
|
||||
}
|
||||
|
||||
println!("--- processing update ids ---");
|
||||
for (id, props) in id_to_props {
|
||||
if let Some(entry) = self.widgets.get(&id) {
|
||||
println!("Updating widget id: {}", id);
|
||||
(entry.update_fn)(&props);
|
||||
} else {
|
||||
println!("Warning: id {} not found in widget_registry", id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_box(props: Map, children: Vec<WidgetNode>, widget_registry: &mut WidgetRegistry) -> Result<gtk::Box> {
|
||||
// Parse initial props to create the widget:
|
||||
let orientation = props
|
||||
.get("orientation")
|
||||
.and_then(|v| v.clone().try_cast::<String>())
|
||||
@@ -65,14 +108,39 @@ pub(super) fn build_gtk_box(props: Map, children: Vec<WidgetNode>) -> Result<gtk
|
||||
gtk_widget.set_homogeneous(space_evenly);
|
||||
|
||||
for child in children {
|
||||
let child_widget = build_gtk_widget(WidgetInput::Node(child))?;
|
||||
let child_widget = build_gtk_widget(WidgetInput::Node(child), widget_registry)?;
|
||||
gtk_widget.add(&child_widget);
|
||||
}
|
||||
|
||||
let gtk_widget_clone = gtk_widget.clone();
|
||||
|
||||
let update_fn: UpdateFn = Box::new(move |props: &Map| {
|
||||
if let Some(orientation_str) = props.get("orientation").and_then(|v| v.clone().try_cast::<String>()) {
|
||||
if let Ok(orientation) = parse_orientation(&orientation_str) {
|
||||
gtk_widget_clone.set_orientation(orientation);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(spacing_val) = props.get("spacing").and_then(|v| v.clone().try_cast::<i64>()) {
|
||||
gtk_widget_clone.set_spacing(spacing_val as i32);
|
||||
}
|
||||
|
||||
if let Ok(space_evenly) = get_bool_prop(props, "space_evenly", Some(true)) {
|
||||
gtk_widget_clone.set_homogeneous(space_evenly);
|
||||
}
|
||||
});
|
||||
|
||||
let id = hash_props_and_type(&props, "Box");
|
||||
|
||||
widget_registry.widgets.insert(id, WidgetEntry {
|
||||
// widget: gtk_widget.upcast(),
|
||||
update_fn,
|
||||
});
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_overlay(children: Vec<WidgetNode>) -> Result<gtk::Overlay> {
|
||||
pub(super) fn build_gtk_overlay(children: Vec<WidgetNode>, widget_registry: &mut WidgetRegistry) -> Result<gtk::Overlay> {
|
||||
let gtk_widget = gtk::Overlay::new();
|
||||
|
||||
let count = children.len();
|
||||
@@ -81,7 +149,7 @@ pub(super) fn build_gtk_overlay(children: Vec<WidgetNode>) -> Result<gtk::Overla
|
||||
bail!("overlay must contain at least one element");
|
||||
}
|
||||
|
||||
let mut children = children.into_iter().map(|child| build_gtk_widget(WidgetInput::Node(child)));
|
||||
let mut children = children.into_iter().map(|child| build_gtk_widget(WidgetInput::Node(child), widget_registry));
|
||||
|
||||
// we have more than one child, we can unwrap
|
||||
let first = children.next().unwrap()?;
|
||||
@@ -96,7 +164,7 @@ pub(super) fn build_gtk_overlay(children: Vec<WidgetNode>) -> Result<gtk::Overla
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_tooltip(children: Vec<WidgetNode>) -> Result<gtk::Box> {
|
||||
pub(super) fn build_tooltip(children: Vec<WidgetNode>, widget_registry: &mut WidgetRegistry) -> Result<gtk::Box> {
|
||||
let gtk_widget = gtk::Box::new(gtk::Orientation::Horizontal, 0);
|
||||
gtk_widget.set_has_tooltip(true);
|
||||
|
||||
@@ -112,14 +180,14 @@ pub(super) fn build_tooltip(children: Vec<WidgetNode>) -> Result<gtk::Box> {
|
||||
let content_node = children.get(1).cloned().ok_or_else(|| anyhow!("missing content"))?;
|
||||
|
||||
// The visible child immediately
|
||||
let content_widget = build_gtk_widget(WidgetInput::Node(content_node))?;
|
||||
let content_widget = build_gtk_widget(WidgetInput::Node(content_node), widget_registry)?;
|
||||
gtk_widget.add(&content_widget);
|
||||
|
||||
let tooltip_node = Rc::new(tooltip_node);
|
||||
let tooltip_widget = build_gtk_widget(WidgetInput::Node(Rc::clone(&tooltip_node).as_ref().clone()), widget_registry)
|
||||
.expect("Failed to build tooltip widget");
|
||||
|
||||
gtk_widget.connect_query_tooltip(move |_widget, _x, _y, _keyboard_mode, tooltip| {
|
||||
let tooltip_widget = build_gtk_widget(WidgetInput::Node(Rc::clone(&tooltip_node).as_ref().clone()))
|
||||
.expect("Failed to build tooltip widget");
|
||||
tooltip.set_custom(Some(&tooltip_widget));
|
||||
true
|
||||
});
|
||||
@@ -127,7 +195,7 @@ pub(super) fn build_tooltip(children: Vec<WidgetNode>) -> Result<gtk::Box> {
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_center_box(props: Map, children: Vec<WidgetNode>) -> Result<gtk::Box> {
|
||||
pub(super) fn build_center_box(props: Map, children: Vec<WidgetNode>, widget_registry: &mut WidgetRegistry) -> Result<gtk::Box> {
|
||||
let orientation = props
|
||||
.get("orientation")
|
||||
.and_then(|v| v.clone().try_cast::<String>())
|
||||
@@ -143,9 +211,9 @@ pub(super) fn build_center_box(props: Map, children: Vec<WidgetNode>) -> Result<
|
||||
bail!("centerbox must contain exactly 3 children, but got more");
|
||||
}
|
||||
|
||||
let first = build_gtk_widget(WidgetInput::Node(children.get(0).cloned().ok_or_else(|| anyhow!("missing child 0"))?))?;
|
||||
let center = build_gtk_widget(WidgetInput::Node(children.get(1).cloned().ok_or_else(|| anyhow!("missing child 1"))?))?;
|
||||
let end = build_gtk_widget(WidgetInput::Node(children.get(2).cloned().ok_or_else(|| anyhow!("missing child 2"))?))?;
|
||||
let first = build_gtk_widget(WidgetInput::Node(children.get(0).cloned().ok_or_else(|| anyhow!("missing child 0"))?), widget_registry)?;
|
||||
let center = build_gtk_widget(WidgetInput::Node(children.get(1).cloned().ok_or_else(|| anyhow!("missing child 1"))?), widget_registry)?;
|
||||
let end = build_gtk_widget(WidgetInput::Node(children.get(2).cloned().ok_or_else(|| anyhow!("missing child 2"))?), widget_registry)?;
|
||||
|
||||
let gtk_widget = gtk::Box::new(orientation, 0);
|
||||
gtk_widget.pack_start(&first, true, true, 0);
|
||||
@@ -156,10 +224,35 @@ pub(super) fn build_center_box(props: Map, children: Vec<WidgetNode>) -> Result<
|
||||
center.show();
|
||||
end.show();
|
||||
|
||||
let gtk_widget_clone = gtk_widget.clone();
|
||||
|
||||
let update_fn: UpdateFn = Box::new(move |props: &Map| {
|
||||
let orientation = match props
|
||||
.get("orientation")
|
||||
.and_then(|v| v.clone().try_cast::<String>())
|
||||
.map(|s| parse_orientation(&s))
|
||||
.transpose() {
|
||||
Ok(opt) => opt.unwrap_or(gtk::Orientation::Horizontal),
|
||||
Err(e) => {
|
||||
eprintln!("Error parsing orientation: {:?}", e);
|
||||
gtk::Orientation::Horizontal
|
||||
}
|
||||
};
|
||||
|
||||
gtk_widget_clone.set_orientation(orientation);
|
||||
});
|
||||
|
||||
|
||||
let id = hash_props_and_type(&props, "CenterBox");
|
||||
|
||||
widget_registry.widgets.insert(id, WidgetEntry {
|
||||
update_fn
|
||||
});
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_event_box(props: Map, children: Vec<WidgetNode>) -> Result<gtk::EventBox> {
|
||||
pub(super) fn build_gtk_event_box(props: Map, children: Vec<WidgetNode>, widget_registry: &mut WidgetRegistry) -> Result<gtk::EventBox> {
|
||||
let gtk_widget = gtk::EventBox::new();
|
||||
|
||||
// Support :hover selector
|
||||
@@ -353,21 +446,23 @@ pub(super) fn build_gtk_event_box(props: Map, children: Vec<WidgetNode>) -> Resu
|
||||
}
|
||||
|
||||
let child = children.get(0).cloned().ok_or_else(|| anyhow!("missing child 0"))?;
|
||||
let child_widget = build_gtk_widget(WidgetInput::Node(child))?;
|
||||
let child_widget = build_gtk_widget(WidgetInput::Node(child), widget_registry)?;
|
||||
gtk_widget.add(&child_widget);
|
||||
child_widget.show();
|
||||
|
||||
let id = hash_props_and_type(&props, "EventBox");
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_stack(props: Map, children: Vec<WidgetNode>) -> Result<gtk::Stack> {
|
||||
pub(super) fn build_gtk_stack(props: Map, children: Vec<WidgetNode>, widget_registry: &mut WidgetRegistry) -> Result<gtk::Stack> {
|
||||
let gtk_widget = gtk::Stack::new();
|
||||
|
||||
if children.is_empty() {
|
||||
return Err(anyhow!("stack must contain at least one element"));
|
||||
}
|
||||
|
||||
let children = children.into_iter().map(|child| build_gtk_widget(WidgetInput::Node(child)));
|
||||
let children = children.into_iter().map(|child| build_gtk_widget(WidgetInput::Node(child), widget_registry));
|
||||
|
||||
for (i, child) in children.enumerate() {
|
||||
let child = child?;
|
||||
@@ -386,10 +481,12 @@ pub(super) fn build_gtk_stack(props: Map, children: Vec<WidgetNode>) -> Result<g
|
||||
let same_size = get_bool_prop(&props, "same_size", Some(false))?;
|
||||
gtk_widget.set_homogeneous(same_size);
|
||||
|
||||
let id = hash_props_and_type(&props, "Stack");
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_transform(props: Map) -> Result<Transform> {
|
||||
pub(super) fn build_transform(props: Map, widget_registry: &mut WidgetRegistry) -> Result<Transform> {
|
||||
let widget = Transform::new();
|
||||
|
||||
// rotate - the percentage to rotate
|
||||
@@ -427,10 +524,12 @@ pub(super) fn build_transform(props: Map) -> Result<Transform> {
|
||||
widget.set_property("scale-y", scale_y);
|
||||
}
|
||||
|
||||
let id = hash_props_and_type(&props, "Transform");
|
||||
|
||||
Ok(widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_circular_progress_bar(props: Map) -> Result<CircProg> {
|
||||
pub(super) fn build_circular_progress_bar(props: Map, widget_registry: &mut WidgetRegistry) -> Result<CircProg> {
|
||||
let widget = CircProg::new();
|
||||
|
||||
if let Ok(value) = get_f64_prop(&props, "value", None) {
|
||||
@@ -449,10 +548,12 @@ pub(super) fn build_circular_progress_bar(props: Map) -> Result<CircProg> {
|
||||
widget.set_property("clockwise", clockwise);
|
||||
}
|
||||
|
||||
let id = hash_props_and_type(&props, "CircularProgressBar");
|
||||
|
||||
Ok(widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_graph(props: Map) -> Result<super::graph::Graph> {
|
||||
pub(super) fn build_graph(props: Map, widget_registry: &mut WidgetRegistry) -> Result<super::graph::Graph> {
|
||||
let widget = super::graph::Graph::new();
|
||||
|
||||
if let Ok(value) = get_f64_prop(&props, "value", None) {
|
||||
@@ -510,10 +611,12 @@ pub(super) fn build_graph(props: Map) -> Result<super::graph::Graph> {
|
||||
widget.set_property("vertical", vertical);
|
||||
}
|
||||
|
||||
let id = hash_props_and_type(&props, "Graph");
|
||||
|
||||
Ok(widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_progress(props: Map) -> Result<gtk::ProgressBar> {
|
||||
pub(super) fn build_gtk_progress(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::ProgressBar> {
|
||||
let gtk_widget = gtk::ProgressBar::new();
|
||||
|
||||
let orientation = props
|
||||
@@ -532,10 +635,13 @@ pub(super) fn build_gtk_progress(props: Map) -> Result<gtk::ProgressBar> {
|
||||
if let Ok(bar_value) = get_f64_prop(&props, "value", None) {
|
||||
gtk_widget.set_fraction(bar_value / 100f64)
|
||||
}
|
||||
|
||||
let id = hash_props_and_type(&props, "Progress");
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_image(props: Map) -> Result<gtk::Image> {
|
||||
pub(super) fn build_gtk_image(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::Image> {
|
||||
let gtk_widget = gtk::Image::new();
|
||||
|
||||
let path = get_string_prop(&props, "path", None)?;
|
||||
@@ -590,10 +696,12 @@ pub(super) fn build_gtk_image(props: Map) -> Result<gtk::Image> {
|
||||
return Ok(gtk_widget);
|
||||
}
|
||||
|
||||
let id = hash_props_and_type(&props, "Image");
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_button(props: Map) -> Result<gtk::Button> {
|
||||
pub(super) fn build_gtk_button(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::Button> {
|
||||
let gtk_widget = gtk::Button::new();
|
||||
|
||||
let timeout = get_duration_prop(&props, "timeout", Some(Duration::from_millis(200)))?;
|
||||
@@ -644,10 +752,12 @@ pub(super) fn build_gtk_button(props: Map) -> Result<gtk::Button> {
|
||||
gtk_widget.set_label(&button_label);
|
||||
}
|
||||
|
||||
let id = hash_props_and_type(&props, "Button");
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_label(props: Map) -> Result<gtk::Label> {
|
||||
pub(super) fn build_gtk_label(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::Label> {
|
||||
let gtk_widget = gtk::Label::new(None);
|
||||
|
||||
let truncate = get_bool_prop(&props, "truncate", Some(false))?;
|
||||
@@ -735,10 +845,12 @@ pub(super) fn build_gtk_label(props: Map) -> Result<gtk::Label> {
|
||||
gtk_widget.set_lines(lines);
|
||||
}
|
||||
|
||||
let id = hash_props_and_type(&props, "Label");
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_input(props: Map) -> Result<gtk::Entry> {
|
||||
pub(super) fn build_gtk_input(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::Entry> {
|
||||
let gtk_widget = gtk::Entry::new();
|
||||
|
||||
if let Ok(value) = get_string_prop(&props, "value", None) {
|
||||
@@ -768,10 +880,12 @@ pub(super) fn build_gtk_input(props: Map) -> Result<gtk::Entry> {
|
||||
let password: bool = get_bool_prop(&props, "password", Some(false))?;
|
||||
gtk_widget.set_visibility(!password);
|
||||
|
||||
let id = hash_props_and_type(&props, "Input");
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_calendar(props: Map) -> Result<gtk::Calendar> {
|
||||
pub(super) fn build_gtk_calendar(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::Calendar> {
|
||||
let gtk_widget = gtk::Calendar::new();
|
||||
|
||||
// day - the selected day
|
||||
@@ -828,10 +942,12 @@ pub(super) fn build_gtk_calendar(props: Map) -> Result<gtk::Calendar> {
|
||||
);
|
||||
}
|
||||
|
||||
let id = hash_props_and_type(&props, "Calendar");
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_combo_box_text(props: Map) -> Result<gtk::ComboBoxText> {
|
||||
pub(super) fn build_gtk_combo_box_text(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::ComboBoxText> {
|
||||
let gtk_widget = gtk::ComboBoxText::new();
|
||||
|
||||
if let Ok(items) = get_vec_string_prop(&props, "items", None) {
|
||||
@@ -851,10 +967,12 @@ pub(super) fn build_gtk_combo_box_text(props: Map) -> Result<gtk::ComboBoxText>
|
||||
})
|
||||
);
|
||||
|
||||
let id = hash_props_and_type(&props, "ComboBoxText");
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_expander(props: Map, children: Vec<WidgetNode>) -> Result<gtk::Expander> {
|
||||
pub(super) fn build_gtk_expander(props: Map, children: Vec<WidgetNode>, widget_registry: &mut WidgetRegistry) -> Result<gtk::Expander> {
|
||||
let gtk_widget = gtk::Expander::new(None);
|
||||
|
||||
let count = children.len();
|
||||
@@ -866,7 +984,7 @@ pub(super) fn build_gtk_expander(props: Map, children: Vec<WidgetNode>) -> Resul
|
||||
}
|
||||
|
||||
let child = children.get(0).cloned().ok_or_else(|| anyhow!("missing child 0"))?;
|
||||
let child_widget = build_gtk_widget(WidgetInput::Node(child))?;
|
||||
let child_widget = build_gtk_widget(WidgetInput::Node(child), widget_registry)?;
|
||||
gtk_widget.add(&child_widget);
|
||||
child_widget.show();
|
||||
|
||||
@@ -878,10 +996,12 @@ pub(super) fn build_gtk_expander(props: Map, children: Vec<WidgetNode>) -> Resul
|
||||
gtk_widget.set_expanded(expanded);
|
||||
}
|
||||
|
||||
let id = hash_props_and_type(&props, "Expander");
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_revealer(props: Map, children: Vec<WidgetNode>) -> Result<gtk::Revealer> {
|
||||
pub(super) fn build_gtk_revealer(props: Map, children: Vec<WidgetNode>, widget_registry: &mut WidgetRegistry) -> Result<gtk::Revealer> {
|
||||
let gtk_widget = gtk::Revealer::new();
|
||||
|
||||
let transition = get_string_prop(&props, "transition", Some("crossfade"))?;
|
||||
@@ -898,7 +1018,7 @@ pub(super) fn build_gtk_revealer(props: Map, children: Vec<WidgetNode>) -> Resul
|
||||
match children.len() {
|
||||
0 => { /* maybe warn? */ }
|
||||
1 => {
|
||||
let child_widget = build_gtk_widget(WidgetInput::Node(children[0].clone()))?;
|
||||
let child_widget = build_gtk_widget(WidgetInput::Node(children[0].clone()), widget_registry)?;
|
||||
gtk_widget.set_child(Some(&child_widget));
|
||||
}
|
||||
n => {
|
||||
@@ -906,10 +1026,12 @@ pub(super) fn build_gtk_revealer(props: Map, children: Vec<WidgetNode>) -> Resul
|
||||
}
|
||||
}
|
||||
|
||||
let id = hash_props_and_type(&props, "Revealer");
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_checkbox(props: Map) -> Result<gtk::CheckButton> {
|
||||
pub(super) fn build_gtk_checkbox(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::CheckButton> {
|
||||
let gtk_widget = gtk::CheckButton::new();
|
||||
|
||||
let checked = get_bool_prop(&props, "checked", Some(false))?;
|
||||
@@ -926,10 +1048,12 @@ pub(super) fn build_gtk_checkbox(props: Map) -> Result<gtk::CheckButton> {
|
||||
})
|
||||
);
|
||||
|
||||
let id = hash_props_and_type(&props, "Checkbox");
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_color_button(props: Map) -> Result<gtk::ColorButton> {
|
||||
pub(super) fn build_gtk_color_button(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::ColorButton> {
|
||||
let gtk_widget = gtk::ColorButton::builder().build();
|
||||
|
||||
// use-alpha - bool to wether or not use alpha
|
||||
@@ -950,10 +1074,12 @@ pub(super) fn build_gtk_color_button(props: Map) -> Result<gtk::ColorButton> {
|
||||
);
|
||||
}
|
||||
|
||||
let id = hash_props_and_type(&props, "ColorButton");
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_color_chooser(props: Map) -> Result<gtk::ColorChooserWidget> {
|
||||
pub(super) fn build_gtk_color_chooser(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::ColorChooserWidget> {
|
||||
let gtk_widget = gtk::ColorChooserWidget::new();
|
||||
|
||||
// use-alpha - bool to wether or not use alpha
|
||||
@@ -974,10 +1100,12 @@ pub(super) fn build_gtk_color_chooser(props: Map) -> Result<gtk::ColorChooserWid
|
||||
);
|
||||
}
|
||||
|
||||
let id = hash_props_and_type(&props, "ColorChooser");
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_scale(props: Map) -> Result<gtk::Scale> {
|
||||
pub(super) fn build_gtk_scale(props: Map, widget_registry: &mut WidgetRegistry) -> Result<gtk::Scale> {
|
||||
let gtk_widget = gtk::Scale::new(gtk::Orientation::Horizontal, Some(>k::Adjustment::new(0.0, 0.0, 100.0, 1.0, 1.0, 1.0)));
|
||||
|
||||
let flipped = get_bool_prop(&props, "flipped", Some(false))?;
|
||||
@@ -1002,10 +1130,12 @@ pub(super) fn build_gtk_scale(props: Map) -> Result<gtk::Scale> {
|
||||
|
||||
resolve_range_attrs(&props, gtk_widget.upcast_ref::<gtk::Range>())?;
|
||||
|
||||
let id = hash_props_and_type(&props, "Scale");
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
pub(super) fn build_gtk_scrolledwindow(props: Map, children: Vec<WidgetNode>) -> Result<gtk::ScrolledWindow> {
|
||||
pub(super) fn build_gtk_scrolledwindow(props: Map, children: Vec<WidgetNode>, widget_registry: &mut WidgetRegistry) -> Result<gtk::ScrolledWindow> {
|
||||
// I don't have single idea of what those two generics are supposed to be, but this works.
|
||||
let gtk_widget = gtk::ScrolledWindow::new(None::<>k::Adjustment>, None::<>k::Adjustment>);
|
||||
|
||||
@@ -1026,10 +1156,12 @@ pub(super) fn build_gtk_scrolledwindow(props: Map, children: Vec<WidgetNode>) ->
|
||||
}
|
||||
|
||||
let child = children.get(0).cloned().ok_or_else(|| anyhow!("missing child 0"))?;
|
||||
let child_widget = build_gtk_widget(WidgetInput::Node(child))?;
|
||||
let child_widget = build_gtk_widget(WidgetInput::Node(child), widget_registry)?;
|
||||
gtk_widget.add(&child_widget);
|
||||
child_widget.show();
|
||||
|
||||
let id = hash_props_and_type(&props, "ScrolledWindow");
|
||||
|
||||
Ok(gtk_widget)
|
||||
}
|
||||
|
||||
|
||||
@@ -24,3 +24,4 @@ serde = { workspace = true, features = ["derive"] }
|
||||
chrono.workspace = true
|
||||
textwrap.workspace =true
|
||||
termsize.workspace = true
|
||||
ahash.workspace = true
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
pub trait IterAverage {
|
||||
fn avg(self) -> f32;
|
||||
}
|
||||
//! Used by buildin_signals.rs
|
||||
//! it is currently removed as buildtin_singals.rs is empty
|
||||
// pub trait IterAverage {
|
||||
// fn avg(self) -> f32;
|
||||
// }
|
||||
|
||||
impl<I: Iterator<Item = f32>> IterAverage for I {
|
||||
fn avg(self) -> f32 {
|
||||
let mut total = 0f32;
|
||||
let mut cnt = 0f32;
|
||||
for value in self {
|
||||
total += value;
|
||||
cnt += 1f32;
|
||||
}
|
||||
total / cnt
|
||||
}
|
||||
}
|
||||
// impl<I: Iterator<Item = f32>> IterAverage for I {
|
||||
// fn avg(self) -> f32 {
|
||||
// let mut total = 0f32;
|
||||
// let mut cnt = 0f32;
|
||||
// for value in self {
|
||||
// total += value;
|
||||
// cnt += 1f32;
|
||||
// }
|
||||
// total / cnt
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
use rhai::Map;
|
||||
use rhai::{Dynamic, Map, Array};
|
||||
use anyhow::{Result, bail};
|
||||
use ahash::AHasher;
|
||||
use std::hash::{Hasher, Hash};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum WidgetNode {
|
||||
@@ -34,3 +38,126 @@ pub enum WidgetNode {
|
||||
Listen { var: String, props: Map },
|
||||
Enter(Vec<WidgetNode>),
|
||||
}
|
||||
|
||||
pub fn get_id_to_props_map(root_node: &WidgetNode) -> Result<HashMap<u64, Map>> {
|
||||
let mut id_to_props = HashMap::new();
|
||||
|
||||
let mut insert_props = |props: &Map, widget_type: &str| -> Result<()> {
|
||||
let id = hash_props_and_type(props, widget_type);
|
||||
id_to_props.insert(id, props.clone());
|
||||
Ok(())
|
||||
};
|
||||
|
||||
match root_node {
|
||||
WidgetNode::Box { props, .. } => insert_props(props, "Box")?,
|
||||
WidgetNode::CenterBox { props, .. } => insert_props(props, "CenterBox")?,
|
||||
WidgetNode::EventBox { props, .. } => insert_props(props, "EventBox")?,
|
||||
WidgetNode::CircularProgress { props } => insert_props(props, "CircularProgress")?,
|
||||
WidgetNode::Graph { props } => insert_props(props, "Graph")?,
|
||||
WidgetNode::Transform { props } => insert_props(props, "Transform")?,
|
||||
WidgetNode::Slider { props } => insert_props(props, "Slider")?,
|
||||
WidgetNode::Progress { props } => insert_props(props, "Progress")?,
|
||||
WidgetNode::Image { props } => insert_props(props, "Image")?,
|
||||
WidgetNode::Button { props } => insert_props(props, "Button")?,
|
||||
WidgetNode::Label { props } => insert_props(props, "Label")?,
|
||||
WidgetNode::Input { props } => insert_props(props, "Input")?,
|
||||
WidgetNode::Calendar { props } => insert_props(props, "Calendar")?,
|
||||
WidgetNode::ColorButton { props } => insert_props(props, "ColorButton")?,
|
||||
WidgetNode::Expander { props, .. } => insert_props(props, "Expander")?,
|
||||
WidgetNode::ColorChooser { props } => insert_props(props, "ColorChooser")?,
|
||||
WidgetNode::ComboBoxText { props } => insert_props(props, "ComboBoxText")?,
|
||||
WidgetNode::Checkbox { props } => insert_props(props, "Checkbox")?,
|
||||
WidgetNode::Revealer { props, .. } => insert_props(props, "Revealer")?,
|
||||
WidgetNode::Scroll { props, .. } => insert_props(props, "Scroll")?,
|
||||
WidgetNode::Stack { props, .. } => insert_props(props, "Stack")?,
|
||||
_ => {
|
||||
// do nothing for now ig?
|
||||
}
|
||||
}
|
||||
|
||||
Ok(id_to_props)
|
||||
}
|
||||
|
||||
pub fn hash_props_and_type(props: &Map, widget_type_str: &str) -> u64 {
|
||||
let mut hasher = AHasher::default();
|
||||
|
||||
widget_type_str.hash(&mut hasher);
|
||||
|
||||
let mut kv_pairs: Vec<_> = props.iter().collect();
|
||||
kv_pairs.sort_by_key(|(k, _)| k.clone());
|
||||
|
||||
for (k, v) in kv_pairs {
|
||||
k.hash(&mut hasher);
|
||||
let val_str = serialize_value(v);
|
||||
val_str.hash(&mut hasher);
|
||||
}
|
||||
|
||||
hasher.finish()
|
||||
}
|
||||
|
||||
fn serialize_value(value: &Dynamic) -> String {
|
||||
if value.is::<String>() {
|
||||
value.clone_cast::<String>()
|
||||
} else if value.is::<bool>() {
|
||||
value.clone_cast::<bool>().to_string()
|
||||
} else if value.is::<i64>() {
|
||||
value.clone_cast::<i64>().to_string()
|
||||
} else if value.is::<f64>() {
|
||||
value.clone_cast::<f64>().to_string()
|
||||
} else if value.is::<Array>() {
|
||||
let arr = value.clone_cast::<Array>();
|
||||
let serialized_items: Vec<String> = arr.iter().map(|v| serialize_value(v)).collect();
|
||||
format!("[{}]", serialized_items.join(","))
|
||||
} else if value.is::<Map>() {
|
||||
let map = value.clone_cast::<Map>();
|
||||
let mut kvs: Vec<_> = map.iter().collect();
|
||||
kvs.sort_by_key(|(k, _)| k.clone());
|
||||
let serialized_pairs: Vec<String> = kvs.iter()
|
||||
.map(|(k, v)| format!("{}:{}", k, serialize_value(v)))
|
||||
.collect();
|
||||
format!("{{{}}}", serialized_pairs.join(","))
|
||||
} else {
|
||||
format!("{:?}", value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rhai::{Map, Dynamic};
|
||||
|
||||
#[test]
|
||||
fn test_hash_props_and_type_consistency() {
|
||||
let mut props = Map::new();
|
||||
props.insert("class".into(), Dynamic::from("mywidget"));
|
||||
props.insert("enabled".into(), Dynamic::from(true));
|
||||
props.insert("count".into(), Dynamic::from(42_i64));
|
||||
|
||||
// Nested map
|
||||
let mut nested = Map::new();
|
||||
nested.insert("nested_key".into(), Dynamic::from("value"));
|
||||
props.insert("nested".into(), Dynamic::from(nested));
|
||||
|
||||
// Array
|
||||
let arr = vec![Dynamic::from(1_i64), Dynamic::from(2_i64), Dynamic::from(3_i64)];
|
||||
props.insert("arr".into(), Dynamic::from(arr));
|
||||
|
||||
let widget_type = "Box";
|
||||
|
||||
let hash1 = hash_props_and_type(&props, widget_type);
|
||||
let hash2 = hash_props_and_type(&props, widget_type);
|
||||
|
||||
assert_eq!(hash1, hash2, "Hashes should be consistent on same input");
|
||||
|
||||
// Change one prop and expect different hash
|
||||
let mut props_modified = props.clone();
|
||||
props_modified.insert("count".into(), Dynamic::from(43_i64));
|
||||
|
||||
let hash3 = hash_props_and_type(&props_modified, widget_type);
|
||||
assert_ne!(hash1, hash3, "Hashes should differ when props change");
|
||||
|
||||
// Different widget type string
|
||||
let hash4 = hash_props_and_type(&props, "Button");
|
||||
assert_ne!(hash1, hash4, "Hashes should differ for different widget types");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user