diff --git a/src/styles/components.rs b/src/styles/components.rs index 8decc34..d8dab6d 100644 --- a/src/styles/components.rs +++ b/src/styles/components.rs @@ -145,7 +145,15 @@ define_components! { /// The [`Color`] to use when rendering text in a more subdued tone. TextColorVariant(Color, "text_color_variant", .surface.on_color_variant) /// A [`Color`] to be used as a highlight color. - HighlightColor(Color,"highlight_color",.primary.color.with_alpha(128)) + HighlightColor(Color,"highlight_color", .primary.color.with_alpha(128)) + /// The primary color from the current theme. + PrimaryColor(Color, "primary_color", .primary.color) + /// The secondary color from the current theme. + SecondaryColor(Color, "secondary_color", .secondary.color) + /// The tertiary color from the current theme. + TertiaryColor(Color, "tertiary_color", .tertiary.color) + /// The error color from the current theme. + ErrorColor(Color, "error_color", .error.color) /// Intrinsic, uniform padding for a widget. /// /// This component is opt-in and does not automatically work for all widgets. diff --git a/src/widget.rs b/src/widget.rs index fdb45cb..5e9dbb4 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -2078,6 +2078,12 @@ impl WidgetId { static COUNTER: AtomicU64 = AtomicU64::new(0); Self(COUNTER.fetch_add(1, atomic::Ordering::Acquire)) } + + /// Finds this widget mounted in this window, if present. + #[must_use] + pub fn find_in(self, context: &WidgetContext<'_, '_>) -> Option { + context.widget().tree.widget(self) + } } /// A [`WidgetId`] that has not been assigned to a [`WidgetInstance`]. diff --git a/src/widgets.rs b/src/widgets.rs index d0d1947..09c6057 100644 --- a/src/widgets.rs +++ b/src/widgets.rs @@ -16,7 +16,7 @@ pub mod label; pub mod layers; mod mode_switch; pub mod progress; -mod radio; +pub mod radio; mod resize; pub mod scroll; pub mod select; @@ -27,7 +27,7 @@ mod style; mod switcher; mod themed; mod tilemap; -mod validated; +pub mod validated; pub mod wrap; pub use align::Align; diff --git a/src/widgets/checkbox.rs b/src/widgets/checkbox.rs index a2b7981..a66d0d7 100644 --- a/src/widgets/checkbox.rs +++ b/src/widgets/checkbox.rs @@ -9,6 +9,7 @@ use kludgine::shapes::{PathBuilder, Shape, StrokeOptions}; use crate::context::{GraphicsContext, LayoutContext}; use crate::styles::components::{LineHeight, OutlineColor, TextColor, WidgetAccentColor}; +use crate::styles::Dimension; use crate::value::{Dynamic, DynamicReader, IntoDynamic, IntoValue, Value}; use crate::widget::{MakeWidget, MakeWidgetWithId, Widget, WidgetInstance}; use crate::widgets::button::ButtonKind; @@ -241,7 +242,7 @@ impl Widget for CheckboxOrnament { _available_space: Size, context: &mut LayoutContext<'_, '_, '_, '_, '_>, ) -> Size { - let checkbox_size = context.get(&LineHeight).into_upx(context.gfx.scale()); // TODO create a component? + let checkbox_size = context.get(&CheckboxSize).into_upx(context.gfx.scale()); Size::squared(checkbox_size) } } @@ -255,3 +256,10 @@ pub trait Checkable: IntoDynamic + Sized { } impl Checkable for T where T: IntoDynamic {} + +define_components! { + Checkbox { + /// The size to render a [`Checkbox`] indicator. + CheckboxSize(Dimension, "size", @LineHeight) + } +} diff --git a/src/widgets/container.rs b/src/widgets/container.rs index 0777a0d..b8c536d 100644 --- a/src/widgets/container.rs +++ b/src/widgets/container.rs @@ -308,8 +308,6 @@ impl Widget for Container { &mut self, context: &mut EventContext<'_, '_>, ) -> Option<(RootBehavior, WidgetInstance)> { - // TODO adjust for shadow, but we need to potentially merge multiple - // dimensions into one. let mut padding = self .padding .as_ref() diff --git a/src/widgets/grid.rs b/src/widgets/grid.rs index ef4dcdd..730c27e 100644 --- a/src/widgets/grid.rs +++ b/src/widgets/grid.rs @@ -659,11 +659,9 @@ mod tests { let mut offset = UPx::ZERO; for ((index, &child), &expected) in flex.iter().enumerate().zip(expected) { assert_eq!( + child.size, expected, + "child {index} measured to {}, expected {expected}", child.size, - expected, - "child {index} measured to {}, expected {}", - child.size, - expected // TODO Display for UPx ); assert_eq!(child.offset, offset); offset += child.size; diff --git a/src/widgets/layers.rs b/src/widgets/layers.rs index 91bc296..fd1a8f9 100644 --- a/src/widgets/layers.rs +++ b/src/widgets/layers.rs @@ -332,8 +332,8 @@ impl OverlayState { .overlays .get_by_index(0) .and_then(|overlay| overlay.relative_to) - .and_then(|relative_to| context.widget.for_other(&relative_to)) - .and_then(|c| c.widget().last_layout()) + .and_then(|relative_to| relative_to.find_in(context)) + .and_then(|w| w.last_layout()) { if !relative_to.contains(location) { return true; @@ -381,13 +381,8 @@ impl OverlayState { context: &mut LayoutContext<'_, '_, '_, '_, '_>, relative_to: WidgetId, ) -> Option> { - // TODO resolving a widgetid should probably be easier let direction = self.overlays[index].direction; - let relative_to = context - .widget - .for_other(&relative_to) - .map(|c| c.widget().clone())? - .last_layout()?; + let relative_to = relative_to.find_in(context)?.last_layout()?; let relative_to_unsigned = relative_to.into_unsigned(); let constraints = match direction { @@ -485,8 +480,8 @@ impl OverlayState { if checking_index != 0 { if let Some(relative_to) = self.overlays[0] .relative_to - .and_then(|relative_to| context.widget.for_other(&relative_to)) - .and_then(|c| c.widget().last_layout()) + .and_then(|relative_to| relative_to.find_in(context)) + .and_then(|w| w.last_layout()) { if relative_to.intersects(layout) { return true; diff --git a/src/widgets/radio.rs b/src/widgets/radio.rs index 120251f..9a49d3e 100644 --- a/src/widgets/radio.rs +++ b/src/widgets/radio.rs @@ -9,6 +9,7 @@ use kludgine::DrawableExt; use crate::context::{GraphicsContext, LayoutContext}; use crate::styles::components::{LineHeight, OutlineColor, WidgetAccentColor}; +use crate::styles::Dimension; use crate::value::{Dynamic, DynamicReader, IntoDynamic, IntoValue, Value}; use crate::widget::{MakeWidget, MakeWidgetWithId, Widget, WidgetInstance}; use crate::widgets::button::ButtonKind; @@ -121,7 +122,14 @@ where _available_space: Size, context: &mut LayoutContext<'_, '_, '_, '_, '_>, ) -> Size { - let radio_size = context.get(&LineHeight).into_upx(context.gfx.scale()); // TODO create a component? Same as checkbox + let radio_size = context.get(&RadioSize).into_upx(context.gfx.scale()); Size::squared(radio_size) } } + +define_components! { + Radio { + /// The size to render a [`Radio`] indicator. + RadioSize(Dimension, "size", @LineHeight) + } +} diff --git a/src/widgets/validated.rs b/src/widgets/validated.rs index 34dc976..1139fcc 100644 --- a/src/widgets/validated.rs +++ b/src/widgets/validated.rs @@ -1,9 +1,13 @@ +//! A widget that displays the result of validation. + use std::fmt::Debug; -use kludgine::figures::units::Lp; use kludgine::Color; -use crate::styles::components::{LineHeight, OutlineColor, TextColor, TextSize}; +use crate::styles::components::{ + ErrorColor, LineHeight, LineHeight2, OutlineColor, TextColor, TextSize, TextSize2, +}; +use crate::styles::Dimension; use crate::value::{Dynamic, IntoDynamic, IntoValue, MapEach, Validation, Value}; use crate::widget::{MakeWidget, MakeWidgetWithId, WidgetInstance, WidgetRef, WrapperWidget}; @@ -71,9 +75,8 @@ impl MakeWidgetWithId for Validated { .and( message .with(&TextColor, color) - // TODO these should be components - .with(&TextSize, Lp::points(9)) - .with(&LineHeight, Lp::points(13)) + .with_dynamic(&TextSize, ValidatedTextSize) + .with_dynamic(&LineHeight, ValidatedLineHeight) .align_left(), ) .into_rows(), @@ -101,8 +104,20 @@ impl WrapperWidget for ValidatedWidget { &mut self, context: &mut crate::context::GraphicsContext<'_, '_, '_, '_, '_>, ) { - // TODO move these to components. - self.error_color.set(context.theme().error.color); - self.default_color.set(context.theme().surface.outline); + self.error_color.set(context.get(&InvalidTextColor)); + self.default_color.set(context.get(&HintTextColor)); + } +} + +define_components! { + Validated { + /// The color of the hint text. + HintTextColor(Color, "hint_color", @OutlineColor) + /// The color of invalid text. + InvalidTextColor(Color, "invalid_color", @ErrorColor) + /// The text size for the validation message in a [`Validated`] widget. + ValidatedTextSize(Dimension, "text_size", @TextSize2) + /// The line hgiht for the validation message in a [`Validated`] widget. + ValidatedLineHeight(Dimension, "line_height", @LineHeight2) } } diff --git a/src/widgets/wrap.rs b/src/widgets/wrap.rs index b536ce8..ff05378 100644 --- a/src/widgets/wrap.rs +++ b/src/widgets/wrap.rs @@ -228,8 +228,6 @@ pub enum VerticalAlign { Middle, /// Align towards the bottom. - // TODO the default should be `Baseline`, but that requires a refactor for - // layout() to return something other than a Size. #[default] Bottom, } diff --git a/src/window.rs b/src/window.rs index c87929e..803eacc 100644 --- a/src/window.rs +++ b/src/window.rs @@ -661,9 +661,7 @@ where if let Some(theme) = &mut self.theme { if theme.has_updated() { self.current_theme = theme.get(); - // TODO invalidate everything, but right now we don't have much - // cached. Maybe widgets should be told the theme has changed in - // case some things like images have been cached. + self.root.invalidate(); } }