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