Przeglądaj źródła

wallet/chatedit: create WrappedLines utility which manages wrapped lines and provides useful calcs. Use this to cleanup cursor positioning routine.

darkfi 1 rok temu
rodzic
commit
0d2628a53d

+ 1 - 1
bin/darkwallet/src/app/mod.rs

@@ -162,7 +162,7 @@ impl App {
 
 
         let window = window.setup(|me| Window::new(me, self.render_api.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_test(&self, window).await;
 
 
         debug!(target: "app", "Schema loaded");
         debug!(target: "app", "Schema loaded");
     }
     }

+ 88 - 1
bin/darkwallet/src/app/schema.rs

@@ -259,6 +259,34 @@ pub(super) async fn make_test(app: &App, window: SceneNodePtr) {
         node.setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())).await;
         node.setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())).await;
     layer_node.clone().link(node);
     layer_node.clone().link(node);
 
 
+    // Another debug tool for the chatedit
+    let node = create_vector_art("debugtool-chatedit");
+    let prop = node.get_property("rect").unwrap();
+    prop.set_f32(Role::App, 0, 0.).unwrap();
+    prop.set_f32(Role::App, 1, 300. - 5.).unwrap();
+    prop.set_expr(Role::App, 2, expr::load_var("w")).unwrap();
+    prop.set_f32(Role::App, 3, 200. + 10.).unwrap();
+    node.set_property_u32(Role::App, "z_index", 2).unwrap();
+
+    let mut shape = VectorShape::new();
+    shape.add_filled_box(
+        expr::const_f32(0.),
+        expr::const_f32(0.),
+        expr::load_var("w"),
+        expr::const_f32(5.),
+        [0., 1., 1., 1.],
+    );
+    shape.add_filled_box(
+        expr::const_f32(0.),
+        cc.compile("h - 5").unwrap(),
+        expr::load_var("w"),
+        expr::load_var("h"),
+        [0., 1., 1., 1.],
+    );
+    let node =
+        node.setup(|me| VectorArt::new(me, shape, app.render_api.clone(), app.ex.clone())).await;
+    layer_node.clone().link(node);
+
     // Create KING GNU!
     // Create KING GNU!
     let node = create_image("king");
     let node = create_image("king");
     let prop = node.get_property("rect").unwrap();
     let prop = node.get_property("rect").unwrap();
@@ -474,7 +502,66 @@ pub(super) async fn make_test(app: &App, window: SceneNodePtr) {
             ChatView::new(
             ChatView::new(
                 me,
                 me,
                 chat_tree,
                 chat_tree,
-                window_scale,
+                window_scale.clone(),
+                app.render_api.clone(),
+                app.text_shaper.clone(),
+                app.ex.clone(),
+            )
+        })
+        .await;
+    layer_node.clone().link(node);
+
+    // Text edit
+    let node = create_chatedit("editz");
+    node.set_property_bool(Role::App, "is_active", true).unwrap();
+    node.set_property_bool(Role::App, "is_focused", true).unwrap();
+
+    node.set_property_f32(Role::App, "max_height", 200.).unwrap();
+
+    let prop = node.get_property("rect").unwrap();
+    prop.set_f32(Role::App, 0, 0.).unwrap();
+    prop.set_f32(Role::App, 1, 300.).unwrap();
+    prop.set_expr(Role::App, 2, expr::load_var("parent_w")).unwrap();
+    prop.set_f32(Role::App, 3, 50.).unwrap();
+
+    node.set_property_f32(Role::App, "baseline", 34.).unwrap();
+    node.set_property_f32(Role::App, "linespacing", 34.).unwrap();
+    node.set_property_f32(Role::App, "descent", 10.).unwrap();
+    node.set_property_f32(Role::App, "font_size", 20.).unwrap();
+    //node.set_property_str(Role::App, "text", "hello king!😁🍆jelly 🍆1234").unwrap();
+    let prop = node.get_property("text_color").unwrap();
+    prop.set_f32(Role::App, 0, 1.).unwrap();
+    prop.set_f32(Role::App, 1, 1.).unwrap();
+    prop.set_f32(Role::App, 2, 1.).unwrap();
+    prop.set_f32(Role::App, 3, 1.).unwrap();
+    let prop = node.get_property("text_hi_color").unwrap();
+    prop.set_f32(Role::App, 0, 0.44).unwrap();
+    prop.set_f32(Role::App, 1, 0.96).unwrap();
+    prop.set_f32(Role::App, 2, 1.).unwrap();
+    prop.set_f32(Role::App, 3, 1.).unwrap();
+    let prop = node.get_property("cursor_color").unwrap();
+    prop.set_f32(Role::App, 0, 0.816).unwrap();
+    prop.set_f32(Role::App, 1, 0.627).unwrap();
+    prop.set_f32(Role::App, 2, 1.).unwrap();
+    prop.set_f32(Role::App, 3, 1.).unwrap();
+    node.set_property_f32(Role::App, "cursor_ascent", EDITCHAT_CURSOR_ASCENT).unwrap();
+    node.set_property_f32(Role::App, "cursor_descent", EDITCHAT_CURSOR_DESCENT).unwrap();
+    node.set_property_f32(Role::App, "select_ascent", EDITCHAT_SELECT_ASCENT).unwrap();
+    node.set_property_f32(Role::App, "select_descent", EDITCHAT_SELECT_DESCENT).unwrap();
+    let prop = node.get_property("hi_bg_color").unwrap();
+    prop.set_f32(Role::App, 0, 0.5).unwrap();
+    prop.set_f32(Role::App, 1, 0.5).unwrap();
+    prop.set_f32(Role::App, 2, 0.5).unwrap();
+    prop.set_f32(Role::App, 3, 1.).unwrap();
+    let prop = node.get_property("selected").unwrap();
+    prop.set_null(Role::App, 0).unwrap();
+    prop.set_null(Role::App, 1).unwrap();
+    node.set_property_u32(Role::App, "z_index", 3).unwrap();
+    let node = node
+        .setup(|me| {
+            ChatEdit::new(
+                me,
+                window_scale.clone(),
                 app.render_api.clone(),
                 app.render_api.clone(),
                 app.text_shaper.clone(),
                 app.text_shaper.clone(),
                 app.ex.clone(),
                 app.ex.clone(),

+ 116 - 52
bin/darkwallet/src/ui/chatedit.rs

@@ -76,6 +76,7 @@ struct TextWrap {
     font_size: PropertyFloat32,
     font_size: PropertyFloat32,
     window_scale: PropertyFloat32,
     window_scale: PropertyFloat32,
     baseline: PropertyFloat32,
     baseline: PropertyFloat32,
+    linespacing: PropertyFloat32,
 }
 }
 
 
 impl TextWrap {
 impl TextWrap {
@@ -84,6 +85,7 @@ impl TextWrap {
         font_size: PropertyFloat32,
         font_size: PropertyFloat32,
         window_scale: PropertyFloat32,
         window_scale: PropertyFloat32,
         baseline: PropertyFloat32,
         baseline: PropertyFloat32,
+        linespacing: PropertyFloat32,
     ) -> Self {
     ) -> Self {
         Self {
         Self {
             editable: Editable::new(
             editable: Editable::new(
@@ -97,6 +99,7 @@ impl TextWrap {
             font_size,
             font_size,
             window_scale,
             window_scale,
             baseline,
             baseline,
+            linespacing,
         }
         }
     }
     }
 
 
@@ -110,14 +113,19 @@ impl TextWrap {
         self.rendered.as_ref().unwrap()
         self.rendered.as_ref().unwrap()
     }
     }
 
 
-    fn wrap(&mut self, width: f32) -> Vec<WrappedLine> {
+    fn wrap(&mut self, width: f32) -> WrappedLines {
         let font_size = self.font_size.get();
         let font_size = self.font_size.get();
         let window_scale = self.window_scale.get();
         let window_scale = self.window_scale.get();
         let baseline = self.baseline.get();
         let baseline = self.baseline.get();
+        let linespacing = self.linespacing.get();
 
 
         let rendered = self.get_render();
         let rendered = self.get_render();
         let glyphs = text::wrap(width, font_size, window_scale, &rendered.glyphs);
         let glyphs = text::wrap(width, font_size, window_scale, &rendered.glyphs);
-        glyphs.into_iter().map(|g| WrappedLine::new(g, font_size, window_scale, baseline)).collect()
+        let lines = glyphs
+            .into_iter()
+            .map(|g| WrappedLine::new(g, font_size, window_scale, baseline))
+            .collect();
+        WrappedLines::new(lines, font_size, linespacing)
     }
     }
 
 
     fn cursor_pos(&mut self) -> usize {
     fn cursor_pos(&mut self) -> usize {
@@ -155,6 +163,77 @@ impl WrappedLine {
     }
     }
 }
 }
 
 
+struct WrappedLines {
+    lines: Vec<WrappedLine>,
+    font_size: f32,
+    linespacing: f32,
+}
+
+impl WrappedLines {
+    fn new(lines: Vec<WrappedLine>, font_size: f32, linespacing: f32) -> Self {
+        Self { lines, font_size, linespacing }
+    }
+
+    /// Convert an (x, y) point to a glyph pos
+    fn point_to_pos(&self, linespacing: f32, point: Point) -> usize {
+        0
+        //let total_height = wrapped_lines.len() as f32 * linespacing;
+        //if total_height < point.y {
+        //    return
+        //}
+    }
+
+    fn height(&self) -> f32 {
+        self.last_y() + self.linespacing
+    }
+
+    fn last_y(&self) -> f32 {
+        if self.lines.is_empty() {
+            return 0.
+        }
+        (self.lines.len() - 1) as f32 * self.linespacing
+    }
+
+    fn last_rhs(&self) -> f32 {
+        if self.lines.is_empty() {
+            return 0.
+        }
+        let last_line = self.lines.last().unwrap();
+        let mut rhs = last_line.rhs();
+        rhs += eol_nudge(self.font_size, &last_line.glyphs);
+        rhs
+    }
+
+    fn get_glyph_pos(&self, mut pos: TextPos) -> Point {
+        if pos == 0 {
+            return Point::zero();
+        }
+
+        let mut y = 0.;
+        for wrap_line in &self.lines {
+            assert!(!wrap_line.glyphs.is_empty());
+
+            if pos < wrap_line.len() {
+                // Cursor is on this line
+                let mut pos_iter = wrap_line.pos_iter();
+                pos_iter.advance_by(pos).unwrap();
+
+                let glyph_rect = pos_iter.next().unwrap();
+
+                let x = glyph_rect.x;
+                return Point::new(x, y)
+            }
+
+            pos -= wrap_line.len();
+            y += self.linespacing;
+        }
+
+        let rhs = self.last_rhs();
+        let last_y = self.last_y();
+        Point::new(rhs, last_y)
+    }
+}
+
 #[derive(Clone)]
 #[derive(Clone)]
 struct TouchStartInfo {
 struct TouchStartInfo {
     pos: Point,
     pos: Point,
@@ -232,6 +311,7 @@ pub struct ChatEdit {
     key_repeat: SyncMutex<PressedKeysSmoothRepeat>,
     key_repeat: SyncMutex<PressedKeysSmoothRepeat>,
 
 
     glyphs: SyncMutex<Vec<Glyph>>,
     glyphs: SyncMutex<Vec<Glyph>>,
+    main_dc_key: u64,
     /// DC key for the text
     /// DC key for the text
     text_dc_key: u64,
     text_dc_key: u64,
     cursor_mesh: SyncMutex<Option<GfxDrawMesh>>,
     cursor_mesh: SyncMutex<Option<GfxDrawMesh>>,
@@ -344,6 +424,7 @@ impl ChatEdit {
             key_repeat: SyncMutex::new(PressedKeysSmoothRepeat::new(400, 50)),
             key_repeat: SyncMutex::new(PressedKeysSmoothRepeat::new(400, 50)),
 
 
             glyphs: SyncMutex::new(glyphs),
             glyphs: SyncMutex::new(glyphs),
+            main_dc_key: OsRng.gen(),
             text_dc_key: OsRng.gen(),
             text_dc_key: OsRng.gen(),
             cursor_mesh: SyncMutex::new(None),
             cursor_mesh: SyncMutex::new(None),
             cursor_dc_key: OsRng.gen(),
             cursor_dc_key: OsRng.gen(),
@@ -353,7 +434,7 @@ impl ChatEdit {
             max_height,
             max_height,
             rect,
             rect,
             baseline: baseline.clone(),
             baseline: baseline.clone(),
-            linespacing,
+            linespacing: linespacing.clone(),
             descent,
             descent,
             scroll: scroll.clone(),
             scroll: scroll.clone(),
             scroll_speed,
             scroll_speed,
@@ -381,6 +462,7 @@ impl ChatEdit {
                 font_size,
                 font_size,
                 window_scale.clone(),
                 window_scale.clone(),
                 baseline,
                 baseline,
+                linespacing,
             )),
             )),
 
 
             mouse_btn_held: AtomicBool::new(false),
             mouse_btn_held: AtomicBool::new(false),
@@ -404,6 +486,10 @@ impl ChatEdit {
         self.node.upgrade().unwrap()
         self.node.upgrade().unwrap()
     }
     }
 
 
+    fn wrap_width(&self) -> f32 {
+        self.rect.prop().get_f32(2).unwrap() - self.cursor_width.get()
+    }
+
     /// Called whenever the text or any text property changes.
     /// Called whenever the text or any text property changes.
     fn regen_text_mesh(&self) -> GfxDrawMesh {
     fn regen_text_mesh(&self) -> GfxDrawMesh {
         let is_focused = self.is_focused.get();
         let is_focused = self.is_focused.get();
@@ -421,7 +507,7 @@ impl ChatEdit {
         //debug!(target: "ui::chatedit", "Rendering text '{text}' clip={clip:?}");
         //debug!(target: "ui::chatedit", "Rendering text '{text}' clip={clip:?}");
         //debug!(target: "ui::chatedit", "    cursor_pos={cursor_pos}, is_focused={is_focused}");
         //debug!(target: "ui::chatedit", "    cursor_pos={cursor_pos}, is_focused={is_focused}");
 
 
-        let width = self.rect.prop().get_f32(2).unwrap();
+        let width = self.wrap_width();
 
 
         let (atlas, wrapped_lines) = {
         let (atlas, wrapped_lines) = {
             let mut text_wrap = self.text_wrap.lock().unwrap();
             let mut text_wrap = self.text_wrap.lock().unwrap();
@@ -431,8 +517,7 @@ impl ChatEdit {
             (atlas, wrapped_lines)
             (atlas, wrapped_lines)
         };
         };
 
 
-        let total_height = std::cmp::max(wrapped_lines.len(), 1) as f32 * linespacing;
-        let mut height = total_height + self.descent.get();
+        let mut height = wrapped_lines.height() + self.descent.get();
         height = height.clamp(0., self.max_height.get());
         height = height.clamp(0., self.max_height.get());
 
 
         self.rect.prop().set_f32(Role::Internal, 3, height);
         self.rect.prop().set_f32(Role::Internal, 3, height);
@@ -455,7 +540,7 @@ impl ChatEdit {
         let mut mesh = MeshBuilder::with_clip(clip.clone());
         let mut mesh = MeshBuilder::with_clip(clip.clone());
         let mut curr_y = 0.;
         let mut curr_y = 0.;
 
 
-        for wrap_line in wrapped_lines {
+        for wrap_line in wrapped_lines.lines {
             /*
             /*
             let selections = self.select.lock().unwrap().clone();
             let selections = self.select.lock().unwrap().clone();
             // Just an assert
             // Just an assert
@@ -529,53 +614,19 @@ impl ChatEdit {
         let font_size = self.font_size.get();
         let font_size = self.font_size.get();
         let window_scale = self.window_scale.get();
         let window_scale = self.window_scale.get();
         let linespacing = self.linespacing.get();
         let linespacing = self.linespacing.get();
+        let scroll = self.scroll.get();
 
 
-        let width = self.rect.prop().get_f32(2).unwrap();
+        let width = self.wrap_width();
 
 
-        let (mut cursor_pos, wrapped_lines) = {
+        let (cursor_pos, wrapped_lines) = {
             let mut text_wrap = self.text_wrap.lock().unwrap();
             let mut text_wrap = self.text_wrap.lock().unwrap();
             (text_wrap.cursor_pos(), text_wrap.wrap(width))
             (text_wrap.cursor_pos(), text_wrap.wrap(width))
         };
         };
 
 
-        if cursor_pos == 0 {
-            return Point::zero();
-        }
-
-        let mut y = 0.;
-        for wrap_line in &wrapped_lines {
-            assert!(!wrap_line.glyphs.is_empty());
-
-            if cursor_pos < wrap_line.len() {
-                // Cursor is on this line
-                let mut pos_iter = wrap_line.pos_iter();
-                pos_iter.advance_by(cursor_pos).unwrap();
-                let glyph_rect = pos_iter.next().unwrap();
-                let mut x = glyph_rect.x;
-                if x > width {
-                    x = width;
-                }
-                return Point::new(x, y)
-            }
-
-            cursor_pos -= wrap_line.len();
-            y += linespacing;
-        }
-
-        let mut rhs = 0.;
-        let mut last_y = 0.;
-
-        if !wrapped_lines.is_empty() {
-            let last_line = wrapped_lines.last().unwrap();
-            rhs = last_line.rhs();
-            rhs += eol_nudge(font_size, &last_line.glyphs);
-            if rhs > width {
-                rhs = width;
-            }
-
-            last_y = (wrapped_lines.len() - 1) as f32 * linespacing;
-        }
-
-        Point::new(rhs, last_y)
+        let mut point = wrapped_lines.get_glyph_pos(cursor_pos);
+        point.x = point.x.clamp(0., width);
+        point.y -= scroll;
+        point
     }
     }
 
 
     fn mark_selected_glyphs(&self, glyphs: &Vec<Glyph>, selections: Vec<Selection>) -> Vec<bool> {
     fn mark_selected_glyphs(&self, glyphs: &Vec<Glyph>, selections: Vec<Selection>) -> Vec<bool> {
@@ -1326,9 +1377,14 @@ impl ChatEdit {
 
 
         let mut cursor_instrs = vec![];
         let mut cursor_instrs = vec![];
 
 
-        let rect_w = self.rect.get().w;
-
         let cursor_pos = self.get_cursor_pos();
         let cursor_pos = self.get_cursor_pos();
+
+        // There is some mess here since ApplyView is in abs screen coords but should be
+        // relative, and also work together with Move. We will fix this later in gfx subsys.
+        let mut view_rect = self.rect.get();
+        view_rect.h = self.max_height.get();
+
+        cursor_instrs.push(GfxDrawInstruction::ApplyView(view_rect));
         cursor_instrs.push(GfxDrawInstruction::Move(cursor_pos));
         cursor_instrs.push(GfxDrawInstruction::Move(cursor_pos));
 
 
         let cursor_mesh = {
         let cursor_mesh = {
@@ -1351,8 +1407,16 @@ impl ChatEdit {
         let rect = self.rect.get();
         let rect = self.rect.get();
 
 
         Some(DrawUpdate {
         Some(DrawUpdate {
-            key: self.text_dc_key,
+            key: self.main_dc_key,
             draw_calls: vec![
             draw_calls: vec![
+                (
+                    self.main_dc_key,
+                    GfxDrawCall {
+                        instrs: vec![],
+                        dcs: vec![self.text_dc_key, self.cursor_dc_key],
+                        z_index: self.z_index.get(),
+                    },
+                ),
                 (
                 (
                     self.text_dc_key,
                     self.text_dc_key,
                     GfxDrawCall {
                     GfxDrawCall {
@@ -1360,7 +1424,7 @@ impl ChatEdit {
                             GfxDrawInstruction::Move(rect.pos()),
                             GfxDrawInstruction::Move(rect.pos()),
                             GfxDrawInstruction::Draw(text_mesh),
                             GfxDrawInstruction::Draw(text_mesh),
                         ],
                         ],
-                        dcs: vec![self.cursor_dc_key],
+                        dcs: vec![],
                         z_index: self.z_index.get(),
                         z_index: self.z_index.get(),
                     },
                     },
                 ),
                 ),