From 0e0ffc3b92d9e98fe227c42b373736aac0eee78c Mon Sep 17 00:00:00 2001 From: FloVanGH <flovanpt@posteo.de> Date: Fri, 23 Nov 2018 23:00:22 +0100 Subject: [PATCH] Port all widgets to new structure. Update readme example. --- README.md | 16 ++---- examples/minimal.rs | 24 ++++---- src/application/window.rs | 13 +---- src/event/key.rs | 26 +++++++-- src/event/mouse.rs | 35 ++++++++++-- src/widget/button.rs | 65 ++++++--------------- src/widget/center.rs | 35 +++--------- src/widget/column.rs | 33 +++-------- src/widget/container.rs | 5 +- src/widget/mod.rs | 68 +++++++++++----------- src/widget/row.rs | 35 +++--------- src/widget/scroll_viewer.rs | 43 ++++---------- src/widget/stack.rs | 26 ++------- src/widget/text_block.rs | 7 ++- src/widget/text_box.rs | 110 +++++++++--------------------------- 15 files changed, 195 insertions(+), 346 deletions(-) diff --git a/README.md b/README.md index 5ad68b6..b33170b 100644 --- a/README.md +++ b/README.md @@ -52,19 +52,14 @@ crate](https://github.com/AngryLawyer/rust-sdl2#user-content-requirements). ```rust extern crate orbtk; use orbtk::*; -use std::rc::Rc; struct MainView; impl Widget for MainView { - fn template(&self) -> Template { - Template::Single(Rc::new(Container { - child: Some(Rc::new(TextBlock { - label: Property::new(Label(String::from("OrbTk"))), - ..Default::default() - })), - ..Default::default() - })) + fn template() -> Template { + Container::template() + .as_parent_type(ParentType::Single) + .with_child(TextBlock::template()) } } @@ -74,7 +69,8 @@ fn main() { .create_window() .with_bounds(Rect::new(0, 0, 420, 730)) .with_title("Orbtk") - .with_root(MainView) + .with_root(MainView::template()) + .with_debug_flag(true) .build(); application.run(); } diff --git a/examples/minimal.rs b/examples/minimal.rs index 59db9fc..c64664d 100644 --- a/examples/minimal.rs +++ b/examples/minimal.rs @@ -1,20 +1,15 @@ extern crate orbtk; use orbtk::*; -use std::rc::Rc; -// struct MainView; +struct MainView; -// impl Widget for MainView { -// fn template(&self) -> Template { -// Template::Single(Rc::new(Container { -// child: Some(Rc::new(TextBlock { -// label: Property::new(Label(String::from("OrbTk"))), -// ..Default::default() -// })), -// ..Default::default() -// })) -// } -// } +impl Widget for MainView { + fn template() -> Template { + Container::template() + .as_parent_type(ParentType::Single) + .with_child(TextBlock::template()) + } +} fn main() { let mut application = Application::default(); @@ -22,7 +17,8 @@ fn main() { .create_window() .with_bounds(Rect::new(0, 0, 420, 730)) .with_title("Orbtk") - // .with_root(MainView) + .with_root(MainView::template()) + .with_debug_flag(true) .build(); application.run(); } diff --git a/src/application/window.rs b/src/application/window.rs index 81b3da1..539e796 100644 --- a/src/application/window.rs +++ b/src/application/window.rs @@ -17,18 +17,7 @@ use systems::{EventSystem, LayoutSystem, RenderSystem, StateSystem}; use theme::Theme; use tree::Tree; use Global; - -pub struct Template { - children: Vec<Template>, - parent_type: ParentType, - state: Option<Rc<State>>, - event_handlers: Vec<Rc<EventHandler>>, - render_object: Option<Box<RenderObject>>, - layout_object: Box<LayoutObject>, - - // todo: only one prop type per widget. - properties: Vec<ComponentBox>, -} +use widget::Template; impl Default for Template { fn default() -> Self { diff --git a/src/event/key.rs b/src/event/key.rs index fe20c4d..fac5444 100644 --- a/src/event/key.rs +++ b/src/event/key.rs @@ -299,20 +299,38 @@ pub type KeyHandler = Rc<Fn(Key, &mut WidgetContainer) -> bool + 'static>; #[derive(Default)] pub struct KeyEventHandler { - pub on_key_up: Option<KeyHandler>, - pub on_key_down: Option<KeyHandler>, + key_up: Option<KeyHandler>, + key_down: Option<KeyHandler>, +} + +impl KeyEventHandler { + pub fn on_key_up(mut self, handler: KeyHandler) -> Self { + self.key_up = Some(handler); + self + } + + pub fn on_key_down(mut self, handler: KeyHandler) -> Self { + self.key_down = Some(handler); + self + } +} + +impl Into<Rc<EventHandler>> for KeyEventHandler { + fn into(self) -> Rc<EventHandler> { + Rc::new(self) + } } impl EventHandler for KeyEventHandler { fn handle_event(&self, event: &EventBox, widget: &mut WidgetContainer) -> bool { if let Ok(event) = event.downcast_ref::<KeyDownEvent>() { - if let Some(handler) = &self.on_key_down { + if let Some(handler) = &self.key_down { return (handler)(event.key, widget); } } if let Ok(event) = event.downcast_ref::<KeyUpEvent>() { - if let Some(handler) = &self.on_key_up { + if let Some(handler) = &self.key_up { return (handler)(event.key, widget); } } diff --git a/src/event/mouse.rs b/src/event/mouse.rs index 697977e..dd00806 100644 --- a/src/event/mouse.rs +++ b/src/event/mouse.rs @@ -66,27 +66,50 @@ pub type OnMouseUp = Rc<Fn() + 'static>; #[derive(Default)] pub struct MouseEventHandler { - pub on_mouse_up: Option<MouseHandler>, - pub on_mouse_down: Option<MouseHandler>, - pub on_click: Option<MouseHandler>, + mouse_up: Option<MouseHandler>, + mouse_down: Option<MouseHandler>, + click: Option<MouseHandler>, +} + +impl MouseEventHandler { + pub fn on_mouse_up(mut self, handler: MouseHandler) -> Self { + self.mouse_up = Some(handler); + self + } + + pub fn on_mouse_down(mut self, handler: MouseHandler) -> Self { + self.mouse_down = Some(handler); + self + } + + pub fn on_click(mut self, handler: MouseHandler) -> Self { + self.click = Some(handler); + self + } +} + +impl Into<Rc<EventHandler>> for MouseEventHandler { + fn into(self) -> Rc<EventHandler> { + Rc::new(self) + } } impl EventHandler for MouseEventHandler { fn handle_event(&self, event: &EventBox, widget: &mut WidgetContainer) -> bool { if let Ok(event) = event.downcast_ref::<ClickEvent>() { - if let Some(handler) = &self.on_click { + if let Some(handler) = &self.click { return (handler)(event.position, widget); } } if let Ok(event) = event.downcast_ref::<MouseDownEvent>() { - if let Some(handler) = &self.on_mouse_down { + if let Some(handler) = &self.mouse_down { return (handler)(event.position, widget); } } if let Ok(event) = event.downcast_ref::<MouseUpEvent>() { - if let Some(handler) = &self.on_mouse_up { + if let Some(handler) = &self.mouse_up { (handler)(event.position, widget); return true; } diff --git a/src/widget/button.rs b/src/widget/button.rs index 7dc684d..d58fbd0 100644 --- a/src/widget/button.rs +++ b/src/widget/button.rs @@ -1,11 +1,11 @@ use std::rc::Rc; -use event::{EventHandler, Pressed, MouseOver}; +use event::Pressed; use state::State; use theme::Selector; use widget::{ - add_selector_to_widget, remove_selector_from_widget, Center, Container, Label, Property, - PropertyResult, Template, TextBlock, Widget, WidgetContainer, + add_selector_to_widget, remove_selector_from_widget, Center, Container, Label, Template, + TextBlock, Widget, WidgetContainer, }; /// The `ButtonState` handles the pressed state of the `Button` widget. @@ -26,54 +26,25 @@ impl State for ButtonState { } } -/// The `Button` struct represents a widget that can be clicked by user. It's used to peform an action. -pub struct Button { - pub label: Property<Label>, - pub selector: Property<Selector>, - pub event_handlers: Vec<Rc<EventHandler>>, - pub state: Rc<ButtonState>, -} - -impl Default for Button { - fn default() -> Button { - Button { - label: Property::new(Label(String::from("label"))), - selector: Property::new(Selector::new(Some(String::from("button")))), - event_handlers: vec![], - state: Rc::new(ButtonState::default()), - } +impl Into<Rc<State>> for ButtonState { + fn into(self) -> Rc<State> { + Rc::new(self) } } +/// The `Button` struct represents a widget that can be clicked by user. It's used to peform an action. +pub struct Button; + impl Widget for Button { - fn template(&self) -> Template { + fn template() -> Template { print!("Button -> "); - Template::Single(Rc::new(Container { - selector: self.selector.clone(), - child: Some(Rc::new(Center { - child: Some(Rc::new(TextBlock { - label: self.label.clone(), - selector: self.selector.clone(), - })), - })), - ..Default::default() - })) - } - - fn properties(&self) -> Vec<PropertyResult> { - vec![ - self.selector.build(), - self.label.build(), - Property::new(Pressed::default()).build(), - Property::new(MouseOver::default()).build(), - ] - } - - fn state(&self) -> Option<Rc<State>> { - Some(self.state.clone()) - } - - fn event_handlers(&self) -> Vec<Rc<EventHandler>> { - self.event_handlers.to_vec() + Template::default() + .with_property(Label::from("Button")) + .with_property(Selector::new().with("button")) + .with_child( + Container::template() + .with_child(Center::template().with_child(TextBlock::template())), + ) + .with_state(ButtonState::default()) } } diff --git a/src/widget/center.rs b/src/widget/center.rs index b51c9a3..e7643b7 100644 --- a/src/widget/center.rs +++ b/src/widget/center.rs @@ -1,36 +1,15 @@ -use std::rc::Rc; - -use layout_object::{CenterLayoutObject, LayoutObject}; +use layout_object::CenterLayoutObject; use widget::{Template, Widget}; +use enums::ParentType; /// This layout widget centers its children within itself. -pub struct Center { - pub child: Option<Rc<Widget>>, -} - -impl Default for Center { - fn default() -> Center { - Center { - child: None, - } - } -} +pub struct Center; impl Widget for Center { - fn template(&self) -> Template { + fn template() -> Template { print!("Center -> "); - if let Some(child) = &self.child { - Template::Single(child.clone()) - } else { - Template::Empty - } + Template::default() + .as_parent_type(ParentType::Single) + .with_layout_object(CenterLayoutObject) } - - fn layout_object(&self) -> Box<LayoutObject> { - Box::new(CenterLayoutObject) - } -} - -pub struct PbCenter { - } diff --git a/src/widget/column.rs b/src/widget/column.rs index e74ba59..dcefaeb 100644 --- a/src/widget/column.rs +++ b/src/widget/column.rs @@ -1,35 +1,16 @@ -use std::rc::Rc; - -use layout_object::{FlexLayoutObject, LayoutObject}; +use layout_object::FlexLayoutObject; use enums::Alignment; use widget::{Template, Widget}; +use enums::ParentType; /// This layout widget orders its children vertical. -pub struct Column { - pub children: Vec<Rc<Widget>>, -} - -impl Default for Column { - fn default() -> Column { - Column { - children: vec![], - } - } -} +pub struct Column; impl Widget for Column { - fn template(&self) -> Template { + fn template() -> Template { print!("Column -> "); - if self.children.is_empty() { - Template::Empty - } else if self.children.len() == 1 { - Template::Single(self.children[0].clone()) - } else { - Template::Mutli(self.children.to_vec()) - } - } - - fn layout_object(&self) -> Box<LayoutObject> { - Box::new(FlexLayoutObject::new(Alignment::Vertical)) + Template::default() + .as_parent_type(ParentType::Multi) + .with_layout_object(FlexLayoutObject::new(Alignment::Vertical)) } } diff --git a/src/widget/container.rs b/src/widget/container.rs index c030485..5e9c38d 100644 --- a/src/widget/container.rs +++ b/src/widget/container.rs @@ -2,14 +2,15 @@ use layout_object::PaddingLayoutObject; use render_object::{RectangleRenderObject}; use theme::Selector; -use widget::Widget; +use widget::{Template, Widget}; use enums::ParentType; -use application::Template; +/// The `Container` layout surrounds its child with a padding. Draws a box arround the child. pub struct Container; impl Widget for Container { fn template() -> Template { + print!("Container -> "); Template::default() .as_parent_type(ParentType::Single) .with_property(Selector::new().with("container")) diff --git a/src/widget/mod.rs b/src/widget/mod.rs index de46c3a..3a54228 100644 --- a/src/widget/mod.rs +++ b/src/widget/mod.rs @@ -1,39 +1,37 @@ //! Contains concret implementations of OrbTk's default widgets. It contains also layout widgets. -use std::any::{Any, TypeId}; -use std::cell::Cell; +use std::any::TypeId; use std::rc::Rc; use dces::{Component, ComponentBox, Entity, EntityComponentManager, NotFound, SharedComponentBox}; +use enums::ParentType; use event::EventHandler; -use layout_object::{DefaultLayoutObject, LayoutObject}; +use layout_object::LayoutObject; use render_object::RenderObject; use state::State; use theme::Selector; use tree::Tree; -use application::Template; -// pub use self::button::*; -// pub use self::center::*; -// pub use self::column::*; +pub use self::button::*; +pub use self::center::*; +pub use self::column::*; pub use self::container::*; -// pub use self::row::*; -// pub use self::scroll_viewer::*; -// pub use self::stack::*; +pub use self::row::*; +pub use self::scroll_viewer::*; +pub use self::stack::*; pub use self::text_block::*; -// pub use self::text_box::*; +pub use self::text_box::*; -// mod button; -// mod center; -// mod column; +mod button; +mod center; +mod column; mod container; -// mod macros; -// mod row; -// mod scroll_viewer; -// mod stack; +mod row; +mod scroll_viewer; +mod stack; mod text_block; -//mod text_box; +mod text_box; #[derive(Copy, Clone)] pub struct Drawable; @@ -47,19 +45,17 @@ pub struct Offset(pub i32, pub i32); pub struct Label(pub String); impl From<&str> for Label { - fn from (s: &str) -> Label { + fn from(s: &str) -> Label { Label(s.to_string()) } } impl From<String> for Label { - fn from (s: String) -> Label { + fn from(s: String) -> Label { Label(s) } } - - // pub struct Key(pub String); pub struct Padding { @@ -69,6 +65,22 @@ pub struct Padding { pub bottom: u32, } +pub struct Template { + pub children: Vec<Template>, + pub parent_type: ParentType, + pub state: Option<Rc<State>>, + pub event_handlers: Vec<Rc<EventHandler>>, + pub render_object: Option<Box<RenderObject>>, + pub layout_object: Box<LayoutObject>, + + // todo: only one prop type per widget. + pub properties: Vec<ComponentBox>, +} + +pub trait Widget { + fn template() -> Template; +} + pub struct WidgetContainer<'a> { tree: &'a Tree, ecm: &'a mut EntityComponentManager, @@ -177,12 +189,4 @@ pub fn remove_selector_from_widget(pseudo_class: &str, widget: &mut WidgetContai // } // } -pub struct Property { - -} - - - -pub trait Widget { - fn template() -> Template; -} +pub struct Property {} diff --git a/src/widget/row.rs b/src/widget/row.rs index a48985b..c0b527b 100644 --- a/src/widget/row.rs +++ b/src/widget/row.rs @@ -1,35 +1,16 @@ -use std::rc::Rc; - -use layout_object::{FlexLayoutObject, LayoutObject}; +use layout_object::FlexLayoutObject; use enums::Alignment; use widget::{Template, Widget}; +use enums::ParentType; /// This layout widget orders its children horizontal. -pub struct Row { - pub children: Vec<Rc<Widget>>, -} - -impl Default for Row { - fn default() -> Row { - Row { - children: vec![], - } - } -} +pub struct Row; impl Widget for Row { - fn template(&self) -> Template { + fn template() -> Template { print!("Row -> "); - if self.children.is_empty() { - Template::Empty - } else if self.children.len() == 1 { - Template::Single(self.children[0].clone()) - } else { - Template::Mutli(self.children.to_vec()) - } - } - - fn layout_object(&self) -> Box<LayoutObject> { - Box::new(FlexLayoutObject::new(Alignment::Horizontal)) + Template::default() + .as_parent_type(ParentType::Multi) + .with_layout_object(FlexLayoutObject::new(Alignment::Horizontal)) } -} +} \ No newline at end of file diff --git a/src/widget/scroll_viewer.rs b/src/widget/scroll_viewer.rs index fed9ae1..5d63c78 100644 --- a/src/widget/scroll_viewer.rs +++ b/src/widget/scroll_viewer.rs @@ -1,39 +1,16 @@ -use std::rc::Rc; +use enums::ParentType; +use layout_object::ScrollLayoutObject; +use widget::{Offset, Template, Widget}; -use widget::{Property, PropertyResult, Template, Widget}; -use layout_object::{LayoutObject, ScrollLayoutObject}; - - -/// This layout widget orders its children vertical. -pub struct ScrollViewer { - pub child: Option<Rc<Widget>>, - pub offset: Property<Offset>, -} - -impl Default for ScrollViewer { - fn default() -> ScrollViewer { - ScrollViewer { - child: None, - offset: Property::new(Offset::default()), - } - } -} +/// Use to scroll its content. +pub struct ScrollViewer; impl Widget for ScrollViewer { - fn template(&self) -> Template { + fn template() -> Template { print!("ScrollViewer -> "); - if let Some(child) = &self.child { - Template::Single(child.clone()) - } else { - Template::Empty - } - } - - fn properties(&self) -> Vec<PropertyResult> { - vec![self.offset.build()] - } - - fn layout_object(&self) -> Box<LayoutObject> { - Box::new(ScrollLayoutObject) + Template::default() + .as_parent_type(ParentType::Single) + .with_property(Offset::default()) + .with_layout_object(ScrollLayoutObject) } } diff --git a/src/widget/stack.rs b/src/widget/stack.rs index 977b4b9..f7a9fe6 100644 --- a/src/widget/stack.rs +++ b/src/widget/stack.rs @@ -1,27 +1,13 @@ -use std::rc::Rc; - use widget::{Template, Widget}; +use enums::ParentType; /// Use this layout widget to overlay its children (on z axis). -pub struct Stack { - pub children: Vec<Rc<Widget>>, -} - -impl Default for Stack { - fn default() -> Stack { - Stack { children: vec![] } - } -} +pub struct Stack; impl Widget for Stack { - fn template(&self) -> Template { + fn template() -> Template { print!("Stack -> "); - if self.children.is_empty() { - Template::Empty - } else if self.children.len() == 1 { - Template::Single(self.children[0].clone()) - } else { - Template::Mutli(self.children.to_vec()) - } + Template::default() + .as_parent_type(ParentType::Multi) } -} +} \ No newline at end of file diff --git a/src/widget/text_block.rs b/src/widget/text_block.rs index 32df735..c5b1711 100644 --- a/src/widget/text_block.rs +++ b/src/widget/text_block.rs @@ -1,17 +1,18 @@ use layout_object::TextSizeLayoutObject; use render_object::TextRenderObject; use theme::Selector; -use widget::{Label, Widget}; -use application::Template; +use widget::{Label, Template, Widget}; +/// The `TextBlock` widget is used to draw text. pub struct TextBlock; impl Widget for TextBlock { fn template() -> Template { + print!("TextBlock -> "); Template::default() .with_property(Label::from("TextBlock")) .with_property(Selector::new().with("textblock")) .with_layout_object(TextSizeLayoutObject) .with_render_object(TextRenderObject) } -} \ No newline at end of file +} diff --git a/src/widget/text_box.rs b/src/widget/text_box.rs index 6b0a36d..5efb16d 100644 --- a/src/widget/text_box.rs +++ b/src/widget/text_box.rs @@ -1,13 +1,10 @@ +use event::{Focused, Key}; use std::cell::{Cell, RefCell}; use std::rc::Rc; - -use super::Property; -use event::{EventHandler, KeyEventHandler}; -use event::{Focused, Key}; use theme::Selector; use widget::{ - add_selector_to_widget, remove_selector_from_widget, Container, Label, PropertyResult, - ScrollViewer, Stack, State, Template, TextBlock, Widget, WidgetContainer, + add_selector_to_widget, remove_selector_from_widget, Container, Label, ScrollViewer, Stack, + State, Template, TextBlock, Widget, WidgetContainer, }; /// The `TextBoxState`handles the text processing of the `TextBox` widget. @@ -18,6 +15,12 @@ pub struct TextBoxState { updated: Cell<bool>, } +impl Into<Rc<State>> for TextBoxState { + fn into(self) -> Rc<State> { + Rc::new(self) + } +} + impl TextBoxState { fn update_text(&self, key: Key) -> bool { if !self.focused.get() { @@ -27,13 +30,13 @@ impl TextBoxState { match <Option<u8>>::from(key) { Some(byte) => { (*self.text.borrow_mut()).push(byte as char); - }, + } None => match key { Key::Backspace => { (*self.text.borrow_mut()).pop(); } _ => {} - } + }, } self.updated.set(true); @@ -70,82 +73,25 @@ impl State for TextBoxState { } } -/// A single line text input widget. -pub struct TextBox { - pub label: Property<Label>, - pub selector: Property<Selector>, - pub event_handlers: Vec<Rc<EventHandler>>, - pub state: Rc<TextBoxState>, -} - -impl Default for TextBox { - fn default() -> TextBox { - TextBox { - label: Property::new(Label(String::from("TextBox"))), - selector: Property::new(Selector::new(Some(String::from("textbox")))), - event_handlers: vec![], - state: Rc::new(TextBoxState::default()), - } - } -} +/// The `TextBoxState`handles the text processing of the `TextBox` widget. +pub struct TextBox; impl Widget for TextBox { - fn template(&self) -> Template { + fn template() -> Template { print!("TextBox -> "); - - // Initial set state text to textbox label. - if let Some(text) = &self.label.property { - *self.state.text.borrow_mut() = text.0.clone(); - } - - Template::Single(Rc::new(Container { - selector: self.selector.clone(), - child: Some(Rc::new(Stack { - children: vec![ - Rc::new(ScrollViewer { - child: Some(Rc::new(TextBlock { - label: self.label.clone(), - selector: self.selector.clone(), - })), - ..Default::default() - }), - // todo: Cursor as rectangle -> predifined bounds - // scroll handling by cursor - // Rc::new(TextBlock { - // ..Default::default() - // }), - ], - })), - ..Default::default() - })) - } - - fn properties(&self) -> Vec<PropertyResult> { - vec![ - self.label.build(), - self.selector.build(), - Property::new(Focused(false)).build(), - ] - } - - fn state(&self) -> Option<Rc<State>> { - Some(self.state.clone()) - } - - fn event_handlers(&self) -> Vec<Rc<EventHandler>> { - let state = self.state.clone(); - - let mut event_handlers: Vec<Rc<EventHandler>> = vec![Rc::new(KeyEventHandler { - on_key_down: Some(Rc::new( - move |key: Key, _widget: &mut WidgetContainer| -> bool { state.update_text(key) }, - )), - ..Default::default() - })]; - - for handler in &self.event_handlers { - event_handlers.push(handler.clone()); - } - - event_handlers + Template::default() + .with_property(Label::from("TextBox")) + .with_property(Selector::new().with("textbox")) + .with_property(Focused(false)) + .with_child( + Container::template().with_child( + Stack::template() + .with_child(ScrollViewer::template().with_child(TextBlock::template())), + ), + ) + .with_state(TextBoxState::default()) + // .with_event_handler(KeyEventHandler::default().on_key_down(Rc::new( + // move |key: Key, _widget: &mut WidgetContainer| -> bool { state.update_text(key) }, + // ))) } } -- GitLab