Przeglądaj źródła

wallet: finish migration for handle_(mouse_btn_down|mouse_btn_up|mouse_move|mouse_wheel|touch)

darkfi 1 rok temu
rodzic
commit
4a0d6ce8f4

+ 72 - 116
bin/darkwallet/src/ui/button.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 async_trait::async_trait;
 use miniquad::{MouseButton, TouchPhase};
 use miniquad::{MouseButton, TouchPhase};
 use std::sync::{
 use std::sync::{
     atomic::{AtomicBool, Ordering},
     atomic::{AtomicBool, Ordering},
@@ -24,9 +25,9 @@ use std::sync::{
 
 
 use crate::{
 use crate::{
     gfx::{GraphicsEventPublisherPtr, Point, Rectangle},
     gfx::{GraphicsEventPublisherPtr, Point, Rectangle},
-    prop::{PropertyBool, PropertyPtr, Role, PropertyUint32},
+    prop::{PropertyBool, PropertyPtr, PropertyUint32, Role},
     pubsub::Subscription,
     pubsub::Subscription,
-    scene::{Pimpl, SceneGraphPtr2, SceneNodeId},
+    scene::{Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId},
     ExecutorPtr,
     ExecutorPtr,
 };
 };
 
 
@@ -36,8 +37,6 @@ pub type ButtonPtr = Arc<Button>;
 
 
 pub struct Button {
 pub struct Button {
     node_id: SceneNodeId,
     node_id: SceneNodeId,
-    #[allow(dead_code)]
-    tasks: Vec<smol::Task<()>>,
     sg: SceneGraphPtr2,
     sg: SceneGraphPtr2,
 
 
     is_active: PropertyBool,
     is_active: PropertyBool,
@@ -63,180 +62,137 @@ impl Button {
         //let sig = node.get_signal("click").expect("Button::click");
         //let sig = node.get_signal("click").expect("Button::click");
         drop(scene_graph);
         drop(scene_graph);
 
 
-        let self_ = Arc::new_cyclic(|me: &Weak<Self>| {
-            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_touch();
-            let me2 = me.clone();
-            let touch_task =
-                ex.spawn(async move { while Self::process_touch(&me2, &ev_sub).await {} });
-
-            let tasks = vec![mouse_btn_down_task, mouse_btn_up_task, touch_task];
-
-            Self { node_id, tasks, sg, is_active, rect, 
-                z_index,
-                mouse_btn_held: AtomicBool::new(false) }
+        let self_ = Arc::new_cyclic(|me: &Weak<Self>| Self {
+            node_id,
+            sg,
+            is_active,
+            rect,
+            z_index,
+            mouse_btn_held: AtomicBool::new(false),
         });
         });
 
 
         Pimpl::Button(self_)
         Pimpl::Button(self_)
     }
     }
 
 
-    async fn process_mouse_btn_down(
-        me: &Weak<Self>,
-        ev_sub: &Subscription<(MouseButton, f32, f32)>,
-    ) -> bool {
-        let Ok((btn, mouse_x, mouse_y)) = ev_sub.receive().await else {
-            debug!(target: "ui::button", "Event relayer closed");
-            return false
-        };
-
-        let Some(self_) = me.upgrade() else {
-            // Should not happen
-            panic!("self destroyed before mouse_btn_down_task was stopped!");
+    fn get_cached_rect(&self) -> Option<Rectangle> {
+        let Ok(rect) = read_rect(self.rect.clone()) else {
+            error!(target: "ui::button", "cached_rect is None");
+            return None
         };
         };
-
-        if !self_.is_active.get() {
-            return true
-        }
-
-        self_.handle_mouse_btn_down(btn, mouse_x, mouse_y);
-        true
+        Some(rect)
     }
     }
 
 
-    async fn process_mouse_btn_up(
-        me: &Weak<Self>,
-        ev_sub: &Subscription<(MouseButton, f32, f32)>,
-    ) -> bool {
-        let Ok((btn, mouse_x, mouse_y)) = ev_sub.receive().await else {
-            debug!(target: "ui::button", "Event relayer closed");
-            return false
-        };
-
-        let Some(self_) = me.upgrade() else {
-            // Should not happen
-            panic!("self destroyed before mouse_btn_up_task was stopped!");
-        };
-
-        if !self_.is_active.get() {
-            return true
+    pub fn set_parent_rect(&self, parent_rect: &Rectangle) {
+        if let Err(err) = eval_rect(self.rect.clone(), parent_rect) {
+            panic!("Button bad rect property: {}", err);
         }
         }
+    }
+}
 
 
-        self_.handle_mouse_btn_up(btn, mouse_x, mouse_y).await;
-        true
+#[async_trait]
+impl UIObject for Button {
+    fn z_index(&self) -> u32 {
+        self.z_index.get()
     }
     }
 
 
-    async fn process_touch(
-        me: &Weak<Self>,
-        ev_sub: &Subscription<(TouchPhase, u64, f32, f32)>,
+    async fn handle_mouse_btn_down(
+        &self,
+        sg: &SceneGraph,
+        btn: MouseButton,
+        mouse_x: f32,
+        mouse_y: f32,
     ) -> bool {
     ) -> bool {
-        let Ok((phase, id, touch_x, touch_y)) = ev_sub.receive().await else {
-            debug!(target: "ui::button", "Event relayer closed");
+        if !self.is_active.get() {
             return false
             return false
-        };
-
-        let Some(self_) = me.upgrade() else {
-            // Should not happen
-            panic!("self destroyed before touch_task was stopped!");
-        };
-
-        if !self_.is_active.get() {
-            return true
         }
         }
 
 
-        self_.handle_touch(phase, id, touch_x, touch_y).await;
-        true
-    }
-
-    fn handle_mouse_btn_down(&self, btn: MouseButton, mouse_x: f32, mouse_y: f32) {
         if btn != MouseButton::Left {
         if btn != MouseButton::Left {
-            return
+            return false
         }
         }
 
 
         let mouse_pos = Point::from([mouse_x, mouse_y]);
         let mouse_pos = Point::from([mouse_x, mouse_y]);
 
 
-        let Some(rect) = self.get_cached_rect() else { return };
+        let Some(rect) = self.get_cached_rect() else { return false };
         if !rect.contains(&mouse_pos) {
         if !rect.contains(&mouse_pos) {
-            return
+            return false
         }
         }
 
 
         self.mouse_btn_held.store(true, Ordering::Relaxed);
         self.mouse_btn_held.store(true, Ordering::Relaxed);
+        true
     }
     }
 
 
-    async fn handle_mouse_btn_up(&self, btn: MouseButton, mouse_x: f32, mouse_y: f32) {
+    async fn handle_mouse_btn_up(
+        &self,
+        sg: &SceneGraph,
+        btn: MouseButton,
+        mouse_x: f32,
+        mouse_y: f32,
+    ) -> bool {
+        if !self.is_active.get() {
+            return false
+        }
+
         if btn != MouseButton::Left {
         if btn != MouseButton::Left {
-            return
+            return false
         }
         }
 
 
         // Did we start the click inside the button?
         // Did we start the click inside the button?
         let btn_held = self.mouse_btn_held.swap(false, Ordering::Relaxed);
         let btn_held = self.mouse_btn_held.swap(false, Ordering::Relaxed);
         if !btn_held {
         if !btn_held {
-            return
+            return false
         }
         }
 
 
         let mouse_pos = Point::from([mouse_x, mouse_y]);
         let mouse_pos = Point::from([mouse_x, mouse_y]);
 
 
         // Are we releasing the click inside the button?
         // Are we releasing the click inside the button?
-        let Some(rect) = self.get_cached_rect() else { return };
+        let Some(rect) = self.get_cached_rect() else { return false };
         if !rect.contains(&mouse_pos) {
         if !rect.contains(&mouse_pos) {
-            return
+            return false
         }
         }
 
 
         debug!(target: "ui::button", "Mouse button clicked!");
         debug!(target: "ui::button", "Mouse button clicked!");
         let scene_graph = self.sg.lock().await;
         let scene_graph = self.sg.lock().await;
         let node = scene_graph.get_node(self.node_id).unwrap();
         let node = scene_graph.get_node(self.node_id).unwrap();
         node.trigger("click", vec![]).await.unwrap();
         node.trigger("click", vec![]).await.unwrap();
+
+        true
     }
     }
 
 
-    async fn handle_touch(&self, phase: TouchPhase, id: u64, touch_x: f32, touch_y: f32) {
+    async fn handle_touch(
+        &self,
+        sg: &SceneGraph,
+        phase: TouchPhase,
+        id: u64,
+        touch_x: f32,
+        touch_y: f32,
+    ) -> bool {
+        if !self.is_active.get() {
+            return false
+        }
+
         // Ignore multi-touch
         // Ignore multi-touch
         if id != 0 {
         if id != 0 {
-            return
+            return false
         }
         }
 
 
-        let Some(rect) = self.get_cached_rect() else { return };
+        let Some(rect) = self.get_cached_rect() else { return false };
         let touch_pos = Point { x: touch_x, y: touch_y };
         let touch_pos = Point { x: touch_x, y: touch_y };
         if !rect.contains(&touch_pos) {
         if !rect.contains(&touch_pos) {
             //debug!(target: "ui::chatview", "not inside rect");
             //debug!(target: "ui::chatview", "not inside rect");
-            return
+            return false
         }
         }
 
 
         // Simulate mouse events
         // Simulate mouse events
         match phase {
         match phase {
-            TouchPhase::Started => self.handle_mouse_btn_down(MouseButton::Left, touch_x, touch_y),
+            TouchPhase::Started => {
+                self.handle_mouse_btn_down(sg, MouseButton::Left, touch_x, touch_y).await;
+            }
             TouchPhase::Moved => {}
             TouchPhase::Moved => {}
             TouchPhase::Ended => {
             TouchPhase::Ended => {
-                self.handle_mouse_btn_up(MouseButton::Left, touch_x, touch_y).await
+                self.handle_mouse_btn_up(sg, MouseButton::Left, touch_x, touch_y).await;
             }
             }
             TouchPhase::Cancelled => {}
             TouchPhase::Cancelled => {}
         }
         }
-    }
-
-    fn get_cached_rect(&self) -> Option<Rectangle> {
-        let Ok(rect) = read_rect(self.rect.clone()) else {
-            error!(target: "ui::button", "cached_rect is None");
-            return None
-        };
-        Some(rect)
-    }
-
-    pub fn set_parent_rect(&self, parent_rect: &Rectangle) {
-        if let Err(err) = eval_rect(self.rect.clone(), parent_rect) {
-            panic!("Button bad rect property: {}", err);
-        }
-    }
-}
-
-impl UIObject for Button {
-    fn z_index(&self) -> u32 {
-        self.z_index.get()
+        true
     }
     }
 }
 }
-

+ 190 - 280
bin/darkwallet/src/ui/chatview/mod.rs

@@ -16,14 +16,12 @@
  * 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 async_trait::async_trait;
 use async_lock::Mutex as AsyncMutex;
 use async_lock::Mutex as AsyncMutex;
+use async_trait::async_trait;
 use atomic_float::AtomicF32;
 use atomic_float::AtomicF32;
 use chrono::{Local, TimeZone};
 use chrono::{Local, TimeZone};
 use darkfi::system::{msleep, CondVar};
 use darkfi::system::{msleep, CondVar};
-use darkfi_serial::{
-    deserialize, Decodable, Encodable, SerialDecodable, SerialEncodable,
-};
+use darkfi_serial::{deserialize, Decodable, Encodable, SerialDecodable, SerialEncodable};
 use miniquad::{KeyCode, KeyMods, MouseButton, TouchPhase};
 use miniquad::{KeyCode, KeyMods, MouseButton, TouchPhase};
 use rand::{rngs::OsRng, Rng};
 use rand::{rngs::OsRng, Rng};
 use sled_overlay::sled;
 use sled_overlay::sled;
@@ -217,31 +215,6 @@ impl ChatView {
         drop(scene_graph);
         drop(scene_graph);
 
 
         let self_ = Arc::new_cyclic(|me: &Weak<Self>| {
         let self_ = Arc::new_cyclic(|me: &Weak<Self>| {
-            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_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_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_touch();
-            let me2 = me.clone();
-            let touch_task =
-                ex.spawn(async move { while Self::process_touch(&me2, &ev_sub).await {} });
-
             let me2 = me.clone();
             let me2 = me.clone();
             let insert_line_method_task =
             let insert_line_method_task =
                 ex.spawn(
                 ex.spawn(
@@ -291,16 +264,7 @@ impl ChatView {
             //on_modify.when_change(rect.clone(), redraw);
             //on_modify.when_change(rect.clone(), redraw);
             //on_modify.when_change(debug.prop(), redraw);
             //on_modify.when_change(debug.prop(), redraw);
 
 
-            let mut tasks = vec![
-                mouse_wheel_task,
-                mouse_move_task,
-                mouse_btn_down_task,
-                mouse_btn_up_task,
-                touch_task,
-                insert_line_method_task,
-                motion_task,
-                bgload_task,
-            ];
+            let mut tasks = vec![insert_line_method_task, motion_task, bgload_task];
             tasks.append(&mut on_modify.tasks);
             tasks.append(&mut on_modify.tasks);
 
 
             Self {
             Self {
@@ -358,90 +322,6 @@ impl ChatView {
         Pimpl::ChatView(self_)
         Pimpl::ChatView(self_)
     }
     }
 
 
-    async fn process_mouse_wheel(me: &Weak<Self>, ev_sub: &Subscription<(f32, f32)>) -> bool {
-        let Ok((wheel_x, wheel_y)) = ev_sub.receive().await else {
-            debug!(target: "ui::chatview", "Event relayer closed");
-            return false
-        };
-
-        let Some(self_) = me.upgrade() else {
-            // Should not happen
-            panic!("self destroyed before mouse_wheel_task was stopped!");
-        };
-
-        self_.handle_mouse_wheel(wheel_x, wheel_y).await;
-        true
-    }
-
-    async fn process_mouse_move(me: &Weak<Self>, ev_sub: &Subscription<(f32, f32)>) -> bool {
-        let Ok((mouse_x, mouse_y)) = ev_sub.receive().await else {
-            debug!(target: "ui::chatview", "Event relayer closed");
-            return false
-        };
-
-        let Some(self_) = me.upgrade() else {
-            // Should not happen
-            panic!("self destroyed before mouse_move_task was stopped!");
-        };
-
-        self_.handle_mouse_move(mouse_x, mouse_y).await;
-        true
-    }
-
-    async fn process_mouse_btn_down(
-        me: &Weak<Self>,
-        ev_sub: &Subscription<(MouseButton, f32, f32)>,
-    ) -> bool {
-        let Ok((btn, mouse_x, mouse_y)) = ev_sub.receive().await else {
-            debug!(target: "ui::button", "Event relayer closed");
-            return false
-        };
-
-        let Some(self_) = me.upgrade() else {
-            // Should not happen
-            panic!("self destroyed before mouse_btn_down_task was stopped!");
-        };
-
-        self_.handle_mouse_btn_down(btn, mouse_x, mouse_y).await;
-        true
-    }
-
-    async fn process_mouse_btn_up(
-        me: &Weak<Self>,
-        ev_sub: &Subscription<(MouseButton, f32, f32)>,
-    ) -> bool {
-        let Ok((btn, mouse_x, mouse_y)) = ev_sub.receive().await else {
-            debug!(target: "ui::button", "Event relayer closed");
-            return false
-        };
-
-        let Some(self_) = me.upgrade() else {
-            // Should not happen
-            panic!("self destroyed before mouse_btn_up_task was stopped!");
-        };
-
-        self_.handle_mouse_btn_up(btn, mouse_x, mouse_y).await;
-        true
-    }
-
-    async fn process_touch(
-        me: &Weak<Self>,
-        ev_sub: &Subscription<(TouchPhase, u64, f32, f32)>,
-    ) -> bool {
-        let Ok((phase, id, touch_x, touch_y)) = ev_sub.receive().await else {
-            debug!(target: "ui::chatview", "Event relayer closed");
-            return false
-        };
-
-        let Some(self_) = me.upgrade() else {
-            // Should not happen
-            panic!("self destroyed before touch_task was stopped!");
-        };
-
-        self_.handle_touch(phase, id, touch_x, touch_y).await;
-        true
-    }
-
     async fn process_insert_line_method(
     async fn process_insert_line_method(
         me: &Weak<Self>,
         me: &Weak<Self>,
         recvr: &async_channel::Receiver<Vec<u8>>,
         recvr: &async_channel::Receiver<Vec<u8>>,
@@ -474,162 +354,6 @@ impl ChatView {
         true
         true
     }
     }
 
 
-    async fn handle_mouse_wheel(&self, wheel_x: f32, wheel_y: f32) {
-        //debug!(target: "ui::chatview", "handle_mouse_wheel({wheel_x}, {wheel_y})");
-
-        let Ok(rect) = read_rect(self.rect.clone()) else { return };
-
-        let mouse_pos = self.mouse_pos.lock().unwrap().clone();
-        if !rect.contains(&mouse_pos) {
-            //debug!(target: "ui::chatview", "not inside rect");
-            return
-        }
-
-        self.speed.fetch_add(wheel_y * self.scroll_start_accel.get(), Ordering::Relaxed);
-        self.motion_cv.notify();
-    }
-
-    async fn handle_mouse_move(&self, mouse_x: f32, mouse_y: f32) {
-        //debug!(target: "ui::chatview", "handle_mouse_move({mouse_x}, {mouse_y})");
-        let mouse_pos = Point::from([mouse_x, mouse_y]);
-
-        // We store the mouse pos for use in handle_mouse_wheel()
-        *self.mouse_pos.lock().unwrap() = mouse_pos.clone();
-
-        if !self.mouse_btn_held.load(Ordering::Relaxed) {
-            return
-        }
-
-        let Ok(rect) = read_rect(self.rect.clone()) else { return };
-        if !rect.contains(&mouse_pos) {
-            return
-        }
-
-        self.select_line(mouse_y).await;
-    }
-
-    async fn handle_mouse_btn_down(&self, btn: MouseButton, mouse_x: f32, mouse_y: f32) {
-        if btn != MouseButton::Left {
-            return
-        }
-
-        let mouse_pos = Point::from([mouse_x, mouse_y]);
-
-        let Ok(rect) = read_rect(self.rect.clone()) else { return };
-        if !rect.contains(&mouse_pos) {
-            return
-        }
-
-        self.select_line(mouse_y).await;
-        self.mouse_btn_held.store(true, Ordering::Relaxed);
-    }
-
-    async fn handle_mouse_btn_up(&self, btn: MouseButton, mouse_x: f32, mouse_y: f32) {
-        if btn != MouseButton::Left {
-            return
-        }
-
-        self.mouse_btn_held.store(false, Ordering::Relaxed);
-    }
-
-    async fn handle_touch(&self, phase: TouchPhase, id: u64, touch_x: f32, touch_y: f32) {
-        // Ignore multi-touch
-        if id != 0 {
-            return
-        }
-
-        let Ok(rect) = read_rect(self.rect.clone()) else { return };
-        //debug!(target: "ui::chatview", "handle_touch({phase:?}, {touch_x}, {touch_y})");
-
-        let touch_pos = Point { x: touch_x, y: touch_y };
-        if !rect.contains(&touch_pos) {
-            match phase {
-                TouchPhase::Started => *self.touch_info.lock().unwrap() = None,
-                _ => self.end_touch_phase(touch_y),
-            }
-            return
-        }
-
-        let select_hold_time = self.select_hold_time.get();
-
-        // Simulate mouse events
-        match phase {
-            TouchPhase::Started => {
-                self.touch_is_active.store(true, Ordering::Relaxed);
-
-                let mut touch_info = self.touch_info.lock().unwrap();
-                *touch_info = Some(TouchInfo::new(self.scroll.get(), touch_y));
-            }
-            TouchPhase::Moved => {
-                let (start_scroll, start_y, start_elapsed, do_update, is_select_mode) = {
-                    let mut touch_info = self.touch_info.lock().unwrap();
-                    let Some(touch_info) = &mut *touch_info else { return };
-
-                    touch_info.last_y = touch_y;
-
-                    let start_scroll = touch_info.start_scroll;
-                    let start_y = touch_info.start_y;
-
-                    let start_elapsed = touch_info.start_instant.elapsed().as_millis_f32();
-                    if start_elapsed > select_hold_time && touch_info.is_select_mode.is_none() {
-                        // Did we move?
-                        if (touch_y - start_y).abs() < BIG_EPSILON {
-                            touch_info.is_select_mode = Some(true);
-                        } else {
-                            touch_info.is_select_mode = Some(false);
-                        }
-                    }
-                    let is_select_mode = touch_info.is_select_mode.clone();
-
-                    touch_info.push_sample(touch_y);
-
-                    // Only update screen every 20ms. Avoid wasting cycles.
-                    let last_elapsed = touch_info.last_instant.elapsed().as_millis_f32();
-                    let do_update = last_elapsed > 20.;
-                    if do_update {
-                        touch_info.last_instant = std::time::Instant::now();
-                    }
-
-                    (start_scroll, start_y, start_elapsed, do_update, is_select_mode)
-                };
-
-                debug!(target: "ui::chatview", "touch phase moved, is_select_mode={is_select_mode:?}");
-
-                // When scrolling if we suddenly grab the screen for more than a brief period
-                // of time then stop the scrolling completely.
-                if start_elapsed > 200. {
-                    //debug!(target: "ui::chatview", "Stopping scroll accel");
-                    self.speed.store(0., Ordering::Relaxed);
-                }
-
-                // Only update every so often to prevent wasting resources.
-                if !do_update {
-                    return
-                }
-
-                // We are in selection mode so don't scroll the screen until touch phase ends.
-                if let Some(is_select_mode) = is_select_mode &&
-                    is_select_mode
-                {
-                    self.select_line(touch_y).await;
-                    return
-                }
-
-                let dist = touch_y - start_y;
-                // No movement so just return
-                if dist.abs() < BIG_EPSILON {
-                    return
-                }
-                let scroll = start_scroll + dist;
-                // Redraws the screen from the cache
-                self.scrollview(scroll).await;
-            }
-            TouchPhase::Ended | TouchPhase::Cancelled => {
-                self.end_touch_phase(touch_y);
-            }
-        }
-    }
-
     /// Mark line as selected
     /// Mark line as selected
     async fn select_line(&self, mut y: f32) {
     async fn select_line(&self, mut y: f32) {
         // The cursor is inside the rect. We just have to find which line it clicked.
         // The cursor is inside the rect. We just have to find which line it clicked.
@@ -998,7 +722,13 @@ impl UIObject for ChatView {
         self.z_index.get()
         self.z_index.get()
     }
     }
 
 
-    async fn handle_key_down(&self, sg: &SceneGraph, key: KeyCode, mods: KeyMods, repeat: bool) -> bool {
+    async fn handle_key_down(
+        &self,
+        sg: &SceneGraph,
+        key: KeyCode,
+        mods: KeyMods,
+        repeat: bool,
+    ) -> bool {
         if repeat {
         if repeat {
             return false
             return false
         }
         }
@@ -1017,4 +747,184 @@ impl UIObject for ChatView {
 
 
         true
         true
     }
     }
+
+    async fn handle_mouse_btn_down(
+        &self,
+        sg: &SceneGraph,
+        btn: MouseButton,
+        mouse_x: f32,
+        mouse_y: f32,
+    ) -> bool {
+        if btn != MouseButton::Left {
+            return false
+        }
+
+        let mouse_pos = Point::from([mouse_x, mouse_y]);
+
+        let Ok(rect) = read_rect(self.rect.clone()) else { return false };
+        if !rect.contains(&mouse_pos) {
+            return false
+        }
+
+        self.select_line(mouse_y).await;
+        self.mouse_btn_held.store(true, Ordering::Relaxed);
+        true
+    }
+
+    async fn handle_mouse_btn_up(
+        &self,
+        sg: &SceneGraph,
+        btn: MouseButton,
+        mouse_x: f32,
+        mouse_y: f32,
+    ) -> bool {
+        if btn != MouseButton::Left {
+            return false
+        }
+
+        self.mouse_btn_held.store(false, Ordering::Relaxed);
+        true
+    }
+
+    async fn handle_mouse_move(&self, sg: &SceneGraph, mouse_x: f32, mouse_y: f32) -> bool {
+        //debug!(target: "ui::chatview", "handle_mouse_move({mouse_x}, {mouse_y})");
+        let mouse_pos = Point::from([mouse_x, mouse_y]);
+
+        // We store the mouse pos for use in handle_mouse_wheel()
+        *self.mouse_pos.lock().unwrap() = mouse_pos.clone();
+
+        if !self.mouse_btn_held.load(Ordering::Relaxed) {
+            return false
+        }
+
+        let Ok(rect) = read_rect(self.rect.clone()) else { return false };
+        if !rect.contains(&mouse_pos) {
+            return false
+        }
+
+        self.select_line(mouse_y).await;
+        false
+    }
+
+    async fn handle_mouse_wheel(&self, sg: &SceneGraph, wheel_x: f32, wheel_y: f32) -> bool {
+        debug!(target: "ui::chatview", "handle_mouse_wheel({wheel_x}, {wheel_y})");
+
+        let Ok(rect) = read_rect(self.rect.clone()) else { return false };
+
+        let mouse_pos = self.mouse_pos.lock().unwrap().clone();
+        if !rect.contains(&mouse_pos) {
+            //debug!(target: "ui::chatview", "not inside rect");
+            return false
+        }
+
+        self.speed.fetch_add(wheel_y * self.scroll_start_accel.get(), Ordering::Relaxed);
+        self.motion_cv.notify();
+        true
+    }
+
+    async fn handle_touch(
+        &self,
+        sg: &SceneGraph,
+        phase: TouchPhase,
+        id: u64,
+        touch_x: f32,
+        touch_y: f32,
+    ) -> bool {
+        // Ignore multi-touch
+        if id != 0 {
+            return false
+        }
+
+        let Ok(rect) = read_rect(self.rect.clone()) else { return false };
+        //debug!(target: "ui::chatview", "handle_touch({phase:?}, {touch_x}, {touch_y})");
+
+        let touch_pos = Point { x: touch_x, y: touch_y };
+        if !rect.contains(&touch_pos) {
+            match phase {
+                TouchPhase::Started => *self.touch_info.lock().unwrap() = None,
+                _ => self.end_touch_phase(touch_y),
+            }
+            return false
+        }
+
+        let select_hold_time = self.select_hold_time.get();
+
+        // Simulate mouse events
+        match phase {
+            TouchPhase::Started => {
+                self.touch_is_active.store(true, Ordering::Relaxed);
+
+                let mut touch_info = self.touch_info.lock().unwrap();
+                *touch_info = Some(TouchInfo::new(self.scroll.get(), touch_y));
+            }
+            TouchPhase::Moved => {
+                let (start_scroll, start_y, start_elapsed, do_update, is_select_mode) = {
+                    let mut touch_info = self.touch_info.lock().unwrap();
+                    let Some(touch_info) = &mut *touch_info else { return false };
+
+                    touch_info.last_y = touch_y;
+
+                    let start_scroll = touch_info.start_scroll;
+                    let start_y = touch_info.start_y;
+
+                    let start_elapsed = touch_info.start_instant.elapsed().as_millis_f32();
+                    if start_elapsed > select_hold_time && touch_info.is_select_mode.is_none() {
+                        // Did we move?
+                        if (touch_y - start_y).abs() < BIG_EPSILON {
+                            touch_info.is_select_mode = Some(true);
+                        } else {
+                            touch_info.is_select_mode = Some(false);
+                        }
+                    }
+                    let is_select_mode = touch_info.is_select_mode.clone();
+
+                    touch_info.push_sample(touch_y);
+
+                    // Only update screen every 20ms. Avoid wasting cycles.
+                    let last_elapsed = touch_info.last_instant.elapsed().as_millis_f32();
+                    let do_update = last_elapsed > 20.;
+                    if do_update {
+                        touch_info.last_instant = std::time::Instant::now();
+                    }
+
+                    (start_scroll, start_y, start_elapsed, do_update, is_select_mode)
+                };
+
+                debug!(target: "ui::chatview", "touch phase moved, is_select_mode={is_select_mode:?}");
+
+                // When scrolling if we suddenly grab the screen for more than a brief period
+                // of time then stop the scrolling completely.
+                if start_elapsed > 200. {
+                    //debug!(target: "ui::chatview", "Stopping scroll accel");
+                    self.speed.store(0., Ordering::Relaxed);
+                }
+
+                // Only update every so often to prevent wasting resources.
+                if !do_update {
+                    return true
+                }
+
+                // We are in selection mode so don't scroll the screen until touch phase ends.
+                if let Some(is_select_mode) = is_select_mode &&
+                    is_select_mode
+                {
+                    self.select_line(touch_y).await;
+                    return true
+                }
+
+                let dist = touch_y - start_y;
+                // No movement so just return
+                if dist.abs() < BIG_EPSILON {
+                    return true
+                }
+                let scroll = start_scroll + dist;
+                // Redraws the screen from the cache
+                self.scrollview(scroll).await;
+            }
+            TouchPhase::Ended | TouchPhase::Cancelled => {
+                self.end_touch_phase(touch_y);
+            }
+        }
+        true
+    }
 }
 }

+ 71 - 126
bin/darkwallet/src/ui/editbox.rs

@@ -217,26 +217,6 @@ impl EditBox {
         let glyphs = text_shaper.shape(text.get(), font_size.get()).await;
         let glyphs = text_shaper.shape(text.get(), font_size.get()).await;
 
 
         let self_ = Arc::new_cyclic(|me: &Weak<Self>| {
         let self_ = Arc::new_cyclic(|me: &Weak<Self>| {
-            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_touch();
-            let me2 = me.clone();
-            let touch_task =
-                ex.spawn(async move { while Self::process_touch(&me2, &ev_sub).await {} });
-
             let mut on_modify = OnModify::new(ex, node_name, node_id, me.clone());
             let mut on_modify = OnModify::new(ex, node_name, node_id, me.clone());
             on_modify.when_change(is_focused.prop(), Self::change_focus);
             on_modify.when_change(is_focused.prop(), Self::change_focus);
 
 
@@ -270,14 +250,7 @@ impl EditBox {
             on_modify.when_change(z_index.prop(), redraw);
             on_modify.when_change(z_index.prop(), redraw);
             on_modify.when_change(debug.prop(), redraw);
             on_modify.when_change(debug.prop(), redraw);
 
 
-            // on modify tasks too
-            let mut tasks = vec![
-                mouse_btn_down_task,
-                mouse_btn_up_task,
-                mouse_move_task,
-                touch_task,
-            ];
-            tasks.append(&mut on_modify.tasks);
+            let tasks = on_modify.tasks;
 
 
             Self {
             Self {
                 node_id,
                 node_id,
@@ -449,91 +422,6 @@ impl EditBox {
         Ok(())
         Ok(())
     }
     }
 
 
-    async fn process_mouse_btn_down(
-        me: &Weak<Self>,
-        ev_sub: &Subscription<(MouseButton, f32, f32)>,
-    ) -> bool {
-        let Ok((btn, mouse_x, mouse_y)) = ev_sub.receive().await else {
-            debug!(target: "ui::editbox", "Event relayer closed");
-            return false
-        };
-
-        let Some(self_) = me.upgrade() else {
-            // Should not happen
-            panic!("self destroyed before mouse_btn_down_task was stopped!");
-        };
-
-        if !self_.is_active.get() {
-            return true
-        }
-
-        self_.handle_mouse_btn_down(btn, mouse_x, mouse_y).await;
-        true
-    }
-
-    async fn process_mouse_btn_up(
-        me: &Weak<Self>,
-        ev_sub: &Subscription<(MouseButton, f32, f32)>,
-    ) -> bool {
-        let Ok((btn, mouse_x, mouse_y)) = ev_sub.receive().await else {
-            debug!(target: "ui::editbox", "Event relayer closed");
-            return false
-        };
-
-        let Some(self_) = me.upgrade() else {
-            // Should not happen
-            panic!("self destroyed before mouse_btn_up_task was stopped!");
-        };
-
-        if !self_.is_active.get() {
-            return true
-        }
-
-        self_.handle_mouse_btn_up(btn, mouse_x, mouse_y);
-        true
-    }
-
-    async fn process_mouse_move(me: &Weak<Self>, ev_sub: &Subscription<(f32, f32)>) -> bool {
-        let Ok((mouse_x, mouse_y)) = ev_sub.receive().await else {
-            debug!(target: "ui::editbox", "Event relayer closed");
-            return false
-        };
-
-        let Some(self_) = me.upgrade() else {
-            // Should not happen
-            panic!("self destroyed before mouse_move_task was stopped!");
-        };
-
-        if !self_.is_active.get() {
-            return true
-        }
-
-        self_.handle_mouse_move(mouse_x, mouse_y).await;
-        true
-    }
-
-    async fn process_touch(
-        me: &Weak<Self>,
-        ev_sub: &Subscription<(TouchPhase, u64, f32, f32)>,
-    ) -> bool {
-        let Ok((phase, id, touch_x, touch_y)) = ev_sub.receive().await else {
-            debug!(target: "ui::editbox", "Event relayer closed");
-            return false
-        };
-
-        let Some(self_) = me.upgrade() else {
-            // Should not happen
-            panic!("self destroyed before touch_task was stopped!");
-        };
-
-        if !self_.is_active.get() {
-            return true
-        }
-
-        self_.handle_touch(phase, id, touch_x, touch_y).await;
-        true
-    }
-
     async fn change_focus(self: Arc<Self>) {
     async fn change_focus(self: Arc<Self>) {
         if !self.is_active.get() {
         if !self.is_active.get() {
             return
             return
@@ -544,7 +432,7 @@ impl EditBox {
         self.redraw().await;
         self.redraw().await;
     }
     }
 
 
-    async fn handle_mouse_btn_down(&self, btn: MouseButton, mouse_x: f32, mouse_y: f32) {
+    async fn handle_click_down(&self, btn: MouseButton, mouse_x: f32, mouse_y: f32) {
         if btn != MouseButton::Left {
         if btn != MouseButton::Left {
             return
             return
         }
         }
@@ -590,7 +478,7 @@ impl EditBox {
 
 
         self.redraw().await;
         self.redraw().await;
     }
     }
-    fn handle_mouse_btn_up(&self, btn: MouseButton, _mouse_x: f32, _mouse_y: f32) {
+    fn handle_click_up(&self, btn: MouseButton, _mouse_x: f32, _mouse_y: f32) {
         if btn != MouseButton::Left {
         if btn != MouseButton::Left {
             return
             return
         }
         }
@@ -598,7 +486,7 @@ impl EditBox {
         // releasing mouse button will end selection
         // releasing mouse button will end selection
         self.mouse_btn_held.store(false, Ordering::Relaxed);
         self.mouse_btn_held.store(false, Ordering::Relaxed);
     }
     }
-    async fn handle_mouse_move(&self, mouse_x: f32, _mouse_y: f32) {
+    async fn handle_cursor_move(&self, mouse_x: f32, _mouse_y: f32) {
         if !self.mouse_btn_held.load(Ordering::Relaxed) {
         if !self.mouse_btn_held.load(Ordering::Relaxed) {
             return;
             return;
         }
         }
@@ -626,10 +514,10 @@ impl EditBox {
         // Simulate mouse events
         // Simulate mouse events
         match phase {
         match phase {
             TouchPhase::Started => {
             TouchPhase::Started => {
-                self.handle_mouse_btn_down(MouseButton::Left, touch_x, touch_y).await
+                self.handle_click_down(MouseButton::Left, touch_x, touch_y).await
             }
             }
-            TouchPhase::Moved => self.handle_mouse_move(touch_x, touch_y).await,
-            TouchPhase::Ended => self.handle_mouse_btn_up(MouseButton::Left, touch_x, touch_y),
+            TouchPhase::Moved => self.handle_cursor_move(touch_x, touch_y).await,
+            TouchPhase::Ended => self.handle_click_up(MouseButton::Left, touch_x, touch_y),
             TouchPhase::Cancelled => {}
             TouchPhase::Cancelled => {}
         }
         }
     }
     }
@@ -1080,9 +968,7 @@ impl EditBox {
     }
     }
 
 
     fn draw_cached(&self) -> Option<DrawUpdate> {
     fn draw_cached(&self) -> Option<DrawUpdate> {
-        let Ok(rect) = read_rect(self.rect.clone()) else {
-            panic!("Node bad rect property")
-        };
+        let Ok(rect) = read_rect(self.rect.clone()) else { panic!("Node bad rect property") };
 
 
         // draw will recalc this when it's None
         // draw will recalc this when it's None
         let render_info = self.regen_mesh(rect.clone());
         let render_info = self.regen_mesh(rect.clone());
@@ -1105,9 +991,7 @@ impl EditBox {
             num_elements: render_info.mesh.num_elements,
             num_elements: render_info.mesh.num_elements,
         };
         };
 
 
-        let Some(parent_rect) = self.parent_rect.lock().unwrap().clone() else {
-            return None
-        };
+        let Some(parent_rect) = self.parent_rect.lock().unwrap().clone() else { return None };
 
 
         let off_x = rect.x / parent_rect.w;
         let off_x = rect.x / parent_rect.w;
         let off_y = rect.y / parent_rect.h;
         let off_y = rect.y / parent_rect.h;
@@ -1216,7 +1100,13 @@ impl UIObject for EditBox {
         true
         true
     }
     }
 
 
-    async fn handle_key_down(&self, sg: &SceneGraph, key: KeyCode, mods: KeyMods, repeat: bool) -> bool {
+    async fn handle_key_down(
+        &self,
+        sg: &SceneGraph,
+        key: KeyCode,
+        mods: KeyMods,
+        repeat: bool,
+    ) -> bool {
         // First filter for only single digit keys
         // First filter for only single digit keys
         // Avoid processing events handled by insert_char()
         // Avoid processing events handled by insert_char()
         if !ALLOWED_KEYCODES.contains(&key) {
         if !ALLOWED_KEYCODES.contains(&key) {
@@ -1240,6 +1130,61 @@ impl UIObject for EditBox {
         }
         }
         true
         true
     }
     }
+
+    async fn handle_mouse_btn_down(
+        &self,
+        sg: &SceneGraph,
+        btn: MouseButton,
+        mouse_x: f32,
+        mouse_y: f32,
+    ) -> bool {
+        if !self.is_active.get() {
+            return true
+        }
+
+        self.handle_click_down(btn, mouse_x, mouse_y).await;
+        true
+    }
+
+    async fn handle_mouse_btn_up(
+        &self,
+        sg: &SceneGraph,
+        btn: MouseButton,
+        mouse_x: f32,
+        mouse_y: f32,
+    ) -> bool {
+        if !self.is_active.get() {
+            return true
+        }
+
+        self.handle_click_up(btn, mouse_x, mouse_y);
+        true
+    }
+
+    async fn handle_mouse_move(&self, sg: &SceneGraph, mouse_x: f32, mouse_y: f32) -> bool {
+        if !self.is_active.get() {
+            return false
+        }
+
+        self.handle_cursor_move(mouse_x, mouse_y).await;
+        false
+    }
+
+    async fn handle_touch(
+        &self,
+        sg: &SceneGraph,
+        phase: TouchPhase,
+        id: u64,
+        touch_x: f32,
+        touch_y: f32,
+    ) -> bool {
+        if !self.is_active.get() {
+            return true
+        }
+
+        self.handle_touch(phase, id, touch_x, touch_y).await;
+        true
+    }
 }
 }
 
 
 /// Filter these char events from being handled since we handle them
 /// Filter these char events from being handled since we handle them

+ 0 - 1
bin/darkwallet/src/ui/image.rs

@@ -235,4 +235,3 @@ impl UIObject for Image {
         self.z_index.get()
         self.z_index.get()
     }
     }
 }
 }
-

+ 104 - 10
bin/darkwallet/src/ui/layer.rs

@@ -16,20 +16,23 @@
  * 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 async_trait::async_trait;
 use async_recursion::async_recursion;
 use async_recursion::async_recursion;
-use miniquad::{KeyMods, KeyCode};
+use async_trait::async_trait;
+use miniquad::{KeyCode, KeyMods, MouseButton, TouchPhase};
 use rand::{rngs::OsRng, Rng};
 use rand::{rngs::OsRng, Rng};
 use std::sync::{Arc, Weak};
 use std::sync::{Arc, Weak};
 
 
 use crate::{
 use crate::{
     gfx::{GfxDrawCall, GfxDrawInstruction, Rectangle, RenderApiPtr},
     gfx::{GfxDrawCall, GfxDrawInstruction, Rectangle, RenderApiPtr},
-    prop::{PropertyBool, PropertyPtr, Role, PropertyUint32},
+    prop::{PropertyBool, PropertyPtr, PropertyUint32, Role},
     scene::{Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId},
     scene::{Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId},
     ExecutorPtr,
     ExecutorPtr,
 };
 };
 
 
-use super::{eval_rect, get_parent_rect, read_rect, DrawUpdate, OnModify, Stoppable, UIObject, get_child_nodes_ordered, get_ui_object};
+use super::{
+    eval_rect, get_child_nodes_ordered, get_parent_rect, get_ui_object, read_rect, DrawUpdate,
+    OnModify, Stoppable, UIObject,
+};
 
 
 pub type RenderLayerPtr = Arc<RenderLayer>;
 pub type RenderLayerPtr = Arc<RenderLayer>;
 
 
@@ -77,14 +80,20 @@ impl RenderLayer {
                 dc_key: OsRng.gen(),
                 dc_key: OsRng.gen(),
                 is_visible,
                 is_visible,
                 rect,
                 rect,
-                z_index
+                z_index,
             }
             }
         });
         });
 
 
         Pimpl::RenderLayer(self_)
         Pimpl::RenderLayer(self_)
     }
     }
 
 
-    pub async fn handle_char(&self, sg: &SceneGraph, key: char, mods: KeyMods, repeat: bool) -> bool {
+    pub async fn handle_char(
+        &self,
+        sg: &SceneGraph,
+        key: char,
+        mods: KeyMods,
+        repeat: bool,
+    ) -> bool {
         false
         false
     }
     }
 
 
@@ -194,22 +203,107 @@ impl UIObject for RenderLayer {
         for child_id in get_child_nodes_ordered(&sg, self.node_id) {
         for child_id in get_child_nodes_ordered(&sg, self.node_id) {
             let node = sg.get_node(child_id).unwrap();
             let node = sg.get_node(child_id).unwrap();
             let obj = get_ui_object(node);
             let obj = get_ui_object(node);
-            if obj.handle_char(&sg, key, mods, repeat).await {
+            if obj.handle_char(sg, key, mods, repeat).await {
                 return true
                 return true
             }
             }
         }
         }
         false
         false
     }
     }
 
 
-    async fn handle_key_down(&self, sg: &SceneGraph, key: KeyCode, mods: KeyMods, repeat: bool) -> bool {
+    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) {
         for child_id in get_child_nodes_ordered(&sg, self.node_id) {
             let node = sg.get_node(child_id).unwrap();
             let node = sg.get_node(child_id).unwrap();
             let obj = get_ui_object(node);
             let obj = get_ui_object(node);
-            if obj.handle_key_down(&sg, key, mods, repeat).await {
+            if obj.handle_key_down(sg, key, mods, repeat).await {
                 return true
                 return true
             }
             }
         }
         }
         false
         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 {
+                return true
+            }
+        }
+        false
+    }
+    async fn handle_mouse_btn_down(
+        &self,
+        sg: &SceneGraph,
+        btn: MouseButton,
+        mouse_x: f32,
+        mouse_y: f32,
+    ) -> 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_mouse_btn_down(sg, btn, mouse_x, mouse_y).await {
+                return true
+            }
+        }
+        false
+    }
+    async fn handle_mouse_btn_up(
+        &self,
+        sg: &SceneGraph,
+        btn: MouseButton,
+        mouse_x: f32,
+        mouse_y: f32,
+    ) -> 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_mouse_btn_up(sg, btn, mouse_x, mouse_y).await {
+                return true
+            }
+        }
+        false
+    }
+    async fn handle_mouse_move(&self, sg: &SceneGraph, mouse_x: f32, mouse_y: f32) -> 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_mouse_move(sg, mouse_x, mouse_y).await {
+                return true
+            }
+        }
+        false
+    }
+    async fn handle_mouse_wheel(&self, sg: &SceneGraph, wheel_x: f32, wheel_y: f32) -> 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_mouse_wheel(sg, wheel_x, wheel_y).await {
+                return true
+            }
+        }
+        false
+    }
+    async fn handle_touch(
+        &self,
+        sg: &SceneGraph,
+        phase: TouchPhase,
+        id: u64,
+        touch_x: f32,
+        touch_y: f32,
+    ) -> 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_touch(sg, phase, id, touch_x, touch_y).await {
+                return true
+            }
+        }
+        false
+    }
+}

+ 61 - 16
bin/darkwallet/src/ui/mod.rs

@@ -17,15 +17,15 @@
  */
  */
 
 
 use async_trait::async_trait;
 use async_trait::async_trait;
+use miniquad::{KeyCode, KeyMods, MouseButton, TouchPhase};
 use std::sync::{Arc, Weak};
 use std::sync::{Arc, Weak};
-use miniquad::{KeyMods, KeyCode};
 
 
 use crate::{
 use crate::{
     error::{Error, Result},
     error::{Error, Result},
     expr::{SExprMachine, SExprVal},
     expr::{SExprMachine, SExprVal},
     gfx::{GfxBufferId, GfxDrawCall, GfxTextureId, Rectangle},
     gfx::{GfxBufferId, GfxDrawCall, GfxTextureId, Rectangle},
     prop::{PropertyPtr, Role},
     prop::{PropertyPtr, Role},
-    scene::{SceneGraph, SceneNode, SceneNodeId, SceneNodeType, Pimpl},
+    scene::{Pimpl, SceneGraph, SceneNode, SceneNodeId, SceneNodeType},
     ExecutorPtr,
     ExecutorPtr,
 };
 };
 
 
@@ -57,9 +57,55 @@ pub trait Stoppable {
 pub trait UIObject: Sync {
 pub trait UIObject: Sync {
     fn z_index(&self) -> u32;
     fn z_index(&self) -> u32;
 
 
-    async fn handle_char(&self, sg: &SceneGraph, key: char, mods: KeyMods, repeat: bool) -> bool { false }
-    async fn handle_key_down(&self, sg: &SceneGraph, key: KeyCode, mods: KeyMods, repeat: bool) -> bool { false }
-    async fn handle_key_up(&self, sg: &SceneGraph, key: KeyCode, mods: KeyMods) -> bool { false }
+    async fn handle_char(&self, sg: &SceneGraph, key: char, mods: KeyMods, repeat: bool) -> bool {
+        false
+    }
+    async fn handle_key_down(
+        &self,
+        sg: &SceneGraph,
+        key: KeyCode,
+        mods: KeyMods,
+        repeat: bool,
+    ) -> bool {
+        false
+    }
+    async fn handle_key_up(&self, sg: &SceneGraph, key: KeyCode, mods: KeyMods) -> bool {
+        false
+    }
+    async fn handle_mouse_btn_down(
+        &self,
+        sg: &SceneGraph,
+        btn: MouseButton,
+        mouse_x: f32,
+        mouse_y: f32,
+    ) -> bool {
+        false
+    }
+    async fn handle_mouse_btn_up(
+        &self,
+        sg: &SceneGraph,
+        btn: MouseButton,
+        mouse_x: f32,
+        mouse_y: f32,
+    ) -> bool {
+        false
+    }
+    async fn handle_mouse_move(&self, sg: &SceneGraph, mouse_x: f32, mouse_y: f32) -> bool {
+        false
+    }
+    async fn handle_mouse_wheel(&self, sg: &SceneGraph, wheel_x: f32, wheel_y: f32) -> bool {
+        false
+    }
+    async fn handle_touch(
+        &self,
+        sg: &SceneGraph,
+        phase: TouchPhase,
+        id: u64,
+        touch_x: f32,
+        touch_y: f32,
+    ) -> bool {
+        false
+    }
 }
 }
 
 
 pub struct DrawUpdate {
 pub struct DrawUpdate {
@@ -225,16 +271,16 @@ pub fn get_parent_rect(sg: &SceneGraph, node: &SceneNode) -> Option<Rectangle> {
 }
 }
 
 
 pub fn get_ui_object<'a>(node: &'a SceneNode) -> &'a dyn UIObject {
 pub fn get_ui_object<'a>(node: &'a SceneNode) -> &'a dyn UIObject {
-        match &node.pimpl {
-            Pimpl::RenderLayer(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(),
-            _ => panic!("unhandled type for get_ui_object"),
-        }
+    match &node.pimpl {
+        Pimpl::RenderLayer(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(),
+        _ => panic!("unhandled type for get_ui_object"),
+    }
 }
 }
 
 
 pub fn get_child_nodes_ordered(sg: &SceneGraph, node_id: SceneNodeId) -> Vec<SceneNodeId> {
 pub fn get_child_nodes_ordered(sg: &SceneGraph, node_id: SceneNodeId) -> Vec<SceneNodeId> {
@@ -251,4 +297,3 @@ pub fn get_child_nodes_ordered(sg: &SceneGraph, node_id: SceneNodeId) -> Vec<Sce
     let nodes = child_nodes.into_iter().rev().map(|(node_id, _)| node_id).collect();
     let nodes = child_nodes.into_iter().rev().map(|(node_id, _)| node_id).collect();
     nodes
     nodes
 }
 }
-

+ 0 - 1
bin/darkwallet/src/ui/text.rs

@@ -264,4 +264,3 @@ impl UIObject for Text {
         self.z_index.get()
         self.z_index.get()
     }
     }
 }
 }
-

+ 0 - 1
bin/darkwallet/src/ui/vector_art/mod.rs

@@ -185,4 +185,3 @@ impl UIObject for VectorArt {
         self.z_index.get()
         self.z_index.get()
     }
     }
 }
 }
-

+ 162 - 12
bin/darkwallet/src/ui/win.rs

@@ -16,8 +16,8 @@
  * 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 miniquad::{KeyCode, KeyMods, MouseButton, TouchPhase};
 use std::sync::{Arc, Weak};
 use std::sync::{Arc, Weak};
-use miniquad::{KeyMods, KeyCode};
 
 
 use crate::{
 use crate::{
     gfx::{GfxDrawCall, GraphicsEventPublisherPtr, Rectangle, RenderApiPtr},
     gfx::{GfxDrawCall, GraphicsEventPublisherPtr, Rectangle, RenderApiPtr},
@@ -27,7 +27,7 @@ use crate::{
     ExecutorPtr,
     ExecutorPtr,
 };
 };
 
 
-use super::{OnModify, Stoppable, get_child_nodes_ordered, get_ui_object};
+use super::{get_child_nodes_ordered, get_ui_object, OnModify, Stoppable};
 
 
 pub type WindowPtr = Arc<Window>;
 pub type WindowPtr = Arc<Window>;
 
 
@@ -103,7 +103,6 @@ impl Window {
             let key_up_task =
             let key_up_task =
                 ex.spawn(async move { while Self::process_key_up(&me2, &ev_sub).await {} });
                 ex.spawn(async move { while Self::process_key_up(&me2, &ev_sub).await {} });
 
 
-            /*
             let ev_sub = event_pub.subscribe_mouse_btn_down();
             let ev_sub = event_pub.subscribe_mouse_btn_down();
             let me2 = me.clone();
             let me2 = me.clone();
             let mouse_btn_down_task =
             let mouse_btn_down_task =
@@ -114,11 +113,20 @@ impl Window {
             let mouse_btn_up_task =
             let mouse_btn_up_task =
                 ex.spawn(async move { while Self::process_mouse_btn_up(&me2, &ev_sub).await {} });
                 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 ev_sub = event_pub.subscribe_touch();
             let me2 = me.clone();
             let me2 = me.clone();
             let touch_task =
             let touch_task =
                 ex.spawn(async move { while Self::process_touch(&me2, &ev_sub).await {} });
                 ex.spawn(async move { while Self::process_touch(&me2, &ev_sub).await {} });
-            */
 
 
             let sg2 = sg.clone();
             let sg2 = sg.clone();
             let redraw_fn = move |self_: Arc<Self>| {
             let redraw_fn = move |self_: Arc<Self>| {
@@ -137,10 +145,11 @@ impl Window {
                 char_task,
                 char_task,
                 key_down_task,
                 key_down_task,
                 key_up_task,
                 key_up_task,
-                //mouse_btn_down_task,
-                //mouse_btn_up_task,
-                //mouse_move_task,
-                //touch_task,
+                mouse_btn_down_task,
+                mouse_btn_up_task,
+                mouse_move_task,
+                mouse_wheel_task,
+                touch_task,
             ];
             ];
             tasks.append(&mut on_modify.tasks);
             tasks.append(&mut on_modify.tasks);
 
 
@@ -183,10 +192,7 @@ impl Window {
         true
         true
     }
     }
 
 
-    async fn process_key_up(
-        me: &Weak<Self>,
-        ev_sub: &Subscription<(KeyCode, KeyMods)>,
-    ) -> bool {
+    async fn process_key_up(me: &Weak<Self>, ev_sub: &Subscription<(KeyCode, KeyMods)>) -> bool {
         let Ok((key, mods)) = ev_sub.receive().await else {
         let Ok((key, mods)) = ev_sub.receive().await else {
             debug!(target: "ui::win", "Event relayer closed");
             debug!(target: "ui::win", "Event relayer closed");
             return false
             return false
@@ -201,6 +207,90 @@ impl Window {
         true
         true
     }
     }
 
 
+    async fn process_mouse_btn_down(
+        me: &Weak<Self>,
+        ev_sub: &Subscription<(MouseButton, f32, f32)>,
+    ) -> bool {
+        let Ok((btn, mouse_x, mouse_y)) = ev_sub.receive().await else {
+            debug!(target: "ui::editbox", "Event relayer closed");
+            return false
+        };
+
+        let Some(self_) = me.upgrade() else {
+            // Should not happen
+            panic!("self destroyed before mouse_btn_down_task was stopped!");
+        };
+
+        self_.handle_mouse_btn_down(btn, mouse_x, mouse_y).await;
+        true
+    }
+
+    async fn process_mouse_btn_up(
+        me: &Weak<Self>,
+        ev_sub: &Subscription<(MouseButton, f32, f32)>,
+    ) -> bool {
+        let Ok((btn, mouse_x, mouse_y)) = ev_sub.receive().await else {
+            debug!(target: "ui::editbox", "Event relayer closed");
+            return false
+        };
+
+        let Some(self_) = me.upgrade() else {
+            // Should not happen
+            panic!("self destroyed before mouse_btn_up_task was stopped!");
+        };
+
+        self_.handle_mouse_btn_up(btn, mouse_x, mouse_y).await;
+        true
+    }
+
+    async fn process_mouse_move(me: &Weak<Self>, ev_sub: &Subscription<(f32, f32)>) -> bool {
+        let Ok((mouse_x, mouse_y)) = ev_sub.receive().await else {
+            debug!(target: "ui::editbox", "Event relayer closed");
+            return false
+        };
+
+        let Some(self_) = me.upgrade() else {
+            // Should not happen
+            panic!("self destroyed before mouse_move_task was stopped!");
+        };
+
+        self_.handle_mouse_move(mouse_x, mouse_y).await;
+        true
+    }
+
+    async fn process_mouse_wheel(me: &Weak<Self>, ev_sub: &Subscription<(f32, f32)>) -> bool {
+        let Ok((wheel_x, wheel_y)) = ev_sub.receive().await else {
+            debug!(target: "ui::chatview", "Event relayer closed");
+            return false
+        };
+
+        let Some(self_) = me.upgrade() else {
+            // Should not happen
+            panic!("self destroyed before mouse_wheel_task was stopped!");
+        };
+
+        self_.handle_mouse_wheel(wheel_x, wheel_y).await;
+        true
+    }
+
+    async fn process_touch(
+        me: &Weak<Self>,
+        ev_sub: &Subscription<(TouchPhase, u64, f32, f32)>,
+    ) -> bool {
+        let Ok((phase, id, touch_x, touch_y)) = ev_sub.receive().await else {
+            debug!(target: "ui::editbox", "Event relayer closed");
+            return false
+        };
+
+        let Some(self_) = me.upgrade() else {
+            // Should not happen
+            panic!("self destroyed before touch_task was stopped!");
+        };
+
+        self_.handle_touch(phase, id, touch_x, touch_y).await;
+        true
+    }
+
     async fn handle_char(&self, key: char, mods: KeyMods, repeat: bool) {
     async fn handle_char(&self, key: char, mods: KeyMods, repeat: bool) {
         let sg = self.sg.lock().await;
         let sg = self.sg.lock().await;
 
 
@@ -237,6 +327,66 @@ impl Window {
         }
         }
     }
     }
 
 
+    async fn handle_mouse_btn_down(&self, btn: MouseButton, mouse_x: f32, mouse_y: f32) {
+        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_down(&sg, btn.clone(), mouse_x, mouse_y).await {
+                return
+            }
+        }
+    }
+
+    async fn handle_mouse_btn_up(&self, btn: MouseButton, mouse_x: f32, mouse_y: f32) {
+        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_x, mouse_y).await {
+                return
+            }
+        }
+    }
+
+    async fn handle_mouse_move(&self, mouse_x: f32, mouse_y: f32) {
+        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_x, mouse_y).await {
+                return
+            }
+        }
+    }
+
+    async fn handle_mouse_wheel(&self, wheel_x: f32, wheel_y: f32) {
+        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_x, wheel_y).await {
+                return
+            }
+        }
+    }
+
+    async fn handle_touch(&self, phase: TouchPhase, id: u64, touch_x: f32, touch_y: f32) {
+        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_x, touch_y).await {
+                return
+            }
+        }
+    }
+
     pub async fn draw(&self, sg: &SceneGraph) {
     pub async fn draw(&self, sg: &SceneGraph) {
         let screen_width = self.screen_size_prop.get_f32(0).unwrap();
         let screen_width = self.screen_size_prop.get_f32(0).unwrap();
         let screen_height = self.screen_size_prop.get_f32(1).unwrap();
         let screen_height = self.screen_size_prop.get_f32(1).unwrap();