From 83cb88925ab43eb1735d5492420f59b825feafec Mon Sep 17 00:00:00 2001 From: Roland Fredenhagen Date: Sun, 12 Nov 2023 00:38:43 +0100 Subject: [PATCH] Make button take MakeWidget --- examples/gameui.rs | 2 +- src/context.rs | 4 +-- src/styles.rs | 2 +- src/widget.rs | 6 +++++ src/widgets/button.rs | 62 +++++++++++++++++-------------------------- src/widgets/label.rs | 16 +++++++++-- 6 files changed, 49 insertions(+), 43 deletions(-) diff --git a/examples/gameui.rs b/examples/gameui.rs index 9c93ee2..4a6cb89 100644 --- a/examples/gameui.rs +++ b/examples/gameui.rs @@ -21,7 +21,7 @@ fn main() -> gooey::Result { .and(Input::new(chat_message.clone()).on_key(move |input| { match (input.state, input.logical_key) { (ElementState::Pressed, Key::Enter) => { - let new_message = chat_message.map_mut(|text| std::mem::take(text)); + let new_message = chat_message.map_mut(std::mem::take); chat_log.map_mut(|chat_log| { chat_log.push_str(&new_message); chat_log.push('\n'); diff --git a/src/context.rs b/src/context.rs index 594eb0b..a55c566 100644 --- a/src/context.rs +++ b/src/context.rs @@ -196,7 +196,7 @@ impl<'context, 'window> EventContext<'context, 'window> { } true } - Err(_) => false, + Err(()) => false, }; if new { if let Some(active) = self.pending_state.active.clone() { @@ -250,7 +250,7 @@ impl<'context, 'window> EventContext<'context, 'window> { } true } - Err(_) => false, + Err(()) => false, }; if new { if let Some(focus) = self.pending_state.focus.clone() { diff --git a/src/styles.rs b/src/styles.rs index 346a0db..7abad4b 100644 --- a/src/styles.rs +++ b/src/styles.rs @@ -58,7 +58,7 @@ impl Styles { /// Adds a [`Component`] for the name provided and returns self. #[must_use] - pub fn with(mut self, name: &impl NamedComponent, component: impl Into) -> Self { + pub fn with(mut self, name: &impl NamedComponent, component: impl IntoComponentValue) -> Self { self.insert(name, component); self } diff --git a/src/widget.rs b/src/widget.rs index 9cf564c..cfb68b3 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -572,6 +572,12 @@ pub trait MakeWidget: Sized { fn horizontal_scroll(self) -> Scroll { Scroll::horizontal(self) } + + /// Creates a [`WidgetRef`] for use as child widget. + #[must_use] + fn widget_ref(self) -> WidgetRef { + WidgetRef::new(self) + } } /// A type that can create a [`WidgetInstance`] with a preallocated diff --git a/src/widgets/button.rs b/src/widgets/button.rs index 1893f7e..d55a961 100644 --- a/src/widgets/button.rs +++ b/src/widgets/button.rs @@ -5,26 +5,27 @@ use std::time::Duration; use kludgine::app::winit::event::{DeviceId, ElementState, KeyEvent, MouseButton}; use kludgine::figures::units::{Px, UPx}; -use kludgine::figures::{IntoUnsigned, Point, Rect, ScreenScale, Size}; -use kludgine::text::Text; +use kludgine::figures::{IntoSigned, IntoUnsigned, Point, Rect, ScreenScale, Size}; use kludgine::Color; use crate::animation::{AnimationHandle, AnimationTarget, Spawn}; -use crate::context::{EventContext, GraphicsContext, LayoutContext, WidgetContext}; +use crate::context::{AsEventContext, EventContext, GraphicsContext, LayoutContext, WidgetContext}; use crate::names::Name; use crate::styles::components::{ AutoFocusableControls, Easing, IntrinsicPadding, SurfaceColor, TextColor, }; -use crate::styles::{ColorExt, ComponentDefinition, ComponentGroup, ComponentName, NamedComponent}; +use crate::styles::{ + ColorExt, ComponentDefinition, ComponentGroup, ComponentName, NamedComponent, Styles, +}; use crate::utils::ModifiersExt; use crate::value::{Dynamic, IntoValue, Value}; -use crate::widget::{Callback, EventHandling, Widget, HANDLED, IGNORED}; +use crate::widget::{Callback, EventHandling, MakeWidget, Widget, WidgetRef, HANDLED, IGNORED}; /// A clickable button. #[derive(Debug)] pub struct Button { /// The label to display on the button. - pub label: Value, + pub content: WidgetRef, /// The callback that is invoked when the button is clicked. pub on_click: Option>, /// The enabled state of the button. @@ -38,9 +39,9 @@ pub struct Button { impl Button { /// Returns a new button with the provided label. - pub fn new(label: impl IntoValue) -> Self { + pub fn new(content: impl MakeWidget) -> Self { Self { - label: label.into_value(), + content: content.widget_ref(), on_click: None, enabled: Value::Constant(true), currently_enabled: true, @@ -143,25 +144,27 @@ impl Button { } _ => { self.background_color = Some(Dynamic::new(background_color)); - self.text_color = Some(Dynamic::new(text_color)); + let text_color = Dynamic::new(text_color); + self.text_color = Some(text_color.clone()); + context.attach_styles(Styles::new().with(&TextColor, text_color)); } } } - fn current_colors(&mut self, context: &WidgetContext<'_, '_>) -> (Color, Color) { + fn current_background(&mut self, context: &WidgetContext<'_, '_>) -> Color { if self.background_color.is_none() { self.update_colors(context, false); } let background_color = self.background_color.as_ref().expect("always initialized"); - let text_color = self.text_color.as_ref().expect("always initialized"); // TODO combine these into a single option context.redraw_when_changed(background_color); - (background_color.get(), text_color.get()) + background_color.get() } } impl Widget for Button { fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { + #![allow(clippy::similar_names)] let enabled = self.enabled.get(); // TODO This seems ugly. It needs context, so it can't be moved into the // dynamic system. @@ -170,28 +173,17 @@ impl Widget for Button { self.currently_enabled = enabled; } - let size = context.gfx.region().size; - let center = Point::from(size) / 2; - self.label.redraw_when_changed(context); self.enabled.redraw_when_changed(context); - let (background_color, text_color) = self.current_colors(context); + let background_color = self.current_background(context); context.gfx.fill(background_color); if context.focused() { context.draw_focus_ring(); } - self.label.map(|label| { - context.gfx.draw_text( - Text::new(label, text_color) - .origin(kludgine::text::TextOrigin::Center) - .wrap_at(size.width), - center, - None, - None, - ); - }); + let content = self.content.mounted(&mut context.as_event_context()); + context.for_other(&content).redraw(); } fn hit_test(&mut self, _location: Point, _context: &mut EventContext<'_, '_>) -> bool { @@ -266,17 +258,13 @@ impl Widget for Button { .query_style(&IntrinsicPadding) .into_px(context.gfx.scale()) .into_unsigned(); - let width = available_space.width.max().try_into().unwrap_or(Px::MAX); - self.label.map(|label| { - let measured = context - .gfx - .measure_text::(Text::from(label).wrap_at(width)); - - let mut size = measured.size.into_unsigned(); - size.width += padding * 2; - size.height = size.height.max(measured.line_height.into_unsigned()) + padding * 2; - size - }) + let mounted = self.content.mounted(&mut context.as_event_context()); + let size = context.for_other(&mounted).layout(available_space); + context.set_child_layout( + &mounted, + Rect::new(Point::new(padding, padding), size).into_signed(), + ); + size + padding * 2 } fn keyboard_input( diff --git a/src/widgets/label.rs b/src/widgets/label.rs index e3c77d5..f27365d 100644 --- a/src/widgets/label.rs +++ b/src/widgets/label.rs @@ -9,8 +9,8 @@ use kludgine::Color; use crate::context::{GraphicsContext, LayoutContext, WidgetContext}; use crate::styles::components::{IntrinsicPadding, TextColor}; use crate::styles::{ComponentDefinition, ComponentGroup, ComponentName, NamedComponent}; -use crate::value::{IntoValue, Value}; -use crate::widget::Widget; +use crate::value::{Dynamic, IntoValue, Value}; +use crate::widget::{MakeWidget, Widget, WidgetInstance}; use crate::{ConstraintLimit, Name}; /// A read-only text widget. @@ -108,3 +108,15 @@ impl ComponentDefinition for LabelBackground { Color::CLEAR_WHITE } } + +macro_rules! impl_make_widget { + ($($type:ty),*) => { + $(impl MakeWidget for $type { + fn make_widget(self) -> WidgetInstance { + Label::new(self).make_widget() + } + })* + }; +} + +impl_make_widget!(&'_ str, String, Value, Dynamic);