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

app: chatedit working scroll (all platforms)

darkfi 1 год назад
Родитель
Сommit
3ca5b77324
4 измененных файлов с 91 добавлено и 74 удалено
  1. 1 0
      bin/app/src/android.rs
  2. 5 2
      bin/app/src/text2/editor/android.rs
  3. 4 1
      bin/app/src/text2/editor/parley.rs
  4. 81 71
      bin/app/src/ui/chatedit.rs

+ 1 - 0
bin/app/src/android.rs

@@ -259,6 +259,7 @@ pub fn get_editable(id: usize) -> Option<Editable> {
 
 
         let buffer =
         let buffer =
             ndk_utils::call_object_method!(env, input_connect, "rawText", "()Ljava/lang/String;");
             ndk_utils::call_object_method!(env, input_connect, "rawText", "()Ljava/lang/String;");
+        assert!(!buffer.is_null());
         let buffer = ndk_utils::get_utf_str!(env, buffer).to_string();
         let buffer = ndk_utils::get_utf_str!(env, buffer).to_string();
 
 
         let select_start =
         let select_start =

+ 5 - 2
bin/app/src/text2/editor/android.rs

@@ -83,8 +83,11 @@ impl Editor {
     pub fn setup(&mut self) {
     pub fn setup(&mut self) {
         assert!(self.composer_id != usize::MAX);
         assert!(self.composer_id != usize::MAX);
         t!("Initialized composer [{}]", self.composer_id);
         t!("Initialized composer [{}]", self.composer_id);
-        let atxt = "A berry is small 😊 and pulpy.";
-        android::set_text(self.composer_id, atxt).unwrap();
+        //let atxt = "A berry is small 😊 and pulpy.";
+        //let atxt = "A berry is a small, pulpy, and often edible fruit. Typically, berries are juicy, rounded, brightly colored, sweet, sour or tart, and do not have a stone or pit, although many pips or seeds may be present. Common examples of berries in the culinary sense are strawberries, raspberries, blueberries, blackberries, white currants, blackcurrants, and redcurrants. In Britain, soft fruit is a horticultural term for such fruits. The common usage of the term berry is different from the scientific or botanical definition of a berry, which refers to a fruit produced from the ovary of a single flower where the outer layer of the ovary wall develops into an edible fleshy portion (pericarp). The botanical definition includes many fruits that are not commonly known or referred to as berries, such as grapes, tomatoes, cucumbers, eggplants, bananas, and chili peppers.";
+        // This will initialize the editable and set the cursor.
+        // Otherwise get_editable() will segfault.
+        android::set_text(self.composer_id, "").unwrap();
         self.is_init = true;
         self.is_init = true;
     }
     }
 
 

+ 4 - 1
bin/app/src/text2/editor/parley.rs

@@ -43,7 +43,9 @@ impl Editor {
         window_scale: PropertyFloat32,
         window_scale: PropertyFloat32,
         lineheight: PropertyFloat32,
         lineheight: PropertyFloat32,
     ) -> Self {
     ) -> Self {
-        let editor = parley::PlainEditor::new(1.);
+        let mut editor = parley::PlainEditor::new(1.);
+        //let atxt = "A berry is a small, pulpy, and often edible fruit. Typically, berries are juicy, rounded, brightly colored, sweet, sour or tart, and do not have a stone or pit, although many pips or seeds may be present. Common examples of berries in the culinary sense are strawberries, raspberries, blueberries, blackberries, white currants, blackcurrants, and redcurrants. In Britain, soft fruit is a horticultural term for such fruits. The common usage of the term berry is different from the scientific or botanical definition of a berry, which refers to a fruit produced from the ovary of a single flower where the outer layer of the ovary wall develops into an edible fleshy portion (pericarp). The botanical definition includes many fruits that are not commonly known or referred to as berries, such as grapes, tomatoes, cucumbers, eggplants, bananas, and chili peppers.";
+        //editor.set_text(atxt);
         let mut self_ = Self { text, editor, font_size, text_color, window_scale, lineheight };
         let mut self_ = Self { text, editor, font_size, text_color, window_scale, lineheight };
         self_.refresh_layout().await;
         self_.refresh_layout().await;
         self_
         self_
@@ -112,6 +114,7 @@ impl Editor {
     pub fn selection(&self) -> parley::Selection {
     pub fn selection(&self) -> parley::Selection {
         *self.editor.raw_selection()
         *self.editor.raw_selection()
     }
     }
+    pub fn set_selection(&self, select_start: usize, select_end: usize) {}
 
 
     pub fn buffer(&self) -> String {
     pub fn buffer(&self) -> String {
         self.editor.raw_text().to_string()
         self.editor.raw_text().to_string()

+ 81 - 71
bin/app/src/ui/chatedit.rs

@@ -214,7 +214,8 @@ pub struct ChatEdit {
     key_repeat: SyncMutex<PressedKeysSmoothRepeat>,
     key_repeat: SyncMutex<PressedKeysSmoothRepeat>,
 
 
     glyphs: SyncMutex<Vec<Glyph>>,
     glyphs: SyncMutex<Vec<Glyph>>,
-    main_dc_key: u64,
+    root_dc_key: u64,
+    content_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>>,
@@ -347,7 +348,8 @@ 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(),
+            root_dc_key: OsRng.gen(),
+            content_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(),
@@ -433,8 +435,10 @@ impl ChatEdit {
         let rect = self.rect.get();
         let rect = self.rect.get();
         *point -= rect.pos();
         *point -= rect.pos();
         *point -= self.inner_pos();
         *point -= self.inner_pos();
+        point.y += self.scroll.get();
     }
     }
 
 
+    /// Inner position used for rendering
     fn inner_pos(&self) -> Point {
     fn inner_pos(&self) -> Point {
         let content_height = self.content_height.get();
         let content_height = self.content_height.get();
         let rect_h = self.rect.get_height();
         let rect_h = self.rect.get_height();
@@ -442,11 +446,19 @@ impl ChatEdit {
         if content_height < rect_h {
         if content_height < rect_h {
             inner_pos.y = (rect_h - content_height) / 2.;
             inner_pos.y = (rect_h - content_height) / 2.;
         }
         }
-        t!("inner_pos() -> {inner_pos:?}  rect_h={rect_h}, content_height={content_height}");
-        inner_pos.y -= self.scroll.get();
         inner_pos
         inner_pos
     }
     }
 
 
+    /// Maximum allowed scroll value
+    fn max_scroll(&self) -> f32 {
+        let content_height = self.content_height.get();
+        let rect_h = self.rect.get_height();
+        let mut max_scroll = (content_height - rect_h).max(0.);
+        // Arbitrary little space at the bottom
+        max_scroll += self.font_size.get() / 3.;
+        max_scroll
+    }
+
     fn regen_cursor_mesh(&self) -> GfxDrawMesh {
     fn regen_cursor_mesh(&self) -> GfxDrawMesh {
         let cursor_width = self.cursor_width.get();
         let cursor_width = self.cursor_width.get();
         let cursor_ascent = self.cursor_ascent.get();
         let cursor_ascent = self.cursor_ascent.get();
@@ -471,47 +483,25 @@ impl ChatEdit {
         let select_ascent = self.select_ascent.get();
         let select_ascent = self.select_ascent.get();
         let handle_descent = self.handle_descent.get();
         let handle_descent = self.handle_descent.get();
         let color = self.text_hi_color.get();
         let color = self.text_hi_color.get();
-        let linespacing = self.linespacing.get();
-        let scroll = self.scroll.get();
         // Transparent for fade
         // Transparent for fade
         let mut color_trans = color.clone();
         let mut color_trans = color.clone();
         color_trans[3] = 0.;
         color_trans[3] = 0.;
 
 
-        // find start_x
         let x = pos.x;
         let x = pos.x;
-        let y_off = pos.y - scroll;
-
-        let y = y_off + baseline + handle_descent;
-        if y < 0. {
-            return
-        }
+        let mut y = pos.y + baseline;
 
 
         // Vertical line downwards. We use this instead of draw_box() so we have a fade.
         // Vertical line downwards. We use this instead of draw_box() so we have a fade.
         let verts = vec![
         let verts = vec![
-            Vertex {
-                pos: [x - side * 1., y_off + baseline - select_ascent],
-                color: color_trans,
-                uv: [0., 0.],
-            },
-            Vertex {
-                pos: [x + side * 4., y_off + baseline - select_ascent],
-                color: color_trans,
-                uv: [0., 0.],
-            },
-            Vertex {
-                pos: [x - side * 1., y_off + baseline + handle_descent + 5.],
-                color,
-                uv: [0., 0.],
-            },
-            Vertex {
-                pos: [x + side * 4., y_off + baseline + handle_descent + 5.],
-                color,
-                uv: [0., 0.],
-            },
+            Vertex { pos: [x - side * 1., y - select_ascent], color: color_trans, uv: [0., 0.] },
+            Vertex { pos: [x + side * 4., y - select_ascent], color: color_trans, uv: [0., 0.] },
+            Vertex { pos: [x - side * 1., y + handle_descent + 5.], color, uv: [0., 0.] },
+            Vertex { pos: [x + side * 4., y + handle_descent + 5.], color, uv: [0., 0.] },
         ];
         ];
         let indices = vec![0, 2, 1, 1, 2, 3];
         let indices = vec![0, 2, 1, 1, 2, 3];
         mesh.append(verts, indices);
         mesh.append(verts, indices);
 
 
+        y += handle_descent;
+
         // The arrow itself.
         // The arrow itself.
         // Go anti-clockwise
         // Go anti-clockwise
         let verts = vec![
         let verts = vec![
@@ -893,21 +883,14 @@ impl ChatEdit {
                 self.redraw_select().await;
                 self.redraw_select().await;
             }
             }
             TouchStateAction::ScrollVert { start_pos, scroll_start } => {
             TouchStateAction::ScrollVert { start_pos, scroll_start } => {
-                /*
-                let max_scroll = {
-                    let mut text_wrap = self.text_wrap.lock();
-                    self.max_scroll(&mut text_wrap)
-                };
-
                 let y_dist = start_pos.y - touch_pos.y;
                 let y_dist = start_pos.y - touch_pos.y;
                 let mut scroll = scroll_start + y_dist;
                 let mut scroll = scroll_start + y_dist;
-                scroll = scroll.clamp(0., max_scroll);
+                scroll = scroll.clamp(0., self.max_scroll());
                 if (self.scroll.get() - scroll).abs() < VERT_SCROLL_UPDATE_INC {
                 if (self.scroll.get() - scroll).abs() < VERT_SCROLL_UPDATE_INC {
                     return true
                     return true
                 }
                 }
                 self.scroll.set(atom, scroll);
                 self.scroll.set(atom, scroll);
-                self.redraw().await;
-                */
+                self.redraw_scroll().await;
             }
             }
             TouchStateAction::SetCursorPos => {
             TouchStateAction::SetCursorPos => {
                 // TBH I can't even see the cursor under my thumb so I'll just
                 // TBH I can't even see the cursor under my thumb so I'll just
@@ -1045,13 +1028,30 @@ impl ChatEdit {
         let atom = &mut PropertyAtomicGuard::new();
         let atom = &mut PropertyAtomicGuard::new();
         let trace_id = rand::random();
         let trace_id = rand::random();
         let timest = unixtime();
         let timest = unixtime();
-        let Some(draw_update) = self.make_draw_calls(trace_id, atom).await else {
-            error!(target: "ui::chatedit", "Text failed to draw");
-            return
-        };
+        let draw_update = self.make_draw_calls(trace_id, atom).await;
         self.render_api.replace_draw_calls(timest, draw_update.draw_calls);
         self.render_api.replace_draw_calls(timest, draw_update.draw_calls);
     }
     }
 
 
+    /// Called when scroll changes. Moves content up or down. Nothing more.
+    async fn redraw_scroll(&self) {
+        let timest = unixtime();
+        let rect = self.rect.get();
+        let scroll = self.scroll.get();
+
+        let draw_main = vec![(
+            self.content_dc_key,
+            GfxDrawCall {
+                instrs: vec![
+                    GfxDrawInstruction::ApplyView(rect),
+                    GfxDrawInstruction::Move(Point::new(0., -scroll)),
+                ],
+                dcs: vec![self.text_dc_key, self.cursor_dc_key, self.select_dc_key],
+                z_index: self.z_index.get(),
+            },
+        )];
+        self.render_api.replace_draw_calls(timest, draw_main);
+    }
+
     async fn redraw_cursor(&self) {
     async fn redraw_cursor(&self) {
         let timest = unixtime();
         let timest = unixtime();
         let instrs = self.get_cursor_instrs().await;
         let instrs = self.get_cursor_instrs().await;
@@ -1091,14 +1091,13 @@ impl ChatEdit {
         cursor_instrs
         cursor_instrs
     }
     }
 
 
-    async fn regen_txt_mesh(&self) -> Vec<GfxDrawInstruction> {
+    fn regen_bg_mesh(&self) -> Vec<GfxDrawInstruction> {
         let padding_top = self.padding_top();
         let padding_top = self.padding_top();
         let padding_bottom = self.padding_bottom();
         let padding_bottom = self.padding_bottom();
 
 
         let mut instrs = vec![];
         let mut instrs = vec![];
         // BUG FIXME SHOULDNT NEED THIS!
         // BUG FIXME SHOULDNT NEED THIS!
         instrs.push(GfxDrawInstruction::Move(Point::zero()));
         instrs.push(GfxDrawInstruction::Move(Point::zero()));
-
         if self.debug.get() {
         if self.debug.get() {
             let mut rect = self.rect.get();
             let mut rect = self.rect.get();
             rect.x = 0.;
             rect.x = 0.;
@@ -1113,8 +1112,11 @@ impl ChatEdit {
             mesh.draw_outline(&rect, [0., 1., 0., 0.5], 1.);
             mesh.draw_outline(&rect, [0., 1., 0., 0.5], 1.);
             instrs.push(GfxDrawInstruction::Draw(mesh.alloc(&self.render_api).draw_untextured()));
             instrs.push(GfxDrawInstruction::Draw(mesh.alloc(&self.render_api).draw_untextured()));
         }
         }
+        instrs
+    }
 
 
-        instrs.push(GfxDrawInstruction::Move(self.inner_pos()));
+    async fn regen_txt_mesh(&self) -> Vec<GfxDrawInstruction> {
+        let mut instrs = vec![GfxDrawInstruction::Move(self.inner_pos())];
 
 
         let editor = self.editor.lock().await;
         let editor = self.editor.lock().await;
         let layout = editor.layout();
         let layout = editor.layout();
@@ -1215,29 +1217,44 @@ impl ChatEdit {
             .unwrap();
             .unwrap();
     }
     }
 
 
-    async fn make_draw_calls(
-        &self,
-        trace_id: u32,
-        atom: &mut PropertyAtomicGuard,
-    ) -> Option<DrawUpdate> {
+    async fn make_draw_calls(&self, trace_id: u32, atom: &mut PropertyAtomicGuard) -> DrawUpdate {
         self.eval_rect(atom).await;
         self.eval_rect(atom).await;
 
 
         let rect = self.rect.get();
         let rect = self.rect.get();
+        let max_scroll = self.max_scroll();
+        let mut scroll = self.scroll.get();
+        if scroll > max_scroll {
+            scroll = max_scroll;
+            self.scroll.set(atom, scroll);
+        }
+
         let cursor_instrs = self.get_cursor_instrs().await;
         let cursor_instrs = self.get_cursor_instrs().await;
         let txt_instrs = self.regen_txt_mesh().await;
         let txt_instrs = self.regen_txt_mesh().await;
         let sel_instrs = self.regen_select_mesh().await;
         let sel_instrs = self.regen_select_mesh().await;
 
 
-        Some(DrawUpdate {
-            key: self.main_dc_key,
+        let mut root_instrs = vec![GfxDrawInstruction::ApplyView(rect)];
+        let mut bg_instrs = self.regen_bg_mesh();
+        root_instrs.append(&mut bg_instrs);
+
+        DrawUpdate {
+            key: self.root_dc_key,
             draw_calls: vec![
             draw_calls: vec![
                 (
                 (
-                    self.main_dc_key,
+                    self.root_dc_key,
                     GfxDrawCall {
                     GfxDrawCall {
-                        instrs: vec![GfxDrawInstruction::ApplyView(rect)],
-                        dcs: vec![self.text_dc_key, self.cursor_dc_key, self.select_dc_key],
+                        instrs: root_instrs,
+                        dcs: vec![self.content_dc_key],
                         z_index: self.z_index.get(),
                         z_index: self.z_index.get(),
                     },
                     },
                 ),
                 ),
+                (
+                    self.content_dc_key,
+                    GfxDrawCall {
+                        instrs: vec![GfxDrawInstruction::Move(Point::new(0., -scroll))],
+                        dcs: vec![self.text_dc_key, self.cursor_dc_key, self.select_dc_key],
+                        z_index: 0,
+                    },
+                ),
                 (self.select_dc_key, GfxDrawCall { instrs: sel_instrs, dcs: vec![], z_index: 0 }),
                 (self.select_dc_key, GfxDrawCall { instrs: sel_instrs, dcs: vec![], z_index: 0 }),
                 (self.text_dc_key, GfxDrawCall { instrs: txt_instrs, dcs: vec![], z_index: 1 }),
                 (self.text_dc_key, GfxDrawCall { instrs: txt_instrs, dcs: vec![], z_index: 1 }),
                 (
                 (
@@ -1245,7 +1262,7 @@ impl ChatEdit {
                     GfxDrawCall { instrs: cursor_instrs, dcs: vec![], z_index: 2 },
                     GfxDrawCall { instrs: cursor_instrs, dcs: vec![], z_index: 2 },
                 ),
                 ),
             ],
             ],
-        })
+        }
     }
     }
 
 
     async fn process_insert_text_method(me: &Weak<Self>, sub: &MethodCallSub) -> bool {
     async fn process_insert_text_method(me: &Weak<Self>, sub: &MethodCallSub) -> bool {
@@ -1460,7 +1477,7 @@ impl UIObject for ChatEdit {
         t!("ChatEdit::draw({:?}, {trace_id})", self.node.upgrade().unwrap());
         t!("ChatEdit::draw({:?}, {trace_id})", self.node.upgrade().unwrap());
         *self.parent_rect.lock() = Some(parent_rect);
         *self.parent_rect.lock() = Some(parent_rect);
 
 
-        self.make_draw_calls(trace_id, atom).await
+        Some(self.make_draw_calls(trace_id, atom).await)
     }
     }
 
 
     async fn handle_char(&self, key: char, mods: KeyMods, repeat: bool) -> bool {
     async fn handle_char(&self, key: char, mods: KeyMods, repeat: bool) -> bool {
@@ -1636,7 +1653,7 @@ impl UIObject for ChatEdit {
 
 
         self.pause_blinking();
         self.pause_blinking();
         //self.apply_cursor_scrolling();
         //self.apply_cursor_scrolling();
-        self.redraw().await;
+        self.redraw_select().await;
         true
         true
     }
     }
 
 
@@ -1647,18 +1664,11 @@ impl UIObject for ChatEdit {
 
 
         let atom = &mut PropertyAtomicGuard::new();
         let atom = &mut PropertyAtomicGuard::new();
 
 
-        /*
-        let max_scroll = {
-            let mut text_wrap = self.text_wrap.lock();
-            self.max_scroll(&mut text_wrap)
-        };
-
         let mut scroll = self.scroll.get() - wheel_pos.y * self.scroll_speed.get();
         let mut scroll = self.scroll.get() - wheel_pos.y * self.scroll_speed.get();
-        scroll = scroll.clamp(0., max_scroll);
+        scroll = scroll.clamp(0., self.max_scroll());
         t!("handle_mouse_wheel({wheel_pos:?}) [scroll={scroll}]");
         t!("handle_mouse_wheel({wheel_pos:?}) [scroll={scroll}]");
         self.scroll.set(atom, scroll);
         self.scroll.set(atom, scroll);
-        self.redraw().await;
-        */
+        self.redraw_scroll().await;
 
 
         true
         true
     }
     }