Просмотр исходного кода

wallet: connect button signal with slot that clears editbox

darkfi 2 лет назад
Родитель
Сommit
7bd760bea9

+ 43 - 13
bin/darkwallet/src/app.rs

@@ -20,16 +20,17 @@ use async_recursion::async_recursion;
 use chrono::{NaiveDate, NaiveDateTime};
 use darkfi_serial::Encodable;
 use futures::{stream::FuturesUnordered, StreamExt};
-use std::{sync::Arc, thread};
+use std::{sync::{Arc, Mutex as SyncMutex}, thread};
+use smol::Task;
 
 use crate::{
     error::Error,
     expr::Op,
     gfx2::{GraphicsEventPublisherPtr, RenderApiPtr, Vertex},
-    prop::{Property, PropertySubType, PropertyType, Role},
+    prop::{Property, PropertySubType, PropertyType, Role, PropertyStr},
     scene::{
         CallArgType, MethodResponseFn, Pimpl, SceneGraph, SceneGraphPtr2, SceneNodeId,
-        SceneNodeType,
+        SceneNodeType, Slot
     },
     text2::TextShaperPtr,
     ui::{chatview, Button, ChatView, EditBox, Image, Mesh, RenderLayer, Stoppable, Text, Window},
@@ -54,28 +55,28 @@ const KING_PATH: &str = "assets/king.png";
 const LIGHTMODE: bool = false;
 
 pub struct AsyncRuntime {
-    signal: smol::channel::Sender<()>,
-    shutdown: smol::channel::Receiver<()>,
-    exec_threadpool: std::sync::Mutex<Option<thread::JoinHandle<()>>>,
+    signal: async_channel::Sender<()>,
+    shutdown: async_channel::Receiver<()>,
+    exec_threadpool: SyncMutex<Option<thread::JoinHandle<()>>>,
     ex: ExecutorPtr,
-    tasks: std::sync::Mutex<Vec<smol::Task<()>>>,
+    tasks: SyncMutex<Vec<Task<()>>>,
 }
 
 impl AsyncRuntime {
     pub fn new(ex: ExecutorPtr) -> Self {
-        let (signal, shutdown) = smol::channel::unbounded::<()>();
+        let (signal, shutdown) = async_channel::unbounded::<()>();
 
         Self {
             signal,
             shutdown,
-            exec_threadpool: std::sync::Mutex::new(None),
+            exec_threadpool: SyncMutex::new(None),
             ex,
-            tasks: std::sync::Mutex::new(vec![]),
+            tasks: SyncMutex::new(vec![]),
         }
     }
 
     pub fn start(&self) {
-        let n_threads = std::thread::available_parallelism().unwrap().get();
+        let n_threads = thread::available_parallelism().unwrap().get();
         let shutdown = self.shutdown.clone();
         let ex = self.ex.clone();
         let exec_threadpool = thread::spawn(move || {
@@ -88,7 +89,7 @@ impl AsyncRuntime {
         debug!(target: "async_runtime", "Started runtime");
     }
 
-    pub fn push_task(&self, task: smol::Task<()>) {
+    pub fn push_task(&self, task: Task<()>) {
         self.tasks.lock().unwrap().push(task);
     }
 
@@ -120,12 +121,15 @@ impl AsyncRuntime {
     }
 }
 
+pub type AppPtr = Arc<App>;
+
 pub struct App {
     sg: SceneGraphPtr2,
     ex: ExecutorPtr,
     render_api: RenderApiPtr,
     event_pub: GraphicsEventPublisherPtr,
     text_shaper: TextShaperPtr,
+    tasks: SyncMutex<Vec<Task<()>>>,
 }
 
 impl App {
@@ -136,7 +140,7 @@ impl App {
         event_pub: GraphicsEventPublisherPtr,
         text_shaper: TextShaperPtr,
     ) -> Arc<Self> {
-        Arc::new(Self { sg, ex, render_api, event_pub, text_shaper })
+        Arc::new(Self { sg, ex, render_api, event_pub, text_shaper, tasks: SyncMutex::new(vec![]) })
     }
 
     pub async fn start(self: Arc<Self>) {
@@ -212,6 +216,7 @@ impl App {
     }
 
     async fn make_me_a_schema_plox(&self) {
+        let mut tasks = vec![];
         // Create a layer called view
         let mut sg = self.sg.lock().await;
         let layer_node_id = create_layer(&mut sg, "view");
@@ -356,6 +361,13 @@ impl App {
         prop.set_f32(Role::App, 2, 200.).unwrap();
         prop.set_f32(Role::App, 3, 60.).unwrap();
 
+        let (sender, btn_click_recvr) = async_channel::unbounded();
+        let slot_click = Slot {
+            name: "button_clicked".to_string(),
+            notify: sender
+        };
+        node.register("click", slot_click).unwrap();
+
         drop(sg);
         let pimpl =
             Button::new(self.ex.clone(), self.sg.clone(), node_id, self.event_pub.clone()).await;
@@ -624,6 +636,16 @@ impl App {
         node.set_property_u32(Role::App, "z_index", 1).unwrap();
         //node.set_property_bool(Role::App, "debug", true).unwrap();
 
+        let editbox_text = PropertyStr::wrap(node, Role::App, "text", 0).unwrap();
+        let task = self.ex.spawn(async move {
+            while let Ok(_) = btn_click_recvr.recv().await {
+                let text = editbox_text.get();
+                editbox_text.prop().unset(Role::App, 0);
+                debug!(target: "app", "sending text {text}");
+            }
+        });
+        tasks.push(task);
+
         drop(sg);
         let pimpl = EditBox::new(
             self.ex.clone(),
@@ -730,6 +752,8 @@ impl App {
         // Also we need to think about nesting of layers.
         //let window_node = sg.get_node_mut(window_id).unwrap();
         //win_node.set_property_f32(Role::App, "scale", 1.6).unwrap();
+
+        *self.tasks.lock().unwrap() = tasks;
     }
 
     async fn trigger_redraw(&self) {
@@ -742,6 +766,12 @@ impl App {
     }
 }
 
+impl Drop for App {
+    fn drop(&mut self) {
+        debug!(target: "app", "dropping app");
+    }
+}
+
 // Just for testing
 fn populate_tree(tree: &sled::Tree) {
     let chat_txt = include_str!("../chat.txt");

+ 6 - 2
bin/darkwallet/src/gfx2.rs

@@ -33,7 +33,7 @@ use std::{
 };
 
 use crate::{
-    app::AsyncRuntime,
+    app::{AsyncRuntime, AppPtr},
     error::{Error, Result},
     pubsub::{Publisher, PublisherPtr, Subscription, SubscriptionId},
     shader,
@@ -626,6 +626,7 @@ impl GraphicsEventPublisher {
 }
 
 struct Stage {
+    app: AppPtr,
     async_runtime: AsyncRuntime,
 
     ctx: Box<dyn RenderingBackend>,
@@ -640,6 +641,7 @@ struct Stage {
 
 impl Stage {
     pub fn new(
+        app: AppPtr,
         async_runtime: AsyncRuntime,
         method_rep: mpsc::Receiver<GraphicsMethod>,
         event_pub: GraphicsEventPublisherPtr,
@@ -694,6 +696,7 @@ impl Stage {
         );
 
         Stage {
+            app,
             async_runtime,
             ctx,
             pipeline,
@@ -880,6 +883,7 @@ impl EventHandler for Stage {
 }
 
 pub fn run_gui(
+    app: AppPtr,
     async_runtime: AsyncRuntime,
     method_rep: mpsc::Receiver<GraphicsMethod>,
     event_pub: GraphicsEventPublisherPtr,
@@ -898,5 +902,5 @@ pub fn run_gui(
     conf.platform.apple_gfx_api =
         if metal { conf::AppleGfxApi::Metal } else { conf::AppleGfxApi::OpenGl };
 
-    miniquad::start(conf, || Box::new(Stage::new(async_runtime, method_rep, event_pub)));
+    miniquad::start(conf, || Box::new(Stage::new(app, async_runtime, method_rep, event_pub)));
 }

+ 2 - 2
bin/darkwallet/src/main.rs

@@ -271,7 +271,7 @@ fn main() {
 
     let app =
         app::App::new(sg.clone(), ex.clone(), render_api.clone(), event_pub.clone(), text_shaper);
-    let app_task = ex.spawn(app.start());
+    let app_task = ex.spawn(app.clone().start());
     async_runtime.push_task(app_task);
     //app.clone().start();
 
@@ -326,7 +326,7 @@ fn main() {
     async_runtime.push_task(ev_relay_task);
 
     //let stage = gfx2::Stage::new(method_rep, event_pub);
-    gfx2::run_gui(async_runtime, method_rep, event_pub);
+    gfx2::run_gui(app, async_runtime, method_rep, event_pub);
     debug!(target: "main", "Started GFX backend");
 }
 

+ 5 - 3
bin/darkwallet/src/scene.rs

@@ -537,14 +537,16 @@ impl SceneNode {
     pub async fn trigger(&self, sig_name: &str, data: Vec<u8>) -> Result<()> {
         let sig = self.get_signal(sig_name).ok_or(Error::SignalNotFound)?;
         let futures = FuturesUnordered::new();
+        // TODO: autoremove slots which fail to send
         for (_, slot) in sig.get_slots() {
+            debug!(target: "scene", "triggering {}", slot.name);
             // Trigger the slot
             futures.push(async {
-                // Ignore the result
-                let _ = slot.notify.send(data.clone()).await;
+                slot.notify.send(data.clone()).await.is_ok()
             });
         }
-        let _: Vec<_> = futures.collect().await;
+        let success: Vec<_> = futures.collect().await;
+        debug!(target: "scene", "trigger success: {success:?}");
         Ok(())
     }
 

+ 4 - 3
bin/darkwallet/src/text2/atlas.rs

@@ -137,9 +137,10 @@ impl<'a> Atlas<'a> {
     /// `rendered_atlas.fetch_uv(my_glyph_id)`.
     /// The texture ID is a struct member: `rendered_atlas.texture_id`.
     pub async fn make(self) -> Result<RenderedAtlas> {
-        if self.glyph_ids.is_empty() {
-            return Err(Error::AtlasIsEmpty);
-        }
+        //if self.glyph_ids.is_empty() {
+        //    return Err(Error::AtlasIsEmpty);
+        //}
+
         assert_eq!(self.glyph_ids.len(), self.sprites.len());
         assert_eq!(self.glyph_ids.len(), self.x_pos.len());
 

+ 15 - 4
bin/darkwallet/src/ui/editbox.rs

@@ -259,6 +259,16 @@ impl EditBox {
             let mut on_modify = OnModify::new(ex, 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;
             }
@@ -271,7 +281,7 @@ impl EditBox {
             //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(), redraw);
+            on_modify.when_change(text.prop(), reset);
             on_modify.when_change(text_color.prop(), redraw);
             on_modify.when_change(cursor_color.prop(), redraw);
             on_modify.when_change(hi_bg_color.prop(), redraw);
@@ -328,6 +338,7 @@ impl EditBox {
     /// This MUST be called whenever the text property is changed.
     async fn regen_glyphs(&self) {
         let glyphs = self.text_shaper.shape(self.text.get(), self.font_size.get()).await;
+        // TODO: we aren't freeing textures
         *self.glyphs.lock().unwrap() = glyphs;
     }
 
@@ -1264,9 +1275,9 @@ impl EditBox {
         debug!(target: "ui::editbox", "sending text {}", text);
 
         // This should probably be unset instead
-        self.text.set(String::new());
-        self.cursor_pos.set(0);
-        self.redraw().await;
+        //self.text.set(String::new());
+        //self.cursor_pos.set(0);
+        //self.redraw().await;
     }
 }