Kaynağa Gözat

wallet: migrate to new scene graph and render API backend

darkfi 1 yıl önce
ebeveyn
işleme
b6f1e571ea

+ 55 - 47
bin/darkwallet/src/app/mod.rs

@@ -28,17 +28,14 @@ use std::{
 };
 
 use crate::{
-    darkirc::{DarkIrcBackendPtr, Privmsg},
     error::Error,
     expr::Op,
     gfx::{GraphicsEventPublisherPtr, RenderApiPtr, Vertex},
     prop::{Property, PropertyBool, PropertyStr, PropertySubType, PropertyType, Role},
-    scene::{
-        CallArgType, MethodResponseFn, Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId,
-        SceneNodeType, Slot,
-    },
+    scene::{Pimpl, SceneNode as SceneNode3, SceneNodePtr, SceneNodeType as SceneNodeType3},
     text::TextShaperPtr,
-    ui::{chatview, Button, ChatView, EditBox, Image, Layer, Stoppable, Text, VectorArt, Window},
+    //ui::{chatview, Button, ChatView, EditBox, Image, Layer, Stoppable, Text, VectorArt, Window},
+    ui::Window,
     ExecutorPtr,
 };
 
@@ -119,37 +116,43 @@ impl AsyncRuntime {
 pub type AppPtr = Arc<App>;
 
 pub struct App {
-    pub(self) sg: SceneGraphPtr2,
-    pub(self) ex: ExecutorPtr,
+    pub(self) sg_root: SceneNodePtr,
     pub(self) render_api: RenderApiPtr,
     pub(self) event_pub: GraphicsEventPublisherPtr,
     pub(self) text_shaper: TextShaperPtr,
-    pub(self) darkirc_backend: DarkIrcBackendPtr,
+    //pub(self) darkirc_backend: DarkIrcBackendPtr,
     pub(self) tasks: SyncMutex<Vec<Task<()>>>,
+    pub(self) ex: ExecutorPtr,
 }
 
 impl App {
     pub fn new(
-        sg: SceneGraphPtr2,
-        ex: ExecutorPtr,
+        sg_root: SceneNodePtr,
         render_api: RenderApiPtr,
         event_pub: GraphicsEventPublisherPtr,
         text_shaper: TextShaperPtr,
-        darkirc_backend: DarkIrcBackendPtr,
+        //darkirc_backend: DarkIrcBackendPtr,
+        ex: ExecutorPtr,
     ) -> Arc<Self> {
         Arc::new(Self {
-            sg,
+            sg_root,
             ex,
             render_api,
             event_pub,
             text_shaper,
-            darkirc_backend,
+            //darkirc_backend,
             tasks: SyncMutex::new(vec![]),
         })
     }
 
     pub async fn start(self: Arc<Self>) {
         debug!(target: "app", "App::start()");
+
+        ////////////////////////////////////////////////////////////////////////////////////
+        // OLD
+        ////////////////////////////////////////////////////////////////////////////////////
+
+        /*
         // Setup UI
         let mut sg = self.sg.lock().await;
 
@@ -193,11 +196,38 @@ impl App {
 
         drop(sg);
 
-        schema::make(&self).await;
+        //schema::make(&self).await;
+        debug!(target: "app", "Schema loaded");
+        */
+
+        ////////////////////////////////////////////////////////////////////////////////////
+        // NEW
+        ////////////////////////////////////////////////////////////////////////////////////
+        let mut window = SceneNode3::new("window", SceneNodeType3::Window);
+
+        let mut prop = Property::new("screen_size", PropertyType::Float32, PropertySubType::Pixel);
+        prop.set_array_len(2);
+        // Window not yet initialized so we can't set these.
+        //prop.set_f32(Role::App, 0, screen_width);
+        //prop.set_f32(Role::App, 1, screen_height);
+        window.add_property(prop).unwrap();
+
+        let mut prop = Property::new("scale", PropertyType::Float32, PropertySubType::Pixel);
+        prop.set_defaults_f32(vec![1.]).unwrap();
+        window.add_property(prop).unwrap();
+
+        let window = window
+            .setup(|me| {
+                Window::new(me, self.render_api.clone(), self.event_pub.clone(), self.ex.clone())
+            })
+            .await;
+        self.sg_root.link(window.clone());
+        schema::make(&self, window).await;
+
         debug!(target: "app", "Schema loaded");
 
         // Access drawable in window node and call draw()
-        self.trigger_redraw().await;
+        self.trigger_draw().await;
 
         // Start the backend
         //if let Err(err) = self.darkirc_backend.start(self.sg.clone(), self.ex.clone()).await {
@@ -213,37 +243,15 @@ impl App {
         });
     }
 
+    /// Shutdown code here
     async fn async_stop(&self) {
-        self.darkirc_backend.stop().await;
-
-        let sg = self.sg.lock().await;
-        let window_id = sg.lookup_node("/window").unwrap().id;
-        self.stop_node(&sg, window_id).await;
-        drop(sg);
-    }
-
-    #[async_recursion]
-    async fn stop_node(&self, sg: &SceneGraph, node_id: SceneNodeId) {
-        let node = sg.get_node(node_id).unwrap();
-        for child_inf in node.get_children2() {
-            self.stop_node(sg, child_inf.id).await;
-        }
-        match &node.pimpl {
-            Pimpl::Window(win) => win.stop().await,
-            Pimpl::Layer(layer) => layer.stop().await,
-            Pimpl::VectorArt(svg) => svg.stop().await,
-            Pimpl::Text(txt) => txt.stop().await,
-            Pimpl::EditBox(ebox) => ebox.stop().await,
-            Pimpl::ChatView(_) | Pimpl::Image(_) | Pimpl::Button(_) => {}
-            _ => panic!("unhandled pimpl type"),
-        };
+        //self.darkirc_backend.stop().await;
     }
 
-    async fn trigger_redraw(&self) {
-        let sg = self.sg.lock().await;
-        let window_node = sg.lookup_node("/window").expect("no window attached!");
+    async fn trigger_draw(&self) {
+        let window_node = self.sg_root.clone().lookup_node("/window").expect("no window attached!");
         match &window_node.pimpl {
-            Pimpl::Window(win) => win.draw(&sg).await,
+            Pimpl::Window(win) => win.draw().await,
             _ => panic!("wrong pimpl"),
         }
     }
@@ -280,11 +288,11 @@ fn populate_tree(tree: &sled::Tree) {
         let mut key = [0u8; 8 + 32];
         key[..8].clone_from_slice(&timest);
 
-        let msg = chatview::ChatMsg { nick, text };
-        let mut val = vec![];
-        msg.encode(&mut val).unwrap();
+        //let msg = chatview::ChatMsg { nick, text };
+        //let mut val = vec![];
+        //msg.encode(&mut val).unwrap();
 
-        tree.insert(&key, val).unwrap();
+        //tree.insert(&key, val).unwrap();
     }
     // O(n)
     debug!(target: "app", "populated db with {} lines", tree.len());

+ 22 - 30
bin/darkwallet/src/app/node.rs

@@ -17,23 +17,18 @@
  */
 
 use crate::{
-    darkirc::{DarkIrcBackendPtr, Privmsg},
     error::Error,
     expr::Op,
     gfx::{GraphicsEventPublisherPtr, RenderApiPtr, Vertex},
     prop::{Property, PropertyBool, PropertyStr, PropertySubType, PropertyType, Role},
-    scene::{
-        CallArgType, MethodResponseFn, Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId,
-        SceneNodeType, Slot,
-    },
+    scene::{CallArgType, MethodResponseFn, SceneNode, SceneNodeType, Slot},
     text::TextShaperPtr,
-    ui::{chatview, Button, ChatView, EditBox, Image, Layer, Stoppable, Text, VectorArt, Window},
     ExecutorPtr,
 };
 
-pub fn create_layer(sg: &mut SceneGraph, name: &str) -> SceneNodeId {
+pub fn create_layer(name: &str) -> SceneNode {
     debug!(target: "app", "create_layer({name})");
-    let node = sg.add_node(name, SceneNodeType::Layer);
+    let mut node = SceneNode::new(name, SceneNodeType::Layer);
     let prop = Property::new("is_visible", PropertyType::Bool, PropertySubType::Null);
     node.add_property(prop).unwrap();
 
@@ -45,12 +40,12 @@ pub fn create_layer(sg: &mut SceneGraph, name: &str) -> SceneNodeId {
     let prop = Property::new("z_index", PropertyType::Uint32, PropertySubType::Null);
     node.add_property(prop).unwrap();
 
-    node.id
+    node
 }
 
-pub fn create_vector_art(sg: &mut SceneGraph, name: &str) -> SceneNodeId {
+pub fn create_vector_art(name: &str) -> SceneNode {
     debug!(target: "app", "create_vector_art({name})");
-    let node = sg.add_node(name, SceneNodeType::VectorArt);
+    let mut node = SceneNode::new(name, SceneNodeType::VectorArt);
 
     let mut prop = Property::new("rect", PropertyType::Float32, PropertySubType::Pixel);
     prop.set_array_len(4);
@@ -60,12 +55,12 @@ pub fn create_vector_art(sg: &mut SceneGraph, name: &str) -> SceneNodeId {
     let prop = Property::new("z_index", PropertyType::Uint32, PropertySubType::Null);
     node.add_property(prop).unwrap();
 
-    node.id
+    node
 }
 
-pub fn create_button(sg: &mut SceneGraph, name: &str) -> SceneNodeId {
+pub fn create_button(name: &str) -> SceneNode {
     debug!(target: "app", "create_button({name})");
-    let node = sg.add_node(name, SceneNodeType::Button);
+    let mut node = SceneNode::new(name, SceneNodeType::Button);
 
     let mut prop = Property::new("is_active", PropertyType::Bool, PropertySubType::Null);
     prop.set_ui_text("Is Active", "An active Button can be clicked");
@@ -81,12 +76,12 @@ pub fn create_button(sg: &mut SceneGraph, name: &str) -> SceneNodeId {
 
     node.add_signal("click", "Button clicked event", vec![]).unwrap();
 
-    node.id
+    node
 }
 
-pub fn create_image(sg: &mut SceneGraph, name: &str) -> SceneNodeId {
+pub fn create_image(name: &str) -> SceneNode {
     debug!(target: "app", "create_image({name})");
-    let node = sg.add_node(name, SceneNodeType::Image);
+    let mut node = SceneNode::new(name, SceneNodeType::Image);
 
     let mut prop = Property::new("rect", PropertyType::Float32, PropertySubType::Pixel);
     prop.set_array_len(4);
@@ -99,12 +94,12 @@ pub fn create_image(sg: &mut SceneGraph, name: &str) -> SceneNodeId {
     let prop = Property::new("path", PropertyType::Str, PropertySubType::Null);
     node.add_property(prop).unwrap();
 
-    node.id
+    node
 }
 
-pub fn create_text(sg: &mut SceneGraph, name: &str) -> SceneNodeId {
+pub fn create_text(name: &str) -> SceneNode {
     debug!(target: "app", "create_text({name})");
-    let node = sg.add_node(name, SceneNodeType::Text);
+    let mut node = SceneNode::new(name, SceneNodeType::Text);
 
     let mut prop = Property::new("rect", PropertyType::Float32, PropertySubType::Pixel);
     prop.set_array_len(4);
@@ -131,12 +126,12 @@ pub fn create_text(sg: &mut SceneGraph, name: &str) -> SceneNodeId {
     let prop = Property::new("debug", PropertyType::Bool, PropertySubType::Null);
     node.add_property(prop).unwrap();
 
-    node.id
+    node
 }
 
-pub fn create_editbox(sg: &mut SceneGraph, name: &str) -> SceneNodeId {
+pub fn create_editbox(name: &str) -> SceneNode {
     debug!(target: "app", "create_editbox({name})");
-    let node = sg.add_node(name, SceneNodeType::EditBox);
+    let mut node = SceneNode::new(name, SceneNodeType::EditBox);
 
     let mut prop = Property::new("is_active", PropertyType::Bool, PropertySubType::Null);
     prop.set_ui_text("Is Active", "An active EditBox can be focused");
@@ -194,15 +189,12 @@ pub fn create_editbox(sg: &mut SceneGraph, name: &str) -> SceneNodeId {
     let prop = Property::new("debug", PropertyType::Bool, PropertySubType::Null);
     node.add_property(prop).unwrap();
 
-    node.id
+    node
 }
 
-pub fn create_chatview(
-    sg: &mut SceneGraph,
-    name: &str,
-) -> (SceneNodeId, async_channel::Receiver<Vec<u8>>) {
+pub fn create_chatview(name: &str) -> (SceneNode, async_channel::Receiver<Vec<u8>>) {
     debug!(target: "app", "create_chatview({name})");
-    let node = sg.add_node(name, SceneNodeType::ChatView);
+    let mut node = SceneNode::new(name, SceneNodeType::ChatView);
 
     let mut prop = Property::new("rect", PropertyType::Float32, PropertySubType::Pixel);
     prop.set_array_len(4);
@@ -287,5 +279,5 @@ pub fn create_chatview(
     )
     .unwrap();
 
-    (node.id, recvr)
+    (node, recvr)
 }

+ 134 - 240
bin/darkwallet/src/app/schema.rs

@@ -19,20 +19,25 @@
 use sled_overlay::sled;
 
 use crate::{
-    darkirc::{DarkIrcBackendPtr, Privmsg},
     error::Error,
-    expr::Compiler,
+    expr::{self, Compiler},
     gfx::{GraphicsEventPublisherPtr, Rectangle, RenderApiPtr, Vertex},
     mesh::{Color, MeshBuilder},
-    prop::{Property, PropertyBool, PropertyStr, PropertySubType, PropertyType, Role},
-    scene::{
-        CallArgType, MethodResponseFn, Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId,
-        SceneNodeType, Slot,
+    prop::{
+        Property, PropertyBool, PropertyFloat32, PropertyStr, PropertySubType, PropertyType, Role,
     },
+    scene::SceneNodePtr,
     text::TextShaperPtr,
     ui::{
-        chatview, vector_art::shape, Button, ChatView, EditBox, Image, Layer, ShapeVertex,
-        Stoppable, Text, VectorArt, VectorShape, Window,
+        Image,
+        Layer,
+        ShapeVertex,
+        //chatview, vector_art::shape, Button, ChatView, EditBox, Image, Layer, ShapeVertex,
+        //Stoppable, Text, VectorArt, VectorShape, Window,
+        Text,
+        VectorArt,
+        VectorShape,
+        Window,
     },
     ExecutorPtr,
 };
@@ -82,75 +87,52 @@ const FONTSIZE: f32 = 40.;
 #[cfg(target_os = "linux")]
 const FONTSIZE: f32 = 20.;
 
-pub(super) async fn make_old(app: &App) {
+pub(super) async fn make_test(app: &App, window: SceneNodePtr) {
     let mut cc = Compiler::new();
 
-    //let mut tasks = vec![];
     // Create a layer called view
-    let mut sg = app.sg.lock().await;
-    let layer_node_id = create_layer(&mut sg, "view");
-
-    // Customize our layer
-    let node = sg.get_node(layer_node_id).unwrap();
-    let prop = node.get_property("rect").unwrap();
+    let layer_node = create_layer("view");
+    let prop = layer_node.get_property("rect").unwrap();
     prop.set_f32(Role::App, 0, 0.).unwrap();
     prop.set_f32(Role::App, 1, 0.).unwrap();
-    prop.set_expr(Role::App, 2, shape::load_var("w")).unwrap();
-    prop.set_expr(Role::App, 3, shape::load_var("h")).unwrap();
-    node.set_property_bool(Role::App, "is_visible", true).unwrap();
-
-    // Setup the pimpl
-    let node_id = node.id;
-    drop(sg);
-    let pimpl = Layer::new(app.ex.clone(), app.sg.clone(), node_id, app.render_api.clone()).await;
-    let mut sg = app.sg.lock().await;
-    let node = sg.get_node_mut(node_id).unwrap();
-    node.pimpl = pimpl;
-
-    let window_id = sg.lookup_node("/window").unwrap().id;
-    sg.link(node_id, window_id).unwrap();
+    prop.set_expr(Role::App, 2, expr::load_var("w")).unwrap();
+    prop.set_expr(Role::App, 3, expr::load_var("h")).unwrap();
+    layer_node.set_property_bool(Role::App, "is_visible", true).unwrap();
+    let layer_node =
+        layer_node.setup(|me| Layer::new(me, app.render_api.clone(), app.ex.clone())).await;
+    window.link(layer_node.clone());
 
     // Create a bg mesh
-    let node_id = create_vector_art(&mut sg, "bg");
-
-    let node = sg.get_node_mut(node_id).unwrap();
+    let node = create_vector_art("bg");
     let prop = node.get_property("rect").unwrap();
     prop.set_f32(Role::App, 0, 0.).unwrap();
     prop.set_f32(Role::App, 1, 0.).unwrap();
-    prop.set_expr(Role::App, 2, shape::load_var("w")).unwrap();
-    prop.set_expr(Role::App, 3, shape::load_var("h")).unwrap();
+    prop.set_expr(Role::App, 2, expr::load_var("w")).unwrap();
+    prop.set_expr(Role::App, 3, expr::load_var("h")).unwrap();
+    node.set_property_u32(Role::App, "z_index", 0).unwrap();
 
     let c = if LIGHTMODE { 1. } else { 0. };
-    // Setup the pimpl
-    let node_id = node.id;
     let mut shape = VectorShape::new();
     shape.add_filled_box(
-        shape::const_f32(0.),
-        shape::const_f32(0.),
-        shape::load_var("w"),
-        shape::load_var("h"),
+        expr::const_f32(0.),
+        expr::const_f32(0.),
+        expr::load_var("w"),
+        expr::load_var("h"),
         [c, c, c, 1.],
     );
-    drop(sg);
-    let pimpl =
-        VectorArt::new(app.ex.clone(), app.sg.clone(), node_id, app.render_api.clone(), shape)
-            .await;
-    let mut sg = app.sg.lock().await;
-    let node = sg.get_node_mut(node_id).unwrap();
-    node.pimpl = pimpl;
-
-    sg.link(node_id, layer_node_id).unwrap();
+    let node =
+        node.setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())).await;
+    layer_node.link(node);
 
     // Create button bg
-    let node_id = create_vector_art(&mut sg, "btnbg");
-
-    let node = sg.get_node_mut(node_id).unwrap();
+    let node = create_vector_art("btnbg");
     let prop = node.get_property("rect").unwrap();
     let code = cc.compile("w - 220").unwrap();
     prop.set_expr(Role::App, 0, code).unwrap();
     prop.set_f32(Role::App, 1, 10.).unwrap();
     prop.set_f32(Role::App, 2, 200.).unwrap();
     prop.set_f32(Role::App, 3, 60.).unwrap();
+    node.set_property_u32(Role::App, "z_index", 1).unwrap();
 
     // Setup the pimpl
     let verts = if LIGHTMODE {
@@ -170,16 +152,11 @@ pub(super) async fn make_old(app: &App) {
     };
     let indices = vec![0, 2, 1, 1, 2, 3];
     let shape = VectorShape { verts, indices };
-    drop(sg);
-    let pimpl =
-        VectorArt::new(app.ex.clone(), app.sg.clone(), node_id, app.render_api.clone(), shape)
-            .await;
-    let mut sg = app.sg.lock().await;
-    let node = sg.get_node_mut(node_id).unwrap();
-    node.pimpl = pimpl;
-
-    sg.link(node_id, layer_node_id).unwrap();
+    let node =
+        node.setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())).await;
+    layer_node.link(node);
 
+    /*
     // Create the button
     let node_id = create_button(&mut sg, "btn");
 
@@ -203,16 +180,16 @@ pub(super) async fn make_old(app: &App) {
     node.pimpl = pimpl;
 
     sg.link(node_id, layer_node_id).unwrap();
+    */
 
     // Create another mesh
-    let node_id = create_vector_art(&mut sg, "box");
-
-    let node = sg.get_node_mut(node_id).unwrap();
+    let node = create_vector_art("box");
     let prop = node.get_property("rect").unwrap();
     prop.set_f32(Role::App, 0, 10.).unwrap();
     prop.set_f32(Role::App, 1, 10.).unwrap();
     prop.set_f32(Role::App, 2, 60.).unwrap();
     prop.set_f32(Role::App, 3, 60.).unwrap();
+    node.set_property_u32(Role::App, "z_index", 1).unwrap();
 
     // Setup the pimpl
     let verts = if LIGHTMODE {
@@ -232,81 +209,54 @@ pub(super) async fn make_old(app: &App) {
     };
     let indices = vec![0, 2, 1, 1, 2, 3];
     let shape = VectorShape { verts, indices };
-    drop(sg);
-    let pimpl =
-        VectorArt::new(app.ex.clone(), app.sg.clone(), node_id, app.render_api.clone(), shape)
-            .await;
-    let mut sg = app.sg.lock().await;
-    let node = sg.get_node_mut(node_id).unwrap();
-    node.pimpl = pimpl;
-
-    sg.link(node_id, layer_node_id).unwrap();
+    let node =
+        node.setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())).await;
+    layer_node.link(node);
 
     // Debugging tool
-    let node_id = create_vector_art(&mut sg, "debugtool");
-
-    let node = sg.get_node_mut(node_id).unwrap();
+    let node = create_vector_art("debugtool");
     let prop = node.get_property("rect").unwrap();
     prop.set_f32(Role::App, 0, 0.).unwrap();
     let code = cc.compile("h/2").unwrap();
     prop.set_expr(Role::App, 1, code).unwrap();
-    prop.set_expr(Role::App, 2, shape::load_var("w")).unwrap();
+    prop.set_expr(Role::App, 2, expr::load_var("w")).unwrap();
     let code = cc.compile("h/2 - 200").unwrap();
     prop.set_expr(Role::App, 3, code).unwrap();
-
     node.set_property_u32(Role::App, "z_index", 2).unwrap();
 
-    // Setup the pimpl
     let mut shape = VectorShape::new();
     shape.add_filled_box(
-        shape::const_f32(0.),
-        shape::const_f32(0.),
-        shape::load_var("w"),
-        shape::const_f32(5.),
+        expr::const_f32(0.),
+        expr::const_f32(0.),
+        expr::load_var("w"),
+        expr::const_f32(5.),
         [0., 1., 0., 1.],
     );
     shape.add_filled_box(
-        shape::const_f32(0.),
+        expr::const_f32(0.),
         cc.compile("h - 5").unwrap(),
-        shape::load_var("w"),
-        shape::load_var("h"),
+        expr::load_var("w"),
+        expr::load_var("h"),
         [0., 1., 0., 1.],
     );
-    drop(sg);
-    let pimpl =
-        VectorArt::new(app.ex.clone(), app.sg.clone(), node_id, app.render_api.clone(), shape)
-            .await;
-    let mut sg = app.sg.lock().await;
-    let node = sg.get_node_mut(node_id).unwrap();
-    node.pimpl = pimpl;
-
-    sg.link(node_id, layer_node_id).unwrap();
+    let node =
+        node.setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())).await;
+    layer_node.link(node);
 
     // Create KING GNU!
-    let node_id = create_image(&mut sg, "king");
-
-    let node = sg.get_node_mut(node_id).unwrap();
+    let node = create_image("king");
     let prop = node.get_property("rect").unwrap();
     prop.set_f32(Role::App, 0, 80.).unwrap();
     prop.set_f32(Role::App, 1, 10.).unwrap();
     prop.set_f32(Role::App, 2, 60.).unwrap();
     prop.set_f32(Role::App, 3, 60.).unwrap();
-
     node.set_property_str(Role::App, "path", KING_PATH).unwrap();
-
-    // Setup the pimpl
-    drop(sg);
-    let pimpl = Image::new(app.ex.clone(), app.sg.clone(), node_id, app.render_api.clone()).await;
-    let mut sg = app.sg.lock().await;
-    let node = sg.get_node_mut(node_id).unwrap();
-    node.pimpl = pimpl;
-
-    sg.link(node_id, layer_node_id).unwrap();
+    node.set_property_u32(Role::App, "z_index", 1).unwrap();
+    let node = node.setup(|me| Image::new(me, app.render_api.clone(), app.ex.clone())).await;
+    layer_node.link(node);
 
     // Create some text
-    let node_id = create_text(&mut sg, "label");
-
-    let node = sg.get_node_mut(node_id).unwrap();
+    let node = create_text("label");
     let prop = node.get_property("rect").unwrap();
     prop.set_f32(Role::App, 0, 100.).unwrap();
     prop.set_f32(Role::App, 1, 100.).unwrap();
@@ -321,22 +271,14 @@ pub(super) async fn make_old(app: &App) {
     prop.set_f32(Role::App, 1, 1.).unwrap();
     prop.set_f32(Role::App, 2, 0.).unwrap();
     prop.set_f32(Role::App, 3, 1.).unwrap();
+    node.set_property_u32(Role::App, "z_index", 1).unwrap();
 
-    drop(sg);
-    let pimpl = Text::new(
-        app.ex.clone(),
-        app.sg.clone(),
-        node_id,
-        app.render_api.clone(),
-        app.text_shaper.clone(),
-    )
-    .await;
-    let mut sg = app.sg.lock().await;
-    let node = sg.get_node_mut(node_id).unwrap();
-    node.pimpl = pimpl;
-
-    sg.link(node_id, layer_node_id).unwrap();
+    let node = node
+        .setup(|me| Text::new(me, app.render_api.clone(), app.text_shaper.clone(), app.ex.clone()))
+        .await;
+    layer_node.link(node);
 
+    /*
     // Text edit
     let node_id = create_editbox(&mut sg, "editz");
     let node = sg.get_node(node_id).unwrap();
@@ -438,7 +380,7 @@ pub(super) async fn make_old(app: &App) {
     prop.set_f32(Role::App, 0, 0.).unwrap();
     let code = cc.compile("h/2").unwrap();
     prop.set_expr(Role::App, 1, code).unwrap();
-    prop.set_expr(Role::App, 2, shape::load_var("w")).unwrap();
+    prop.set_expr(Role::App, 2, expr::load_var("w")).unwrap();
     let code = cc.compile("h/2 - 200").unwrap();
     prop.set_expr(Role::App, 3, code).unwrap();
     node.set_property_f32(Role::App, "font_size", 20.).unwrap();
@@ -527,10 +469,13 @@ pub(super) async fn make_old(app: &App) {
     //let window_node = sg.get_node_mut(window_id).unwrap();
     //win_node.set_property_f32(Role::App, "scale", 1.6).unwrap();
 
-    //*app.tasks.lock().unwrap() = tasks;
+    // *app.tasks.lock().unwrap() = tasks;
+    */
 }
 
-pub(super) async fn make(app: &App) {
+pub(super) async fn make(app: &App, window: SceneNodePtr) {
+    let screen_scale = PropertyFloat32::wrap(&window, Role::Internal, "scale", 0).unwrap();
+
     let mut cc = Compiler::new();
 
     cc.add_const_f32("EDITCHAT_HEIGHT", EDITCHAT_HEIGHT);
@@ -538,35 +483,24 @@ pub(super) async fn make(app: &App) {
     cc.add_const_f32("SENDLABEL_LHS_PAD", SENDLABEL_LHS_PAD);
 
     // Main view
-    let mut sg = app.sg.lock().await;
-    let layer_node_id = create_layer(&mut sg, "view");
-    let node = sg.get_node(layer_node_id).unwrap();
-    let prop = node.get_property("rect").unwrap();
+    let layer_node = create_layer("view");
+    let prop = layer_node.get_property("rect").unwrap();
     prop.set_f32(Role::App, 0, 0.).unwrap();
     prop.set_f32(Role::App, 1, 0.).unwrap();
-    prop.set_expr(Role::App, 2, shape::load_var("w")).unwrap();
-    prop.set_expr(Role::App, 3, shape::load_var("h")).unwrap();
-    node.set_property_bool(Role::App, "is_visible", true).unwrap();
-
-    let node_id = node.id;
-    drop(sg);
-    let pimpl = Layer::new(app.ex.clone(), app.sg.clone(), node_id, app.render_api.clone()).await;
-    let mut sg = app.sg.lock().await;
-    let node = sg.get_node_mut(node_id).unwrap();
-    node.pimpl = pimpl;
-
-    let window_id = sg.lookup_node("/window").unwrap().id;
-    sg.link(node_id, window_id).unwrap();
+    prop.set_expr(Role::App, 2, expr::load_var("w")).unwrap();
+    prop.set_expr(Role::App, 3, expr::load_var("h")).unwrap();
+    layer_node.set_property_bool(Role::App, "is_visible", true).unwrap();
+    let layer_node =
+        layer_node.setup(|me| Layer::new(me, app.render_api.clone(), app.ex.clone())).await;
+    window.link(layer_node.clone());
 
     // Create a bg mesh
-    let node_id = create_vector_art(&mut sg, "bg");
-
-    let node = sg.get_node_mut(node_id).unwrap();
+    let node = create_vector_art("bg");
     let prop = node.get_property("rect").unwrap();
     prop.set_f32(Role::App, 0, 0.).unwrap();
     prop.set_f32(Role::App, 1, 0.).unwrap();
-    prop.set_expr(Role::App, 2, shape::load_var("w")).unwrap();
-    prop.set_expr(Role::App, 3, shape::load_var("h")).unwrap();
+    prop.set_expr(Role::App, 2, expr::load_var("w")).unwrap();
+    prop.set_expr(Role::App, 3, expr::load_var("h")).unwrap();
     node.set_property_u32(Role::App, "z_index", 0).unwrap();
 
     let c = if LIGHTMODE { 1. } else { 0.05 };
@@ -574,60 +508,45 @@ pub(super) async fn make(app: &App) {
     let node_id = node.id;
     let mut shape = VectorShape::new();
     shape.add_filled_box(
-        shape::const_f32(0.),
-        shape::const_f32(0.),
-        shape::load_var("w"),
-        shape::load_var("h"),
+        expr::const_f32(0.),
+        expr::const_f32(0.),
+        expr::load_var("w"),
+        expr::load_var("h"),
         [c, c, c, 1.],
     );
-    drop(sg);
-    let pimpl =
-        VectorArt::new(app.ex.clone(), app.sg.clone(), node_id, app.render_api.clone(), shape)
-            .await;
-    let mut sg = app.sg.lock().await;
-    let node = sg.get_node_mut(node_id).unwrap();
-    node.pimpl = pimpl;
-
-    sg.link(node_id, layer_node_id).unwrap();
+    let node =
+        node.setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())).await;
+    layer_node.link(node);
 
     // Create the toolbar bg
-    let node_id = create_vector_art(&mut sg, "toolbar_bg");
-
-    let node = sg.get_node_mut(node_id).unwrap();
+    let node = create_vector_art("toolbar_bg");
     let prop = node.get_property("rect").unwrap();
     prop.set_f32(Role::App, 0, 0.).unwrap();
     prop.set_f32(Role::App, 1, 0.).unwrap();
-    prop.set_expr(Role::App, 2, shape::load_var("w")).unwrap();
+    prop.set_expr(Role::App, 2, expr::load_var("w")).unwrap();
     prop.set_f32(Role::App, 3, EDITCHAT_HEIGHT).unwrap();
     node.set_property_u32(Role::App, "z_index", 1).unwrap();
-    drop(sg);
+
     let mut shape = VectorShape::new();
     shape.add_outline(
-        shape::const_f32(0.),
-        shape::const_f32(0.),
-        shape::load_var("w"),
-        shape::load_var("h"),
+        expr::const_f32(0.),
+        expr::const_f32(0.),
+        expr::load_var("w"),
+        expr::load_var("h"),
         1.,
         [0.4, 0.4, 0.4, 1.],
     );
 
-    let pimpl =
-        VectorArt::new(app.ex.clone(), app.sg.clone(), node_id, app.render_api.clone(), shape)
-            .await;
-    let mut sg = app.sg.lock().await;
-    let node = sg.get_node_mut(node_id).unwrap();
-    node.pimpl = pimpl;
-
-    sg.link(node_id, layer_node_id).unwrap();
+    let node =
+        node.setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())).await;
+    layer_node.link(node);
 
     // Create some text
-    let node_id = create_text(&mut sg, "channel_label");
-
-    let node = sg.get_node_mut(node_id).unwrap();
+    let node = create_text("channel_label");
     let prop = node.get_property("rect").unwrap();
     prop.set_f32(Role::App, 0, SENDLABEL_LHS_PAD).unwrap();
     prop.set_f32(Role::App, 1, 0.).unwrap();
-    prop.set_expr(Role::App, 2, shape::load_var("w")).unwrap();
+    prop.set_expr(Role::App, 2, expr::load_var("w")).unwrap();
     prop.set_f32(Role::App, 3, EDITCHAT_HEIGHT).unwrap();
     node.set_property_u32(Role::App, "z_index", 2).unwrap();
     node.set_property_f32(Role::App, "baseline", (EDITCHAT_HEIGHT + 20.) / 2.).unwrap();
@@ -639,29 +558,21 @@ pub(super) async fn make(app: &App) {
     prop.set_f32(Role::App, 1, 1.).unwrap();
     prop.set_f32(Role::App, 2, 1.).unwrap();
     prop.set_f32(Role::App, 3, 1.).unwrap();
+    node.set_property_u32(Role::App, "z_index", 1).unwrap();
 
-    drop(sg);
-    let pimpl = Text::new(
-        app.ex.clone(),
-        app.sg.clone(),
-        node_id,
-        app.render_api.clone(),
-        app.text_shaper.clone(),
-    )
-    .await;
-    let mut sg = app.sg.lock().await;
-    let node = sg.get_node_mut(node_id).unwrap();
-    node.pimpl = pimpl;
-
-    sg.link(node_id, layer_node_id).unwrap();
+    let node = node
+        .setup(|me| Text::new(me, app.render_api.clone(), app.text_shaper.clone(), app.ex.clone()))
+        .await;
+    layer_node.link(node);
 
+    /*
     // ChatView
     let (node_id, recvr) = create_chatview(&mut sg, "chatty");
     let node = sg.get_node(node_id).unwrap();
     let prop = node.get_property("rect").unwrap();
     prop.set_f32(Role::App, 0, 0.).unwrap();
     prop.set_f32(Role::App, 1, EDITCHAT_HEIGHT).unwrap();
-    prop.set_expr(Role::App, 2, shape::load_var("w")).unwrap();
+    prop.set_expr(Role::App, 2, expr::load_var("w")).unwrap();
     let code = cc.compile("h - 2 * EDITCHAT_HEIGHT").unwrap();
     prop.set_expr(Role::App, 3, code).unwrap();
     node.set_property_f32(Role::App, "font_size", FONTSIZE).unwrap();
@@ -749,56 +660,47 @@ pub(super) async fn make(app: &App) {
     node.pimpl = pimpl;
 
     sg.link(node_id, layer_node_id).unwrap();
+    */
 
     // Create the editbox bg
-    let node_id = create_vector_art(&mut sg, "editbox_bg");
-
-    let node = sg.get_node_mut(node_id).unwrap();
+    let node = create_vector_art("editbox_bg");
     let prop = node.get_property("rect").unwrap();
     prop.set_f32(Role::App, 0, 0.).unwrap();
     let code = cc.compile("h - EDITCHAT_HEIGHT").unwrap();
     prop.set_expr(Role::App, 1, code).unwrap();
-    prop.set_expr(Role::App, 2, shape::load_var("w")).unwrap();
+    prop.set_expr(Role::App, 2, expr::load_var("w")).unwrap();
     prop.set_f32(Role::App, 3, EDITCHAT_HEIGHT).unwrap();
     node.set_property_u32(Role::App, "z_index", 1).unwrap();
-    drop(sg);
+
     let mut shape = VectorShape::new();
     shape.add_filled_box(
-        shape::const_f32(0.),
-        shape::const_f32(0.),
+        expr::const_f32(0.),
+        expr::const_f32(0.),
         cc.compile("w - SENDLABEL_WIDTH").unwrap(),
-        shape::load_var("h"),
+        expr::load_var("h"),
         [0., 0.13, 0.08, 1.],
     );
     shape.add_filled_box(
         cc.compile("w - SENDLABEL_WIDTH").unwrap(),
-        shape::const_f32(0.),
+        expr::const_f32(0.),
         cc.compile("w - SENDLABEL_WIDTH - 1").unwrap(),
-        shape::load_var("h"),
+        expr::load_var("h"),
         [0.4, 0.4, 0.4, 1.],
     );
     shape.add_outline(
-        shape::const_f32(0.),
-        shape::const_f32(0.),
-        shape::load_var("w"),
-        shape::load_var("h"),
+        expr::const_f32(0.),
+        expr::const_f32(0.),
+        expr::load_var("w"),
+        expr::load_var("h"),
         1.,
         [0.4, 0.4, 0.4, 1.],
     );
-
-    let pimpl =
-        VectorArt::new(app.ex.clone(), app.sg.clone(), node_id, app.render_api.clone(), shape)
-            .await;
-    let mut sg = app.sg.lock().await;
-    let node = sg.get_node_mut(node_id).unwrap();
-    node.pimpl = pimpl;
-
-    sg.link(node_id, layer_node_id).unwrap();
+    let node =
+        node.setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())).await;
+    layer_node.link(node);
 
     // Create some text
-    let node_id = create_text(&mut sg, "send_label");
-
-    let node = sg.get_node_mut(node_id).unwrap();
+    let node = create_text("send_label");
     let prop = node.get_property("rect").unwrap();
     let code = cc.compile("w - (SENDLABEL_WIDTH - SENDLABEL_LHS_PAD)").unwrap();
     prop.set_expr(Role::App, 0, code).unwrap();
@@ -817,21 +719,12 @@ pub(super) async fn make(app: &App) {
     prop.set_f32(Role::App, 3, 1.).unwrap();
     node.set_property_u32(Role::App, "z_index", 2).unwrap();
 
-    drop(sg);
-    let pimpl = Text::new(
-        app.ex.clone(),
-        app.sg.clone(),
-        node_id,
-        app.render_api.clone(),
-        app.text_shaper.clone(),
-    )
-    .await;
-    let mut sg = app.sg.lock().await;
-    let node = sg.get_node_mut(node_id).unwrap();
-    node.pimpl = pimpl;
-
-    sg.link(node_id, layer_node_id).unwrap();
+    let node = node
+        .setup(|me| Text::new(me, app.render_api.clone(), app.text_shaper.clone(), app.ex.clone()))
+        .await;
+    layer_node.link(node);
 
+    /*
     // Text edit
     let node_id = create_editbox(&mut sg, "editz");
     let node = sg.get_node(node_id).unwrap();
@@ -918,4 +811,5 @@ pub(super) async fn make(app: &App) {
     node.pimpl = pimpl;
 
     sg.link(node_id, layer_node_id).unwrap();
+    */
 }

+ 7 - 0
bin/darkwallet/src/expr/mod.rs

@@ -28,6 +28,13 @@ use std::io::{Read, Write};
 mod compile;
 pub use compile::Compiler;
 
+pub fn const_f32(x: f32) -> SExprCode {
+    vec![Op::ConstFloat32(x)]
+}
+pub fn load_var<S: Into<String>>(var: S) -> SExprCode {
+    vec![Op::LoadVar(var.into())]
+}
+
 #[derive(Clone, Debug, PartialEq, SerialEncodable, SerialDecodable)]
 pub enum SExprVal {
     Null,

+ 68 - 2
bin/darkwallet/src/gfx/linalg.rs

@@ -16,7 +16,41 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use std::ops::{Add, AddAssign, Sub, SubAssign};
+use std::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign};
+
+#[derive(Clone, Copy, Debug)]
+pub struct Dimension {
+    pub w: f32,
+    pub h: f32,
+}
+
+impl Dimension {
+    pub fn contains(&self, other: &Dimension) -> bool {
+        other.w <= self.w && other.h <= self.h
+    }
+}
+
+impl From<[f32; 2]> for Dimension {
+    fn from(dim: [f32; 2]) -> Self {
+        Self { w: dim[0], h: dim[1] }
+    }
+}
+
+impl Mul<f32> for Dimension {
+    type Output = Dimension;
+
+    fn mul(self, scale: f32) -> Self::Output {
+        Self { w: self.w * scale, h: self.h * scale }
+    }
+}
+
+impl Div<f32> for Dimension {
+    type Output = Dimension;
+
+    fn div(self, scale: f32) -> Self::Output {
+        Self { w: self.w / scale, h: self.h / scale }
+    }
+}
 
 #[derive(Clone, Copy, Debug)]
 pub struct Point {
@@ -29,6 +63,10 @@ impl Point {
         Self { x, y }
     }
 
+    pub fn zero() -> Self {
+        Self { x: 0., y: 0. }
+    }
+
     pub fn unpack(&self) -> (f32, f32) {
         (self.x, self.y)
     }
@@ -80,7 +118,7 @@ impl SubAssign for Point {
     }
 }
 
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, Copy)]
 pub struct Rectangle {
     pub x: f32,
     pub y: f32,
@@ -97,6 +135,8 @@ impl Rectangle {
         Self { x: 0., y: 0., w: 0., h: 0. }
     }
 
+    /// Use from() instead
+    #[deprecated]
     pub fn from_array(arr: [f32; 4]) -> Self {
         Self { x: arr[0], y: arr[1], w: arr[2], h: arr[3] }
     }
@@ -164,6 +204,10 @@ impl Rectangle {
         Point { x: self.x + self.w, y: self.y + self.h }
     }
 
+    pub fn dim(&self) -> Dimension {
+        Dimension { w: self.w, h: self.h }
+    }
+
     #[deprecated]
     pub fn top_left(&self) -> Point {
         Point { x: self.x, y: self.y }
@@ -178,6 +222,12 @@ impl Rectangle {
     }
 }
 
+impl From<[f32; 4]> for Rectangle {
+    fn from(rect: [f32; 4]) -> Self {
+        Self { x: rect[0], y: rect[1], w: rect[2], h: rect[3] }
+    }
+}
+
 impl Add<Point> for Rectangle {
     type Output = Rectangle;
 
@@ -193,3 +243,19 @@ impl Sub<Point> for Rectangle {
         Self { x: self.x - other.x, y: self.y - other.y, w: self.w, h: self.h }
     }
 }
+
+impl Mul<f32> for Rectangle {
+    type Output = Rectangle;
+
+    fn mul(self, scale: f32) -> Self::Output {
+        Self { x: self.x * scale, y: self.y * scale, w: self.w * scale, h: self.h * scale }
+    }
+}
+
+impl Div<f32> for Rectangle {
+    type Output = Rectangle;
+
+    fn div(self, scale: f32) -> Self::Output {
+        Self { x: self.x / scale, y: self.y / scale, w: self.w / scale, h: self.h / scale }
+    }
+}

+ 92 - 15
bin/darkwallet/src/gfx/mod.rs

@@ -31,7 +31,7 @@ use std::{
 };
 
 mod linalg;
-pub use linalg::{Point, Rectangle};
+pub use linalg::{Dimension, Point, Rectangle};
 mod shader;
 
 use crate::{
@@ -144,6 +144,9 @@ impl GfxDrawMesh {
 
 #[derive(Debug, Clone)]
 pub enum GfxDrawInstruction {
+    SetScale(f32),
+    Move(Point),
+    ApplyView(Rectangle),
     ApplyViewport(Rectangle),
     ApplyMatrix(glam::Mat4),
     Draw(GfxDrawMesh),
@@ -156,6 +159,9 @@ impl GfxDrawInstruction {
         buffers: &HashMap<GfxBufferId, miniquad::BufferId>,
     ) -> DrawInstruction {
         match self {
+            Self::SetScale(scale) => DrawInstruction::SetScale(scale),
+            Self::Move(off) => DrawInstruction::Move(off),
+            Self::ApplyView(view) => DrawInstruction::ApplyView(view),
             Self::ApplyViewport(rect) => DrawInstruction::ApplyViewport(rect),
             Self::ApplyMatrix(mat) => DrawInstruction::ApplyMatrix(mat),
             Self::Draw(mesh) => DrawInstruction::Draw(mesh.compile(textures, buffers)),
@@ -194,6 +200,9 @@ struct DrawMesh {
 
 #[derive(Debug, Clone)]
 enum DrawInstruction {
+    SetScale(f32),
+    Move(Point),
+    ApplyView(Rectangle),
     ApplyViewport(Rectangle),
     ApplyMatrix(glam::Mat4),
     Draw(DrawMesh),
@@ -211,6 +220,10 @@ struct RenderContext<'a> {
     draw_calls: &'a HashMap<u64, DrawCall>,
     uniforms_data: [u8; 128],
     white_texture: miniquad::TextureId,
+
+    scale: f32,
+    view: Rectangle,
+    cursor: Point,
 }
 
 impl<'a> RenderContext<'a> {
@@ -218,40 +231,88 @@ impl<'a> RenderContext<'a> {
         if DEBUG_RENDER {
             debug!(target: "gfx", "RenderContext::draw()");
         }
+        let curr_pos = Point::zero();
         self.draw_call(&self.draw_calls[&0], 0);
         if DEBUG_RENDER {
             debug!(target: "gfx", "RenderContext::draw() [DONE]");
         }
     }
 
-    fn apply_view(&mut self, view: &Rectangle) {
+    fn apply_view(&mut self) {
+        let view = self.view / self.scale;
+
         let (_, screen_height) = window::screen_size();
 
         let view_x = view.x.round() as i32;
         let view_y = screen_height - (view.y + view.h);
-        let view_y = view_y.round() as i32;
+        let view_y = view.y.round() as i32;
         let view_w = view.w.round() as i32;
         let view_h = view.h.round() as i32;
 
+        //if DEBUG_RENDER {
+        //    debug!(target: "gfx", "=> viewport {view_x} {view_y} {view_w} {view_h}");
+        //}
         self.ctx.apply_viewport(view_x, view_y, view_w, view_h);
         self.ctx.apply_scissor_rect(view_x, view_y, view_w, view_h);
     }
 
+    fn apply_model(&mut self) {
+        let off_x = self.cursor.x / self.view.w;
+        let off_y = self.cursor.y / self.view.h;
+
+        let scale_w = self.scale / self.view.w;
+        let scale_h = self.scale / self.view.h;
+
+        let model = glam::Mat4::from_translation(glam::Vec3::new(off_x, off_y, 0.)) *
+            glam::Mat4::from_scale(glam::Vec3::new(scale_w, scale_h, 1.));
+
+        let data: [u8; 64] = unsafe { std::mem::transmute_copy(&model) };
+        self.uniforms_data[64..].copy_from_slice(&data);
+        self.ctx.apply_uniforms_from_bytes(self.uniforms_data.as_ptr(), self.uniforms_data.len());
+    }
+
     fn draw_call(&mut self, draw_call: &DrawCall, indent: u32) {
         let ws = if DEBUG_RENDER { " ".repeat(indent as usize * 4) } else { String::new() };
 
-        let mut prev_view = None;
+        //// This is buggy since it does not reset back to parent
+        //let mut prev_view = None;
+        let old_view = self.view;
+        let old_cursor = self.cursor;
 
         for instr in &draw_call.instrs {
             match instr {
+                DrawInstruction::SetScale(scale) => self.scale = *scale,
+                DrawInstruction::Move(off) => {
+                    self.cursor += *off;
+                    if DEBUG_RENDER {
+                        debug!(target: "gfx",
+                            "{ws}move({off:?})  cursor={:?}, scale={}, view={:?}",
+                            self.cursor, self.scale, self.view
+                        );
+                    }
+                    self.apply_model();
+                }
+                DrawInstruction::ApplyView(view) => {
+                    self.view = *view;
+                    if DEBUG_RENDER {
+                        debug!(target: "gfx",
+                            "{ws}apply_view({view:?})  scale={}, view={:?}",
+                            self.scale, self.view
+                        );
+                    }
+                    self.apply_view();
+                }
                 DrawInstruction::ApplyViewport(view) => {
+                    /*
                     if DEBUG_RENDER {
                         debug!(target: "gfx", "{}apply_viewport({:?})", ws, view);
                     }
                     prev_view = Some(view.clone());
                     self.apply_view(view);
+                    */
                 }
                 DrawInstruction::ApplyMatrix(model) => {
+                    /*
                     if DEBUG_RENDER {
                         debug!(target: "gfx", "{}apply_matrix(", ws);
                         debug!(target: "gfx", "{}    {:?}", ws, model.row(0).to_array());
@@ -266,10 +327,11 @@ impl<'a> RenderContext<'a> {
                         self.uniforms_data.as_ptr(),
                         self.uniforms_data.len(),
                     );
+                    */
                 }
                 DrawInstruction::Draw(mesh) => {
                     if DEBUG_RENDER {
-                        debug!(target: "gfx", "{}draw({:?})", ws, mesh);
+                        debug!(target: "gfx", "{ws}draw({mesh:?})");
                     }
                     let texture = match mesh.texture {
                         Some(texture) => texture,
@@ -287,12 +349,16 @@ impl<'a> RenderContext<'a> {
         }
 
         let mut draw_calls: Vec<_> =
-            draw_call.dcs.iter().map(|key| &self.draw_calls[key]).collect();
-        draw_calls.sort_unstable_by_key(|dc| dc.z_index);
+            draw_call.dcs.iter().map(|key| (key, &self.draw_calls[key])).collect();
+        draw_calls.sort_unstable_by_key(|(_, dc)| dc.z_index);
 
-        for dc in draw_calls {
+        for (dc_key, dc) in draw_calls {
+            if DEBUG_RENDER {
+                debug!(target: "gfx", "{ws}drawcall {dc_key}");
+            }
             self.draw_call(dc, indent + 1);
 
+            /*
             // Reset view back again in case the draw call changed it
             if let Some(view) = &prev_view {
                 if DEBUG_RENDER {
@@ -300,7 +366,14 @@ impl<'a> RenderContext<'a> {
                 }
                 self.apply_view(view);
             }
+            */
         }
+
+        self.cursor = old_cursor;
+        self.apply_model();
+
+        self.view = old_view;
+        self.apply_view();
     }
 }
 
@@ -317,7 +390,7 @@ pub enum GraphicsMethod {
 pub type GraphicsEventPublisherPtr = Arc<GraphicsEventPublisher>;
 
 pub struct GraphicsEventPublisher {
-    resize: PublisherPtr<(f32, f32)>,
+    resize: PublisherPtr<Dimension>,
     key_down: PublisherPtr<(KeyCode, KeyMods, bool)>,
     key_up: PublisherPtr<(KeyCode, KeyMods)>,
     chr: PublisherPtr<(char, KeyMods, bool)>,
@@ -343,9 +416,8 @@ impl GraphicsEventPublisher {
         })
     }
 
-    fn notify_resize(&self, w: f32, h: f32) {
-        let ev = (w, h);
-        self.resize.notify(ev);
+    fn notify_resize(&self, screen_size: Dimension) {
+        self.resize.notify(screen_size);
     }
     fn notify_key_down(&self, key: KeyCode, mods: KeyMods, repeat: bool) {
         let ev = (key, mods, repeat);
@@ -379,7 +451,7 @@ impl GraphicsEventPublisher {
         self.touch.notify(ev);
     }
 
-    pub fn subscribe_resize(&self) -> Subscription<(f32, f32)> {
+    pub fn subscribe_resize(&self) -> Subscription<Dimension> {
         self.resize.clone().subscribe()
     }
     pub fn subscribe_key_down(&self) -> Subscription<(KeyCode, KeyMods, bool)> {
@@ -441,7 +513,7 @@ impl Stage {
         #[cfg(target_os = "android")]
         {
             let (screen_width, screen_height) = window::screen_size();
-            event_pub.notify_resize(screen_width, screen_height);
+            event_pub.notify_resize(Dimension::from([screen_width, screen_height]));
         }
 
         let white_texture = ctx.new_texture_from_rgba8(1, 1, &[255, 255, 255, 255]);
@@ -614,11 +686,16 @@ impl EventHandler for Stage {
         //uniforms_data[64..].copy_from_slice(&data);
         assert_eq!(128, 2 * UniformType::Mat4.size());
 
+        let (screen_w, screen_h) = miniquad::window::screen_size();
+
         let mut render_ctx = RenderContext {
             ctx: &mut self.ctx,
             draw_calls: &self.draw_calls,
             uniforms_data,
             white_texture: self.white_texture,
+            scale: 1.,
+            view: Rectangle::from([0., 0., screen_w, screen_h]),
+            cursor: Point::from([0., 0.]),
         };
         render_ctx.draw();
 
@@ -626,7 +703,7 @@ impl EventHandler for Stage {
     }
 
     fn resize_event(&mut self, width: f32, height: f32) {
-        self.event_pub.notify_resize(width, height);
+        self.event_pub.notify_resize(Dimension::from([width, height]));
     }
 
     fn key_down_event(&mut self, keycode: KeyCode, mods: KeyMods, repeat: bool) {

+ 23 - 16
bin/darkwallet/src/main.rs

@@ -28,12 +28,16 @@
 #![feature(let_chains)]
 // consume a box
 #![feature(box_into_inner)]
+// we need Arc::get_mut_unchecked() to workaround the lack of Arc::new_cyclic() which
+// accepts async fns.
+// See https://github.com/rust-lang/rust/issues/112566
+#![feature(get_mut_unchecked)]
 
 // Use these to incrementally fix warnings with cargo fix
 //#![allow(warnings, unused)]
 //#![deny(unused_imports)]
 
-use async_lock::Mutex as AsyncMutex;
+use async_lock::{Mutex as AsyncMutex, RwLock as AsyncRwLock};
 use std::sync::{mpsc, Arc};
 
 #[macro_use]
@@ -42,23 +46,26 @@ extern crate log;
 use log::LevelFilter;
 
 mod app;
-mod darkirc;
+//mod darkirc;
 mod error;
 mod expr;
 mod gfx;
 mod mesh;
-mod net;
+//mod net;
 //mod plugin;
 mod prop;
 mod pubsub;
 //mod py;
 mod ringbuf;
+//mod scene;
 mod scene;
+use scene::SceneNode as SceneNode3;
 mod text;
 mod ui;
 mod util;
 
-use crate::{darkirc::DarkIrcBackend, net::ZeroMQAdapter, scene::SceneGraph, text::TextShaper};
+//use crate::{darkirc::DarkIrcBackend, net::ZeroMQAdapter, scene::SceneGraph, text::TextShaper};
+use crate::text::TextShaper;
 
 pub type ExecutorPtr = Arc<smol::Executor<'static>>;
 
@@ -99,18 +106,18 @@ fn main() {
     }
 
     let ex = Arc::new(smol::Executor::new());
-    let sg = Arc::new(AsyncMutex::new(SceneGraph::new()));
+    let sg3 = SceneNode3::root();
 
     let async_runtime = app::AsyncRuntime::new(ex.clone());
     async_runtime.start();
 
-    let sg2 = sg.clone();
-    let ex2 = ex.clone();
-    let zmq_task = ex.spawn(async {
-        let zmq_rpc = ZeroMQAdapter::new(sg2, ex2).await;
-        zmq_rpc.run().await;
-    });
-    async_runtime.push_task(zmq_task);
+    //let sg2 = sg.clone();
+    //let ex2 = ex.clone();
+    //let zmq_task = ex.spawn(async {
+    //    let zmq_rpc = ZeroMQAdapter::new(sg2, ex2).await;
+    //    zmq_rpc.run().await;
+    //});
+    //async_runtime.push_task(zmq_task);
 
     let (method_req, method_rep) = mpsc::channel();
     // The UI actually needs to be running for this to reply back.
@@ -120,14 +127,14 @@ fn main() {
 
     let text_shaper = TextShaper::new();
 
-    let darkirc_backend = DarkIrcBackend::new();
+    //let darkirc_backend = DarkIrcBackend::new();
     let app = app::App::new(
-        sg.clone(),
-        ex.clone(),
+        sg3.clone(),
         render_api.clone(),
         event_pub.clone(),
         text_shaper,
-        darkirc_backend,
+        //darkirc_backend,
+        ex.clone(),
     );
     let app_task = ex.spawn(app.clone().start());
     async_runtime.push_task(app_task);

+ 12 - 2
bin/darkwallet/src/prop/mod.rs

@@ -31,8 +31,8 @@ use crate::{
 
 mod wrap;
 pub use wrap::{
-    PropertyBool, PropertyColor, PropertyFloat32, PropertyPoint, PropertyRect, PropertyStr,
-    PropertyUint32,
+    PropertyBool, PropertyColor, PropertyDimension, PropertyFloat32, PropertyPoint, PropertyRect,
+    PropertyStr, PropertyUint32,
 };
 
 type Buffer = Arc<Vec<u8>>;
@@ -335,6 +335,16 @@ impl Property {
         self.defaults = defaults.into_iter().map(|v| PropertyValue::Str(v)).collect();
         Ok(())
     }
+    pub fn set_defaults_null(&mut self) -> Result<()> {
+        if !self.is_null_allowed {
+            return Err(Error::PropertyNullNotAllowed)
+        }
+        if !self.is_bounded() {
+            return Err(Error::PropertyWrongLen)
+        }
+        self.defaults = (0..self.array_len).map(|_| PropertyValue::Null).collect();
+        Ok(())
+    }
 
     // Set
 

+ 59 - 9
bin/darkwallet/src/prop/wrap.rs

@@ -19,8 +19,8 @@
 use crate::{
     error::{Error, Result},
     expr::{SExprMachine, SExprVal},
-    gfx::{Point, Rectangle},
-    scene::SceneNode,
+    gfx::{Dimension, Point, Rectangle},
+    scene::SceneNode as SceneNode3,
 };
 
 use super::{PropertyPtr, Role};
@@ -33,7 +33,7 @@ pub struct PropertyBool {
 }
 
 impl PropertyBool {
-    pub fn wrap(node: &SceneNode, role: Role, prop_name: &str, idx: usize) -> Result<Self> {
+    pub fn wrap(node: &SceneNode3, role: Role, prop_name: &str, idx: usize) -> Result<Self> {
         let prop = node.get_property(prop_name).ok_or(Error::PropertyNotFound)?;
 
         // Test if it works
@@ -70,7 +70,7 @@ impl PropertyUint32 {
         Ok(Self { prop, role, idx })
     }
 
-    pub fn wrap(node: &SceneNode, role: Role, prop_name: &str, idx: usize) -> Result<Self> {
+    pub fn wrap(node: &SceneNode3, role: Role, prop_name: &str, idx: usize) -> Result<Self> {
         let prop = node.get_property(prop_name).ok_or(Error::PropertyNotFound)?;
 
         // Test if it works
@@ -100,7 +100,7 @@ pub struct PropertyFloat32 {
 }
 
 impl PropertyFloat32 {
-    pub fn wrap(node: &SceneNode, role: Role, prop_name: &str, idx: usize) -> Result<Self> {
+    pub fn wrap(node: &SceneNode3, role: Role, prop_name: &str, idx: usize) -> Result<Self> {
         let prop = node.get_property(prop_name).ok_or(Error::PropertyNotFound)?;
 
         // Test if it works
@@ -130,7 +130,7 @@ pub struct PropertyStr {
 }
 
 impl PropertyStr {
-    pub fn wrap(node: &SceneNode, role: Role, prop_name: &str, idx: usize) -> Result<Self> {
+    pub fn wrap(node: &SceneNode3, role: Role, prop_name: &str, idx: usize) -> Result<Self> {
         let prop = node.get_property(prop_name).ok_or(Error::PropertyNotFound)?;
 
         // Test if it works
@@ -159,7 +159,7 @@ pub struct PropertyColor {
 }
 
 impl PropertyColor {
-    pub fn wrap(node: &SceneNode, role: Role, prop_name: &str) -> Result<Self> {
+    pub fn wrap(node: &SceneNode3, role: Role, prop_name: &str) -> Result<Self> {
         let prop = node.get_property(prop_name).ok_or(Error::PropertyNotFound)?;
 
         if !prop.is_bounded() || prop.get_len() != 4 {
@@ -193,6 +193,40 @@ impl PropertyColor {
     }
 }
 
+#[derive(Clone)]
+pub struct PropertyDimension {
+    prop: PropertyPtr,
+    role: Role,
+}
+
+impl PropertyDimension {
+    pub fn wrap(node: &SceneNode3, role: Role, prop_name: &str) -> Result<Self> {
+        let prop = node.get_property(prop_name).ok_or(Error::PropertyNotFound)?;
+
+        if !prop.is_bounded() || prop.get_len() != 2 {
+            return Err(Error::PropertyWrongLen)
+        }
+
+        // Test if it works
+        let _ = prop.get_f32(0)?;
+
+        Ok(Self { prop, role })
+    }
+
+    pub fn get(&self) -> Dimension {
+        [self.prop.get_f32(0).unwrap(), self.prop.get_f32(1).unwrap()].into()
+    }
+
+    pub fn set(&self, dim: Dimension) {
+        self.prop.set_f32(self.role, 0, dim.w).unwrap();
+        self.prop.set_f32(self.role, 1, dim.h).unwrap();
+    }
+
+    pub fn prop(&self) -> PropertyPtr {
+        self.prop.clone()
+    }
+}
+
 #[derive(Clone)]
 pub struct PropertyPoint {
     prop: PropertyPtr,
@@ -200,7 +234,7 @@ pub struct PropertyPoint {
 }
 
 impl PropertyPoint {
-    pub fn wrap(node: &SceneNode, role: Role, prop_name: &str) -> Result<Self> {
+    pub fn wrap(node: &SceneNode3, role: Role, prop_name: &str) -> Result<Self> {
         let prop = node.get_property(prop_name).ok_or(Error::PropertyNotFound)?;
 
         if !prop.is_bounded() || prop.get_len() != 2 {
@@ -234,7 +268,7 @@ pub struct PropertyRect {
 }
 
 impl PropertyRect {
-    pub fn wrap(node: &SceneNode, role: Role, prop_name: &str) -> Result<Self> {
+    pub fn wrap(node: &SceneNode3, role: Role, prop_name: &str) -> Result<Self> {
         let prop = node.get_property(prop_name).ok_or(Error::PropertyNotFound)?;
 
         if !prop.is_bounded() || prop.get_len() != 4 {
@@ -278,6 +312,22 @@ impl PropertyRect {
         ])
     }
 
+    pub fn get_opt(&self) -> Option<Rectangle> {
+        Some(Rectangle::from_array([
+            self.prop.get_f32(0).ok()?,
+            self.prop.get_f32(1).ok()?,
+            self.prop.get_f32(2).ok()?,
+            self.prop.get_f32(3).ok()?,
+        ]))
+    }
+
+    pub fn set(&self, rect: &Rectangle) {
+        self.prop.set_f32(self.role, 0, rect.x).unwrap();
+        self.prop.set_f32(self.role, 1, rect.y).unwrap();
+        self.prop.set_f32(self.role, 2, rect.y).unwrap();
+        self.prop.set_f32(self.role, 3, rect.y).unwrap();
+    }
+
     pub fn prop(&self) -> PropertyPtr {
         self.prop.clone()
     }

+ 100 - 420
bin/darkwallet/src/scene.rs

@@ -17,10 +17,16 @@
  */
 
 use async_channel::Sender;
-use async_lock::Mutex;
-use darkfi_serial::{async_trait, FutAsyncWriteExt, SerialDecodable, SerialEncodable};
-use futures::{stream::FuturesUnordered, StreamExt};
-use std::{fmt, str::FromStr, sync::Arc};
+use async_lock::RwLock as AsyncRwLock;
+use async_trait::async_trait;
+use darkfi_serial::{FutAsyncWriteExt, SerialDecodable, SerialEncodable};
+use std::{
+    collections::VecDeque,
+    fmt,
+    future::Future,
+    str::FromStr,
+    sync::{Arc, RwLock as SyncRwLock, Weak},
+};
 
 use crate::{
     error::{Error, Result},
@@ -28,33 +34,7 @@ use crate::{
     ui,
 };
 
-pub type SceneNodeId = u32;
-
-#[derive(Debug, Copy, Clone, PartialEq, SerialEncodable, SerialDecodable)]
-#[repr(u8)]
-pub enum SceneNodeType {
-    Null = 0,
-    Root = 1,
-    Window = 2,
-    WindowInput = 6,
-    Keyboard = 7,
-    Mouse = 8,
-    Layer = 3,
-    Object = 4,
-    VectorArt = 5,
-    Text = 9,
-    Texture = 13,
-    Fonts = 10,
-    Font = 11,
-    Plugins = 14,
-    Plugin = 15,
-    ChatView = 16,
-    EditBox = 17,
-    Image = 18,
-    Button = 19,
-}
-
-pub struct ScenePath(Vec<String>);
+pub struct ScenePath(VecDeque<String>);
 
 impl<S: Into<String>> From<S> for ScenePath {
     fn from(path: S) -> Self {
@@ -81,7 +61,7 @@ impl FromStr for ScenePath {
             return Err(Error::InvalidScenePath);
         }
         if s == "/" {
-            return Ok(ScenePath(vec![]));
+            return Ok(ScenePath(VecDeque::new()));
         }
 
         let mut tokens = s.split('/');
@@ -91,343 +71,126 @@ impl FromStr for ScenePath {
             return Err(Error::InvalidScenePath);
         }
 
-        let mut path = vec![];
+        let mut path = VecDeque::new();
         for token in tokens {
             // There should not be any double slashes //
             if token.is_empty() {
                 return Err(Error::InvalidScenePath);
             }
-            path.push(token.to_string());
+            path.push_back(token.to_string());
         }
         Ok(ScenePath(path))
     }
 }
 
-pub type SceneGraphPtr2 = Arc<Mutex<SceneGraph>>;
+pub type SceneNodePtr = Arc<SceneNode>;
+pub type SceneNodeWeak = Weak<SceneNode>;
 
-pub struct SceneGraph {
-    // Node 0 is always the root
-    nodes: Vec<SceneNode>,
-    freed: Vec<SceneNodeId>,
+pub type SceneNodeId = u32;
+
+#[derive(Debug, Copy, Clone, PartialEq, SerialEncodable, SerialDecodable)]
+#[repr(u8)]
+pub enum SceneNodeType {
+    Null = 0,
+    Root = 1,
+    Window = 2,
+    WindowInput = 6,
+    Keyboard = 7,
+    Mouse = 8,
+    Layer = 3,
+    Object = 4,
+    VectorArt = 5,
+    Text = 9,
+    Texture = 13,
+    Fonts = 10,
+    Font = 11,
+    Plugins = 14,
+    Plugin = 15,
+    ChatView = 16,
+    EditBox = 17,
+    Image = 18,
+    Button = 19,
 }
 
-impl SceneGraph {
-    pub const ROOT_ID: SceneNodeId = 0;
+pub struct SceneNode {
+    pub name: String,
+    pub id: SceneNodeId,
+    pub typ: SceneNodeType,
+    parent: SyncRwLock<Option<Weak<Self>>>,
+    children: SyncRwLock<Vec<SceneNodePtr>>,
+    pub props: Vec<PropertyPtr>,
+    pub sigs: Vec<Signal>,
+    pub methods: Vec<Method>,
+    pub pimpl: Pimpl,
+}
 
-    pub fn new() -> Self {
-        let root = SceneNode {
-            name: "/".to_string(),
-            id: 0,
-            typ: SceneNodeType::Root,
-            parents: vec![],
-            children: vec![],
-            props: vec![],
-            sigs: vec![],
-            methods: vec![],
-            pimpl: Pimpl::Null,
-        };
-        Self { nodes: vec![root], freed: vec![] }
+impl SceneNode {
+    pub fn root() -> SceneNodePtr {
+        Arc::new(Self::new("", SceneNodeType::Root))
     }
 
-    pub fn add_node<S: Into<String>>(&mut self, name: S, typ: SceneNodeType) -> &mut SceneNode {
-        let node = SceneNode {
+    pub fn new<S: Into<String>>(name: S, typ: SceneNodeType) -> Self {
+        Self {
             name: name.into(),
-            // We set this at the end
-            id: 0,
+            id: rand::random(),
             typ,
-            parents: vec![],
-            children: vec![],
+            parent: SyncRwLock::new(None),
+            children: SyncRwLock::new(vec![]),
             props: vec![],
             sigs: vec![],
             methods: vec![],
             pimpl: Pimpl::Null,
-        };
-
-        let node_id = if self.freed.is_empty() {
-            let node_id = self.nodes.len() as SceneNodeId;
-            self.nodes.push(node);
-            node_id
-        } else {
-            let node_id = self.freed.pop().unwrap();
-            let _ = std::mem::replace(&mut self.nodes[node_id as usize], node);
-            node_id
-        };
-
-        self.nodes[node_id as usize].id = node_id;
-        &mut self.nodes[node_id as usize]
-    }
-
-    pub fn remove_node(&mut self, id: SceneNodeId) -> Result<()> {
-        let node = self.get_node_mut(id).ok_or(Error::NodeNotFound)?;
-        if !node.parents.is_empty() {
-            return Err(Error::NodeHasParents);
-        }
-        if !node.children.is_empty() {
-            return Err(Error::NodeHasChildren);
         }
-        node.name.clear();
-        node.typ = SceneNodeType::Null;
-        node.props.clear();
-        self.freed.push(id);
-        Ok(())
     }
 
-    #[allow(dead_code)]
-    fn root(&self) -> &SceneNode {
-        &self.nodes[0]
-    }
-    #[allow(dead_code)]
-    fn root_mut(&mut self) -> &mut SceneNode {
-        &mut self.nodes[0]
-    }
-
-    fn exists(&self, id: SceneNodeId) -> bool {
-        id < self.nodes.len() as SceneNodeId && !self.freed.contains(&id)
-    }
-
-    pub fn get_node(&self, id: SceneNodeId) -> Option<&SceneNode> {
-        if self.exists(id) {
-            Some(&self.nodes[id as usize])
-        } else {
-            None
-        }
-    }
-    pub fn get_node_mut(&mut self, id: SceneNodeId) -> Option<&mut SceneNode> {
-        if self.exists(id) {
-            Some(&mut self.nodes[id as usize])
-        } else {
-            None
+    pub async fn setup<F, Fut>(self, pimpl_fn: F) -> SceneNodePtr
+    where
+        F: FnOnce(SceneNodeWeak) -> Fut,
+        Fut: Future<Output = Pimpl>,
+    {
+        let mut self_ = Arc::new(self);
+        let weak_self = Arc::downgrade(&self_);
+        let pimpl = pimpl_fn(weak_self).await;
+        // Arc::new_cyclic() doesnt allow async so we do this instead
+        unsafe {
+            Arc::get_mut_unchecked(&mut self_).pimpl = pimpl;
         }
+        self_
     }
 
-    pub fn link(&mut self, child_id: SceneNodeId, parent_id: SceneNodeId) -> Result<()> {
-        // Check both nodes are not already linked
-        let is_linked = self.is_linked(child_id, parent_id)?;
-        if is_linked {
-            return Err(Error::NodesAreLinked);
-        }
-
-        if child_id == parent_id {
-            return Err(Error::NodesAreSame);
-        }
-
-        let parent = self.get_node(parent_id).unwrap();
-        let parent_inf =
-            SceneNodeInfo { name: parent.name.clone(), id: parent_id, typ: parent.typ };
-        let child_name = &self.get_node(child_id).unwrap().name;
-        if parent.has_child(child_name) {
-            return Err(Error::NodeChildNameConflict);
-        }
+    pub fn link(&self, child: SceneNodePtr) {
+        let mut childs_parent = child.parent.write().unwrap();
+        assert!(childs_parent.is_none());
+        *childs_parent = Some(Arc::downgrade(&child));
+        drop(childs_parent);
 
-        // Link parent into child
-        let child = self.get_node_mut(child_id).unwrap();
-        if child.has_parent(&parent_inf.name) {
-            return Err(Error::NodeParentNameConflict);
-        }
-        let child_inf = SceneNodeInfo { name: child.name.clone(), id: child_id, typ: child.typ };
-        assert!(!child.has_parent_id(parent_id));
-        child.parents.push(parent_inf);
-
-        // Link child into parent
-        let parent = self.get_node_mut(parent_id).unwrap();
-        assert!(!parent.has_child(&child_inf.name));
-        parent.children.push(child_inf);
-        Ok(())
+        let mut children = self.children.write().unwrap();
+        children.push(child);
     }
 
-    pub fn unlink(&mut self, child_id: SceneNodeId, parent_id: SceneNodeId) -> Result<()> {
-        // Check both nodes are actually linked
-        let is_linked = self.is_linked(child_id, parent_id)?;
-        if !is_linked {
-            return Err(Error::NodesNotLinked);
-        }
-
-        // Unlink parent from child
-        let child = self.get_node_mut(child_id).unwrap();
-        child.remove_parent(parent_id);
-
-        // Unlink child from parent
-        let parent = self.get_node_mut(parent_id).unwrap();
-        parent.remove_child(child_id);
-        Ok(())
+    pub fn get_children(&self) -> Vec<SceneNodePtr> {
+        self.children.read().unwrap().clone()
     }
 
-    pub fn is_linked(&self, child_id: SceneNodeId, parent_id: SceneNodeId) -> Result<bool> {
-        let parent = self.get_node(parent_id).ok_or(Error::ParentNodeNotFound)?;
-        let child = self.get_node(child_id).ok_or(Error::ChildNodeNotFound)?;
-        let parent_has_child = parent.has_child_id(child_id);
-        let child_has_parent = child.has_parent_id(parent_id);
-        // Internal consistency checks
-        if parent_has_child {
-            assert!(child_has_parent);
-        } else {
-            assert!(!child_has_parent);
-        }
-        Ok(parent_has_child)
-    }
-
-    pub fn lookup_node_id<P: Into<ScenePath>>(&self, path: P) -> Option<SceneNodeId> {
+    pub fn lookup_node<P: Into<ScenePath>>(self: Arc<Self>, path: P) -> Option<SceneNodePtr> {
         let path: ScenePath = path.into();
-        let mut current_id = Self::ROOT_ID;
-        for node_name in path.0 {
-            let parent_node = self.get_node(current_id).unwrap();
-            match parent_node.get_child(&node_name) {
-                Some(child_id) => {
-                    current_id = child_id;
-                }
-                None => return None,
-            }
+        let mut path = path.0;
+        if path.is_empty() {
+            return Some(self)
         }
-        Some(current_id)
-    }
-
-    pub fn lookup_node<P: Into<ScenePath>>(&self, path: P) -> Option<&SceneNode> {
-        let node_id = self.lookup_node_id(path)?;
-        Some(self.get_node(node_id).unwrap())
-    }
-    pub fn lookup_node_mut<P: Into<ScenePath>>(&mut self, path: P) -> Option<&mut SceneNode> {
-        let node_id = self.lookup_node_id(path)?;
-        Some(self.get_node_mut(node_id).unwrap())
-    }
-
-    pub fn rename_node<S: Into<String>>(
-        &mut self,
-        node_id: SceneNodeId,
-        node_name: S,
-    ) -> Result<()> {
-        let node_name = node_name.into();
-        for sibling_inf in self.node_siblings(node_id)? {
-            if sibling_inf.name == node_name {
-                return Err(Error::NodeSiblingNameConflict)
-            }
-        }
-        let node = self.get_node_mut(node_id).unwrap();
-        node.name = node_name.clone();
-
-        // Now update it for all children and parents too
-        let parent_ids: Vec<_> = node.parents.iter().map(|parent_inf| parent_inf.id).collect();
-        let child_ids: Vec<_> = node.children.iter().map(|child_inf| child_inf.id).collect();
-
-        'next_parent: for parent_id in parent_ids {
-            let parent = self.get_node_mut(parent_id).unwrap();
-            for child in &mut parent.children {
-                if child.id == node_id {
-                    child.name = node_name.clone();
-                    continue 'next_parent
-                }
-            }
-            panic!("child {} not found in parent {}!", node_id, parent.id)
-        }
-
-        'next_child: for child_id in child_ids {
-            let child = self.get_node_mut(child_id).unwrap();
-            for parent in &mut child.parents {
-                if parent.id == node_id {
-                    parent.name = node_name.clone();
-                    continue 'next_child
-                }
-            }
-            panic!("parent {} not found in child {}!", node_id, child.id)
-        }
-        Ok(())
-    }
-    fn node_siblings(&self, node_id: SceneNodeId) -> Result<Vec<SceneNodeInfo>> {
-        let mut siblings = vec![];
-        let node = self.get_node(node_id).ok_or(Error::NodeNotFound)?;
-        for parent_inf in &node.parents {
-            let parent = self.get_node(parent_inf.id).ok_or(Error::ParentNodeNotFound)?;
-            let mut sibling_infs = parent
-                .children
-                .iter()
-                .cloned()
-                .filter(|child_inf| child_inf.id != node_id)
-                .collect();
-            siblings.append(&mut sibling_infs);
-        }
-        Ok(siblings)
-    }
-
-    pub fn scan_dangling(&self) -> Vec<SceneNodeId> {
-        let mut dangling = vec![];
-        for node in &self.nodes {
-            if node.id == Self::ROOT_ID {
-                continue
-            }
-            if self.freed.contains(&node.id) {
-                continue
-            }
-            if node.parents.is_empty() {
-                dangling.push(node.id);
-            }
-        }
-        dangling
-    }
-}
-
-#[derive(Clone)]
-pub struct SceneNodeInfo {
-    pub name: String,
-    pub id: SceneNodeId,
-    pub typ: SceneNodeType,
-}
-
-pub struct SceneNode {
-    pub name: String,
-    pub id: SceneNodeId,
-    pub typ: SceneNodeType,
-    pub parents: Vec<SceneNodeInfo>,
-    pub children: Vec<SceneNodeInfo>,
-    pub props: Vec<PropertyPtr>,
-    pub sigs: Vec<Signal>,
-    pub methods: Vec<Method>,
-    pub pimpl: Pimpl,
-}
-
-impl SceneNode {
-    fn has_parent_id(&self, parent_id: SceneNodeId) -> bool {
-        self.parents.iter().any(|parent| parent.id == parent_id)
-    }
-    fn has_child_id(&self, child_id: SceneNodeId) -> bool {
-        self.children.iter().any(|child| child.id == child_id)
-    }
-
-    fn has_parent(&self, parent_name: &str) -> bool {
-        self.parents.iter().any(|parent| parent.name == parent_name)
-    }
-    fn has_child(&self, child_name: &str) -> bool {
-        self.children.iter().any(|child| child.name == child_name)
-    }
-    fn get_child(&self, child_name: &str) -> Option<SceneNodeId> {
-        for child in &self.children {
+        let child_name = path.pop_front().unwrap();
+        for child in self.get_children() {
             if child.name == child_name {
-                return Some(child.id);
+                let path = ScenePath(path);
+                return child.lookup_node(path)
             }
         }
         None
     }
 
-    // Panics if parent is not linked
-    fn remove_parent(&mut self, parent_id: SceneNodeId) {
-        let parent_idx = self.parents.iter().position(|parent| parent.id == parent_id).unwrap();
-        self.parents.swap_remove(parent_idx);
-    }
-    // Panics if child is not linked
-    fn remove_child(&mut self, child_id: SceneNodeId) {
-        let child_idx = self.children.iter().position(|child| child.id == child_id).unwrap();
-        self.children.swap_remove(child_idx);
-    }
-
-    pub fn get_children(&self, allowed_types: &[SceneNodeType]) -> Vec<SceneNodeInfo> {
-        self.children
-            .iter()
-            .cloned()
-            .filter(move |child_inf| allowed_types.contains(&child_inf.typ))
-            .collect()
-    }
-    pub fn get_children2(&self) -> Vec<SceneNodeInfo> {
-        self.children.iter().cloned().collect()
+    fn has_property(&self, name: &str) -> bool {
+        self.props.iter().any(|prop| prop.name == name)
     }
-
     pub fn add_property(&mut self, prop: Property) -> Result<()> {
         if self.has_property(&prop.name) {
             return Err(Error::PropertyAlreadyExists);
@@ -436,10 +199,6 @@ impl SceneNode {
         Ok(())
     }
 
-    fn has_property(&self, name: &str) -> bool {
-        self.props.iter().any(|prop| prop.name == name)
-    }
-
     pub fn get_property(&self, name: &str) -> Option<PropertyPtr> {
         self.props.iter().find(|prop| prop.name == name).map(|prop| prop.clone())
     }
@@ -507,48 +266,6 @@ impl SceneNode {
     fn has_signal(&self, name: &str) -> bool {
         self.sigs.iter().any(|sig| sig.name == name)
     }
-    pub fn get_signal(&self, name: &str) -> Option<&Signal> {
-        self.sigs.iter().find(|sig| sig.name == name)
-    }
-    fn get_signal_mut(&mut self, name: &str) -> Option<&mut Signal> {
-        self.sigs.iter_mut().find(|sig| sig.name == name)
-    }
-
-    pub fn register(&mut self, sig_name: &str, slot: Slot) -> Result<SlotId> {
-        let sig = self.get_signal_mut(sig_name).ok_or(Error::SignalNotFound)?;
-        let slot_id = if sig.freed.is_empty() {
-            let slot_id = sig.slots.len() as SlotId;
-            sig.slots.push(slot);
-            slot_id
-        } else {
-            let slot_id = sig.freed.pop().unwrap();
-            let _ = std::mem::replace(&mut sig.slots[slot_id as usize], slot);
-            slot_id
-        };
-        Ok(slot_id)
-    }
-    pub fn unregister(&mut self, sig_name: &str, slot_id: SlotId) -> Result<()> {
-        let sig = self.get_signal_mut(sig_name).ok_or(Error::SignalNotFound)?;
-        if !sig.slot_exists(slot_id) {
-            return Err(Error::SlotNotFound);
-        }
-        sig.freed.push(slot_id);
-        Ok(())
-    }
-
-    pub async fn trigger(&self, sig_name: &str, data: Vec<u8>) -> Result<()> {
-        let sig = self.get_signal(sig_name).ok_or(Error::SignalNotFound)?;
-        let futures = FuturesUnordered::new();
-        // TODO: autoremove slots which fail to send
-        for (_, slot) in sig.get_slots() {
-            debug!(target: "scene", "triggering {}", slot.name);
-            // Trigger the slot
-            futures.push(async { slot.notify.send(data.clone()).await.is_ok() });
-        }
-        let success: Vec<_> = futures.collect().await;
-        debug!(target: "scene", "trigger success: {success:?}");
-        Ok(())
-    }
 
     pub fn add_method<S: Into<String>>(
         &mut self,
@@ -558,7 +275,7 @@ impl SceneNode {
         method_fn: MethodRequestFn,
     ) -> Result<()> {
         let name = name.into();
-        if self.has_signal(&name) {
+        if self.has_method(&name) {
             return Err(Error::MethodAlreadyExists);
         }
         let args = args
@@ -573,19 +290,14 @@ impl SceneNode {
         Ok(())
     }
 
-    pub fn get_method(&self, name: &str) -> Option<&Method> {
-        self.methods.iter().find(|method| method.name == name)
+    fn has_method(&self, name: &str) -> bool {
+        self.methods.iter().any(|sig| sig.name == name)
     }
+}
 
-    pub fn call_method(
-        &mut self,
-        name: &str,
-        arg_data: Vec<u8>,
-        response_fn: MethodResponseFn,
-    ) -> Result<()> {
-        let method = self.get_method(name).ok_or(Error::MethodNotFound)?;
-        (method.method_fn)(arg_data, response_fn);
-        Ok(())
+impl std::fmt::Debug for SceneNode {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "'{}':{}", self.name, self.id)
     }
 }
 
@@ -622,32 +334,6 @@ pub struct Signal {
     freed: Vec<SlotId>,
 }
 
-impl Signal {
-    fn slot_exists(&self, slot_id: SlotId) -> bool {
-        if slot_id >= self.slots.len() as SlotId {
-            return false;
-        }
-        return !self.freed.contains(&slot_id);
-    }
-
-    pub fn get_slots<'a>(&'a self) -> impl Iterator<Item = (SlotId, &'a Slot)> {
-        self.slots
-            .iter()
-            .enumerate()
-            .filter(|(slot_id, _)| !self.freed.contains(&(*slot_id as SlotId)))
-            .map(|(slot_id, slot)| (slot_id as SlotId, slot))
-    }
-
-    pub fn lookup_slot_id(&self, slot_name: &str) -> Option<SlotId> {
-        for (slot_id, slot) in self.get_slots() {
-            if slot.name == slot_name {
-                return Some(slot_id);
-            }
-        }
-        None
-    }
-}
-
 type MethodRequestFn = Box<dyn Fn(Vec<u8>, MethodResponseFn) + Send + Sync>;
 pub type MethodResponseFn = Box<dyn Fn(Result<Vec<u8>>) + Send + Sync>;
 
@@ -664,14 +350,8 @@ pub enum Pimpl {
     Layer(ui::LayerPtr),
     VectorArt(ui::VectorArtPtr),
     Text(ui::TextPtr),
-    EditBox(ui::EditBoxPtr),
-    ChatView(ui::ChatViewPtr),
+    //EditBox(ui::EditBoxPtr),
+    //ChatView(ui::ChatViewPtr),
     Image(ui::ImagePtr),
-    Button(ui::ButtonPtr),
-}
-
-impl std::fmt::Debug for SceneNode {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "'{}':{}", self.name, self.id)
-    }
+    //Button(ui::ButtonPtr),
 }

+ 70 - 74
bin/darkwallet/src/ui/image.rs

@@ -28,16 +28,16 @@ use crate::{
     gfx::{GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, GfxTextureId, Rectangle, RenderApiPtr},
     mesh::{MeshBuilder, MeshInfo, COLOR_WHITE},
     prop::{PropertyPtr, PropertyRect, PropertyStr, PropertyUint32, Role},
-    scene::{Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId},
+    scene::{Pimpl, SceneNodePtr, SceneNodeWeak},
     ExecutorPtr,
 };
 
-use super::{get_parent_rect, DrawUpdate, OnModify, UIObject};
+use super::{DrawUpdate, OnModify, UIObject};
 
 pub type ImagePtr = Arc<Image>;
 
 pub struct Image {
-    sg: SceneGraphPtr2,
+    node: SceneNodeWeak,
     render_api: RenderApiPtr,
     #[allow(dead_code)]
     tasks: Vec<smol::Task<()>>,
@@ -46,26 +46,24 @@ pub struct Image {
     texture: SyncMutex<Option<GfxTextureId>>,
     dc_key: u64,
 
-    node_id: SceneNodeId,
     rect: PropertyRect,
     z_index: PropertyUint32,
     path: PropertyStr,
+
+    parent_rect: SyncMutex<Option<Rectangle>>,
 }
 
 impl Image {
-    pub async fn new(
-        ex: ExecutorPtr,
-        sg: SceneGraphPtr2,
-        node_id: SceneNodeId,
-        render_api: RenderApiPtr,
-    ) -> Pimpl {
-        let scene_graph = sg.lock().await;
-        let node = scene_graph.get_node(node_id).unwrap();
-        let node_name = node.name.clone();
-        let rect = PropertyRect::wrap(node, Role::Internal, "rect").unwrap();
-        let z_index = PropertyUint32::wrap(node, Role::Internal, "z_index", 0).unwrap();
-        let path = PropertyStr::wrap(node, Role::Internal, "path", 0).unwrap();
-        drop(scene_graph);
+    pub async fn new(node: SceneNodeWeak, render_api: RenderApiPtr, ex: ExecutorPtr) -> Pimpl {
+        debug!(target: "ui::image", "Image::new()");
+
+        let node_ref = &node.upgrade().unwrap();
+        let rect = PropertyRect::wrap(node_ref, Role::Internal, "rect").unwrap();
+        let z_index = PropertyUint32::wrap(node_ref, Role::Internal, "z_index", 0).unwrap();
+        let path = PropertyStr::wrap(node_ref, Role::Internal, "path", 0).unwrap();
+
+        let node_name = node_ref.name.clone();
+        let node_id = node_ref.id;
 
         let self_ = Arc::new_cyclic(|me: &Weak<Self>| {
             let mut on_modify = OnModify::new(ex, node_name, node_id, me.clone());
@@ -74,16 +72,19 @@ impl Image {
             on_modify.when_change(path.prop(), Self::reload);
 
             Self {
-                sg,
+                node,
                 render_api,
                 tasks: on_modify.tasks,
+
                 mesh: SyncMutex::new(None),
                 texture: SyncMutex::new(None),
                 dc_key: OsRng.gen(),
-                node_id,
+
                 rect,
                 z_index,
                 path,
+
+                parent_rect: SyncMutex::new(None),
             }
         });
 
@@ -128,68 +129,41 @@ impl Image {
     }
 
     async fn redraw(self: Arc<Self>) {
-        let sg = self.sg.lock().await;
-        let node = sg.get_node(self.node_id).unwrap();
-
-        let Some(parent_rect) = get_parent_rect(&sg, node) else {
-            return;
-        };
+        let Some(parent_rect) = self.parent_rect.lock().unwrap().clone() else { return };
 
-        let Some(draw_update) = self.draw(&sg, &parent_rect).await else {
-            error!(target: "ui::text", "Text {:?} failed to draw", node);
+        let Some(draw_update) = self.get_draw_calls(parent_rect).await else {
+            error!(target: "ui::image", "Image failed to draw");
             return;
         };
         self.render_api.replace_draw_calls(draw_update.draw_calls);
-        debug!(target: "ui::text", "replace draw calls done");
+        debug!(target: "ui::image", "replace draw calls done");
+        for texture in draw_update.freed_textures {
+            self.render_api.delete_texture(texture);
+        }
+        for buff in draw_update.freed_buffers {
+            self.render_api.delete_buffer(buff);
+        }
     }
 
     /// Called whenever any property changes.
-    fn regen_mesh(&self, _clip: Rectangle) -> MeshInfo {
-        let basic = Rectangle { x: 0., y: 0., w: 1., h: 1. };
-
+    fn regen_mesh(&self) -> MeshInfo {
+        let rect = self.rect.get();
+        let uv = Rectangle::from([0., 0., 1., 1.]);
+        let mesh_rect = Rectangle::from([0., 0., rect.w, rect.h]);
         let mut mesh = MeshBuilder::new();
-        mesh.draw_box(&basic, COLOR_WHITE, &basic);
+        mesh.draw_box(&mesh_rect, COLOR_WHITE, &uv);
         mesh.alloc(&self.render_api)
     }
-}
 
-impl Drop for Image {
-    fn drop(&mut self) {
-        // TODO: Delete own draw call
-
-        // Free buffers
-        // Should this be in drop?
-        if let Some(mesh) = &*self.mesh.lock().unwrap() {
-            let vertex_buffer = mesh.vertex_buffer;
-            let index_buffer = mesh.index_buffer;
-            self.render_api.delete_buffer(vertex_buffer);
-            self.render_api.delete_buffer(index_buffer);
-        }
-        let texture_id = self.texture.lock().unwrap().unwrap();
-        self.render_api.delete_texture(texture_id);
-    }
-}
-
-#[async_trait]
-impl UIObject for Image {
-    fn z_index(&self) -> u32 {
-        self.z_index.get()
-    }
-
-    async fn draw(&self, sg: &SceneGraph, parent_rect: &Rectangle) -> Option<DrawUpdate> {
-        debug!(target: "ui::text", "Text::draw()");
-        // Only used for debug messages
-        let node = sg.get_node(self.node_id).unwrap();
-
-        self.rect.eval(parent_rect).ok()?;
+    async fn get_draw_calls(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
+        self.rect.eval(&parent_rect).ok()?;
         let rect = self.rect.get();
 
-        // draw will recalc this when it's None
-        let mesh = self.regen_mesh(rect.clone());
+        let mesh = self.regen_mesh();
         let old_mesh = std::mem::replace(&mut *self.mesh.lock().unwrap(), Some(mesh.clone()));
 
         let Some(texture_id) = *self.texture.lock().unwrap() else {
-            panic!("Node {:?} missing texture_id!", node);
+            panic!("Node missing texture_id!");
         };
 
         // We're finished with these so clean up.
@@ -206,21 +180,13 @@ impl UIObject for Image {
             num_elements: mesh.num_elements,
         };
 
-        let off_x = rect.x / parent_rect.w;
-        let off_y = rect.y / parent_rect.h;
-        // We could use pixels here if we want to. No difference really.
-        let scale_x = rect.w / parent_rect.w;
-        let scale_y = rect.h / parent_rect.h;
-        let model = glam::Mat4::from_translation(glam::Vec3::new(off_x, off_y, 0.)) *
-            glam::Mat4::from_scale(glam::Vec3::new(scale_x, scale_y, 1.));
-
         Some(DrawUpdate {
             key: self.dc_key,
             draw_calls: vec![(
                 self.dc_key,
                 GfxDrawCall {
                     instrs: vec![
-                        GfxDrawInstruction::ApplyMatrix(model),
+                        GfxDrawInstruction::Move(rect.pos()),
                         GfxDrawInstruction::Draw(mesh),
                     ],
                     dcs: vec![],
@@ -232,3 +198,33 @@ impl UIObject for Image {
         })
     }
 }
+
+#[async_trait]
+impl UIObject for Image {
+    fn z_index(&self) -> u32 {
+        self.z_index.get()
+    }
+
+    async fn draw(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
+        debug!(target: "ui::image", "Image::draw()");
+        *self.parent_rect.lock().unwrap() = Some(parent_rect);
+        self.get_draw_calls(parent_rect).await
+    }
+}
+
+impl Drop for Image {
+    fn drop(&mut self) {
+        // TODO: Delete own draw call
+
+        // Free buffers
+        // Should this be in drop?
+        if let Some(mesh) = &*self.mesh.lock().unwrap() {
+            let vertex_buffer = mesh.vertex_buffer;
+            let index_buffer = mesh.index_buffer;
+            self.render_api.delete_buffer(vertex_buffer);
+            self.render_api.delete_buffer(index_buffer);
+        }
+        let texture_id = self.texture.lock().unwrap().unwrap();
+        self.render_api.delete_texture(texture_id);
+    }
+}

+ 129 - 150
bin/darkwallet/src/ui/layer.rs

@@ -18,136 +18,88 @@
 
 use async_recursion::async_recursion;
 use async_trait::async_trait;
+use atomic_float::AtomicF32;
 use miniquad::{KeyCode, KeyMods, MouseButton, TouchPhase};
 use rand::{rngs::OsRng, Rng};
-use std::sync::{Arc, Weak};
+use std::sync::{atomic::Ordering, Arc, Mutex as SyncMutex, Weak};
 
 use crate::{
     gfx::{GfxDrawCall, GfxDrawInstruction, Point, Rectangle, RenderApiPtr},
-    prop::{PropertyBool, PropertyPtr, PropertyRect, PropertyUint32, Role},
-    scene::{Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId},
+    prop::{PropertyBool, PropertyFloat32, PropertyPtr, PropertyRect, PropertyUint32, Role},
+    scene::{Pimpl, SceneNodePtr, SceneNodeWeak},
     ExecutorPtr,
 };
 
-use super::{
-    get_child_nodes_ordered, get_parent_rect, get_ui_object, DrawUpdate, OnModify, Stoppable,
-    UIObject,
-};
+use super::{get_children_ordered, get_ui_object3, DrawUpdate, OnModify, UIObject};
 
 pub type LayerPtr = Arc<Layer>;
 
 pub struct Layer {
-    sg: SceneGraphPtr2,
-    node_id: SceneNodeId,
-    // Task is dropped at the end of the scope for Layer, hence ending it
-    #[allow(dead_code)]
-    tasks: Vec<smol::Task<()>>,
+    node: SceneNodeWeak,
     render_api: RenderApiPtr,
-
+    _tasks: Vec<smol::Task<()>>,
     dc_key: u64,
 
     is_visible: PropertyBool,
     rect: PropertyRect,
     z_index: PropertyUint32,
+
+    parent_rect: SyncMutex<Option<Rectangle>>,
 }
 
 impl Layer {
-    pub async fn new(
-        ex: ExecutorPtr,
-        sg_ptr: SceneGraphPtr2,
-        node_id: SceneNodeId,
-        render_api: RenderApiPtr,
-    ) -> Pimpl {
-        let sg = sg_ptr.lock().await;
-        let node = sg.get_node(node_id).unwrap();
-        let node_name = node.name.clone();
-
-        let is_visible =
-            PropertyBool::wrap(node, Role::Internal, "is_visible", 0).expect("Layer::is_visible");
-        let rect = PropertyRect::wrap(node, Role::Internal, "rect").unwrap();
-        let z_index = PropertyUint32::wrap(node, Role::Internal, "z_index", 0).unwrap();
-        drop(sg);
+    pub async fn new(node: SceneNodeWeak, render_api: RenderApiPtr, ex: ExecutorPtr) -> Pimpl {
+        debug!(target: "ui::layer", "Layer::new()");
+
+        let node_ref = &node.upgrade().unwrap();
+        let is_visible = PropertyBool::wrap(node_ref, Role::Internal, "is_visible", 0).unwrap();
+        let rect = PropertyRect::wrap(node_ref, Role::Internal, "rect").unwrap();
+        let z_index = PropertyUint32::wrap(node_ref, Role::Internal, "z_index", 0).unwrap();
+
+        let node_name = node_ref.name.clone();
+        let node_id = node_ref.id;
 
         let self_ = Arc::new_cyclic(|me: &Weak<Self>| {
             let mut on_modify = OnModify::new(ex.clone(), node_name, node_id, me.clone());
             on_modify.when_change(rect.prop(), Self::redraw);
 
             Self {
-                sg: sg_ptr,
-                node_id,
-                tasks: on_modify.tasks,
+                node,
                 render_api,
+                _tasks: on_modify.tasks,
                 dc_key: OsRng.gen(),
+
                 is_visible,
                 rect,
                 z_index,
+
+                parent_rect: SyncMutex::new(None),
             }
         });
 
         Pimpl::Layer(self_)
     }
 
-    pub async fn handle_char(
-        &self,
-        sg: &SceneGraph,
-        key: char,
-        mods: KeyMods,
-        repeat: bool,
-    ) -> bool {
-        false
+    fn get_children(&self) -> Vec<SceneNodePtr> {
+        let node = self.node.upgrade().unwrap();
+        get_children_ordered(&node)
     }
 
     async fn redraw(self: Arc<Self>) {
-        let sg = self.sg.lock().await;
-        let node = sg.get_node(self.node_id).unwrap();
+        let Some(parent_rect) = self.parent_rect.lock().unwrap().clone() else { return };
 
-        let Some(parent_rect) = get_parent_rect(&sg, node) else {
-            return;
-        };
-
-        let Some(draw_update) = self.draw(&sg, &parent_rect).await else {
-            error!(target: "ui::layer", "Layer {:?} failed to draw", node);
+        let Some(draw_update) = self.get_draw_calls(parent_rect).await else {
+            error!(target: "ui::layer", "Layer failed to draw");
             return;
         };
         self.render_api.replace_draw_calls(draw_update.draw_calls);
         debug!(target: "ui::layer", "replace draw calls done");
     }
-}
 
-impl Stoppable for Layer {
-    async fn stop(&self) {}
-}
-
-#[async_trait]
-impl UIObject for Layer {
-    fn z_index(&self) -> u32 {
-        self.z_index.get()
-    }
-
-    async fn draw(&self, sg: &SceneGraph, parent_rect: &Rectangle) -> Option<DrawUpdate> {
-        debug!(target: "ui::layer", "Layer::draw()");
-        let node = sg.get_node(self.node_id).unwrap();
-
-        if !self.is_visible.get() {
-            debug!(target: "ui::layer", "invisible layer node '{}':{}", node.name, node.id);
-            return None
-        }
-
-        self.rect.eval(parent_rect).ok()?;
-
-        let mut screen_rect = self.rect.get() + parent_rect.pos();
-
-        if !parent_rect.includes(&screen_rect) {
-            error!(
-                target: "ui::layer",
-                "layer '{}':{} rect {:?} is not inside parent {:?}",
-                node.name, node.id, screen_rect, parent_rect
-            );
-            return None
-        }
-
-        debug!(target: "ui::layer", "Parent rect: {:?}", parent_rect);
-        debug!(target: "ui::layer", "Viewport rect: {:?}", screen_rect);
+    async fn get_draw_calls(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
+        debug!(target: "ui::layer", "Layer::get_draw_calls()");
+        self.rect.eval(&parent_rect).ok()?;
+        let rect = self.rect.get();
 
         // Apply viewport
 
@@ -156,11 +108,10 @@ impl UIObject for Layer {
         let mut freed_textures = vec![];
         let mut freed_buffers = vec![];
 
-        for child_id in get_child_nodes_ordered(&sg, self.node_id) {
-            let node = sg.get_node(child_id).unwrap();
-            let obj = get_ui_object(node);
-            let Some(mut draw_update) = obj.draw(sg, &screen_rect).await else {
-                debug!(target: "ui::layer", "Skipped draw() of {node:?}");
+        for child in self.get_children() {
+            let obj = get_ui_object3(&child);
+            let Some(mut draw_update) = obj.draw(rect).await else {
+                debug!(target: "ui::layer", "Skipped draw() of {child:?}");
                 continue
             };
 
@@ -171,118 +122,146 @@ impl UIObject for Layer {
         }
 
         let dc = GfxDrawCall {
-            instrs: vec![GfxDrawInstruction::ApplyViewport(screen_rect)],
+            instrs: vec![GfxDrawInstruction::ApplyView(rect)],
             dcs: child_calls,
             z_index: 0,
         };
         draw_calls.push((self.dc_key, dc));
         Some(DrawUpdate { key: self.dc_key, draw_calls, freed_textures, freed_buffers })
     }
+}
+
+#[async_trait]
+impl UIObject for Layer {
+    fn z_index(&self) -> u32 {
+        self.z_index.get()
+    }
+
+    async fn draw(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
+        debug!(target: "ui::layer", "Layer::draw()");
+        *self.parent_rect.lock().unwrap() = Some(parent_rect);
+
+        // If we are invisible, then when layer is made visible again, the children
+        // draw calls will be recalculated and they will get the updated parent_rect.
+        if !self.is_visible.get() {
+            debug!(target: "ui::layer", "invisible layer node");
+            return None
+        }
+
+        debug!(target: "ui::layer", "Parent rect: {:?}", parent_rect);
+
+        /*
+        if !parent_rect.dim().contains(&offset_rect) {
+            error!(
+                target: "ui::layer",
+                "layer rect {:?} is not inside parent {:?}",
+                offset_rect, parent_rect
+            );
+            return None
+        }
+        */
 
-    async fn handle_char(&self, sg: &SceneGraph, key: char, mods: KeyMods, repeat: bool) -> bool {
-        for child_id in get_child_nodes_ordered(&sg, self.node_id) {
-            let node = sg.get_node(child_id).unwrap();
-            let obj = get_ui_object(node);
-            if obj.handle_char(sg, key, mods, repeat).await {
+        self.get_draw_calls(parent_rect).await
+    }
+
+    async fn handle_char(&self, key: char, mods: KeyMods, repeat: bool) -> bool {
+        if !self.is_visible.get() {
+            return false
+        }
+        for child in self.get_children() {
+            let obj = get_ui_object3(&child);
+            if obj.handle_char(key, mods, repeat).await {
                 return true
             }
         }
         false
     }
 
-    async fn handle_key_down(
-        &self,
-        sg: &SceneGraph,
-        key: KeyCode,
-        mods: KeyMods,
-        repeat: bool,
-    ) -> bool {
-        for child_id in get_child_nodes_ordered(&sg, self.node_id) {
-            let node = sg.get_node(child_id).unwrap();
-            let obj = get_ui_object(node);
-            if obj.handle_key_down(sg, key, mods, repeat).await {
+    async fn handle_key_down(&self, key: KeyCode, mods: KeyMods, repeat: bool) -> bool {
+        if !self.is_visible.get() {
+            return false
+        }
+        for child in self.get_children() {
+            let obj = get_ui_object3(&child);
+            if obj.handle_key_down(key, mods, repeat).await {
                 return true
             }
         }
         false
     }
 
-    async fn handle_key_up(&self, sg: &SceneGraph, key: KeyCode, mods: KeyMods) -> bool {
-        for child_id in get_child_nodes_ordered(&sg, self.node_id) {
-            let node = sg.get_node(child_id).unwrap();
-            let obj = get_ui_object(node);
-            if obj.handle_key_up(sg, key, mods).await {
+    async fn handle_key_up(&self, key: KeyCode, mods: KeyMods) -> bool {
+        if !self.is_visible.get() {
+            return false
+        }
+        for child in self.get_children() {
+            let obj = get_ui_object3(&child);
+            if obj.handle_key_up(key, mods).await {
                 return true
             }
         }
         false
     }
-    async fn handle_mouse_btn_down(
-        &self,
-        sg: &SceneGraph,
-        btn: MouseButton,
-        mut mouse_pos: Point,
-    ) -> bool {
+    async fn handle_mouse_btn_down(&self, btn: MouseButton, mut mouse_pos: Point) -> bool {
+        if !self.is_visible.get() {
+            return false
+        }
         mouse_pos -= self.rect.get().pos();
-        for child_id in get_child_nodes_ordered(&sg, self.node_id) {
-            let node = sg.get_node(child_id).unwrap();
-            let obj = get_ui_object(node);
-            if obj.handle_mouse_btn_down(sg, btn, mouse_pos).await {
+        for child in self.get_children() {
+            let obj = get_ui_object3(&child);
+            if obj.handle_mouse_btn_down(btn, mouse_pos).await {
                 return true
             }
         }
         false
     }
-    async fn handle_mouse_btn_up(
-        &self,
-        sg: &SceneGraph,
-        btn: MouseButton,
-        mut mouse_pos: Point,
-    ) -> bool {
+    async fn handle_mouse_btn_up(&self, btn: MouseButton, mut mouse_pos: Point) -> bool {
+        if !self.is_visible.get() {
+            return false
+        }
         mouse_pos -= self.rect.get().pos();
-        for child_id in get_child_nodes_ordered(&sg, self.node_id) {
-            let node = sg.get_node(child_id).unwrap();
-            let obj = get_ui_object(node);
-            if obj.handle_mouse_btn_up(sg, btn, mouse_pos).await {
+        for child in self.get_children() {
+            let obj = get_ui_object3(&child);
+            if obj.handle_mouse_btn_up(btn, mouse_pos).await {
                 return true
             }
         }
         false
     }
-    async fn handle_mouse_move(&self, sg: &SceneGraph, mut mouse_pos: Point) -> bool {
+    async fn handle_mouse_move(&self, mut mouse_pos: Point) -> bool {
+        if !self.is_visible.get() {
+            return false
+        }
         mouse_pos -= self.rect.get().pos();
-        for child_id in get_child_nodes_ordered(&sg, self.node_id) {
-            let node = sg.get_node(child_id).unwrap();
-            let obj = get_ui_object(node);
-            if obj.handle_mouse_move(sg, mouse_pos).await {
+        for child in self.get_children() {
+            let obj = get_ui_object3(&child);
+            if obj.handle_mouse_move(mouse_pos).await {
                 return true
             }
         }
         false
     }
-    async fn handle_mouse_wheel(&self, sg: &SceneGraph, mut wheel_pos: Point) -> bool {
+    async fn handle_mouse_wheel(&self, mut wheel_pos: Point) -> bool {
+        if !self.is_visible.get() {
+            return false
+        }
         wheel_pos -= self.rect.get().pos();
-        for child_id in get_child_nodes_ordered(&sg, self.node_id) {
-            let node = sg.get_node(child_id).unwrap();
-            let obj = get_ui_object(node);
-            if obj.handle_mouse_wheel(sg, wheel_pos).await {
+        for child in self.get_children() {
+            let obj = get_ui_object3(&child);
+            if obj.handle_mouse_wheel(wheel_pos).await {
                 return true
             }
         }
         false
     }
-    async fn handle_touch(
-        &self,
-        sg: &SceneGraph,
-        phase: TouchPhase,
-        id: u64,
-        mut touch_pos: Point,
-    ) -> bool {
+    async fn handle_touch(&self, phase: TouchPhase, id: u64, mut touch_pos: Point) -> bool {
+        if !self.is_visible.get() {
+            return false
+        }
         touch_pos -= self.rect.get().pos();
-        for child_id in get_child_nodes_ordered(&sg, self.node_id) {
-            let node = sg.get_node(child_id).unwrap();
-            let obj = get_ui_object(node);
-            if obj.handle_touch(sg, phase, id, touch_pos).await {
+        for child in self.get_children() {
+            let obj = get_ui_object3(&child);
+            if obj.handle_touch(phase, id, touch_pos).await {
                 return true
             }
         }

+ 31 - 126
bin/darkwallet/src/ui/mod.rs

@@ -25,16 +25,16 @@ use crate::{
     expr::{SExprMachine, SExprVal},
     gfx::{GfxBufferId, GfxDrawCall, GfxTextureId, Point, Rectangle},
     prop::{PropertyPtr, Role},
-    scene::{Pimpl, SceneGraph, SceneNode, SceneNodeId, SceneNodeType},
+    scene::{Pimpl, SceneNode as SceneNode3, SceneNodeId, SceneNodePtr},
     ExecutorPtr,
 };
 
-mod button;
-pub use button::{Button, ButtonPtr};
-pub mod chatview;
-pub use chatview::{ChatView, ChatViewPtr};
-mod editbox;
-pub use editbox::{EditBox, EditBoxPtr};
+//mod button;
+//pub use button::{Button, ButtonPtr};
+//pub mod chatview;
+//pub use chatview::{ChatView, ChatViewPtr};
+//mod editbox;
+//pub use editbox::{EditBox, EditBoxPtr};
 mod image;
 pub use image::{Image, ImagePtr};
 pub mod vector_art;
@@ -49,62 +49,36 @@ pub use text::{Text, TextPtr};
 mod win;
 pub use win::{Window, WindowPtr};
 
-pub trait Stoppable {
-    async fn stop(&self);
-}
-
 #[async_trait]
 pub trait UIObject: Sync {
     fn z_index(&self) -> u32;
 
-    async fn draw(&self, sg: &SceneGraph, parent_rect: &Rectangle) -> Option<DrawUpdate> {
+    async fn draw(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
         None
     }
 
-    async fn handle_char(&self, sg: &SceneGraph, key: char, mods: KeyMods, repeat: bool) -> bool {
+    async fn handle_char(&self, key: char, mods: KeyMods, repeat: bool) -> bool {
         false
     }
-    async fn handle_key_down(
-        &self,
-        sg: &SceneGraph,
-        key: KeyCode,
-        mods: KeyMods,
-        repeat: bool,
-    ) -> bool {
+    async fn handle_key_down(&self, key: KeyCode, mods: KeyMods, repeat: bool) -> bool {
         false
     }
-    async fn handle_key_up(&self, sg: &SceneGraph, key: KeyCode, mods: KeyMods) -> bool {
+    async fn handle_key_up(&self, key: KeyCode, mods: KeyMods) -> bool {
         false
     }
-    async fn handle_mouse_btn_down(
-        &self,
-        sg: &SceneGraph,
-        btn: MouseButton,
-        mouse_pos: Point,
-    ) -> bool {
+    async fn handle_mouse_btn_down(&self, btn: MouseButton, mouse_pos: Point) -> bool {
         false
     }
-    async fn handle_mouse_btn_up(
-        &self,
-        sg: &SceneGraph,
-        btn: MouseButton,
-        mouse_pos: Point,
-    ) -> bool {
+    async fn handle_mouse_btn_up(&self, btn: MouseButton, mouse_pos: Point) -> bool {
         false
     }
-    async fn handle_mouse_move(&self, sg: &SceneGraph, mouse_pos: Point) -> bool {
+    async fn handle_mouse_move(&self, mouse_pos: Point) -> bool {
         false
     }
-    async fn handle_mouse_wheel(&self, sg: &SceneGraph, wheel_pos: Point) -> bool {
+    async fn handle_mouse_wheel(&self, wheel_pos: Point) -> bool {
         false
     }
-    async fn handle_touch(
-        &self,
-        sg: &SceneGraph,
-        phase: TouchPhase,
-        id: u64,
-        touch_pos: Point,
-    ) -> bool {
+    async fn handle_touch(&self, phase: TouchPhase, id: u64, touch_pos: Point) -> bool {
         false
     }
 }
@@ -167,97 +141,28 @@ impl<T: Send + Sync + 'static> OnModify<T> {
     }
 }
 
-fn read_rect(rect_prop: PropertyPtr) -> Result<Rectangle> {
-    if rect_prop.array_len != 4 {
-        return Err(Error::PropertyWrongLen)
-    }
-
-    let mut rect = [0.; 4];
-    for i in 0..4 {
-        if rect_prop.is_expr(i)? {
-            rect[i] = rect_prop.get_cached(i)?.as_f32()?;
-        } else {
-            rect[i] = rect_prop.get_f32(i)?;
-        }
-    }
-    Ok(Rectangle::from_array(rect))
-}
-
-pub fn get_parent_rect(sg: &SceneGraph, node: &SceneNode) -> Option<Rectangle> {
-    // read our parent
-    if node.parents.is_empty() {
-        info!("Layer {:?} has no parents so skipping", node);
-        return None
-    }
-    if node.parents.len() != 1 {
-        error!("Layer {:?} has too many parents so skipping", node);
-        return None
-    }
-    let parent_id = node.parents[0].id;
-    let parent_node = sg.get_node(parent_id).unwrap();
-    let parent_rect = match parent_node.typ {
-        SceneNodeType::Window => {
-            let Some(screen_size_prop) = parent_node.get_property("screen_size") else {
-                error!(
-                    "Window {:?} parent node {:?} missing screen_size property",
-                    node, parent_node
-                );
-                return None
-            };
-            let screen_width = screen_size_prop.get_f32(0).unwrap();
-            let screen_height = screen_size_prop.get_f32(1).unwrap();
-
-            let parent_rect = Rectangle::from_array([0., 0., screen_width, screen_height]);
-            parent_rect
-        }
-        SceneNodeType::Layer => {
-            // get their rect property
-            let Some(parent_rect) = parent_node.get_property("rect") else {
-                error!("Layer {:?} parent node {:?} missing rect property", node, parent_node);
-                return None
-            };
-            // read parent's rect
-            let Ok(parent_rect) = read_rect(parent_rect) else {
-                error!("Layer {:?} parent node {:?} malformed rect property", node, parent_node);
-                return None
-            };
-            parent_rect
-        }
-        _ => {
-            error!(
-                "Layer {:?} parent node {:?} wrong type {:?}",
-                node, parent_node, parent_node.typ
-            );
-            return None
-        }
-    };
-    Some(parent_rect)
-}
-
-pub fn get_ui_object<'a>(node: &'a SceneNode) -> &'a dyn UIObject {
+pub fn get_ui_object3<'a>(node: &'a SceneNode3) -> &'a dyn UIObject {
     match &node.pimpl {
-        Pimpl::Layer(layer) => layer.as_ref(),
-        Pimpl::VectorArt(svg) => svg.as_ref(),
-        Pimpl::Text(txt) => txt.as_ref(),
-        Pimpl::EditBox(editb) => editb.as_ref(),
-        Pimpl::ChatView(chat) => chat.as_ref(),
-        Pimpl::Image(img) => img.as_ref(),
-        Pimpl::Button(btn) => btn.as_ref(),
+        Pimpl::Layer(obj) => obj.as_ref(),
+        Pimpl::VectorArt(obj) => obj.as_ref(),
+        Pimpl::Text(obj) => obj.as_ref(),
+        //Pimpl::EditBox(editb) => editb.as_ref(),
+        //Pimpl::ChatView(chat) => chat.as_ref(),
+        Pimpl::Image(obj) => obj.as_ref(),
+        //Pimpl::Button(btn) => btn.as_ref(),
         _ => panic!("unhandled type for get_ui_object"),
     }
 }
 
-pub fn get_child_nodes_ordered(sg: &SceneGraph, node_id: SceneNodeId) -> Vec<SceneNodeId> {
-    let mut child_nodes = vec![];
-    let self_node = sg.get_node(node_id).unwrap();
-    for child_inf in self_node.get_children2() {
-        let node = sg.get_node(child_inf.id).unwrap();
-        let obj = get_ui_object(node);
+pub fn get_children_ordered(node: &SceneNode3) -> Vec<SceneNodePtr> {
+    let mut child_infs = vec![];
+    for child in node.get_children() {
+        let obj = get_ui_object3(&child);
         let z_index = obj.z_index();
-        child_nodes.push((node.id, z_index));
+        child_infs.push((child, z_index));
     }
-    child_nodes.sort_unstable_by_key(|(node_id, _)| *node_id);
+    child_infs.sort_unstable_by_key(|(_, z_index)| *z_index);
 
-    let nodes = child_nodes.into_iter().rev().map(|(node_id, _)| node_id).collect();
+    let nodes = child_infs.into_iter().rev().map(|(node, _)| node).collect();
     nodes
 }

+ 66 - 67
bin/darkwallet/src/ui/text.rs

@@ -30,12 +30,12 @@ use crate::{
         PropertyBool, PropertyColor, PropertyFloat32, PropertyPtr, PropertyRect, PropertyStr,
         PropertyUint32, Role,
     },
-    scene::{Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId},
+    scene::{Pimpl, SceneNodePtr, SceneNodeWeak},
     text::{self, GlyphPositionIter, TextShaper, TextShaperPtr},
     ExecutorPtr,
 };
 
-use super::{get_parent_rect, DrawUpdate, OnModify, Stoppable, UIObject};
+use super::{DrawUpdate, OnModify, UIObject};
 
 pub type TextPtr = Arc<Text>;
 
@@ -46,7 +46,7 @@ struct TextRenderInfo {
 }
 
 pub struct Text {
-    sg: SceneGraphPtr2,
+    node: SceneNodeWeak,
     render_api: RenderApiPtr,
     text_shaper: TextShaperPtr,
     _tasks: Vec<smol::Task<()>>,
@@ -54,7 +54,6 @@ pub struct Text {
     render_info: SyncMutex<TextRenderInfo>,
     dc_key: u64,
 
-    node_id: SceneNodeId,
     rect: PropertyRect,
     z_index: PropertyUint32,
     text: PropertyStr,
@@ -62,27 +61,30 @@ pub struct Text {
     text_color: PropertyColor,
     baseline: PropertyFloat32,
     debug: PropertyBool,
+
+    parent_rect: SyncMutex<Option<Rectangle>>,
 }
 
 impl Text {
     pub async fn new(
-        ex: ExecutorPtr,
-        sg: SceneGraphPtr2,
-        node_id: SceneNodeId,
+        node: SceneNodeWeak,
         render_api: RenderApiPtr,
         text_shaper: TextShaperPtr,
+        ex: ExecutorPtr,
     ) -> Pimpl {
-        let scene_graph = sg.lock().await;
-        let node = scene_graph.get_node(node_id).unwrap();
-        let node_name = node.name.clone();
-        let rect = PropertyRect::wrap(node, Role::Internal, "rect").unwrap();
-        let z_index = PropertyUint32::wrap(node, Role::Internal, "z_index", 0).unwrap();
-        let text = PropertyStr::wrap(node, Role::Internal, "text", 0).unwrap();
-        let font_size = PropertyFloat32::wrap(node, Role::Internal, "font_size", 0).unwrap();
-        let text_color = PropertyColor::wrap(node, Role::Internal, "text_color").unwrap();
-        let baseline = PropertyFloat32::wrap(node, Role::Internal, "baseline", 0).unwrap();
-        let debug = PropertyBool::wrap(node, Role::Internal, "debug", 0).unwrap();
-        drop(scene_graph);
+        debug!(target: "ui::text", "Text::new()");
+
+        let node_ref = &node.upgrade().unwrap();
+        let rect = PropertyRect::wrap(node_ref, Role::Internal, "rect").unwrap();
+        let z_index = PropertyUint32::wrap(node_ref, Role::Internal, "z_index", 0).unwrap();
+        let text = PropertyStr::wrap(node_ref, Role::Internal, "text", 0).unwrap();
+        let font_size = PropertyFloat32::wrap(node_ref, Role::Internal, "font_size", 0).unwrap();
+        let text_color = PropertyColor::wrap(node_ref, Role::Internal, "text_color").unwrap();
+        let baseline = PropertyFloat32::wrap(node_ref, Role::Internal, "baseline", 0).unwrap();
+        let debug = PropertyBool::wrap(node_ref, Role::Internal, "debug", 0).unwrap();
+
+        let node_name = node_ref.name.clone();
+        let node_id = node_ref.id;
 
         let render_info = Self::regen_mesh(
             &render_api,
@@ -106,13 +108,13 @@ impl Text {
             on_modify.when_change(baseline.prop(), Self::redraw);
 
             Self {
-                sg,
+                node,
                 render_api,
                 text_shaper,
                 _tasks: on_modify.tasks,
                 render_info: SyncMutex::new(render_info),
                 dc_key: OsRng.gen(),
-                node_id,
+
                 rect,
                 z_index,
                 text,
@@ -120,6 +122,8 @@ impl Text {
                 text_color,
                 baseline,
                 debug,
+
+                parent_rect: SyncMutex::new(None),
             }
         });
 
@@ -163,7 +167,6 @@ impl Text {
     async fn redraw(self: Arc<Self>) {
         let old = self.render_info.lock().unwrap().clone();
 
-        // TODO move this to draw
         let render_info = Self::regen_mesh(
             &self.render_api,
             &self.text_shaper,
@@ -174,55 +177,30 @@ impl Text {
             self.debug.get(),
         )
         .await;
+
         *self.render_info.lock().unwrap() = render_info;
 
-        let sg = self.sg.lock().await;
-        let node = sg.get_node(self.node_id).unwrap();
+        let Some(parent_rect) = self.parent_rect.lock().unwrap().clone() else { return };
 
-        let Some(parent_rect) = get_parent_rect(&sg, node) else {
-            return;
-        };
-
-        let Some(draw_update) = self.draw(&sg, &parent_rect).await else {
-            error!(target: "ui::text", "Text {:?} failed to draw", node);
+        let Some(draw_update) = self.get_draw_calls(parent_rect).await else {
+            error!(target: "ui::text", "Text failed to draw");
             return;
         };
         self.render_api.replace_draw_calls(draw_update.draw_calls);
         debug!(target: "ui::text", "replace draw calls done");
 
         // We're finished with these so clean up.
+        assert!(draw_update.freed_textures.is_empty());
+        assert!(draw_update.freed_buffers.is_empty());
         self.render_api.delete_buffer(old.mesh.vertex_buffer);
         self.render_api.delete_buffer(old.mesh.index_buffer);
         self.render_api.delete_texture(old.texture_id);
     }
-}
-
-impl Stoppable for Text {
-    async fn stop(&self) {
-        // TODO: Delete own draw call
 
-        // Free buffers
-        // Should this be in drop?
-        let render_info = self.render_info.lock().unwrap().clone();
-        let vertex_buffer = render_info.mesh.vertex_buffer;
-        let index_buffer = render_info.mesh.index_buffer;
-        let texture_id = render_info.texture_id;
-        self.render_api.delete_buffer(vertex_buffer);
-        self.render_api.delete_buffer(index_buffer);
-        self.render_api.delete_texture(texture_id);
-    }
-}
-
-#[async_trait]
-impl UIObject for Text {
-    fn z_index(&self) -> u32 {
-        self.z_index.get()
-    }
-
-    async fn draw(&self, sg: &SceneGraph, parent_rect: &Rectangle) -> Option<DrawUpdate> {
-        debug!(target: "ui::text", "Text::draw()");
-        // Only used for debug messages
-        let node = sg.get_node(self.node_id).unwrap();
+    async fn get_draw_calls(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
+        debug!(target: "ui::text", "Text::get_draw_calls()");
+        self.rect.eval(&parent_rect).ok()?;
+        let rect = self.rect.get();
 
         let render_info = self.render_info.lock().unwrap().clone();
 
@@ -233,23 +211,13 @@ impl UIObject for Text {
             num_elements: render_info.mesh.num_elements,
         };
 
-        self.rect.eval(parent_rect).ok()?;
-        let rect = self.rect.get();
-
-        let off_x = rect.x / parent_rect.w;
-        let off_y = rect.y / parent_rect.h;
-        let scale_x = 1. / parent_rect.w;
-        let scale_y = 1. / parent_rect.h;
-        let model = glam::Mat4::from_translation(glam::Vec3::new(off_x, off_y, 0.)) *
-            glam::Mat4::from_scale(glam::Vec3::new(scale_x, scale_y, 1.));
-
         Some(DrawUpdate {
             key: self.dc_key,
             draw_calls: vec![(
                 self.dc_key,
                 GfxDrawCall {
                     instrs: vec![
-                        GfxDrawInstruction::ApplyMatrix(model),
+                        GfxDrawInstruction::Move(rect.pos()),
                         GfxDrawInstruction::Draw(mesh),
                     ],
                     dcs: vec![],
@@ -261,3 +229,34 @@ impl UIObject for Text {
         })
     }
 }
+
+#[async_trait]
+impl UIObject for Text {
+    fn z_index(&self) -> u32 {
+        self.z_index.get()
+    }
+
+    async fn draw(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
+        debug!(target: "ui::text", "Text::draw()");
+        *self.parent_rect.lock().unwrap() = Some(parent_rect);
+        self.get_draw_calls(parent_rect).await
+    }
+}
+
+/*
+impl Stoppable for Text {
+    async fn stop(&self) {
+        // TODO: Delete own draw call
+
+        // Free buffers
+        // Should this be in drop?
+        let render_info = self.render_info.lock().unwrap().clone();
+        let vertex_buffer = render_info.mesh.vertex_buffer;
+        let index_buffer = render_info.mesh.index_buffer;
+        let texture_id = render_info.texture_id;
+        self.render_api.delete_buffer(vertex_buffer);
+        self.render_api.delete_buffer(index_buffer);
+        self.render_api.delete_texture(texture_id);
+    }
+}
+*/

+ 65 - 65
bin/darkwallet/src/ui/vector_art/mod.rs

@@ -27,13 +27,13 @@ use crate::{
         GfxBufferId, GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, Rectangle, RenderApiPtr, Vertex,
     },
     mesh::Color,
-    prop::{PropertyPtr, PropertyRect, PropertyUint32, Role},
-    scene::{Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId},
+    prop::{PropertyFloat32, PropertyPtr, PropertyRect, PropertyUint32, Role},
+    scene::{Pimpl, SceneNodePtr, SceneNodeWeak},
     util::enumerate,
     ExecutorPtr,
 };
 
-use super::{get_parent_rect, DrawUpdate, OnModify, Stoppable, UIObject};
+use super::{DrawUpdate, OnModify, UIObject};
 
 pub mod shape;
 use shape::VectorShape;
@@ -41,34 +41,35 @@ use shape::VectorShape;
 pub type VectorArtPtr = Arc<VectorArt>;
 
 pub struct VectorArt {
-    sg: SceneGraphPtr2,
+    node: SceneNodeWeak,
     render_api: RenderApiPtr,
     _tasks: Vec<smol::Task<()>>,
 
     shape: VectorShape,
     buffers: SyncMutex<Option<GfxDrawMesh>>,
-
     dc_key: u64,
 
-    node_id: SceneNodeId,
     rect: PropertyRect,
     z_index: PropertyUint32,
+
+    parent_rect: SyncMutex<Option<Rectangle>>,
 }
 
 impl VectorArt {
     pub async fn new(
-        ex: ExecutorPtr,
-        sg: SceneGraphPtr2,
-        node_id: SceneNodeId,
-        render_api: RenderApiPtr,
+        node: SceneNodeWeak,
         shape: VectorShape,
+        render_api: RenderApiPtr,
+        ex: ExecutorPtr,
     ) -> Pimpl {
-        let scene_graph = sg.lock().await;
-        let node = scene_graph.get_node(node_id).unwrap();
-        let node_name = node.name.clone();
-        let rect = PropertyRect::wrap(node, Role::Internal, "rect").unwrap();
-        let z_index = PropertyUint32::wrap(node, Role::Internal, "z_index", 0).unwrap();
-        drop(scene_graph);
+        debug!(target: "ui::vector_art", "VectorArt::new()");
+
+        let node_ref = &node.upgrade().unwrap();
+        let rect = PropertyRect::wrap(node_ref, Role::Internal, "rect").unwrap();
+        let z_index = PropertyUint32::wrap(node_ref, Role::Internal, "z_index", 0).unwrap();
+
+        let node_name = node_ref.name.clone();
+        let node_id = node_ref.id;
 
         let self_ = Arc::new_cyclic(|me: &Weak<Self>| {
             let mut on_modify = OnModify::new(ex, node_name, node_id, me.clone());
@@ -76,15 +77,18 @@ impl VectorArt {
             on_modify.when_change(z_index.prop(), Self::redraw);
 
             Self {
-                sg,
+                node,
                 render_api,
                 _tasks: on_modify.tasks,
+
                 shape,
                 buffers: SyncMutex::new(None),
                 dc_key: OsRng.gen(),
-                node_id,
+
                 rect,
                 z_index,
+
+                parent_rect: SyncMutex::new(None),
             }
         });
 
@@ -92,54 +96,29 @@ impl VectorArt {
     }
 
     async fn redraw(self: Arc<Self>) {
-        let sg = self.sg.lock().await;
-        let node = sg.get_node(self.node_id).unwrap();
-
-        let Some(parent_rect) = get_parent_rect(&sg, node) else {
-            return;
-        };
+        let Some(parent_rect) = self.parent_rect.lock().unwrap().clone() else { return };
 
-        let Some(draw_update) = self.draw(&sg, &parent_rect).await else {
-            error!(target: "ui::vector_art", "Mesh {:?} failed to draw", node);
+        let Some(draw_update) = self.get_draw_calls(parent_rect).await else {
+            error!(target: "ui::vector_art", "Mesh failed to draw");
             return;
         };
         self.render_api.replace_draw_calls(draw_update.draw_calls);
-        debug!(target: "ui::vector_art", "replace draw calls done");
-    }
-}
-
-impl Stoppable for VectorArt {
-    async fn stop(&self) {
-        // TODO: Delete own draw call
-
-        // Free buffers
-        // Should this be in drop?
-        if let Some(mesh) = &*self.buffers.lock().unwrap() {
-            self.render_api.delete_buffer(mesh.vertex_buffer);
-            self.render_api.delete_buffer(mesh.index_buffer);
+        for texture in draw_update.freed_textures {
+            self.render_api.delete_texture(texture);
         }
+        for buff in draw_update.freed_buffers {
+            self.render_api.delete_buffer(buff);
+        }
+        debug!(target: "ui::vector_art", "replace draw calls done");
     }
-}
-
-#[async_trait]
-impl UIObject for VectorArt {
-    fn z_index(&self) -> u32 {
-        self.z_index.get()
-    }
-
-    async fn draw(&self, sg: &SceneGraph, parent_rect: &Rectangle) -> Option<DrawUpdate> {
-        debug!(target: "ui::vector_art", "VectorArt::draw()");
-        // Only used for debug messages
-        let node = sg.get_node(self.node_id).unwrap();
-
-        self.rect.eval(parent_rect).ok()?;
-        let mut rect = self.rect.get();
-
-        rect.x += parent_rect.x;
-        rect.y += parent_rect.x;
 
+    async fn get_draw_calls(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
+        debug!(target: "ui::vector_art", "VectorArt::draw_cached()");
+        self.rect.eval(&parent_rect).ok()?;
+        let rect = self.rect.get();
         let verts = self.shape.eval(rect.w, rect.h).expect("bad shape");
 
+        //debug!(target: "ui::vector_art", "=> {verts:#?}");
         let vertex_buffer = self.render_api.new_vertex_buffer(verts);
         // You are one lazy motherfucker
         let index_buffer = self.render_api.new_index_buffer(self.shape.indices.clone());
@@ -157,20 +136,13 @@ impl UIObject for VectorArt {
             freed_buffers.push(old_mesh.index_buffer);
         }
 
-        let off_x = rect.x / parent_rect.w;
-        let off_y = rect.y / parent_rect.h;
-        let scale_x = 1. / parent_rect.w;
-        let scale_y = 1. / parent_rect.h;
-        let model = glam::Mat4::from_translation(glam::Vec3::new(off_x, off_y, 0.)) *
-            glam::Mat4::from_scale(glam::Vec3::new(scale_x, scale_y, 1.));
-
         Some(DrawUpdate {
             key: self.dc_key,
             draw_calls: vec![(
                 self.dc_key,
                 GfxDrawCall {
                     instrs: vec![
-                        GfxDrawInstruction::ApplyMatrix(model),
+                        GfxDrawInstruction::Move(rect.pos()),
                         GfxDrawInstruction::Draw(mesh),
                     ],
                     dcs: vec![],
@@ -182,3 +154,31 @@ impl UIObject for VectorArt {
         })
     }
 }
+
+#[async_trait]
+impl UIObject for VectorArt {
+    fn z_index(&self) -> u32 {
+        self.z_index.get()
+    }
+
+    async fn draw(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
+        debug!(target: "ui::vector_art", "VectorArt::draw()");
+        *self.parent_rect.lock().unwrap() = Some(parent_rect);
+        self.get_draw_calls(parent_rect).await
+    }
+}
+
+/*
+impl Stoppable for VectorArt {
+    async fn stop(&self) {
+        // TODO: Delete own draw call
+
+        // Free buffers
+        // Should this be in drop? ---> yes it should
+        if let Some(mesh) = &*self.buffers.lock().unwrap() {
+            self.render_api.delete_buffer(mesh.vertex_buffer);
+            self.render_api.delete_buffer(mesh.index_buffer);
+        }
+    }
+}
+*/

+ 0 - 8
bin/darkwallet/src/ui/vector_art/shape.rs

@@ -24,18 +24,10 @@ use crate::{
     },
     mesh::Color,
     prop::{PropertyPtr, PropertyUint32, Role},
-    scene::{Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId},
     util::enumerate,
     ExecutorPtr,
 };
 
-pub fn const_f32(x: f32) -> SExprCode {
-    vec![Op::ConstFloat32(x)]
-}
-pub fn load_var<S: Into<String>>(var: S) -> SExprCode {
-    vec![Op::LoadVar(var.into())]
-}
-
 #[derive(Debug)]
 pub struct ShapeVertex {
     x: SExprCode,

+ 84 - 109
bin/darkwallet/src/ui/win.rs

@@ -20,74 +20,69 @@ use miniquad::{KeyCode, KeyMods, MouseButton, TouchPhase};
 use std::sync::{Arc, Weak};
 
 use crate::{
-    gfx::{GfxDrawCall, GraphicsEventPublisherPtr, Point, Rectangle, RenderApiPtr},
-    prop::{PropertyFloat32, PropertyPtr, Role},
+    gfx::{
+        GfxDrawCall, GfxDrawInstruction, GraphicsEventPublisherPtr, Point, Rectangle, RenderApiPtr,
+    },
+    prop::{PropertyDimension, PropertyFloat32, PropertyPtr, Role},
     pubsub::Subscription,
-    scene::{Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId},
+    scene::{Pimpl, SceneNodePtr, SceneNodeWeak},
     ExecutorPtr,
 };
 
-use super::{get_child_nodes_ordered, get_ui_object, OnModify, Stoppable};
+use super::{get_children_ordered, get_ui_object3, OnModify};
 
 pub type WindowPtr = Arc<Window>;
 
 pub struct Window {
-    node_id: SceneNodeId,
-    sg: SceneGraphPtr2,
+    node: SceneNodeWeak,
 
     // Task is dropped at the end of the scope for Window, hence ending it
     #[allow(dead_code)]
     tasks: Vec<smol::Task<()>>,
-    screen_w: PropertyFloat32,
-    screen_h: PropertyFloat32,
+    screen_size: PropertyDimension,
+    scale: PropertyFloat32,
     render_api: RenderApiPtr,
 }
 
 impl Window {
     pub async fn new(
-        ex: ExecutorPtr,
-        sg: SceneGraphPtr2,
-        node_id: SceneNodeId,
+        node: SceneNodeWeak,
         render_api: RenderApiPtr,
         event_pub: GraphicsEventPublisherPtr,
+        ex: ExecutorPtr,
     ) -> Pimpl {
         debug!(target: "ui::win", "Window::new()");
 
-        let scene_graph = sg.lock().await;
-        let node = scene_graph.get_node(node_id).unwrap();
-        let node_name = node.name.clone();
-        let screen_w = PropertyFloat32::wrap(node, Role::Internal, "screen_size", 0).unwrap();
-        let screen_h = PropertyFloat32::wrap(node, Role::Internal, "screen_size", 1).unwrap();
-        let scale_prop = node.get_property("scale").unwrap();
-        drop(scene_graph);
+        let node_ref = &node.upgrade().unwrap();
+        let screen_size = PropertyDimension::wrap(node_ref, Role::Internal, "screen_size").unwrap();
+        let scale = PropertyFloat32::wrap(node_ref, Role::Internal, "scale", 0).unwrap();
+
+        let node_name = node_ref.name.clone();
+        let node_id = node_ref.id;
 
         let self_ = Arc::new_cyclic(|me: &Weak<Self>| {
             // Start a task monitoring for window resize events
             // which updates screen_size
             let ev_sub = event_pub.subscribe_resize();
-            let screen_w2 = screen_w.clone();
-            let screen_h2 = screen_h.clone();
+            let screen_size2 = screen_size.clone();
             let me2 = me.clone();
-            let sg2 = sg.clone();
             let resize_task = ex.spawn(async move {
                 loop {
-                    let Ok((w, h)) = ev_sub.receive().await else {
+                    let Ok(size) = ev_sub.receive().await else {
                         debug!(target: "ui::win", "Event relayer closed");
                         break
                     };
 
-                    debug!(target: "ui::win", "Window resized ({w}, {h})");
+                    debug!(target: "ui::win", "Window resized {size:?}");
                     // Now update the properties
-                    screen_w2.set(w);
-                    screen_h2.set(h);
+                    screen_size2.set(size);
 
                     let Some(self_) = me2.upgrade() else {
                         // Should not happen
                         panic!("self destroyed before modify_task was stopped!");
                     };
 
-                    let sg = sg2.lock().await;
-                    self_.draw(&sg).await;
+                    self_.draw().await;
                 }
             });
 
@@ -131,17 +126,12 @@ impl Window {
             let touch_task =
                 ex.spawn(async move { while Self::process_touch(&me2, &ev_sub).await {} });
 
-            let sg2 = sg.clone();
-            let redraw_fn = move |self_: Arc<Self>| {
-                let sg = sg2.clone();
-                async move {
-                    let sg = sg.lock().await;
-                    self_.draw(&sg).await;
-                }
+            let redraw_fn = move |self_: Arc<Self>| async move {
+                self_.draw().await;
             };
 
             let mut on_modify = OnModify::new(ex.clone(), node_name, node_id, me.clone());
-            on_modify.when_change(scale_prop, redraw_fn);
+            on_modify.when_change(scale.prop(), redraw_fn);
 
             let mut tasks = vec![
                 resize_task,
@@ -156,7 +146,7 @@ impl Window {
             ];
             tasks.append(&mut on_modify.tasks);
 
-            Self { node_id, sg, tasks, screen_w, screen_h, render_api }
+            Self { node, tasks, screen_size, scale, render_api }
         });
 
         Pimpl::Window(self_)
@@ -294,122 +284,108 @@ impl Window {
         true
     }
 
-    async fn handle_char(&self, key: char, mods: KeyMods, repeat: bool) {
-        let sg = self.sg.lock().await;
+    fn get_children(&self) -> Vec<SceneNodePtr> {
+        let node = self.node.upgrade().unwrap();
+        get_children_ordered(&node)
+    }
 
-        for child_id in get_child_nodes_ordered(&sg, self.node_id) {
-            let node = sg.get_node(child_id).unwrap();
-            let obj = get_ui_object(node);
-            if obj.handle_char(&sg, key, mods, repeat).await {
+    async fn handle_char(&self, key: char, mods: KeyMods, repeat: bool) {
+        for child in self.get_children() {
+            let obj = get_ui_object3(&child);
+            if obj.handle_char(key, mods, repeat).await {
                 return
             }
         }
     }
 
     async fn handle_key_down(&self, key: KeyCode, mods: KeyMods, repeat: bool) {
-        let sg = self.sg.lock().await;
-
-        for child_id in get_child_nodes_ordered(&sg, self.node_id) {
-            let node = sg.get_node(child_id).unwrap();
-            let obj = get_ui_object(node);
-            if obj.handle_key_down(&sg, key, mods, repeat).await {
+        for child in self.get_children() {
+            let obj = get_ui_object3(&child);
+            if obj.handle_key_down(key, mods, repeat).await {
                 return
             }
         }
     }
 
     async fn handle_key_up(&self, key: KeyCode, mods: KeyMods) {
-        let sg = self.sg.lock().await;
-
-        for child_id in get_child_nodes_ordered(&sg, self.node_id) {
-            let node = sg.get_node(child_id).unwrap();
-            let obj = get_ui_object(node);
-            if obj.handle_key_up(&sg, key, mods).await {
+        for child in self.get_children() {
+            let obj = get_ui_object3(&child);
+            if obj.handle_key_up(key, mods).await {
                 return
             }
         }
     }
 
-    async fn handle_mouse_btn_down(&self, btn: MouseButton, mouse_pos: Point) {
-        let sg = self.sg.lock().await;
+    /// Converts from screen to local coords
+    fn local_scale(&self, point: &mut Point) {
+        point.x /= self.scale.get();
+        point.y /= self.scale.get();
+    }
 
-        for child_id in get_child_nodes_ordered(&sg, self.node_id) {
-            let node = sg.get_node(child_id).unwrap();
-            let obj = get_ui_object(node);
-            if obj.handle_mouse_btn_down(&sg, btn.clone(), mouse_pos).await {
+    async fn handle_mouse_btn_down(&self, btn: MouseButton, mut mouse_pos: Point) {
+        self.local_scale(&mut mouse_pos);
+        for child in self.get_children() {
+            let obj = get_ui_object3(&child);
+            if obj.handle_mouse_btn_down(btn.clone(), mouse_pos).await {
                 return
             }
         }
     }
 
-    async fn handle_mouse_btn_up(&self, btn: MouseButton, mouse_pos: Point) {
-        let sg = self.sg.lock().await;
-
-        for child_id in get_child_nodes_ordered(&sg, self.node_id) {
-            let node = sg.get_node(child_id).unwrap();
-            let obj = get_ui_object(node);
-            if obj.handle_mouse_btn_up(&sg, btn.clone(), mouse_pos).await {
+    async fn handle_mouse_btn_up(&self, btn: MouseButton, mut mouse_pos: Point) {
+        self.local_scale(&mut mouse_pos);
+        for child in self.get_children() {
+            let obj = get_ui_object3(&child);
+            if obj.handle_mouse_btn_up(btn.clone(), mouse_pos).await {
                 return
             }
         }
     }
 
-    async fn handle_mouse_move(&self, mouse_pos: Point) {
-        let sg = self.sg.lock().await;
-
-        for child_id in get_child_nodes_ordered(&sg, self.node_id) {
-            let node = sg.get_node(child_id).unwrap();
-            let obj = get_ui_object(node);
-            if obj.handle_mouse_move(&sg, mouse_pos).await {
+    async fn handle_mouse_move(&self, mut mouse_pos: Point) {
+        self.local_scale(&mut mouse_pos);
+        for child in self.get_children() {
+            let obj = get_ui_object3(&child);
+            if obj.handle_mouse_move(mouse_pos).await {
                 return
             }
         }
     }
 
-    async fn handle_mouse_wheel(&self, wheel_pos: Point) {
-        let sg = self.sg.lock().await;
-
-        for child_id in get_child_nodes_ordered(&sg, self.node_id) {
-            let node = sg.get_node(child_id).unwrap();
-            let obj = get_ui_object(node);
-            if obj.handle_mouse_wheel(&sg, wheel_pos).await {
+    async fn handle_mouse_wheel(&self, mut wheel_pos: Point) {
+        self.local_scale(&mut wheel_pos);
+        for child in self.get_children() {
+            let obj = get_ui_object3(&child);
+            if obj.handle_mouse_wheel(wheel_pos).await {
                 return
             }
         }
     }
 
-    async fn handle_touch(&self, phase: TouchPhase, id: u64, touch_pos: Point) {
-        let sg = self.sg.lock().await;
-
-        for child_id in get_child_nodes_ordered(&sg, self.node_id) {
-            let node = sg.get_node(child_id).unwrap();
-            let obj = get_ui_object(node);
-            if obj.handle_touch(&sg, phase, id, touch_pos).await {
+    async fn handle_touch(&self, phase: TouchPhase, id: u64, mut touch_pos: Point) {
+        self.local_scale(&mut touch_pos);
+        for child in self.get_children() {
+            let obj = get_ui_object3(&child);
+            if obj.handle_touch(phase, id, touch_pos).await {
                 return
             }
         }
     }
 
-    pub async fn draw(&self, sg: &SceneGraph) {
-        let screen_w = self.screen_w.get();
-        let screen_h = self.screen_h.get();
-        debug!(target: "ui::win", "Window::draw({screen_w}, {screen_h})");
-
-        // SceneGraph should remain locked for the entire draw
-        let self_node = sg.get_node(self.node_id).unwrap();
-
-        let screen_rect = Rectangle::new(0., 0., screen_w, screen_h);
+    pub async fn draw(&self) {
+        let local = self.screen_size.get() / self.scale.get();
+        let rect = Rectangle::from([0., 0., local.w, local.h]);
+        debug!(target: "ui::win", "Window::draw({rect:?})");
 
         let mut draw_calls = vec![];
         let mut child_calls = vec![];
         let mut freed_textures = vec![];
         let mut freed_buffers = vec![];
 
-        for child_id in get_child_nodes_ordered(&sg, self.node_id) {
-            let node = sg.get_node(child_id).unwrap();
-            let obj = get_ui_object(node);
-            let Some(mut draw_update) = obj.draw(sg, &screen_rect).await else {
-                error!(target: "ui::layer", "draw() of {node:?} failed");
+        for child in self.get_children() {
+            let obj = get_ui_object3(&child);
+            let Some(mut draw_update) = obj.draw(rect).await else {
+                error!(target: "ui::layer", "draw() of {child:?} failed");
                 continue
             };
 
@@ -419,8 +395,12 @@ impl Window {
             freed_buffers.append(&mut draw_update.freed_buffers);
         }
 
-        let root_dc = GfxDrawCall { instrs: vec![], dcs: child_calls, z_index: 0 };
-        draw_calls.push((0, root_dc));
+        let dc = GfxDrawCall {
+            instrs: vec![GfxDrawInstruction::SetScale(self.scale.get())],
+            dcs: child_calls,
+            z_index: 0,
+        };
+        draw_calls.push((0, dc));
         //debug!(target: "ui::win", "  => {:?}", draw_calls);
 
         self.render_api.replace_draw_calls(draw_calls);
@@ -435,8 +415,3 @@ impl Window {
         debug!(target: "ui::win", "Window::draw() - replaced draw call");
     }
 }
-
-// Nodes should be stopped before being removed
-impl Stoppable for Window {
-    async fn stop(&self) {}
-}