Bläddra i källkod

wallet: App::setup() schema, then once miniquad starts, App::start() will first draw the scenegraph, then start the procs. This way drawing is done ASAP and fast on app startup.

darkfi 1 år sedan
förälder
incheckning
51c204be84

+ 30 - 15
bin/darkwallet/src/app/mod.rs

@@ -145,35 +145,43 @@ impl App {
         })
         })
     }
     }
 
 
-    pub async fn start(self: Arc<Self>) {
-        debug!(target: "app", "App::start()");
+    /// Does not require miniquad to be init. Created the scene graph tree / schema and all
+    /// the objects.
+    pub async fn setup(&self) {
+        debug!(target: "app", "App::setup()");
 
 
         let mut window = SceneNode3::new("window", SceneNodeType3::Window);
         let mut window = SceneNode3::new("window", SceneNodeType3::Window);
 
 
         let mut prop = Property::new("screen_size", PropertyType::Float32, PropertySubType::Pixel);
         let mut prop = Property::new("screen_size", PropertyType::Float32, PropertySubType::Pixel);
         prop.set_array_len(2);
         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();
         window.add_property(prop).unwrap();
 
 
         let mut prop = Property::new("scale", PropertyType::Float32, PropertySubType::Pixel);
         let mut prop = Property::new("scale", PropertyType::Float32, PropertySubType::Pixel);
         prop.set_defaults_f32(vec![1.]).unwrap();
         prop.set_defaults_f32(vec![1.]).unwrap();
         window.add_property(prop).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;
+        let window = window.setup(|me| Window::new(me, self.render_api.clone())).await;
         self.sg_root.clone().link(window.clone());
         self.sg_root.clone().link(window.clone());
         schema::make(&self, window).await;
         schema::make(&self, window).await;
 
 
         debug!(target: "app", "Schema loaded");
         debug!(target: "app", "Schema loaded");
+    }
+
+    /// Begins the draw of the tree, and then starts the UI procs.
+    pub async fn start(self: Arc<Self>) {
+        debug!(target: "app", "App::start()");
+
+        let window_node = self.sg_root.clone().lookup_node("/window").unwrap();
+        let prop = window_node.get_property("screen_size").unwrap();
+        // We can only do this once the window has been created in miniquad.
+        let (screen_width, screen_height) = miniquad::window::screen_size();
+        prop.set_f32(Role::App, 0, screen_width);
+        prop.set_f32(Role::App, 1, screen_height);
 
 
         // Access drawable in window node and call draw()
         // Access drawable in window node and call draw()
         self.trigger_draw().await;
         self.trigger_draw().await;
 
 
+        self.start_procs().await;
         debug!(target: "app", "App started");
         debug!(target: "app", "App started");
     }
     }
 
 
@@ -183,11 +191,6 @@ impl App {
         });
         });
     }
     }
 
 
-    /// Shutdown code here
-    async fn async_stop(&self) {
-        //self.darkirc_backend.stop().await;
-    }
-
     async fn trigger_draw(&self) {
     async fn trigger_draw(&self) {
         let window_node = self.sg_root.clone().lookup_node("/window").expect("no window attached!");
         let window_node = self.sg_root.clone().lookup_node("/window").expect("no window attached!");
         match &window_node.pimpl {
         match &window_node.pimpl {
@@ -195,6 +198,18 @@ impl App {
             _ => panic!("wrong pimpl"),
             _ => panic!("wrong pimpl"),
         }
         }
     }
     }
+    async fn start_procs(&self) {
+        let window_node = self.sg_root.clone().lookup_node("/window").unwrap();
+        match &window_node.pimpl {
+            Pimpl::Window(win) => win.clone().start(self.event_pub.clone(), self.ex.clone()).await,
+            _ => panic!("wrong pimpl"),
+        }
+    }
+
+    /// Shutdown code here
+    async fn async_stop(&self) {
+        //self.darkirc_backend.stop().await;
+    }
 }
 }
 
 
 impl Drop for App {
 impl Drop for App {

+ 9 - 8
bin/darkwallet/src/gfx/mod.rs

@@ -16,6 +16,7 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
  */
 
 
+use darkfi::system::CondVar;
 use darkfi_serial::{async_trait, SerialDecodable, SerialEncodable};
 use darkfi_serial::{async_trait, SerialDecodable, SerialEncodable};
 use futures::AsyncWriteExt;
 use futures::AsyncWriteExt;
 use log::debug;
 use log::debug;
@@ -473,16 +474,13 @@ impl Stage {
         async_runtime: AsyncRuntime,
         async_runtime: AsyncRuntime,
         method_rep: mpsc::Receiver<GraphicsMethod>,
         method_rep: mpsc::Receiver<GraphicsMethod>,
         event_pub: GraphicsEventPublisherPtr,
         event_pub: GraphicsEventPublisherPtr,
+        cv_started: Arc<CondVar>,
     ) -> Self {
     ) -> Self {
         let mut ctx: Box<dyn RenderingBackend> = window::new_rendering_backend();
         let mut ctx: Box<dyn RenderingBackend> = window::new_rendering_backend();
 
 
-        // Maybe should be patched upstream since inconsistent behaviour
-        // Needs testing on other platforms too.
-        #[cfg(target_os = "android")]
-        {
-            let (screen_width, screen_height) = window::screen_size();
-            event_pub.notify_resize(Dimension::from([screen_width, screen_height]));
-        }
+        // This will start the app to start. Needed since we cannot get window size for init
+        // until window is created.
+        cv_started.notify();
 
 
         let white_texture = ctx.new_texture_from_rgba8(1, 1, &[255, 255, 255, 255]);
         let white_texture = ctx.new_texture_from_rgba8(1, 1, &[255, 255, 255, 255]);
 
 
@@ -724,6 +722,7 @@ pub fn run_gui(
     async_runtime: AsyncRuntime,
     async_runtime: AsyncRuntime,
     method_rep: mpsc::Receiver<GraphicsMethod>,
     method_rep: mpsc::Receiver<GraphicsMethod>,
     event_pub: GraphicsEventPublisherPtr,
     event_pub: GraphicsEventPublisherPtr,
+    cv_started: Arc<CondVar>,
 ) {
 ) {
     let mut conf = miniquad::conf::Conf {
     let mut conf = miniquad::conf::Conf {
         high_dpi: true,
         high_dpi: true,
@@ -740,5 +739,7 @@ pub fn run_gui(
     conf.platform.apple_gfx_api =
     conf.platform.apple_gfx_api =
         if metal { conf::AppleGfxApi::Metal } else { conf::AppleGfxApi::OpenGl };
         if metal { conf::AppleGfxApi::Metal } else { conf::AppleGfxApi::OpenGl };
 
 
-    miniquad::start(conf, || Box::new(Stage::new(app, async_runtime, method_rep, event_pub)));
+    miniquad::start(conf, || {
+        Box::new(Stage::new(app, async_runtime, method_rep, event_pub, cv_started))
+    });
 }
 }

+ 9 - 5
bin/darkwallet/src/main.rs

@@ -135,13 +135,17 @@ fn main() {
 
 
     let text_shaper = TextShaper::new();
     let text_shaper = TextShaper::new();
 
 
-    let cv_started = Arc::new(CondVar::new());
-    let cv_started2 = cv_started.clone();
+    let cv_gfxwin_started = Arc::new(CondVar::new());
+    let cv_gfxwin_started2 = cv_gfxwin_started.clone();
+    let cv_app_started = Arc::new(CondVar::new());
+    let cv_app_started2 = cv_app_started.clone();
     let app = app::App::new(sg_root, render_api, event_pub.clone(), text_shaper, ex.clone());
     let app = app::App::new(sg_root, render_api, event_pub.clone(), text_shaper, ex.clone());
     let app2 = app.clone();
     let app2 = app.clone();
     let app_task = ex.spawn(async move {
     let app_task = ex.spawn(async move {
+        app2.setup().await;
+        cv_gfxwin_started2.wait().await;
         app2.start().await;
         app2.start().await;
-        cv_started2.notify();
+        cv_app_started2.notify();
     });
     });
     async_runtime.push_task(app_task);
     async_runtime.push_task(app_task);
 
 
@@ -150,7 +154,7 @@ fn main() {
     let sg_root = app.sg_root.clone();
     let sg_root = app.sg_root.clone();
     let ex2 = ex.clone();
     let ex2 = ex.clone();
     let darkirc_task = ex.spawn(async move {
     let darkirc_task = ex.spawn(async move {
-        cv_started.wait().await;
+        cv_app_started.wait().await;
         let darkirc_evgr = DarkIrcBackend::new(sg_root.clone(), ex2.clone()).await.unwrap();
         let darkirc_evgr = DarkIrcBackend::new(sg_root.clone(), ex2.clone()).await.unwrap();
         *app2.darkirc_evgr.lock().unwrap() = Some(darkirc_evgr.clone());
         *app2.darkirc_evgr.lock().unwrap() = Some(darkirc_evgr.clone());
         if let Err(e) = darkirc_evgr.start(ex2).await {
         if let Err(e) = darkirc_evgr.start(ex2).await {
@@ -213,7 +217,7 @@ fn main() {
     */
     */
 
 
     //let stage = gfx::Stage::new(method_rep, event_pub);
     //let stage = gfx::Stage::new(method_rep, event_pub);
-    gfx::run_gui(app, async_runtime, method_rep, event_pub);
+    gfx::run_gui(app, async_runtime, method_rep, event_pub, cv_gfxwin_started);
     debug!(target: "main", "Started GFX backend");
     debug!(target: "main", "Started GFX backend");
 }
 }
 
 

+ 1 - 1
bin/darkwallet/src/ui/button.rs

@@ -54,7 +54,7 @@ impl Button {
         let rect = PropertyRect::wrap(node_ref, Role::Internal, "rect").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 z_index = PropertyUint32::wrap(node_ref, Role::Internal, "z_index", 0).unwrap();
 
 
-        let self_ = Arc::new_cyclic(|me: &Weak<Self>| Self {
+        let self_ = Arc::new(Self {
             node,
             node,
             is_active,
             is_active,
             rect,
             rect,

+ 133 - 127
bin/darkwallet/src/ui/chatview/mod.rs

@@ -31,7 +31,7 @@ use std::{
     io::Cursor,
     io::Cursor,
     sync::{
     sync::{
         atomic::{AtomicBool, Ordering},
         atomic::{AtomicBool, Ordering},
-        Arc, Mutex as SyncMutex, Weak,
+        Arc, Mutex as SyncMutex, OnceLock, Weak,
     },
     },
 };
 };
 
 
@@ -149,8 +149,7 @@ pub type ChatViewPtr = Arc<ChatView>;
 
 
 pub struct ChatView {
 pub struct ChatView {
     node: SceneNodeWeak,
     node: SceneNodeWeak,
-    #[allow(dead_code)]
-    tasks: Vec<smol::Task<()>>,
+    tasks: OnceLock<Vec<smol::Task<()>>>,
     render_api: RenderApi,
     render_api: RenderApi,
     text_shaper: TextShaperPtr,
     text_shaper: TextShaperPtr,
 
 
@@ -224,130 +223,55 @@ impl ChatView {
         let select_hold_time =
         let select_hold_time =
             PropertyFloat32::wrap(node_ref, Role::Internal, "select_hold_time", 0).unwrap();
             PropertyFloat32::wrap(node_ref, Role::Internal, "select_hold_time", 0).unwrap();
 
 
-        let node_name = node_ref.name.clone();
-        let node_id = node_ref.id;
-
-        let self_ = Arc::new_cyclic(|me: &Weak<Self>| {
-            let method_sub = node_ref.subscribe_method_call("insert_line").unwrap();
-            let me2 = me.clone();
-            let insert_line_method_task = ex.spawn(async move {
-                while Self::process_insert_line_method(&me2, &method_sub).await {}
-            });
-
-            let method_sub = node_ref.subscribe_method_call("insert_unconf_line").unwrap();
-            let me2 = me.clone();
-            let insert_unconf_line_method_task = ex.spawn(async move {
-                while Self::process_insert_unconf_line_method(&me2, &method_sub).await {}
-            });
-
-            let me2 = me.clone();
-            let motion_cv = Arc::new(CondVar::new());
-            let cv = motion_cv.clone();
-            let motion_task = ex.spawn(async move {
-                loop {
-                    cv.wait().await;
-                    let Some(self_) = me2.upgrade() else {
-                        // Should not happen
-                        panic!("self destroyed before motion_task was stopped!");
-                    };
-                    self_.handle_movement().await;
-                    cv.reset();
-                }
-            });
-
-            let me2 = me.clone();
-            let bgload_cv = Arc::new(CondVar::new());
-            let cv = bgload_cv.clone();
-            let bgload_task = ex.spawn(async move {
-                loop {
-                    cv.wait().await;
-                    let Some(self_) = me2.upgrade() else {
-                        // Should not happen
-                        panic!("self destroyed before bgload_task was stopped!");
-                    };
-                    self_.handle_bgload().await;
-                    cv.reset();
-                }
-            });
-
-            let mut on_modify = OnModify::new(ex, node_name, node_id, me.clone());
-
-            async fn reload_view(self_: Arc<ChatView>) {
-                self_.scrollview(self_.scroll.get()).await;
-            }
-            on_modify.when_change(scroll.prop(), reload_view);
-
-            async fn redraw(self_: Arc<ChatView>) {
-                self_.redraw_all().await;
-            }
-            on_modify.when_change(baseline.prop(), redraw);
-            on_modify.when_change(font_size.prop(), redraw);
-            on_modify.when_change(timestamp_font_size.prop(), redraw);
-            on_modify.when_change(timestamp_color.prop(), redraw);
-            on_modify.when_change(timestamp_width.prop(), redraw);
-            on_modify.when_change(line_height.prop(), redraw);
-            on_modify.when_change(message_spacing.prop(), redraw);
-            on_modify.when_change(text_color.prop(), redraw);
-            on_modify.when_change(nick_colors.clone(), redraw);
-            on_modify.when_change(hi_bg_color.prop(), redraw);
-            on_modify.when_change(rect.prop(), redraw);
-            on_modify.when_change(debug.prop(), redraw);
-
-            let mut tasks = vec![
-                insert_line_method_task,
-                insert_unconf_line_method_task,
-                motion_task,
-                bgload_task,
-            ];
-            tasks.append(&mut on_modify.tasks);
-
-            Self {
-                node: node.clone(),
-                tasks,
-                render_api: render_api.clone(),
-                text_shaper: text_shaper.clone(),
-
-                tree,
-                msgbuf: AsyncMutex::new(MessageBuffer::new(
-                    node,
-                    font_size,
-                    timestamp_font_size,
-                    timestamp_width,
-                    line_height,
-                    message_spacing,
-                    baseline,
-                    timestamp_color,
-                    text_color,
-                    nick_colors,
-                    hi_bg_color,
-                    debug,
-                    window_scale,
-                    render_api,
-                    text_shaper,
-                )),
-                dc_key: OsRng.gen(),
-
-                mouse_pos: SyncMutex::new(Point::from([0., 0.])),
-                touch_info: SyncMutex::new(None),
-                touch_is_active: AtomicBool::new(false),
-
-                rect,
-                scroll,
-                z_index,
-
-                scroll_start_accel,
-                scroll_resist,
-                select_hold_time,
-
-                motion_cv,
-                speed: AtomicF32::new(0.),
-
-                mouse_btn_held: AtomicBool::new(false),
-
-                bgload_cv,
-
-                parent_rect: SyncMutex::new(None),
-            }
+        let motion_cv = Arc::new(CondVar::new());
+        let bgload_cv = Arc::new(CondVar::new());
+
+        let self_ = Arc::new(Self {
+            node: node.clone(),
+            tasks: OnceLock::new(),
+            render_api: render_api.clone(),
+            text_shaper: text_shaper.clone(),
+
+            tree,
+            msgbuf: AsyncMutex::new(MessageBuffer::new(
+                node,
+                font_size,
+                timestamp_font_size,
+                timestamp_width,
+                line_height,
+                message_spacing,
+                baseline,
+                timestamp_color,
+                text_color,
+                nick_colors,
+                hi_bg_color,
+                debug,
+                window_scale,
+                render_api,
+                text_shaper,
+            )),
+            dc_key: OsRng.gen(),
+
+            mouse_pos: SyncMutex::new(Point::from([0., 0.])),
+            touch_info: SyncMutex::new(None),
+            touch_is_active: AtomicBool::new(false),
+
+            rect,
+            scroll,
+            z_index,
+
+            scroll_start_accel,
+            scroll_resist,
+            select_hold_time,
+
+            motion_cv,
+            speed: AtomicF32::new(0.),
+
+            mouse_btn_held: AtomicBool::new(false),
+
+            bgload_cv,
+
+            parent_rect: SyncMutex::new(None),
         });
         });
         Pimpl::ChatView(self_)
         Pimpl::ChatView(self_)
     }
     }
@@ -776,6 +700,88 @@ impl UIObject for ChatView {
         self.z_index.get()
         self.z_index.get()
     }
     }
 
 
+    async fn start(self: Arc<Self>, ex: ExecutorPtr) {
+        let me = Arc::downgrade(&self);
+
+        let node_ref = &self.node.upgrade().unwrap();
+        let node_name = node_ref.name.clone();
+        let node_id = node_ref.id;
+
+        let method_sub = node_ref.subscribe_method_call("insert_line").unwrap();
+        let me2 = me.clone();
+        let insert_line_method_task =
+            ex.spawn(
+                async move { while Self::process_insert_line_method(&me2, &method_sub).await {} },
+            );
+
+        let method_sub = node_ref.subscribe_method_call("insert_unconf_line").unwrap();
+        let me2 = me.clone();
+        let insert_unconf_line_method_task = ex.spawn(async move {
+            while Self::process_insert_unconf_line_method(&me2, &method_sub).await {}
+        });
+
+        let me2 = me.clone();
+        let cv = self.motion_cv.clone();
+        let motion_task = ex.spawn(async move {
+            loop {
+                cv.wait().await;
+                let Some(self_) = me2.upgrade() else {
+                    // Should not happen
+                    panic!("self destroyed before motion_task was stopped!");
+                };
+                self_.handle_movement().await;
+                cv.reset();
+            }
+        });
+
+        let me2 = me.clone();
+        let cv = self.bgload_cv.clone();
+        let bgload_task = ex.spawn(async move {
+            loop {
+                cv.wait().await;
+                let Some(self_) = me2.upgrade() else {
+                    // Should not happen
+                    panic!("self destroyed before bgload_task was stopped!");
+                };
+                self_.handle_bgload().await;
+                cv.reset();
+            }
+        });
+
+        let mut on_modify = OnModify::new(ex, node_name, node_id, me.clone());
+
+        async fn reload_view(self_: Arc<ChatView>) {
+            self_.scrollview(self_.scroll.get()).await;
+        }
+        on_modify.when_change(self.scroll.prop(), reload_view);
+
+        async fn redraw(self_: Arc<ChatView>) {
+            self_.redraw_all().await;
+        }
+
+        // These should be inside the class
+        /*
+        on_modify.when_change(self.baseline.prop(), redraw);
+        on_modify.when_change(self.font_size.prop(), redraw);
+        on_modify.when_change(self.timestamp_font_size.prop(), redraw);
+        on_modify.when_change(self.timestamp_color.prop(), redraw);
+        on_modify.when_change(self.timestamp_width.prop(), redraw);
+        on_modify.when_change(self.line_height.prop(), redraw);
+        on_modify.when_change(self.message_spacing.prop(), redraw);
+        on_modify.when_change(self.text_color.prop(), redraw);
+        on_modify.when_change(self.nick_colors.clone(), redraw);
+        on_modify.when_change(self.hi_bg_color.prop(), redraw);
+        on_modify.when_change(self.rect.prop(), redraw);
+        on_modify.when_change(self.debug.prop(), redraw);
+        */
+
+        let mut tasks =
+            vec![insert_line_method_task, insert_unconf_line_method_task, motion_task, bgload_task];
+        tasks.append(&mut on_modify.tasks);
+
+        self.tasks.set(tasks);
+    }
+
     async fn draw(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
     async fn draw(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
         debug!(target: "ui::chatview", "ChatView::draw()");
         debug!(target: "ui::chatview", "ChatView::draw()");
 
 

+ 123 - 111
bin/darkwallet/src/ui/editbox.rs

@@ -26,7 +26,7 @@ use std::{
     collections::HashMap,
     collections::HashMap,
     sync::{
     sync::{
         atomic::{AtomicBool, Ordering},
         atomic::{AtomicBool, Ordering},
-        Arc, Mutex as SyncMutex, Weak,
+        Arc, Mutex as SyncMutex, OnceLock, Weak,
     },
     },
     time::Instant,
     time::Instant,
 };
 };
@@ -259,8 +259,7 @@ pub type EditBoxPtr = Arc<EditBox>;
 
 
 pub struct EditBox {
 pub struct EditBox {
     node: SceneNodeWeak,
     node: SceneNodeWeak,
-    #[allow(dead_code)]
-    tasks: Vec<smol::Task<()>>,
+    tasks: OnceLock<Vec<smol::Task<()>>>,
     render_api: RenderApi,
     render_api: RenderApi,
     text_shaper: TextShaperPtr,
     text_shaper: TextShaperPtr,
     key_repeat: SyncMutex<PressedKeysSmoothRepeat>,
     key_repeat: SyncMutex<PressedKeysSmoothRepeat>,
@@ -287,6 +286,8 @@ pub struct EditBox {
     cursor_width: PropertyFloat32,
     cursor_width: PropertyFloat32,
     cursor_ascent: PropertyFloat32,
     cursor_ascent: PropertyFloat32,
     cursor_descent: PropertyFloat32,
     cursor_descent: PropertyFloat32,
+    cursor_blink_time: PropertyUint32,
+    cursor_idle_time: PropertyUint32,
     hi_bg_color: PropertyColor,
     hi_bg_color: PropertyColor,
     selected: PropertyPtr,
     selected: PropertyPtr,
     z_index: PropertyUint32,
     z_index: PropertyUint32,
@@ -345,114 +346,49 @@ impl EditBox {
         // Must do this whenever the text changes
         // Must do this whenever the text changes
         let glyphs = text_shaper.shape(text.get(), font_size.get(), window_scale.get()).await;
         let glyphs = text_shaper.shape(text.get(), font_size.get(), window_scale.get()).await;
 
 
-        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(is_focused.prop(), Self::change_focus);
-
-            // When text has been changed.
-            // Cursor and selection might be invalidated.
-            async fn reset(self_: Arc<EditBox>) {
-                self_.cursor_pos.set(0);
-                self_.selected.set_null(Role::Internal, 0).unwrap();
-                self_.selected.set_null(Role::Internal, 1).unwrap();
-                self_.scroll.set(0.);
-                self_.regen_glyphs().await;
-                self_.redraw().await;
-            }
-            async fn redraw(self_: Arc<EditBox>) {
-                self_.redraw().await;
-            }
-            on_modify.when_change(rect.prop(), redraw);
-            on_modify.when_change(baseline.prop(), redraw);
-            // The commented properties are modified on input events
-            // So then redraw() will get repeatedly triggered when these properties
-            // are changed. We should find a solution. For now the hooks are disabled.
-            //on_modify.when_change(scroll.prop(), redraw);
-            //on_modify.when_change(cursor_pos.prop(), redraw);
-            on_modify.when_change(font_size.prop(), redraw);
-            // We must also reshape text
-            on_modify.when_change(text.prop(), reset);
-            on_modify.when_change(text_color.prop(), redraw);
-            on_modify.when_change(hi_bg_color.prop(), redraw);
-            //on_modify.when_change(selected.clone(), redraw);
-            on_modify.when_change(z_index.prop(), redraw);
-            on_modify.when_change(debug.prop(), redraw);
-
-            async fn regen_cursor(self_: Arc<EditBox>) {
-                let mesh = std::mem::take(&mut *self_.cursor_mesh.lock().unwrap());
-                let mut freed = self_.freed.lock().unwrap();
-                if let Some(mesh) = mesh {
-                    freed.add_mesh(mesh);
-                }
-            }
-            on_modify.when_change(cursor_color.prop(), regen_cursor);
-            on_modify.when_change(cursor_ascent.prop(), regen_cursor);
-            on_modify.when_change(cursor_descent.prop(), regen_cursor);
-            on_modify.when_change(cursor_width.prop(), regen_cursor);
-
-            let me2 = me.clone();
-            let blinking_cursor_task = ex.spawn(async move {
-                loop {
-                    msleep(cursor_blink_time.get() as u64).await;
-
-                    let self_ = me2.upgrade().unwrap();
-
-                    if self_.blink_is_paused.swap(false, Ordering::Relaxed) {
-                        msleep(cursor_idle_time.get() as u64).await;
-                        continue
-                    }
-
-                    // Invert the bool
-                    self_.cursor_is_visible.fetch_not(Ordering::Relaxed);
-                    self_.redraw_cursor().await;
-                }
-            });
-
-            let mut tasks = on_modify.tasks;
-            tasks.push(blinking_cursor_task);
-
-            Self {
-                node,
-                tasks,
-                render_api,
-                text_shaper: text_shaper.clone(),
-                key_repeat: SyncMutex::new(PressedKeysSmoothRepeat::new(400, 50)),
-
-                text_mesh: SyncMutex::new(None),
-                glyphs: SyncMutex::new(glyphs),
-                text_dc_key: OsRng.gen(),
-                cursor_mesh: SyncMutex::new(None),
-                cursor_dc_key: OsRng.gen(),
-                freed: SyncMutex::new(Default::default()),
-
-                is_active,
-                is_focused,
-                rect,
-                baseline,
-                scroll,
-                cursor_pos,
-                font_size,
-                text,
-                text_color,
-                cursor_color,
-                cursor_width,
-                cursor_ascent,
-                cursor_descent,
-                hi_bg_color,
-                selected,
-                z_index,
-                debug,
-
-                composer: AsyncMutex::new(ComposingText::new(text_shaper)),
-
-                mouse_btn_held: AtomicBool::new(false),
-                cursor_is_visible: AtomicBool::new(true),
-                blink_is_paused: AtomicBool::new(false),
-
-                old_window_scale: AtomicF32::new(window_scale.get()),
-                window_scale,
-                parent_rect: SyncMutex::new(None),
-            }
+        let self_ = Arc::new(Self {
+            node,
+            tasks: OnceLock::new(),
+            render_api,
+            text_shaper: text_shaper.clone(),
+            key_repeat: SyncMutex::new(PressedKeysSmoothRepeat::new(400, 50)),
+
+            text_mesh: SyncMutex::new(None),
+            glyphs: SyncMutex::new(glyphs),
+            text_dc_key: OsRng.gen(),
+            cursor_mesh: SyncMutex::new(None),
+            cursor_dc_key: OsRng.gen(),
+            freed: SyncMutex::new(Default::default()),
+
+            is_active,
+            is_focused,
+            rect,
+            baseline,
+            scroll,
+            cursor_pos,
+            font_size,
+            text,
+            text_color,
+            cursor_color,
+            cursor_width,
+            cursor_ascent,
+            cursor_descent,
+            cursor_blink_time,
+            cursor_idle_time,
+            hi_bg_color,
+            selected,
+            z_index,
+            debug,
+
+            composer: AsyncMutex::new(ComposingText::new(text_shaper)),
+
+            mouse_btn_held: AtomicBool::new(false),
+            cursor_is_visible: AtomicBool::new(true),
+            blink_is_paused: AtomicBool::new(false),
+
+            old_window_scale: AtomicF32::new(window_scale.get()),
+            window_scale,
+            parent_rect: SyncMutex::new(None),
         });
         });
 
 
         Pimpl::EditBox(self_)
         Pimpl::EditBox(self_)
@@ -1374,6 +1310,82 @@ impl UIObject for EditBox {
         self.z_index.get()
         self.z_index.get()
     }
     }
 
 
+    async fn start(self: Arc<Self>, ex: ExecutorPtr) {
+        let me = Arc::downgrade(&self);
+
+        let node_ref = &self.node.upgrade().unwrap();
+        let node_name = node_ref.name.clone();
+        let node_id = node_ref.id;
+
+        let mut on_modify = OnModify::new(ex.clone(), node_name, node_id, me.clone());
+        on_modify.when_change(self.is_focused.prop(), Self::change_focus);
+
+        // When text has been changed.
+        // Cursor and selection might be invalidated.
+        async fn reset(self_: Arc<EditBox>) {
+            self_.cursor_pos.set(0);
+            self_.selected.set_null(Role::Internal, 0).unwrap();
+            self_.selected.set_null(Role::Internal, 1).unwrap();
+            self_.scroll.set(0.);
+            self_.regen_glyphs().await;
+            self_.redraw().await;
+        }
+        async fn redraw(self_: Arc<EditBox>) {
+            self_.redraw().await;
+        }
+        on_modify.when_change(self.rect.prop(), redraw);
+        on_modify.when_change(self.baseline.prop(), redraw);
+        // The commented properties are modified on input events
+        // So then redraw() will get repeatedly triggered when these properties
+        // are changed. We should find a solution. For now the hooks are disabled.
+        //on_modify.when_change(scroll.prop(), redraw);
+        //on_modify.when_change(cursor_pos.prop(), redraw);
+        on_modify.when_change(self.font_size.prop(), redraw);
+        // We must also reshape text
+        on_modify.when_change(self.text.prop(), reset);
+        on_modify.when_change(self.text_color.prop(), redraw);
+        on_modify.when_change(self.hi_bg_color.prop(), redraw);
+        //on_modify.when_change(selected.clone(), redraw);
+        on_modify.when_change(self.z_index.prop(), redraw);
+        on_modify.when_change(self.debug.prop(), redraw);
+
+        async fn regen_cursor(self_: Arc<EditBox>) {
+            let mesh = std::mem::take(&mut *self_.cursor_mesh.lock().unwrap());
+            let mut freed = self_.freed.lock().unwrap();
+            if let Some(mesh) = mesh {
+                freed.add_mesh(mesh);
+            }
+        }
+        on_modify.when_change(self.cursor_color.prop(), regen_cursor);
+        on_modify.when_change(self.cursor_ascent.prop(), regen_cursor);
+        on_modify.when_change(self.cursor_descent.prop(), regen_cursor);
+        on_modify.when_change(self.cursor_width.prop(), regen_cursor);
+
+        let me2 = me.clone();
+        let cursor_blink_time = self.cursor_blink_time.clone();
+        let cursor_idle_time = self.cursor_idle_time.clone();
+        let blinking_cursor_task = ex.spawn(async move {
+            loop {
+                msleep(cursor_blink_time.get() as u64).await;
+
+                let self_ = me2.upgrade().unwrap();
+
+                if self_.blink_is_paused.swap(false, Ordering::Relaxed) {
+                    msleep(cursor_idle_time.get() as u64).await;
+                    continue
+                }
+
+                // Invert the bool
+                self_.cursor_is_visible.fetch_not(Ordering::Relaxed);
+                self_.redraw_cursor().await;
+            }
+        });
+
+        let mut tasks = on_modify.tasks;
+        tasks.push(blinking_cursor_task);
+        self.tasks.set(tasks);
+    }
+
     async fn draw(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
     async fn draw(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
         debug!(target: "ui::editbox", "EditBox::draw()");
         debug!(target: "ui::editbox", "EditBox::draw()");
         *self.parent_rect.lock().unwrap() = Some(parent_rect);
         *self.parent_rect.lock().unwrap() = Some(parent_rect);

+ 30 - 23
bin/darkwallet/src/ui/image.rs

@@ -21,7 +21,7 @@ use image::ImageReader;
 use rand::{rngs::OsRng, Rng};
 use rand::{rngs::OsRng, Rng};
 use std::{
 use std::{
     io::Cursor,
     io::Cursor,
-    sync::{Arc, Mutex as SyncMutex, Weak},
+    sync::{Arc, Mutex as SyncMutex, OnceLock, Weak},
 };
 };
 
 
 use crate::{
 use crate::{
@@ -39,8 +39,7 @@ pub type ImagePtr = Arc<Image>;
 pub struct Image {
 pub struct Image {
     node: SceneNodeWeak,
     node: SceneNodeWeak,
     render_api: RenderApi,
     render_api: RenderApi,
-    #[allow(dead_code)]
-    tasks: Vec<smol::Task<()>>,
+    tasks: OnceLock<Vec<smol::Task<()>>>,
 
 
     mesh: SyncMutex<Option<MeshInfo>>,
     mesh: SyncMutex<Option<MeshInfo>>,
     texture: SyncMutex<Option<GfxTextureId>>,
     texture: SyncMutex<Option<GfxTextureId>>,
@@ -67,29 +66,21 @@ impl Image {
         let node_name = node_ref.name.clone();
         let node_name = node_ref.name.clone();
         let node_id = node_ref.id;
         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());
-            on_modify.when_change(rect.prop(), Self::redraw);
-            on_modify.when_change(uv.prop(), Self::redraw);
-            on_modify.when_change(z_index.prop(), Self::redraw);
-            on_modify.when_change(path.prop(), Self::reload);
+        let self_ = Arc::new(Self {
+            node,
+            render_api,
+            tasks: OnceLock::new(),
 
 
-            Self {
-                node,
-                render_api,
-                tasks: on_modify.tasks,
+            mesh: SyncMutex::new(None),
+            texture: SyncMutex::new(None),
+            dc_key: OsRng.gen(),
 
 
-                mesh: SyncMutex::new(None),
-                texture: SyncMutex::new(None),
-                dc_key: OsRng.gen(),
+            rect,
+            uv,
+            z_index,
+            path,
 
 
-                rect,
-                uv,
-                z_index,
-                path,
-
-                parent_rect: SyncMutex::new(None),
-            }
+            parent_rect: SyncMutex::new(None),
         });
         });
 
 
         *self_.texture.lock().unwrap() = Some(self_.load_texture());
         *self_.texture.lock().unwrap() = Some(self_.load_texture());
@@ -210,6 +201,22 @@ impl UIObject for Image {
         self.z_index.get()
         self.z_index.get()
     }
     }
 
 
+    async fn start(self: Arc<Self>, ex: ExecutorPtr) {
+        let me = Arc::downgrade(&self);
+
+        let node_ref = &self.node.upgrade().unwrap();
+        let node_name = node_ref.name.clone();
+        let node_id = node_ref.id;
+
+        let mut on_modify = OnModify::new(ex, node_name, node_id, me.clone());
+        on_modify.when_change(self.rect.prop(), Self::redraw);
+        on_modify.when_change(self.uv.prop(), Self::redraw);
+        on_modify.when_change(self.z_index.prop(), Self::redraw);
+        on_modify.when_change(self.path.prop(), Self::reload);
+
+        self.tasks.set(on_modify.tasks);
+    }
+
     async fn draw(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
     async fn draw(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
         debug!(target: "ui::image", "Image::draw()");
         debug!(target: "ui::image", "Image::draw()");
         *self.parent_rect.lock().unwrap() = Some(parent_rect);
         *self.parent_rect.lock().unwrap() = Some(parent_rect);

+ 34 - 19
bin/darkwallet/src/ui/layer.rs

@@ -21,7 +21,7 @@ use async_trait::async_trait;
 use atomic_float::AtomicF32;
 use atomic_float::AtomicF32;
 use miniquad::{KeyCode, KeyMods, MouseButton, TouchPhase};
 use miniquad::{KeyCode, KeyMods, MouseButton, TouchPhase};
 use rand::{rngs::OsRng, Rng};
 use rand::{rngs::OsRng, Rng};
-use std::sync::{atomic::Ordering, Arc, Mutex as SyncMutex, Weak};
+use std::sync::{atomic::Ordering, Arc, Mutex as SyncMutex, OnceLock, Weak};
 
 
 use crate::{
 use crate::{
     gfx::{GfxDrawCall, GfxDrawInstruction, Point, Rectangle, RenderApi},
     gfx::{GfxDrawCall, GfxDrawInstruction, Point, Rectangle, RenderApi},
@@ -30,14 +30,16 @@ use crate::{
     ExecutorPtr,
     ExecutorPtr,
 };
 };
 
 
-use super::{get_children_ordered, get_ui_object3, DrawUpdate, OnModify, UIObject};
+use super::{
+    get_children_ordered, get_ui_object3, get_ui_object_ptr, DrawUpdate, OnModify, UIObject,
+};
 
 
 pub type LayerPtr = Arc<Layer>;
 pub type LayerPtr = Arc<Layer>;
 
 
 pub struct Layer {
 pub struct Layer {
     node: SceneNodeWeak,
     node: SceneNodeWeak,
     render_api: RenderApi,
     render_api: RenderApi,
-    _tasks: Vec<smol::Task<()>>,
+    tasks: OnceLock<Vec<smol::Task<()>>>,
     dc_key: u64,
     dc_key: u64,
 
 
     is_visible: PropertyBool,
     is_visible: PropertyBool,
@@ -59,24 +61,17 @@ impl Layer {
         let node_name = node_ref.name.clone();
         let node_name = node_ref.name.clone();
         let node_id = node_ref.id;
         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(is_visible.prop(), Self::redraw);
-            on_modify.when_change(rect.prop(), Self::redraw);
-            on_modify.when_change(z_index.prop(), Self::redraw);
-
-            Self {
-                node,
-                render_api,
-                _tasks: on_modify.tasks,
-                dc_key: OsRng.gen(),
+        let self_ = Arc::new(Self {
+            node,
+            render_api,
+            tasks: OnceLock::new(),
+            dc_key: OsRng.gen(),
 
 
-                is_visible,
-                rect,
-                z_index,
+            is_visible,
+            rect,
+            z_index,
 
 
-                parent_rect: SyncMutex::new(None),
-            }
+            parent_rect: SyncMutex::new(None),
         });
         });
 
 
         Pimpl::Layer(self_)
         Pimpl::Layer(self_)
@@ -143,6 +138,26 @@ impl UIObject for Layer {
         self.z_index.get()
         self.z_index.get()
     }
     }
 
 
+    async fn start(self: Arc<Self>, ex: ExecutorPtr) {
+        let me = Arc::downgrade(&self);
+
+        let node_ref = &self.node.upgrade().unwrap();
+        let node_name = node_ref.name.clone();
+        let node_id = node_ref.id;
+
+        let mut on_modify = OnModify::new(ex.clone(), node_name, node_id, me.clone());
+        on_modify.when_change(self.is_visible.prop(), Self::redraw);
+        on_modify.when_change(self.rect.prop(), Self::redraw);
+        on_modify.when_change(self.z_index.prop(), Self::redraw);
+
+        self.tasks.set(on_modify.tasks);
+
+        for child in self.get_children() {
+            let obj = get_ui_object_ptr(&child);
+            obj.start(ex.clone()).await;
+        }
+    }
+
     async fn draw(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
     async fn draw(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
         debug!(target: "ui::layer", "Layer::draw()");
         debug!(target: "ui::layer", "Layer::draw()");
         *self.parent_rect.lock().unwrap() = Some(parent_rect);
         *self.parent_rect.lock().unwrap() = Some(parent_rect);

+ 14 - 0
bin/darkwallet/src/ui/mod.rs

@@ -53,6 +53,8 @@ pub use win::{Window, WindowPtr};
 pub trait UIObject: Sync {
 pub trait UIObject: Sync {
     fn z_index(&self) -> u32;
     fn z_index(&self) -> u32;
 
 
+    async fn start(self: Arc<Self>, ex: ExecutorPtr) {}
+
     async fn draw(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
     async fn draw(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
         None
         None
     }
     }
@@ -149,6 +151,18 @@ impl<T: Send + Sync + 'static> OnModify<T> {
     }
     }
 }
 }
 
 
+pub fn get_ui_object_ptr(node: &SceneNode3) -> Arc<dyn UIObject + Send> {
+    match &node.pimpl {
+        Pimpl::Layer(obj) => obj.clone(),
+        Pimpl::VectorArt(obj) => obj.clone(),
+        Pimpl::Text(obj) => obj.clone(),
+        Pimpl::EditBox(obj) => obj.clone(),
+        Pimpl::ChatView(obj) => obj.clone(),
+        Pimpl::Image(obj) => obj.clone(),
+        Pimpl::Button(obj) => obj.clone(),
+        _ => panic!("unhandled type for get_ui_object"),
+    }
+}
 pub fn get_ui_object3<'a>(node: &'a SceneNode3) -> &'a dyn UIObject {
 pub fn get_ui_object3<'a>(node: &'a SceneNode3) -> &'a dyn UIObject {
     match &node.pimpl {
     match &node.pimpl {
         Pimpl::Layer(obj) => obj.as_ref(),
         Pimpl::Layer(obj) => obj.as_ref(),

+ 39 - 31
bin/darkwallet/src/ui/text.rs

@@ -18,7 +18,7 @@
 
 
 use async_trait::async_trait;
 use async_trait::async_trait;
 use rand::{rngs::OsRng, Rng};
 use rand::{rngs::OsRng, Rng};
-use std::sync::{Arc, Mutex as SyncMutex, Weak};
+use std::sync::{Arc, Mutex as SyncMutex, OnceLock, Weak};
 
 
 use crate::{
 use crate::{
     gfx::{GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, GfxTextureId, Rectangle, RenderApi},
     gfx::{GfxDrawCall, GfxDrawInstruction, GfxDrawMesh, GfxTextureId, Rectangle, RenderApi},
@@ -46,7 +46,7 @@ pub struct Text {
     node: SceneNodeWeak,
     node: SceneNodeWeak,
     render_api: RenderApi,
     render_api: RenderApi,
     text_shaper: TextShaperPtr,
     text_shaper: TextShaperPtr,
-    _tasks: Vec<smol::Task<()>>,
+    tasks: OnceLock<Vec<smol::Task<()>>>,
 
 
     render_info: SyncMutex<TextRenderInfo>,
     render_info: SyncMutex<TextRenderInfo>,
     dc_key: u64,
     dc_key: u64,
@@ -97,35 +97,24 @@ impl Text {
         )
         )
         .await;
         .await;
 
 
-        let self_ = Arc::new_cyclic(|me: &Weak<Self>| {
-            let mut on_modify = OnModify::new(ex, node_name, node_id, me.clone());
-            on_modify.when_change(rect.prop(), Self::redraw);
-            on_modify.when_change(z_index.prop(), Self::redraw);
-            on_modify.when_change(text.prop(), Self::redraw);
-            on_modify.when_change(font_size.prop(), Self::redraw);
-            on_modify.when_change(text_color.prop(), Self::redraw);
-            on_modify.when_change(debug.prop(), Self::redraw);
-            on_modify.when_change(baseline.prop(), Self::redraw);
-
-            Self {
-                node,
-                render_api,
-                text_shaper,
-                _tasks: on_modify.tasks,
-                render_info: SyncMutex::new(render_info),
-                dc_key: OsRng.gen(),
-
-                rect,
-                z_index,
-                text,
-                font_size,
-                text_color,
-                baseline,
-                debug,
-
-                window_scale,
-                parent_rect: SyncMutex::new(None),
-            }
+        let self_ = Arc::new(Self {
+            node,
+            render_api,
+            text_shaper,
+            tasks: OnceLock::new(),
+            render_info: SyncMutex::new(render_info),
+            dc_key: OsRng.gen(),
+
+            rect,
+            z_index,
+            text,
+            font_size,
+            text_color,
+            baseline,
+            debug,
+
+            window_scale,
+            parent_rect: SyncMutex::new(None),
         });
         });
 
 
         Pimpl::Text(self_)
         Pimpl::Text(self_)
@@ -241,6 +230,25 @@ impl UIObject for Text {
         self.z_index.get()
         self.z_index.get()
     }
     }
 
 
+    async fn start(self: Arc<Self>, ex: ExecutorPtr) {
+        let me = Arc::downgrade(&self);
+
+        let node_ref = &self.node.upgrade().unwrap();
+        let node_name = node_ref.name.clone();
+        let node_id = node_ref.id;
+
+        let mut on_modify = OnModify::new(ex, node_name, node_id, me.clone());
+        on_modify.when_change(self.rect.prop(), Self::redraw);
+        on_modify.when_change(self.z_index.prop(), Self::redraw);
+        on_modify.when_change(self.text.prop(), Self::redraw);
+        on_modify.when_change(self.font_size.prop(), Self::redraw);
+        on_modify.when_change(self.text_color.prop(), Self::redraw);
+        on_modify.when_change(self.debug.prop(), Self::redraw);
+        on_modify.when_change(self.baseline.prop(), Self::redraw);
+
+        self.tasks.set(on_modify.tasks);
+    }
+
     async fn draw(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
     async fn draw(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
         debug!(target: "ui::text", "Text::draw()");
         debug!(target: "ui::text", "Text::draw()");
         *self.parent_rect.lock().unwrap() = Some(parent_rect);
         *self.parent_rect.lock().unwrap() = Some(parent_rect);

+ 26 - 18
bin/darkwallet/src/ui/vector_art/mod.rs

@@ -18,7 +18,7 @@
 
 
 use async_trait::async_trait;
 use async_trait::async_trait;
 use rand::{rngs::OsRng, Rng};
 use rand::{rngs::OsRng, Rng};
-use std::sync::{Arc, Mutex as SyncMutex, Weak};
+use std::sync::{Arc, Mutex as SyncMutex, OnceLock, Weak};
 
 
 use crate::{
 use crate::{
     error::{Error, Result},
     error::{Error, Result},
@@ -43,7 +43,7 @@ pub type VectorArtPtr = Arc<VectorArt>;
 pub struct VectorArt {
 pub struct VectorArt {
     node: SceneNodeWeak,
     node: SceneNodeWeak,
     render_api: RenderApi,
     render_api: RenderApi,
-    _tasks: Vec<smol::Task<()>>,
+    tasks: OnceLock<Vec<smol::Task<()>>>,
 
 
     shape: VectorShape,
     shape: VectorShape,
     buffers: SyncMutex<Option<GfxDrawMesh>>,
     buffers: SyncMutex<Option<GfxDrawMesh>>,
@@ -71,25 +71,19 @@ impl VectorArt {
         let node_name = node_ref.name.clone();
         let node_name = node_ref.name.clone();
         let node_id = node_ref.id;
         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());
-            on_modify.when_change(rect.prop(), Self::redraw);
-            on_modify.when_change(z_index.prop(), Self::redraw);
+        let self_ = Arc::new(Self {
+            node,
+            render_api,
+            tasks: OnceLock::new(),
 
 
-            Self {
-                node,
-                render_api,
-                _tasks: on_modify.tasks,
+            shape,
+            buffers: SyncMutex::new(None),
+            dc_key: OsRng.gen(),
 
 
-                shape,
-                buffers: SyncMutex::new(None),
-                dc_key: OsRng.gen(),
+            rect,
+            z_index,
 
 
-                rect,
-                z_index,
-
-                parent_rect: SyncMutex::new(None),
-            }
+            parent_rect: SyncMutex::new(None),
         });
         });
 
 
         Pimpl::VectorArt(self_)
         Pimpl::VectorArt(self_)
@@ -159,6 +153,20 @@ impl UIObject for VectorArt {
         self.z_index.get()
         self.z_index.get()
     }
     }
 
 
+    async fn start(self: Arc<Self>, ex: ExecutorPtr) {
+        let me = Arc::downgrade(&self);
+
+        let node_ref = &self.node.upgrade().unwrap();
+        let node_name = node_ref.name.clone();
+        let node_id = node_ref.id;
+
+        let mut on_modify = OnModify::new(ex, node_name, node_id, me.clone());
+        on_modify.when_change(self.rect.prop(), Self::redraw);
+        on_modify.when_change(self.z_index.prop(), Self::redraw);
+
+        self.tasks.set(on_modify.tasks);
+    }
+
     async fn draw(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
     async fn draw(&self, parent_rect: Rectangle) -> Option<DrawUpdate> {
         debug!(target: "ui::vector_art", "VectorArt::draw()");
         debug!(target: "ui::vector_art", "VectorArt::draw()");
         *self.parent_rect.lock().unwrap() = Some(parent_rect);
         *self.parent_rect.lock().unwrap() = Some(parent_rect);

+ 111 - 110
bin/darkwallet/src/ui/win.rs

@@ -17,7 +17,7 @@
  */
  */
 
 
 use miniquad::{KeyCode, KeyMods, MouseButton, TouchPhase};
 use miniquad::{KeyCode, KeyMods, MouseButton, TouchPhase};
-use std::sync::{Arc, Weak};
+use std::sync::{Arc, OnceLock, Weak};
 
 
 use crate::{
 use crate::{
     gfx::{
     gfx::{
@@ -29,151 +29,152 @@ use crate::{
     ExecutorPtr,
     ExecutorPtr,
 };
 };
 
 
-use super::{get_children_ordered, get_ui_object3, OnModify};
+use super::{get_children_ordered, get_ui_object3, get_ui_object_ptr, OnModify};
 
 
 pub type WindowPtr = Arc<Window>;
 pub type WindowPtr = Arc<Window>;
 
 
 pub struct Window {
 pub struct Window {
     node: SceneNodeWeak,
     node: SceneNodeWeak,
 
 
-    // Task is dropped at the end of the scope for Window, hence ending it
-    #[allow(dead_code)]
-    tasks: Vec<smol::Task<()>>,
+    tasks: OnceLock<Vec<smol::Task<()>>>,
     screen_size: PropertyDimension,
     screen_size: PropertyDimension,
     scale: PropertyFloat32,
     scale: PropertyFloat32,
     render_api: RenderApi,
     render_api: RenderApi,
 }
 }
 
 
 impl Window {
 impl Window {
-    pub async fn new(
-        node: SceneNodeWeak,
-        render_api: RenderApi,
-        event_pub: GraphicsEventPublisherPtr,
-        ex: ExecutorPtr,
-    ) -> Pimpl {
+    pub async fn new(node: SceneNodeWeak, render_api: RenderApi) -> Pimpl {
         debug!(target: "ui::win", "Window::new()");
         debug!(target: "ui::win", "Window::new()");
 
 
         let node_ref = &node.upgrade().unwrap();
         let node_ref = &node.upgrade().unwrap();
         let screen_size = PropertyDimension::wrap(node_ref, Role::Internal, "screen_size").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 scale = PropertyFloat32::wrap(node_ref, Role::Internal, "scale", 0).unwrap();
 
 
+        let self_ = Arc::new(Self { node, tasks: OnceLock::new(), screen_size, scale, render_api });
+
+        Pimpl::Window(self_)
+    }
+
+    pub async fn start(self: Arc<Self>, event_pub: GraphicsEventPublisherPtr, ex: ExecutorPtr) {
+        let me = Arc::downgrade(&self);
+
+        let node_ref = &self.node.upgrade().unwrap();
         let node_name = node_ref.name.clone();
         let node_name = node_ref.name.clone();
         let node_id = node_ref.id;
         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_size2 = screen_size.clone();
+        // Start a task monitoring for window resize events
+        // which updates screen_size
+        let ev_sub = event_pub.subscribe_resize();
+        let screen_size2 = self.screen_size.clone();
+        let me2 = me.clone();
+        let resize_task = ex.spawn(async move {
+            loop {
+                let Ok(size) = ev_sub.receive().await else {
+                    debug!(target: "ui::win", "Event relayer closed");
+                    break
+                };
+
+                debug!(target: "ui::win", "Window resized {size:?}");
+                // Now update the properties
+                screen_size2.set(size);
+
+                let Some(self_) = me2.upgrade() else {
+                    // Should not happen
+                    panic!("self destroyed before modify_task was stopped!");
+                };
+
+                self_.draw().await;
+            }
+        });
+
+        let ev_sub = event_pub.subscribe_char();
+        let me2 = me.clone();
+        let char_task = ex.spawn(async move { while Self::process_char(&me2, &ev_sub).await {} });
+
+        let ev_sub = event_pub.subscribe_key_down();
+        let me2 = me.clone();
+        let key_down_task =
+            ex.spawn(async move { while Self::process_key_down(&me2, &ev_sub).await {} });
+
+        let ev_sub = event_pub.subscribe_key_up();
+        let me2 = me.clone();
+        let key_up_task =
+            ex.spawn(async move { while Self::process_key_up(&me2, &ev_sub).await {} });
+
+        let ev_sub = event_pub.subscribe_mouse_btn_down();
+        let me2 = me.clone();
+        let mouse_btn_down_task =
+            ex.spawn(async move { while Self::process_mouse_btn_down(&me2, &ev_sub).await {} });
+
+        let ev_sub = event_pub.subscribe_mouse_btn_up();
+        let me2 = me.clone();
+        let mouse_btn_up_task =
+            ex.spawn(async move { while Self::process_mouse_btn_up(&me2, &ev_sub).await {} });
+
+        let ev_sub = event_pub.subscribe_mouse_move();
+        let me2 = me.clone();
+        let mouse_move_task =
+            ex.spawn(async move { while Self::process_mouse_move(&me2, &ev_sub).await {} });
+
+        let ev_sub = event_pub.subscribe_mouse_wheel();
+        let me2 = me.clone();
+        let mouse_wheel_task =
+            ex.spawn(async move { while Self::process_mouse_wheel(&me2, &ev_sub).await {} });
+
+        let ev_sub = event_pub.subscribe_touch();
+        let me2 = me.clone();
+        let touch_task = ex.spawn(async move { while Self::process_touch(&me2, &ev_sub).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(self.scale.prop(), redraw_fn);
+
+        let mut tasks = vec![
+            resize_task,
+            char_task,
+            key_down_task,
+            key_up_task,
+            mouse_btn_down_task,
+            mouse_btn_up_task,
+            mouse_move_task,
+            mouse_wheel_task,
+            touch_task,
+        ];
+
+        tasks.append(&mut on_modify.tasks);
+
+        #[cfg(target_os = "android")]
+        {
+            let (sender, recvr) = async_channel::unbounded();
+            crate::android::set_sender(sender);
             let me2 = me.clone();
             let me2 = me.clone();
-            let resize_task = ex.spawn(async move {
+            let autosuggest_task = ex.spawn(async move {
                 loop {
                 loop {
-                    let Ok(size) = ev_sub.receive().await else {
+                    let Ok(ev) = recvr.recv().await else {
                         debug!(target: "ui::win", "Event relayer closed");
                         debug!(target: "ui::win", "Event relayer closed");
                         break
                         break
                     };
                     };
 
 
-                    debug!(target: "ui::win", "Window resized {size:?}");
-                    // Now update the properties
-                    screen_size2.set(size);
-
                     let Some(self_) = me2.upgrade() else {
                     let Some(self_) = me2.upgrade() else {
                         // Should not happen
                         // Should not happen
                         panic!("self destroyed before modify_task was stopped!");
                         panic!("self destroyed before modify_task was stopped!");
                     };
                     };
 
 
-                    self_.draw().await;
+                    self_.handle_autosuggest(ev).await;
                 }
                 }
             });
             });
+            tasks.push(autosuggest_task);
+        }
 
 
-            let ev_sub = event_pub.subscribe_char();
-            let me2 = me.clone();
-            let char_task =
-                ex.spawn(async move { while Self::process_char(&me2, &ev_sub).await {} });
-
-            let ev_sub = event_pub.subscribe_key_down();
-            let me2 = me.clone();
-            let key_down_task =
-                ex.spawn(async move { while Self::process_key_down(&me2, &ev_sub).await {} });
-
-            let ev_sub = event_pub.subscribe_key_up();
-            let me2 = me.clone();
-            let key_up_task =
-                ex.spawn(async move { while Self::process_key_up(&me2, &ev_sub).await {} });
-
-            let ev_sub = event_pub.subscribe_mouse_btn_down();
-            let me2 = me.clone();
-            let mouse_btn_down_task =
-                ex.spawn(async move { while Self::process_mouse_btn_down(&me2, &ev_sub).await {} });
-
-            let ev_sub = event_pub.subscribe_mouse_btn_up();
-            let me2 = me.clone();
-            let mouse_btn_up_task =
-                ex.spawn(async move { while Self::process_mouse_btn_up(&me2, &ev_sub).await {} });
-
-            let ev_sub = event_pub.subscribe_mouse_move();
-            let me2 = me.clone();
-            let mouse_move_task =
-                ex.spawn(async move { while Self::process_mouse_move(&me2, &ev_sub).await {} });
-
-            let ev_sub = event_pub.subscribe_mouse_wheel();
-            let me2 = me.clone();
-            let mouse_wheel_task =
-                ex.spawn(async move { while Self::process_mouse_wheel(&me2, &ev_sub).await {} });
-
-            let ev_sub = event_pub.subscribe_touch();
-            let me2 = me.clone();
-            let touch_task =
-                ex.spawn(async move { while Self::process_touch(&me2, &ev_sub).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);
-
-            let mut tasks = vec![
-                resize_task,
-                char_task,
-                key_down_task,
-                key_up_task,
-                mouse_btn_down_task,
-                mouse_btn_up_task,
-                mouse_move_task,
-                mouse_wheel_task,
-                touch_task,
-            ];
-
-            tasks.append(&mut on_modify.tasks);
-
-            #[cfg(target_os = "android")]
-            {
-                let (sender, recvr) = async_channel::unbounded();
-                crate::android::set_sender(sender);
-                let me2 = me.clone();
-                let autosuggest_task = ex.spawn(async move {
-                    loop {
-                        let Ok(ev) = recvr.recv().await else {
-                            debug!(target: "ui::win", "Event relayer closed");
-                            break
-                        };
-
-                        let Some(self_) = me2.upgrade() else {
-                            // Should not happen
-                            panic!("self destroyed before modify_task was stopped!");
-                        };
-
-                        self_.handle_autosuggest(ev).await;
-                    }
-                });
-                tasks.push(autosuggest_task);
-            }
+        self.tasks.set(tasks);
 
 
-            Self { node, tasks, screen_size, scale, render_api }
-        });
-
-        Pimpl::Window(self_)
+        for child in self.get_children() {
+            let obj = get_ui_object_ptr(&child);
+            obj.start(ex.clone()).await;
+        }
     }
     }
 
 
     async fn process_char(me: &Weak<Self>, ev_sub: &Subscription<(char, KeyMods, bool)>) -> bool {
     async fn process_char(me: &Weak<Self>, ev_sub: &Subscription<(char, KeyMods, bool)>) -> bool {