diff --git a/examples/shrink_grow_layout.rs b/examples/shrink_grow_layout.rs index 6fc83df873a1701136b4ce7643aa296b36a1743e..c25cbcadb4149682f1aab906368342ad5c990b23 100644 --- a/examples/shrink_grow_layout.rs +++ b/examples/shrink_grow_layout.rs @@ -1,3 +1,9 @@ +//! This example demonstrates how to use a [on_layout](kayak_core::WidgetProps::get_on_layout) +//! event in widgets. +//! +//! The problem here is strictly contrived for example purposes. +//! We use grow/shrink buttons to set the value of a `width` bound to an [crate::Background] element's width +//! On change of layout we print current width of that element and update the text of Width label. use bevy::{ prelude::{App as BevyApp, AssetServer, Commands, Res, ResMut}, window::WindowDescriptor, @@ -9,66 +15,79 @@ use kayak_core::{ }; use kayak_core::{Color, EventType, OnEvent}; use kayak_render_macros::use_state; -use kayak_ui::core::{render, rsx, widget, Index}; +use kayak_ui::{core::{render, rsx, widget, Index}, widgets::Background}; use kayak_ui::widgets::{App, Text, Window}; use kayak_ui::{ bevy::{BevyContext, BevyKayakUIPlugin, FontMapping, UICameraBundle}, widgets::Button, }; +/// This widget provides a theme to its children #[widget] fn GrowShrink() { - let (width, set_width, _) = use_state!(150.0); + // This is width of background element we update via buttons + let (background_width, set_width, _) = use_state!(150.0); + let panel_style = Style { + layout_type: StyleProp::Value(LayoutType::Row), + background_color: StyleProp::Value(Color::new(0.33, 0.33, 0.33,0.2)), + width: StyleProp::Value(Units::Auto), + height: StyleProp::Value(Units::Pixels(50.0)), + ..Default::default() + }; + + // Grow/Shrink button styles let button_styles = Style { width: StyleProp::Value(Units::Pixels(100.0)), - height: StyleProp::Value(Units::Pixels(24.0)), - layout_type: StyleProp::Value(LayoutType::Row), + height: StyleProp::Value(Units::Pixels(24.0)), background_color: StyleProp::Value(Color::new(0.33, 0.33, 0.33, 1.0)), ..Default::default() }; + // The background style of element growing/shrink let fill = Style { - width: StyleProp::Value(Units::Pixels(width)), + width: StyleProp::Value(Units::Pixels(background_width)), height: StyleProp::Value(Units::Pixels(28.0)), layout_type: StyleProp::Value(LayoutType::Column), background_color: StyleProp::Value(Color::new(1.0, 0.0, 0.0, 1.0)), ..Default::default() }; + // Cloned function for use in closures let grow_fn = set_width.clone(); let shrink_fn = set_width.clone(); let grow = OnEvent::new(move |_, event| match event.event_type { - EventType::Click(..) => grow_fn(width + 10.0), + EventType::Click(..) => grow_fn(background_width + 10.0), _ => {} }); let shrink = OnEvent::new(move |_, event| match event.event_type { - EventType::Click(..) => shrink_fn(width - 10.0), + EventType::Click(..) => shrink_fn(background_width - 10.0), _ => {} }); + // layout width will be used by width label which we update `on_layout` let (layout_width, set_layout_width, _) = use_state!(0.0); let update_text = OnLayout::new(move |_, layout_event| { - layout_event.layout.width *= 2.0; - println!("Width = {}", layout_event.layout.width); + println!("Layout changed! New width = {}", layout_event.layout.width); set_layout_width(layout_event.layout.width); }); rsx! { <> - <Window position={(100.0, 100.0)} size={(400.0, 400.0)} title={"Grow Example".to_string()}> + <Window position={(100.0, 100.0)} size={(400.0, 400.0)} title={"Grow/Shrink Example".to_string()}> <Text size={25.0} content={format!("Width: {:?}", layout_width).to_string()} /> - <Button styles={Some(button_styles)} on_event={Some(grow)}> - <Text size={20.0} content={"Grow".to_string()}/> - </Button> - <Button styles={Some(button_styles)} on_event={Some(shrink)}> - <Text size={20.0} content={"Shrink".to_string()}/> - </Button> - <Button styles={Some(fill)} on_layout={Some(update_text)}> - </Button> + <Background styles={Some(panel_style)}> + <Button styles={Some(button_styles)} on_event={Some(grow)}> + <Text size={20.0} content={"Grow".to_string()}/> + </Button> + <Button styles={Some(button_styles)} on_event={Some(shrink)}> + <Text size={20.0} content={"Shrink".to_string()}/> + </Button> + </Background> + <Background styles={Some(fill)} on_layout={Some(update_text)} /> </Window> </> } diff --git a/kayak_core/src/layout.rs b/kayak_core/src/layout.rs index 6f4dd4081b65beb1242a958297fa87bd6823a3a2..841ceee167fbdb3a0bf895924b4373a856d21f7f 100644 --- a/kayak_core/src/layout.rs +++ b/kayak_core/src/layout.rs @@ -1,8 +1,9 @@ use crate::layout_cache::Rect; use crate::Index; -use morphorm::GeometryChanged; +pub use morphorm::GeometryChanged; /// A layout event context sent to widgets +/// Similar and interchangeable with [Rect] #[derive(Debug, Default, Clone, Copy, PartialEq)] pub struct Layout { /// width of the component @@ -14,7 +15,7 @@ pub struct Layout { /// y-coordinates of the component pub y: f32, /// z-coordinates of the component - pub z_index: f32, + pub z: f32, } impl Layout { @@ -31,7 +32,7 @@ impl From<Layout> for Rect { posy: layout.y, width: layout.width, height: layout.height, - z_index: layout.z_index, + z_index: layout.z, } } } @@ -43,17 +44,20 @@ impl From<Rect> for Layout { height: rect.height, x: rect.posx, y: rect.posy, - z_index: rect.z_index, + z: rect.z_index, } } } /// -/// Struct used for +/// Struct used for [crate::OnLayout] as layout event data. /// pub struct LayoutEvent { + /// Layout of target component pub layout: Layout, + /// Flags noting one of dimensions has changed. pub flags: GeometryChanged, + /// the node id of the element receiving the layout event pub target: Index, } diff --git a/kayak_core/src/layout_dispatcher.rs b/kayak_core/src/layout_dispatcher.rs index 2091364842a0b1223a3cb85ebbb769d2d0811e83..fb0aaec7545843ad094cad94677606b1221e55eb 100644 --- a/kayak_core/src/layout_dispatcher.rs +++ b/kayak_core/src/layout_dispatcher.rs @@ -1,5 +1,5 @@ use indexmap::IndexSet; -use morphorm::{Cache, GeometryChanged}; +use morphorm::{Cache}; use crate::{Index, KayakContext, KayakContextRef, LayoutEvent}; diff --git a/kayak_core/src/on_layout.rs b/kayak_core/src/on_layout.rs index 73a201c96469cf96bf0c8f008de68a2e70643bb7..2b4d814079efe37c7bebf52c35f249e39979dd89 100644 --- a/kayak_core/src/on_layout.rs +++ b/kayak_core/src/on_layout.rs @@ -29,8 +29,8 @@ impl OnLayout { /// /// Returns true if the handler was successfully invoked. pub fn try_call(&self, context: &mut KayakContextRef, event: &mut LayoutEvent) -> bool { - if let Ok(mut on_event) = self.0.write() { - on_event(context, event); + if let Ok(mut on_layout) = self.0.write() { + on_layout(context, event); true } else { false diff --git a/src/widgets/background.rs b/src/widgets/background.rs index 56f3b79cf11cc164264effa880065aeb79b3cd96..b2b917fd2b5b0dbd15135bd8ad0754d594639b3a 100644 --- a/src/widgets/background.rs +++ b/src/widgets/background.rs @@ -1,3 +1,4 @@ +use kayak_core::OnLayout; use crate::core::{ render_command::RenderCommand, rsx, @@ -14,6 +15,8 @@ pub struct BackgroundProps { pub children: Option<Children>, #[prop_field(OnEvent)] pub on_event: Option<OnEvent>, + #[prop_field(OnLayout)] + pub on_layout: Option<OnLayout>, #[prop_field(Focusable)] pub focusable: Option<bool>, } @@ -30,6 +33,7 @@ pub struct BackgroundProps { /// | `children` | ✅ | /// | `styles` | ✅ | /// | `on_event` | ✅ | +/// | `on_layout` | ✅ | /// | `focusable` | ✅ | /// pub fn Background(props: BackgroundProps) { diff --git a/src/widgets/button.rs b/src/widgets/button.rs index d2373676d5364635670fbc2a80f1cc2fae8ec32d..0c89c7b4d996cabbfee426dce2aa42e1204f62ca 100644 --- a/src/widgets/button.rs +++ b/src/widgets/button.rs @@ -59,6 +59,7 @@ impl WidgetProps for ButtonProps { /// | `children` | ✅ | /// | `styles` | ✅ | /// | `on_event` | ✅ | +/// | `on_layout` | ✅ | /// | `focusable` | ✅ | /// pub fn Button(props: ButtonProps) { diff --git a/src/widgets/text_box.rs b/src/widgets/text_box.rs index 321b1860ad0c64b366fc4a4b6be9fa4231041022..ceee81f3535a27f37588a36f1afa9722e2131f23 100644 --- a/src/widgets/text_box.rs +++ b/src/widgets/text_box.rs @@ -97,6 +97,7 @@ pub struct Focus(pub bool); /// | `children` | ✅ | /// | `styles` | ✅ | /// | `on_event` | ✅ | +/// | `on_layout` | ✅ | /// | `focusable` | ✅ | /// pub fn TextBox(props: TextBoxProps) {