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

app: scroll working for single/multi line edit

darkfi 10 месяцев назад
Родитель
Сommit
5cbd321700

+ 2 - 0
bin/app/src/app/node.rs

@@ -363,6 +363,8 @@ pub fn create_baseedit(name: &str) -> SceneNode {
     prop.set_range_f32(0., f32::MAX);
     prop.set_range_f32(0., f32::MAX);
     node.add_property(prop).unwrap();
     node.add_property(prop).unwrap();
 
 
+    // Deprecate this
+    // No need to be a property
     let mut prop = Property::new("scroll", PropertyType::Float32, PropertySubType::Pixel);
     let mut prop = Property::new("scroll", PropertyType::Float32, PropertySubType::Pixel);
     prop.set_range_f32(0., f32::MAX);
     prop.set_range_f32(0., f32::MAX);
     node.add_property(prop).unwrap();
     node.add_property(prop).unwrap();

+ 4 - 2
bin/app/src/app/schema/test.rs

@@ -19,8 +19,8 @@
 use crate::{
 use crate::{
     app::{
     app::{
         node::{
         node::{
-            create_chatedit, create_editbox, create_layer, create_singleline_edit, create_text,
-            create_vector_art, create_video,
+            create_chatedit, create_editbox, create_layer, create_multiline_edit,
+            create_singleline_edit, create_text, create_vector_art, create_video,
         },
         },
         App,
         App,
     },
     },
@@ -386,6 +386,7 @@ pub async fn make(app: &App, window: SceneNodePtr, i18n_fish: &I18nBabelFish) {
 
 
     // Text edit
     // Text edit
     let node = create_singleline_edit("editz");
     let node = create_singleline_edit("editz");
+    //let node = create_multiline_edit("editz");
     node.set_property_bool(atom, Role::App, "is_active", true).unwrap();
     node.set_property_bool(atom, Role::App, "is_active", true).unwrap();
     node.set_property_bool(atom, Role::App, "is_focused", true).unwrap();
     node.set_property_bool(atom, Role::App, "is_focused", true).unwrap();
 
 
@@ -461,6 +462,7 @@ pub async fn make(app: &App, window: SceneNodePtr, i18n_fish: &I18nBabelFish) {
                 window_scale.clone(),
                 window_scale.clone(),
                 app.render_api.clone(),
                 app.render_api.clone(),
                 BaseEditType::SingleLine,
                 BaseEditType::SingleLine,
+                //BaseEditType::MultiLine,
             )
             )
         })
         })
         .await;
         .await;

+ 3 - 0
bin/app/src/text2/editor/android.rs

@@ -237,6 +237,9 @@ impl Editor {
     pub fn set_width(&mut self, w: f32) {
     pub fn set_width(&mut self, w: f32) {
         self.width = Some(w);
         self.width = Some(w);
     }
     }
+    pub fn width(&self) -> f32 {
+        self.layout().full_width()
+    }
     pub fn height(&self) -> f32 {
     pub fn height(&self) -> f32 {
         self.layout().height()
         self.layout().height()
     }
     }

+ 3 - 0
bin/app/src/text2/editor/parley.rs

@@ -123,6 +123,9 @@ impl Editor {
     pub fn set_width(&mut self, w: f32) {
     pub fn set_width(&mut self, w: f32) {
         self.editor.set_width(Some(w));
         self.editor.set_width(Some(w));
     }
     }
+    pub fn width(&self) -> f32 {
+        self.layout().full_width()
+    }
     pub fn height(&self) -> f32 {
     pub fn height(&self) -> f32 {
         self.layout().height()
         self.layout().height()
     }
     }

+ 84 - 6
bin/app/src/ui/edit/behave.rs

@@ -29,6 +29,8 @@ use crate::{
 
 
 use super::EditorHandle;
 use super::EditorHandle;
 
 
+macro_rules! t { ($($arg:tt)*) => { trace!(target: "ui::edit::behave", $($arg)*); } }
+
 pub enum BaseEditType {
 pub enum BaseEditType {
     SingleLine,
     SingleLine,
     MultiLine,
     MultiLine,
@@ -38,7 +40,14 @@ pub enum BaseEditType {
 pub(super) trait EditorBehavior: Send + Sync {
 pub(super) trait EditorBehavior: Send + Sync {
     async fn eval_rect(&self, atom: &mut PropertyAtomicGuard);
     async fn eval_rect(&self, atom: &mut PropertyAtomicGuard);
 
 
-    //fn scroll(&self) -> Point;
+    /// Whenever the cursor is modified this MUST be called
+    /// to recalculate the scroll value.
+    async fn apply_cursor_scroll(&self, atom: &mut PropertyAtomicGuard);
+
+    fn scroll(&self) -> Point;
+
+    /// Maximum allowed scroll value
+    async fn max_scroll(&self) -> f32;
 
 
     /// Inner position used for rendering
     /// Inner position used for rendering
     fn inner_pos(&self) -> Point;
     fn inner_pos(&self) -> Point;
@@ -50,8 +59,11 @@ pub(super) struct MultiLine {
     pub min_height: PropertyFloat32,
     pub min_height: PropertyFloat32,
     pub max_height: PropertyFloat32,
     pub max_height: PropertyFloat32,
     pub content_height: PropertyFloat32,
     pub content_height: PropertyFloat32,
+    pub scroll: PropertyFloat32,
     pub rect: PropertyRect,
     pub rect: PropertyRect,
+    pub baseline: PropertyFloat32,
     pub padding: PropertyPtr,
     pub padding: PropertyPtr,
+    pub cursor_descent: PropertyFloat32,
     pub parent_rect: Arc<SyncMutex<Option<Rectangle>>>,
     pub parent_rect: Arc<SyncMutex<Option<Rectangle>>>,
     pub editor: Arc<AsyncMutex<Option<Editor>>>,
     pub editor: Arc<AsyncMutex<Option<Editor>>>,
 }
 }
@@ -72,6 +84,14 @@ impl MultiLine {
     fn padding_bottom(&self) -> f32 {
     fn padding_bottom(&self) -> f32 {
         self.padding.get_f32(1).unwrap()
         self.padding.get_f32(1).unwrap()
     }
     }
+
+    /// Gets the real cursor pos within the rect.
+    async fn get_cursor_pos(&self) -> Point {
+        // This is the position within the content.
+        let cursor_pos = self.lock_editor().await.get_cursor_pos();
+        // Apply the inner padding
+        cursor_pos + self.inner_pos()
+    }
 }
 }
 
 
 #[async_trait]
 #[async_trait]
@@ -118,6 +138,44 @@ impl EditorBehavior for MultiLine {
             .unwrap();
             .unwrap();
     }
     }
 
 
+    async fn apply_cursor_scroll(&self, atom: &mut PropertyAtomicGuard) {
+        let mut scroll = self.scroll.get();
+        let rect_h = self.rect.get_height();
+        let cursor_y0 = self.get_cursor_pos().await.y;
+        let cursor_h = self.baseline.get() + self.cursor_descent.get();
+        // The bottom
+        let cursor_y1 = cursor_y0 + cursor_h;
+        //t!("apply_cursor_scrolling() cursor = [{cursor_y0}, {cursor_y1}] rect_h={rect_h} scroll={scroll}");
+
+        if cursor_y1 > rect_h + scroll {
+            //t!("  cursor bottom below rect");
+            // We want cursor_y1 = rect_h + scroll by adjusting scroll
+            scroll = cursor_y1 - rect_h;
+            self.scroll.set(atom, scroll);
+        } else if cursor_y0 < scroll {
+            //t!("  cursor top above rect");
+            scroll = cursor_y0;
+            self.scroll.set(atom, scroll);
+        }
+    }
+
+    fn scroll(&self) -> Point {
+        Point::new(0., -self.scroll.get())
+    }
+
+    /// Maximum allowed scroll value
+    /// * `content_height` measures the height of the actual content.
+    /// * `outer_height` applies the inner padding.
+    /// * `rect_h` then clips the `outer_height` to min/max values.
+    /// We only allow scrolling when max clipping has been applied.
+    async fn max_scroll(&self) -> f32 {
+        let content_height = self.content_height.get();
+        let outer_height = content_height + self.padding_top() + self.padding_bottom();
+        let rect_h = self.rect.get_height();
+        //t!("max_scroll content_height={content_height}, rect_h={rect_h}");
+        (outer_height - rect_h).max(0.)
+    }
+
     /// Inner position used for rendering
     /// Inner position used for rendering
     fn inner_pos(&self) -> Point {
     fn inner_pos(&self) -> Point {
         let pad_top = self.padding_top();
         let pad_top = self.padding_top();
@@ -142,7 +200,9 @@ impl EditorBehavior for MultiLine {
 
 
 pub(super) struct SingleLine {
 pub(super) struct SingleLine {
     pub content_height: PropertyFloat32,
     pub content_height: PropertyFloat32,
+    pub scroll: PropertyFloat32,
     pub rect: PropertyRect,
     pub rect: PropertyRect,
+    pub cursor_width: PropertyFloat32,
     pub parent_rect: Arc<SyncMutex<Option<Rectangle>>>,
     pub parent_rect: Arc<SyncMutex<Option<Rectangle>>>,
     pub editor: Arc<AsyncMutex<Option<Editor>>>,
     pub editor: Arc<AsyncMutex<Option<Editor>>>,
 }
 }
@@ -157,11 +217,6 @@ impl SingleLine {
 #[async_trait]
 #[async_trait]
 impl EditorBehavior for SingleLine {
 impl EditorBehavior for SingleLine {
     async fn eval_rect(&self, atom: &mut PropertyAtomicGuard) {
     async fn eval_rect(&self, atom: &mut PropertyAtomicGuard) {
-        {
-            let mut editor = self.lock_editor().await;
-            editor.refresh().await;
-        }
-
         let content_height = {
         let content_height = {
             let mut editor = self.lock_editor().await;
             let mut editor = self.lock_editor().await;
             editor.refresh().await;
             editor.refresh().await;
@@ -183,6 +238,29 @@ impl EditorBehavior for SingleLine {
             .unwrap();
             .unwrap();
     }
     }
 
 
+    async fn apply_cursor_scroll(&self, atom: &mut PropertyAtomicGuard) {
+        let scroll = self.scroll.get();
+        let rect_w = self.rect.get_width();
+        let cursor_x0 = self.lock_editor().await.get_cursor_pos().x;
+        let cursor_x1 = cursor_x0 + self.cursor_width.get();
+        if cursor_x0 < scroll {
+            self.scroll.set(atom, cursor_x0);
+        } else if cursor_x1 > rect_w + scroll {
+            let scroll = cursor_x1 - rect_w;
+            self.scroll.set(atom, scroll);
+        }
+    }
+
+    fn scroll(&self) -> Point {
+        Point::new(-self.scroll.get(), 0.)
+    }
+
+    async fn max_scroll(&self) -> f32 {
+        let rect_w = self.rect.get_width();
+        let content_w = self.lock_editor().await.width() + self.cursor_width.get();
+        (content_w - rect_w).max(0.)
+    }
+
     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();

+ 19 - 55
bin/app/src/ui/edit/mod.rs

@@ -316,7 +316,9 @@ impl BaseEdit {
         let behave: Box<dyn EditorBehavior> = match edit_type {
         let behave: Box<dyn EditorBehavior> = match edit_type {
             BaseEditType::SingleLine => Box::new(SingleLine {
             BaseEditType::SingleLine => Box::new(SingleLine {
                 content_height: content_height.clone(),
                 content_height: content_height.clone(),
+                scroll: scroll.clone(),
                 rect: rect.clone(),
                 rect: rect.clone(),
+                cursor_width: cursor_width.clone(),
                 parent_rect: parent_rect.clone(),
                 parent_rect: parent_rect.clone(),
                 editor: editor.clone(),
                 editor: editor.clone(),
             }),
             }),
@@ -324,8 +326,11 @@ impl BaseEdit {
                 min_height: min_height.clone(),
                 min_height: min_height.clone(),
                 max_height: max_height.clone(),
                 max_height: max_height.clone(),
                 content_height: content_height.clone(),
                 content_height: content_height.clone(),
+                scroll: scroll.clone(),
                 rect: rect.clone(),
                 rect: rect.clone(),
+                baseline: baseline.clone(),
                 padding: padding.clone(),
                 padding: padding.clone(),
+                cursor_descent: cursor_descent.clone(),
                 parent_rect: parent_rect.clone(),
                 parent_rect: parent_rect.clone(),
                 editor: editor.clone(),
                 editor: editor.clone(),
             }),
             }),
@@ -414,19 +419,6 @@ impl BaseEdit {
         point.y += self.scroll.get();
         point.y += self.scroll.get();
     }
     }
 
 
-    /// Maximum allowed scroll value
-    /// * `content_height` measures the height of the actual content.
-    /// * `outer_height` applies the inner padding.
-    /// * `rect_h` then clips the `outer_height` to min/max values.
-    /// We only allow scrolling when max clipping has been applied.
-    fn max_scroll(&self) -> f32 {
-        let content_height = self.content_height.get();
-        let outer_height = content_height + self.padding_top() + self.padding_bottom();
-        let rect_h = self.rect.get_height();
-        //t!("max_scroll content_height={content_height}, rect_h={rect_h}");
-        (outer_height - rect_h).max(0.)
-    }
-
     /// Gets the real cursor pos within the rect.
     /// Gets the real cursor pos within the rect.
     async fn get_cursor_pos(&self) -> Point {
     async fn get_cursor_pos(&self) -> Point {
         // This is the position within the content.
         // This is the position within the content.
@@ -551,7 +543,7 @@ impl BaseEdit {
                     if let Some(txt) = miniquad::window::clipboard_get() {
                     if let Some(txt) = miniquad::window::clipboard_get() {
                         self.insert(&txt, atom).await;
                         self.insert(&txt, atom).await;
                         // Maybe insert should call this?
                         // Maybe insert should call this?
-                        self.apply_cursor_scrolling(atom).await;
+                        self.behave.apply_cursor_scroll(atom).await;
                     }
                     }
                 }
                 }
             }
             }
@@ -687,7 +679,7 @@ impl BaseEdit {
         drop(editor);
         drop(editor);
         drop(txt_ctx);
         drop(txt_ctx);
 
 
-        self.apply_cursor_scrolling(atom).await;
+        self.behave.apply_cursor_scroll(atom).await;
         self.pause_blinking();
         self.pause_blinking();
         self.redraw(atom).await;
         self.redraw(atom).await;
 
 
@@ -868,7 +860,7 @@ impl BaseEdit {
             TouchStateAction::ScrollVert { start_pos, scroll_start } => {
             TouchStateAction::ScrollVert { start_pos, scroll_start } => {
                 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., self.max_scroll());
+                scroll = scroll.clamp(0., self.behave.max_scroll().await);
                 if (self.scroll.get() - scroll).abs() < VERT_SCROLL_UPDATE_INC {
                 if (self.scroll.get() - scroll).abs() < VERT_SCROLL_UPDATE_INC {
                     return true
                     return true
                 }
                 }
@@ -920,29 +912,6 @@ impl BaseEdit {
         self.select_text.clone().set_null(atom, Role::Internal, 0).unwrap();
         self.select_text.clone().set_null(atom, Role::Internal, 0).unwrap();
     }
     }
 
 
-    /// Whenever the cursor is modified this MUST be called
-    /// to recalculate the scroll y property.
-    async fn apply_cursor_scrolling(&self, atom: &mut PropertyAtomicGuard) {
-        let mut scroll = self.scroll.get();
-        let rect_h = self.rect.get_height();
-        let cursor_y0 = self.get_cursor_pos().await.y;
-        let cursor_h = self.baseline.get() + self.cursor_descent.get();
-        // The bottom
-        let cursor_y1 = cursor_y0 + cursor_h;
-        //t!("apply_cursor_scrolling() cursor = [{cursor_y0}, {cursor_y1}] rect_h={rect_h} scroll={scroll}");
-
-        if cursor_y1 > rect_h + scroll {
-            //t!("  cursor bottom below rect");
-            // We want cursor_y1 = rect_h + scroll by adjusting scroll
-            scroll = cursor_y1 - rect_h;
-            self.scroll.set(atom, scroll);
-        } else if cursor_y0 < scroll {
-            //t!("  cursor top above rect");
-            scroll = cursor_y0;
-            self.scroll.set(atom, scroll);
-        }
-    }
-
     fn pause_blinking(&self) {
     fn pause_blinking(&self) {
         self.blink_is_paused.store(true, Ordering::Relaxed);
         self.blink_is_paused.store(true, Ordering::Relaxed);
         self.cursor_is_visible.store(true, Ordering::Relaxed);
         self.cursor_is_visible.store(true, Ordering::Relaxed);
@@ -963,12 +932,10 @@ impl BaseEdit {
 
 
         let phone_sel_instrs = self.regen_phone_select_handle_mesh().await;
         let phone_sel_instrs = self.regen_phone_select_handle_mesh().await;
 
 
-        let mut content_instrs = vec![
-            DrawInstruction::ApplyView(rect.with_zero_pos()),
-            DrawInstruction::Move(Point::new(0., -scroll)),
-        ];
+        let mut content_instrs = vec![DrawInstruction::ApplyView(rect.with_zero_pos())];
         let mut bg_instrs = self.regen_bg_mesh();
         let mut bg_instrs = self.regen_bg_mesh();
         content_instrs.append(&mut bg_instrs);
         content_instrs.append(&mut bg_instrs);
+        content_instrs.push(DrawInstruction::Move(self.behave.scroll()));
 
 
         let draw_main = vec![
         let draw_main = vec![
             (
             (
@@ -1109,11 +1076,9 @@ impl BaseEdit {
         self.behave.eval_rect(atom).await;
         self.behave.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 max_scroll = self.behave.max_scroll().await;
+        if self.scroll.get() > max_scroll {
+            self.scroll.set(atom, max_scroll);
         }
         }
 
 
         let cursor_instrs = self.get_cursor_instrs().await;
         let cursor_instrs = self.get_cursor_instrs().await;
@@ -1121,12 +1086,10 @@ impl BaseEdit {
         let sel_instrs = self.regen_select_mesh().await;
         let sel_instrs = self.regen_select_mesh().await;
         let phone_sel_instrs = self.regen_phone_select_handle_mesh().await;
         let phone_sel_instrs = self.regen_phone_select_handle_mesh().await;
 
 
-        let mut content_instrs = vec![
-            DrawInstruction::ApplyView(rect.with_zero_pos()),
-            DrawInstruction::Move(Point::new(0., -scroll)),
-        ];
+        let mut content_instrs = vec![DrawInstruction::ApplyView(rect.with_zero_pos())];
         let mut bg_instrs = self.regen_bg_mesh();
         let mut bg_instrs = self.regen_bg_mesh();
         content_instrs.append(&mut bg_instrs);
         content_instrs.append(&mut bg_instrs);
+        content_instrs.push(DrawInstruction::Move(self.behave.scroll()));
 
 
         // + root (move)
         // + root (move)
         // -+ content (apply view)
         // -+ content (apply view)
@@ -1290,7 +1253,7 @@ impl BaseEdit {
                 editor.on_buffer_changed(atom).await;
                 editor.on_buffer_changed(atom).await;
                 drop(editor);
                 drop(editor);
 
 
-                self.apply_cursor_scrolling(atom).await;
+                self.behave.apply_cursor_scroll(atom).await;
             }
             }
         }
         }
 
 
@@ -1513,6 +1476,7 @@ impl UIObject for BaseEdit {
         t!("Key {:?} has {} actions", key, actions);
         t!("Key {:?} has {} actions", key, actions);
         let key_str = key.to_string().repeat(actions as usize);
         let key_str = key.to_string().repeat(actions as usize);
         self.insert(&key_str, atom).await;
         self.insert(&key_str, atom).await;
+        self.behave.apply_cursor_scroll(atom).await;
         self.redraw(atom).await;
         self.redraw(atom).await;
         true
         true
     }
     }
@@ -1651,7 +1615,7 @@ impl UIObject for BaseEdit {
         }
         }
 
 
         self.pause_blinking();
         self.pause_blinking();
-        self.apply_cursor_scrolling(atom).await;
+        self.behave.apply_cursor_scroll(atom).await;
         self.redraw_scroll(atom.batch_id).await;
         self.redraw_scroll(atom.batch_id).await;
         self.redraw_cursor(atom.batch_id).await;
         self.redraw_cursor(atom.batch_id).await;
         self.redraw_select(atom.batch_id).await;
         self.redraw_select(atom.batch_id).await;
@@ -1666,7 +1630,7 @@ impl UIObject for BaseEdit {
         let atom = &mut self.render_api.make_guard(gfxtag!("BaseEdit::handle_mouse_wheel"));
         let atom = &mut self.render_api.make_guard(gfxtag!("BaseEdit::handle_mouse_wheel"));
 
 
         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., self.max_scroll());
+        scroll = scroll.clamp(0., self.behave.max_scroll().await);
         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_scroll(atom.batch_id).await;
         self.redraw_scroll(atom.batch_id).await;