From 5e817f620330d5fff1288f53b2396d1a1f32a0a2 Mon Sep 17 00:00:00 2001
From: FloVanGH <flovanpt@posteo.de>
Date: Fri, 7 Dec 2018 15:45:08 +0100
Subject: [PATCH] Finish text cursor scrolling. Add scroll mode property.

---
 examples/widgets.rs                  |  6 +--
 src/enums/mod.rs                     |  2 +
 src/enums/scroll_mode.rs             | 15 +++++++
 src/layout_object/scroll.rs          | 37 ++++++++++++------
 src/properties/mod.rs                |  2 +
 src/properties/scroll_viewer_mode.rs | 20 ++++++++++
 src/widget/scroll_viewer.rs          |  4 +-
 src/widget/text_box.rs               | 58 ++++++++++++++++++++++++----
 8 files changed, 120 insertions(+), 24 deletions(-)
 create mode 100644 src/enums/scroll_mode.rs
 create mode 100644 src/properties/scroll_viewer_mode.rs

diff --git a/examples/widgets.rs b/examples/widgets.rs
index 2916ff6..7bca9a2 100644
--- a/examples/widgets.rs
+++ b/examples/widgets.rs
@@ -85,9 +85,9 @@ impl Widget for MainView {
                             .with_child(Container::create().with_child(
                                 TextBox::create().with_property(WaterMark::from("TextBox...")),
                             ))
-                            .with_child(Container::create().with_child(
-                                TextBox::create().with_property(WaterMark::from("TextBox...")),
-                            ))
+                            // .with_child(Container::create().with_child(
+                            //     TextBox::create().with_property(WaterMark::from("TextBox...")),
+                            // ))
                     ),
             )
             .with_shared_property(button_count_label)
diff --git a/src/enums/mod.rs b/src/enums/mod.rs
index 8794a8a..0f945a0 100644
--- a/src/enums/mod.rs
+++ b/src/enums/mod.rs
@@ -2,9 +2,11 @@
 pub use self::alignment::Alignment;
 pub use self::error::*;
 pub use self::parent_type::ParentType;
+pub use self::scroll_mode::ScrollMode;
 pub use self::visibility::Visibility;
 
 mod alignment;
 mod error;
 mod parent_type;
+mod scroll_mode;
 mod visibility;
diff --git a/src/enums/scroll_mode.rs b/src/enums/scroll_mode.rs
new file mode 100644
index 0000000..c36669a
--- /dev/null
+++ b/src/enums/scroll_mode.rs
@@ -0,0 +1,15 @@
+/// The `ScrollMode` defines the mode of a scroll direction.
+#[derive(Copy, Clone, PartialEq)]
+pub enum ScrollMode {
+    /// Scrolling will process by `ScrollViewer` logic
+    Auto,
+
+    /// Scrolling could be handled from outside. It will not be process by `ScrollViewer` logic.
+    None,
+}
+
+impl Default for ScrollMode {
+    fn default() -> Self {
+        ScrollMode::Auto
+    }
+}
\ No newline at end of file
diff --git a/src/layout_object/scroll.rs b/src/layout_object/scroll.rs
index 4863233..5ab3132 100644
--- a/src/layout_object/scroll.rs
+++ b/src/layout_object/scroll.rs
@@ -2,8 +2,9 @@ use std::cell::Cell;
 
 use dces::{Entity, EntityComponentManager};
 
+use enums::ScrollMode;
 use layout_object::{LayoutObject, LayoutResult};
-use properties::{Constraint, Offset, Rect};
+use properties::{Constraint, Offset, Rect, ScrollViewerMode};
 use theme::Theme;
 
 #[derive(Default)]
@@ -46,6 +47,14 @@ impl LayoutObject for ScrollLayoutObject {
 
             let center_size = constraint.perform((width, height));
 
+            let mut vertical_scroll_mode = ScrollMode::default();
+            let mut horizontal_scroll_mode = ScrollMode::default();
+
+            if let Ok(mode) = ecm.borrow_component::<ScrollViewerMode>(entity) {
+                vertical_scroll_mode = mode.vertical;
+                horizontal_scroll_mode = mode.horizontal;
+            }
+
             let mut offset = (0, 0);
 
             let old_bounds = self.child_bounds.get();
@@ -58,22 +67,26 @@ impl LayoutObject for ScrollLayoutObject {
             }
 
             if let Ok(bounds) = ecm.borrow_mut_component::<Rect>(children[0]) {
-                if bounds.width <= center_size.0 {
-                    offset.0 = 0;
-                } else {
-                    let offset_width = old_bounds.width as i32 - bounds.width as i32;
+                if vertical_scroll_mode != ScrollMode::None
+                    && horizontal_scroll_mode != ScrollMode::None
+                {
+                    if bounds.width <= center_size.0 {
+                        offset.0 = 0;
+                    } else {
+                        let offset_width = old_bounds.width as i32 - bounds.width as i32;
+
+                        if offset_width != 0 {
+                            offset.0 = (offset.0 + offset_width).min(0);
+                        }
+                    }
 
-                    if offset_width != 0 {
-                        offset.0 = (offset.0 + offset_width).min(0);
+                    if bounds.height <= center_size.1 {
+                        offset.1 = 0;
                     }
-                }
 
-                if bounds.height <= center_size.1 {
-                    offset.1 = 0;
+                    // todo: vertical scrollint
                 }
 
-                // todo: vertical scrollint
-
                 bounds.x = offset.0;
                 bounds.y = offset.1;
 
diff --git a/src/properties/mod.rs b/src/properties/mod.rs
index 7d40a13..a4aa395 100644
--- a/src/properties/mod.rs
+++ b/src/properties/mod.rs
@@ -11,6 +11,7 @@ pub use self::padding::Padding;
 pub use self::point::Point;
 pub use self::pressed::Pressed;
 pub use self::rect::Rect;
+pub use self::scroll_viewer_mode::ScrollViewerMode;
 pub use self::selected::Selected;
 pub use self::text_selection::TextSelection;
 pub use self::water_mark::WaterMark;
@@ -26,6 +27,7 @@ mod padding;
 mod point;
 mod pressed;
 mod rect;
+mod scroll_viewer_mode;
 mod selected;
 mod text_selection;
 mod water_mark;
diff --git a/src/properties/scroll_viewer_mode.rs b/src/properties/scroll_viewer_mode.rs
new file mode 100644
index 0000000..50b2ad7
--- /dev/null
+++ b/src/properties/scroll_viewer_mode.rs
@@ -0,0 +1,20 @@
+use enums::ScrollMode;
+
+/// The `ScrollViewerMode` struct is used to define the vertical and horizontal scroll behavior of the `ScrollViewer`.
+#[derive(Default)]
+pub struct ScrollViewerMode {
+    /// Vertical scroll mode.
+    pub vertical: ScrollMode,
+
+    /// Horizontal scroll mode.
+    pub horizontal: ScrollMode,
+}
+
+impl ScrollViewerMode {
+    pub fn new(vertical: ScrollMode, horizontal: ScrollMode) -> Self {
+        ScrollViewerMode {
+            vertical,
+            horizontal,
+        }
+    }
+}
diff --git a/src/widget/scroll_viewer.rs b/src/widget/scroll_viewer.rs
index 2b5b0e4..74554e6 100644
--- a/src/widget/scroll_viewer.rs
+++ b/src/widget/scroll_viewer.rs
@@ -1,7 +1,7 @@
 use enums::ParentType;
 use layout_object::ScrollLayoutObject;
 use widget::{Template, Widget};
-use properties::Offset;
+use properties::{Offset, ScrollViewerMode};
 
 /// The `ScrollViewer` represents a layout widget that adds vertial and horizontal offset to its perent. 
 /// It is used to scroll the content if the content's width or height is greater than the ScrollViewers width or height.
@@ -9,6 +9,7 @@ use properties::Offset;
 /// # Properties
 /// 
 /// * `Offset` - Represents the vertial and horizontal scroll offset.
+/// * `ScrollMode` - Scroll mode vertical / horizontal off the scroll viewr.
 /// 
 /// # Others
 /// 
@@ -21,6 +22,7 @@ impl Widget for ScrollViewer {
         Template::default()
             .as_parent_type(ParentType::Single)
             .with_property(Offset::default())
+            .with_property(ScrollViewerMode::default())
             .with_layout_object(ScrollLayoutObject::default())
             .with_debug_name("ScrollViewer")
     }
diff --git a/src/widget/text_box.rs b/src/widget/text_box.rs
index dce09fe..98b8d75 100644
--- a/src/widget/text_box.rs
+++ b/src/widget/text_box.rs
@@ -1,12 +1,12 @@
-use enums::ParentType;
+use enums::{ParentType, ScrollMode};
 use event::{Key, KeyEventHandler, MouseEventHandler};
-use properties::{Focused, Label, Offset, Point, TextSelection, WaterMark};
+use properties::{Focused, Label, Offset, Point, Rect, ScrollViewerMode, TextSelection, WaterMark};
 use std::cell::{Cell, RefCell};
 use std::rc::Rc;
 use theme::Selector;
 use widget::{
     Container, Context, Cursor, ScrollViewer, SharedProperty, Stack, State, Template,
-    WaterMarkTextBlock, Widget, WidgetKey
+    WaterMarkTextBlock, Widget, WidgetKey,
 };
 
 /// The `TextBoxState` handles the text processing of the `TextBox` widget.
@@ -17,6 +17,7 @@ pub struct TextBoxState {
     updated: Cell<bool>,
     selection_start: Cell<usize>,
     selection_length: Cell<usize>,
+    cursor_x: Cell<i32>,
 }
 
 impl Into<Rc<State>> for TextBoxState {
@@ -110,8 +111,42 @@ impl State for TextBoxState {
     }
 
     fn update_post_layout(&self, context: &mut Context) {
-        if let Ok(offset) = context.widget.borrow_mut_property_by_key::<Offset>("Cursor") {
-            println!("Curor Offset: {}", offset.0);
+        let mut cursor_x_delta = 0;
+        let mut scroll_viewer_width = 0;
+
+        if let Ok(bounds) = context
+            .widget
+            .borrow_property_by_key::<Rect>("ScrollViewer")
+        {
+            scroll_viewer_width = bounds.width;
+        }
+
+        // if selection start is 0 and text is changed and text_size > 0 than selection is 1 and offset is 0
+
+        // if selection x >= bounds.with and text is changed offset -= new character width
+
+        // maybe not use scrollviewer here
+
+        // Adjust offset of text and cursor if cursor position is out of bounds
+        if let Ok(bounds) = context.widget.borrow_mut_property_by_key::<Rect>("Cursor") {
+            if bounds.x < 0 || bounds.x > scroll_viewer_width as i32 {
+                cursor_x_delta = self.cursor_x.get() - bounds.x;
+                bounds.x = self.cursor_x.get();
+            }
+            self.cursor_x.set(bounds.x);
+        }
+
+        if cursor_x_delta != 0 {
+            if let Ok(bounds) = context
+                .widget
+                .borrow_mut_property_by_key::<Rect>("TextBlock")
+            {
+                bounds.x += cursor_x_delta;
+            }
+
+            if let Ok(offset) = context.widget.borrow_mut_property::<Offset>() {
+                offset.0 += cursor_x_delta;
+            }
         }
     }
 }
@@ -143,8 +178,9 @@ impl Widget for TextBox {
         let state = Rc::new(TextBoxState::default());
         let click_state = state.clone();
         let text_block_key = WidgetKey::from("TextBlock");
+        let scroll_viewer_key = WidgetKey::from("ScrollViewer");
         let cursor_key = WidgetKey::from("Cursor");
-        
+
         Template::default()
             .as_parent_type(ParentType::Single)
             .with_property(Focused(false))
@@ -159,9 +195,14 @@ impl Widget for TextBox {
                                             .with_shared_property(label.clone())
                                             .with_shared_property(selector.clone())
                                             .with_shared_property(water_mark.clone())
-                                            .with_key(text_block_key.clone())
+                                            .with_key(text_block_key.clone()),
                                     )
-                                    .with_shared_property(offset.clone()),
+                                    .with_shared_property(offset.clone())
+                                    .with_property(ScrollViewerMode::new(
+                                        ScrollMode::None,
+                                        ScrollMode::None,
+                                    ))
+                                    .with_key(scroll_viewer_key.clone()),
                             )
                             .with_child(
                                 Cursor::create()
@@ -190,6 +231,7 @@ impl Widget for TextBox {
             .with_shared_property(focused)
             .with_child_key(text_block_key)
             .with_child_key(cursor_key)
+            .with_child_key(scroll_viewer_key)
             .with_event_handler(
                 KeyEventHandler::default()
                     .on_key_down(Rc::new(move |key: Key| -> bool { state.update_text(key) })),
-- 
GitLab