feat: added a better diffing system

This commit is contained in:
Byson94
2025-08-18 09:31:36 +05:30
parent e1bb573bb1
commit fec794d261
3 changed files with 128 additions and 84 deletions

View File

@@ -23,7 +23,7 @@ use ewwii_shared_util::Span;
use gdk::Monitor;
use glib::ObjectExt;
use gtk::{gdk, glib};
use iirhai::widgetnode::{get_id_to_props_map, WidgetNode};
use iirhai::widgetnode::WidgetNode;
use itertools::Itertools;
use once_cell::sync::Lazy;
use rhai::{Dynamic, Scope};
@@ -344,10 +344,7 @@ impl<B: DisplayBackend> App<B> {
let vars = store.read().unwrap().clone();
if let Ok(new_widget) = generate_new_widgetnode(&vars, &config_path).await {
let mut id_to_prop = HashMap::new();
let _ = get_id_to_props_map(&new_widget, &mut id_to_prop);
widget_reg_store.update_prop_changes(id_to_prop);
widget_reg_store.update_widget_tree(new_widget);
} else {
log::error!("Failed to generate new widgetnode");
}

View File

@@ -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::{hash_props_and_type, WidgetNode};
use iirhai::widgetnode::{hash_props_and_type, WidgetNode, get_id_to_widget_info};
use once_cell::sync::Lazy;
use rhai::Map;
@@ -53,7 +53,7 @@ macro_rules! connect_signal_handler {
pub type UpdateFn = Box<dyn Fn(&Map)>;
pub struct WidgetEntry {
// pub widget: gtk::Widget, // not needed now
pub widget: gtk::Widget,
pub update_fn: UpdateFn,
}
@@ -63,29 +63,24 @@ pub struct WidgetRegistry {
}
pub enum PatchGtkWidget {
Create(u64, Map),
Create(WidgetNode),
Update(u64, Map),
Remove(u64),
}
impl WidgetRegistry {
pub fn new() -> Self {
Self { widgets: HashMap::new() }
}
fn diff_trees(old: Option<WidgetNode>, new: WidgetNode) -> Vec<PatchGtkWidget> {
let mut id_to_prop = HashMap::new();
let _ = get_id_to_props_map(&new_widget, &mut id_to_prop);
Self { widgets: HashMap::new(), stored_widget_node: None }
}
pub fn update_widget_tree(&mut self, new_tree: WidgetNode) {
let old_tree = self.stored_widget_node.take();
let patch = diff_trees(old_tree, new_tree.clone());
let patch = Self::diff_trees(old_tree.as_ref(), &new_tree);
for op in patch {
match op {
PatchGtkWidget::Create(widget_id, props) => self.create_widget(widget_id, props),
PatchGtkWidget::Create(w_node) => build_gtk_widget(WidgetInput::Node(w_node), &mut self),
PatchGtkWidget::Update(widget_id, new_props) => self.update_props(widget_id, new_props),
PatchGtkWidget::Remove(widget_id) => self.remove_widget(widget_id),
}
@@ -94,15 +89,53 @@ impl WidgetRegistry {
self.stored_widget_node = Some(new_tree);
}
pub fn diff_trees(old: Option<&WidgetNode>, new: &WidgetNode) -> Vec<PatchGtkWidget> {
let mut patch = Vec::new();
let mut old_map = HashMap::new();
if let Some(old_node) = old {
let _ = get_id_to_widget_info(old_node, &mut old_map);
}
let mut new_map = HashMap::new();
let _ = get_id_to_widget_info(new, &mut new_map);
// Updates and creations
for (id, new_info) in &new_map {
match old_map.get(id) {
Some(old_info) if old_info.props != new_info.props => {
patch.push(PatchGtkWidget::Update(*id, new_info.props.clone()));
}
None => {
if let Some(info) = new_info.get(&id) {
patch.push(PatchGtkWidget::Create(info.node.clone()));
}
}
_ => {}
}
}
// Removals
for id in old_map.keys() {
if !new_map.contains_key(id) {
patch.push(PatchGtkWidget::Remove(*id));
}
}
patch
}
pub fn update_props(&self, id_to_props: HashMap<u64, Map>) {
for (id, props) in id_to_props {
if let Some(entry) = self.widgets.get(&id) {
(entry.update_fn)(&props);
} // else {
// println!("Warning: id {} not found in widget_registry", id);
// }
}
}
}
pub fn remove_widget(&self, widget_id: u64) {
}
}
pub(super) fn build_gtk_box(props: Map, children: Vec<WidgetNode>, widget_registry: &mut WidgetRegistry) -> Result<gtk::Box> {
@@ -154,7 +187,7 @@ pub(super) fn build_gtk_box(props: Map, children: Vec<WidgetNode>, widget_regist
widget_registry.widgets.insert(
id,
WidgetEntry {
// widget: gtk_widget.upcast(),
widget: gtk_widget.upcast(),
update_fn,
},
);
@@ -281,7 +314,7 @@ pub(super) fn build_center_box(props: Map, children: Vec<WidgetNode>, widget_reg
let id = hash_props_and_type(&props, "CenterBox");
widget_registry.widgets.insert(id, WidgetEntry { update_fn });
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.upcast() });
Ok(gtk_widget)
}
@@ -506,7 +539,7 @@ pub(super) fn build_gtk_event_box(
let id = hash_props_and_type(&props, "EventBox");
widget_registry.widgets.insert(id, WidgetEntry { update_fn });
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.upcast(), });
Ok(gtk_widget)
}
@@ -555,7 +588,7 @@ pub(super) fn build_gtk_stack(props: Map, children: Vec<WidgetNode>, widget_regi
let id = hash_props_and_type(&props, "Stack");
widget_registry.widgets.insert(id, WidgetEntry { update_fn });
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.upcast(), });
Ok(gtk_widget)
}
@@ -616,7 +649,7 @@ pub(super) fn build_transform(props: Map, widget_registry: &mut WidgetRegistry)
let id = hash_props_and_type(&props, "Transform");
widget_registry.widgets.insert(id, WidgetEntry { update_fn });
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: widget.upcast(), });
Ok(widget)
}
@@ -658,7 +691,7 @@ pub(super) fn build_circular_progress_bar(props: Map, widget_registry: &mut Widg
let id = hash_props_and_type(&props, "CircularProgressBar");
widget_registry.widgets.insert(id, WidgetEntry { update_fn });
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: widget.upcast(), });
Ok(widget)
}
@@ -739,7 +772,7 @@ pub(super) fn build_graph(props: Map, widget_registry: &mut WidgetRegistry) -> R
let id = hash_props_and_type(&props, "Graph");
widget_registry.widgets.insert(id, WidgetEntry { update_fn });
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: widget.upcast(), });
Ok(widget)
}
@@ -782,7 +815,7 @@ pub(super) fn build_gtk_progress(props: Map, widget_registry: &mut WidgetRegistr
let id = hash_props_and_type(&props, "Progress");
widget_registry.widgets.insert(id, WidgetEntry { update_fn });
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.upcast(), });
Ok(gtk_widget)
}
@@ -860,7 +893,7 @@ pub(super) fn build_gtk_image(props: Map, widget_registry: &mut WidgetRegistry)
let id = hash_props_and_type(&props, "Image");
widget_registry.widgets.insert(id, WidgetEntry { update_fn });
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.upcast(), });
Ok(gtk_widget)
}
@@ -934,7 +967,7 @@ pub(super) fn build_gtk_button(props: Map, widget_registry: &mut WidgetRegistry)
let id = hash_props_and_type(&props, "Button");
widget_registry.widgets.insert(id, WidgetEntry { update_fn });
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.upcast(), });
Ok(gtk_widget)
}
@@ -1037,7 +1070,7 @@ pub(super) fn build_gtk_label(props: Map, widget_registry: &mut WidgetRegistry)
let id = hash_props_and_type(&props, "Label");
widget_registry.widgets.insert(id, WidgetEntry { update_fn });
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.upcast(), });
Ok(gtk_widget)
}
@@ -1090,7 +1123,7 @@ pub(super) fn build_gtk_input(props: Map, widget_registry: &mut WidgetRegistry)
let id = hash_props_and_type(&props, "Input");
widget_registry.widgets.insert(id, WidgetEntry { update_fn });
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.upcast(), });
Ok(gtk_widget)
}
@@ -1169,7 +1202,7 @@ pub(super) fn build_gtk_calendar(props: Map, widget_registry: &mut WidgetRegistr
let id = hash_props_and_type(&props, "Calendar");
widget_registry.widgets.insert(id, WidgetEntry { update_fn });
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.upcast(), });
Ok(gtk_widget)
}
@@ -1212,7 +1245,7 @@ pub(super) fn build_gtk_combo_box_text(props: Map, widget_registry: &mut WidgetR
let id = hash_props_and_type(&props, "ComboBoxText");
widget_registry.widgets.insert(id, WidgetEntry { update_fn });
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.upcast(), });
Ok(gtk_widget)
}
@@ -1263,7 +1296,7 @@ pub(super) fn build_gtk_expander(
let id = hash_props_and_type(&props, "Expander");
widget_registry.widgets.insert(id, WidgetEntry { update_fn });
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.upcast(), });
Ok(gtk_widget)
}
@@ -1315,7 +1348,7 @@ pub(super) fn build_gtk_revealer(
let id = hash_props_and_type(&props, "Revealer");
widget_registry.widgets.insert(id, WidgetEntry { update_fn });
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.upcast(), });
Ok(gtk_widget)
}
@@ -1355,7 +1388,7 @@ pub(super) fn build_gtk_checkbox(props: Map, widget_registry: &mut WidgetRegistr
let id = hash_props_and_type(&props, "Checkbox");
widget_registry.widgets.insert(id, WidgetEntry { update_fn });
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.upcast(), });
Ok(gtk_widget)
}
@@ -1399,7 +1432,7 @@ pub(super) fn build_gtk_color_button(props: Map, widget_registry: &mut WidgetReg
let id = hash_props_and_type(&props, "ColorButton");
widget_registry.widgets.insert(id, WidgetEntry { update_fn });
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.upcast(), });
Ok(gtk_widget)
}
@@ -1443,7 +1476,7 @@ pub(super) fn build_gtk_color_chooser(props: Map, widget_registry: &mut WidgetRe
let id = hash_props_and_type(&props, "ColorChooser");
widget_registry.widgets.insert(id, WidgetEntry { update_fn });
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.upcast(), });
Ok(gtk_widget)
}
@@ -1488,7 +1521,7 @@ pub(super) fn build_gtk_scale(props: Map, widget_registry: &mut WidgetRegistry)
let id = hash_props_and_type(&props, "Slider");
widget_registry.widgets.insert(id, WidgetEntry { update_fn });
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.upcast(), });
Ok(gtk_widget)
}
@@ -1540,7 +1573,7 @@ pub(super) fn build_gtk_scrolledwindow(
let id = hash_props_and_type(&props, "ScrolledWindow");
widget_registry.widgets.insert(id, WidgetEntry { update_fn });
widget_registry.widgets.insert(id, WidgetEntry { update_fn, widget: gtk_widget.upcast(), });
Ok(gtk_widget)
}

View File

@@ -39,121 +39,135 @@ pub enum WidgetNode {
Enter(Vec<WidgetNode>),
}
// Get `HashMap<widget_id, widget_prop>`
// not exactly a get function as it mutates id_to_props
// instead of returning any value.
// it sounds misleading but i cant think of a better name.
pub fn get_id_to_props_map(
root_node: &WidgetNode,
id_to_props: &mut HashMap<u64, Map>
#[derive(Clone)]
pub struct WidgetInfo {
pub node: WidgetNode,
pub props: Map,
pub widget_type: String,
pub children: Vec<WidgetNode>,
}
pub fn get_id_to_widget_info(
node: &WidgetNode,
id_to_props: &mut HashMap<u64, WidgetInfo>,
) -> Result<()> {
match root_node {
match node {
WidgetNode::Box { props, children } => {
insert_props(props, "Box", id_to_props)?;
insert_wdgt_info(node, props, "Box", children.clone(), id_to_props)?;
for child in children {
get_id_to_props_map(child, id_to_props)?;
get_id_to_widget_info(child, id_to_props)?;
}
}
WidgetNode::CenterBox { props, children } => {
insert_props(props, "CenterBox", id_to_props)?;
insert_wdgt_info(node, props, "CenterBox", children.clone(), id_to_props)?;
for child in children {
get_id_to_props_map(child, id_to_props)?;
get_id_to_widget_info(child, id_to_props)?;
}
}
WidgetNode::EventBox { props, children } => {
insert_props(props, "EventBox", id_to_props)?;
insert_wdgt_info(node, props, "EventBox", children.clone(), id_to_props)?;
for child in children {
get_id_to_props_map(child, id_to_props)?;
get_id_to_widget_info(child, id_to_props)?;
}
}
WidgetNode::CircularProgress { props } => {
insert_props(props, "CircularProgress", id_to_props)?;
insert_wdgt_info(node, props, "CircularProgress", vec![], id_to_props)?;
}
WidgetNode::Graph { props } => {
insert_props(props, "Graph", id_to_props)?;
insert_wdgt_info(node, props, "Graph", vec![], id_to_props)?;
}
WidgetNode::Transform { props } => {
insert_props(props, "Transform", id_to_props)?;
insert_wdgt_info(node, props, "Transform", vec![], id_to_props)?;
}
WidgetNode::Slider { props } => {
insert_props(props, "Slider", id_to_props)?;
insert_wdgt_info(node, props, "Slider", vec![], id_to_props)?;
}
WidgetNode::Progress { props } => {
insert_props(props, "Progress", id_to_props)?;
insert_wdgt_info(node, props, "Progress", vec![], id_to_props)?;
}
WidgetNode::Image { props } => {
insert_props(props, "Image", id_to_props)?;
insert_wdgt_info(node, props, "Image", vec![], id_to_props)?;
}
WidgetNode::Button { props } => {
insert_props(props, "Button", id_to_props)?;
insert_wdgt_info(node, props, "Button", vec![], id_to_props)?;
}
WidgetNode::Label { props } => {
insert_props(props, "Label", id_to_props)?;
insert_wdgt_info(node, props, "Label", vec![], id_to_props)?;
}
WidgetNode::Input { props } => {
insert_props(props, "Input", id_to_props)?;
insert_wdgt_info(node, props, "Input", vec![], id_to_props)?;
}
WidgetNode::Calendar { props } => {
insert_props(props, "Calendar", id_to_props)?;
insert_wdgt_info(node, props, "Calendar", vec![], id_to_props)?;
}
WidgetNode::ColorButton { props } => {
insert_props(props, "ColorButton", id_to_props)?;
insert_wdgt_info(node, props, "ColorButton", vec![], id_to_props)?;
}
WidgetNode::Expander { props, children } => {
insert_props(props, "Expander", id_to_props)?;
insert_wdgt_info(node, props, "Expander", children.clone(), id_to_props)?;
for child in children {
get_id_to_props_map(child, id_to_props)?;
get_id_to_widget_info(child, id_to_props)?;
}
}
WidgetNode::ToolTip { children } => {
for child in children {
get_id_to_props_map(child, id_to_props)?;
get_id_to_widget_info(child, id_to_props)?;
}
}
WidgetNode::ColorChooser { props } => {
insert_props(props, "ColorChooser", id_to_props)?;
insert_wdgt_info(node, props, "ColorChooser", vec![], id_to_props)?;
}
WidgetNode::ComboBoxText { props } => {
insert_props(props, "ComboBoxText", id_to_props)?;
insert_wdgt_info(node, props, "ComboBoxText", vec![], id_to_props)?;
}
WidgetNode::Checkbox { props } => {
insert_props(props, "Checkbox", id_to_props)?;
insert_wdgt_info(node, props, "Checkbox", vec![], id_to_props)?;
}
WidgetNode::Revealer { props, children } => {
insert_props(props, "Revealer", id_to_props)?;
insert_wdgt_info(node, props, "Revealer", children.clone(), id_to_props)?;
for child in children {
get_id_to_props_map(child, id_to_props)?;
get_id_to_widget_info(child, id_to_props)?;
}
}
WidgetNode::Scroll { props, children } => {
insert_props(props, "Scroll", id_to_props)?;
insert_wdgt_info(node, props, "Scroll", children.clone(), id_to_props)?;
for child in children {
get_id_to_props_map(child, id_to_props)?;
get_id_to_widget_info(child, id_to_props)?;
}
}
WidgetNode::OverLay { children } => {
for child in children {
get_id_to_props_map(child, id_to_props)?;
get_id_to_widget_info(child, id_to_props)?;
}
}
WidgetNode::Stack { props, children } => {
insert_props(props, "Stack", id_to_props)?;
insert_wdgt_info(node, props, "Stack", children.clone(), id_to_props)?;
for child in children {
get_id_to_props_map(child, id_to_props)?;
get_id_to_widget_info(child, id_to_props)?;
}
}
_ => {
// do nothing for now ig?
}
_ => { /* do nothinnng */ }
}
Ok(())
}
fn insert_props(props: &Map, widget_type: &str, id_to_props: &mut HashMap<u64, Map>) -> Result<()> {
fn insert_wdgt_info(
node: &WidgetNode,
props: &Map,
widget_type: &str,
children: Vec<WidgetNode>,
id_to_info: &mut HashMap<u64, WidgetInfo>
) -> Result<()> {
let id = hash_props_and_type(props, widget_type);
id_to_props.insert(id, props.clone());
let info = WidgetInfo {
node: node.clone(),
props: props.clone(),
widget_type: widget_type.to_string(),
children,
};
id_to_info.insert(id, info);
Ok(())
}