Jelajahi Sumber

wallet: text shaping algo, use labels for ambiguous break/continue inside if let block

darkfi 2 tahun lalu
induk
melakukan
e540f799d3
3 mengubah file dengan 73 tambahan dan 25 penghapusan
  1. 12 0
      bin/darkwallet/src/main.rs
  2. 23 16
      bin/darkwallet/src/text2.rs
  3. 38 9
      bin/darkwallet/src/ui/editbox.rs

+ 12 - 0
bin/darkwallet/src/main.rs

@@ -133,6 +133,18 @@ fn main() {
         }
     });
     async_runtime.push_task(ev_relay_task);
+    let ev_sub = event_pub.subscribe_char();
+    let ev_relay_task = ex.spawn(async move {
+        debug!(target: "main", "event relayer started");
+        loop {
+            let Ok((key, mods, repeat)) = ev_sub.receive().await else {
+                debug!(target: "main", "Event relayer closed");
+                break
+            };
+            debug!(target: "main", "char event: {:?} {:?} {}", key, mods, repeat);
+        }
+    });
+    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);

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

@@ -270,6 +270,7 @@ impl TextShaper {
     }
 
     pub async fn shape(&self, text: String, font_size: f32) -> Vec<Glyph> {
+        //debug!(target: "text", "shape('{}', {})", text, font_size);
         // Lock font faces
         // Freetype faces are not threadsafe
         let mut faces = self.font_faces.lock().await;
@@ -337,7 +338,7 @@ impl TextShaper {
             let mut prev_cluster = 0;
 
             //for (i, (position, info)) in positions.iter().zip(infos).enumerate() {
-            for (i, (position, info)) in
+            'iter_glyphs: for (i, (position, info)) in
                 glyph_pos_iter.iter().zip(glyph_infos_iter.iter()).enumerate()
             {
                 let glyph_id = info.codepoint as u32;
@@ -373,21 +374,26 @@ impl TextShaper {
                     face_idx,
                 };
                 //debug!(target: "text", "cache_key: {:?}", cache_key);
-                if let Some(sprite) = cache.get(&cache_key) {
-                    let Some(sprite) = sprite.upgrade() else { break };
-
-                    let glyph = Glyph {
-                        glyph_id,
-                        substr: String::new(),
-                        sprite,
-                        x_offset,
-                        y_offset,
-                        x_advance,
-                        y_advance,
-                    };
-
-                    glyphs.push(glyph);
-                    continue
+                'load_sprite: {
+                    if let Some(sprite) = cache.get(&cache_key) {
+                        let Some(sprite) = sprite.upgrade() else {
+                            break 'load_sprite;
+                        };
+
+                        //debug!(target: "text", "found glyph!");
+                        let glyph = Glyph {
+                            glyph_id,
+                            substr: String::new(),
+                            sprite,
+                            x_offset,
+                            y_offset,
+                            x_advance,
+                            y_advance,
+                        };
+
+                        glyphs.push(glyph);
+                        continue 'iter_glyphs;
+                    }
                 }
 
                 let mut flags = ft::face::LoadFlag::DEFAULT;
@@ -467,6 +473,7 @@ impl TextShaper {
                     y_advance,
                 };
 
+                //debug!(target: "text", "pushing glyph...");
                 glyphs.push(glyph);
             }
 

+ 38 - 9
bin/darkwallet/src/ui/editbox.rs

@@ -22,13 +22,6 @@ use crate::{
 
 use super::{eval_rect, get_parent_rect, read_rect, DrawUpdate, OnModify, Stoppable};
 
-// First refactor the event system
-// Each event should have its own unique pipe
-// Advantages:
-// - less overhead when publishing msgs to ppl who dont need them
-// - more advanced locking of streams when widgets capture input
-// also add capturing and make use of it with editbox.
-
 const CURSOR_WIDTH: f32 = 4.;
 
 #[derive(Debug, Clone, Eq, Hash, PartialEq)]
@@ -37,6 +30,10 @@ enum PressedKey {
     Key(KeyCode),
 }
 
+/// On key press (repeat=false), we immediately process the event.
+/// Then there's a delay (repeat=true) and then for every step time
+/// while key press events are being sent, we allow an event.
+/// This ensures smooth typing in the editbox.
 struct PressedKeysSmoothRepeat {
     /// When holding keys, we track from start and last sent time.
     /// This is useful for initial delay and smooth scrolling.
@@ -179,7 +176,7 @@ impl EditBox {
         .await;
 
         // testing
-        //window::show_keyboard(true);
+        window::show_keyboard(true);
 
         let self_ = Arc::new_cyclic(|me: &Weak<Self>| {
             // Start a task monitoring for key down events
@@ -302,7 +299,7 @@ impl EditBox {
         };
 
         // First filter for only single digit keys
-        let disallowed_keys = ['\r', '\u{8}', '\u{7f}', '\t'];
+        let disallowed_keys = ['\r', '\u{8}', '\u{7f}', '\t', '\n'];
         if disallowed_keys.contains(&key) {
             return
         }
@@ -384,6 +381,27 @@ impl EditBox {
 
     async fn handle_key(&self, key: &KeyCode, mods: &KeyMods) {
         debug!(target: "ui::editbox", "handle_key({:?}, {:?})", key, mods);
+        match key {
+            //KeyCode::Left,
+            //KeyCode::Right,
+            //KeyCode::Up,
+            //KeyCode::Down,
+            //KeyCode::Enter,
+            KeyCode::Kp0 => self.insert_char('0').await,
+            KeyCode::Kp1 => self.insert_char('1').await,
+            KeyCode::Kp2 => self.insert_char('2').await,
+            KeyCode::Kp3 => self.insert_char('3').await,
+            KeyCode::Kp4 => self.insert_char('4').await,
+            KeyCode::Kp5 => self.insert_char('5').await,
+            KeyCode::Kp6 => self.insert_char('6').await,
+            KeyCode::Kp7 => self.insert_char('7').await,
+            KeyCode::Kp8 => self.insert_char('8').await,
+            KeyCode::Kp9 => self.insert_char('9').await,
+            KeyCode::KpDecimal => self.insert_char('.').await,
+            KeyCode::Enter | KeyCode::KpEnter => self.send_event().await,
+            //KeyCode::Backspace,
+            _ => {}
+        }
     }
 
     async fn redraw(&self) {
@@ -465,6 +483,16 @@ impl EditBox {
             )],
         })
     }
+
+    async fn send_event(&self) {
+        let text = self.text.get();
+        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;
+    }
 }
 
 impl Stoppable for EditBox {
@@ -496,4 +524,5 @@ static ALLOWED_KEYCODES: &'static [KeyCode] = &[
     KeyCode::Kp9,
     KeyCode::KpDecimal,
     KeyCode::KpEnter,
+    KeyCode::Backspace,
 ];