diff --git a/examples/tic-tac-toe.rs b/examples/tic-tac-toe.rs index d560279..69b1425 100644 --- a/examples/tic-tac-toe.rs +++ b/examples/tic-tac-toe.rs @@ -17,7 +17,7 @@ fn main() -> gooey::Result { AppState::Winner(winner) => game_end(*winner, &app).make_widget(), } })) - .switcher() + .into_switcher() .contain() .width(Lp::inches(2)..Lp::inches(6)) .height(Lp::inches(2)..Lp::inches(6)) diff --git a/src/context.rs b/src/context.rs index 20a16d0..771c694 100644 --- a/src/context.rs +++ b/src/context.rs @@ -623,15 +623,6 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'window, ' self.gfx.set_font_weight(self.widget.get(&FontWeight)); } - // /// Applies the current style settings for font family, text size, font - // /// style, and font weight. - // pub fn apply_current_font_settings_to<'a>(&self, attrs: Attrs<'a>) -> Attrs<'a> { - // attrs.set_available_font_family(&self.widget.get(&FontFamily)); - // attrs.set_font_size(self.widget.get(&TextSize)); - // attrs.set_font_style(self.widget.get(&FontStyle)); - // attrs.set_font_weight(self.widget.get(&FontWeight)); - // } - /// Invokes [`Widget::redraw()`](crate::widget::Widget::redraw) on this /// context's widget. /// @@ -1270,6 +1261,14 @@ pub trait ManageWidget { fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed; } +impl ManageWidget for WidgetId { + type Managed = Option; + + fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed { + context.current_node.tree.widget(*self) + } +} + impl ManageWidget for WidgetInstance { type Managed = Option; diff --git a/src/graphics.rs b/src/graphics.rs index 4dc73f3..d8a31de 100644 --- a/src/graphics.rs +++ b/src/graphics.rs @@ -19,6 +19,7 @@ use crate::styles::FontFamilyList; pub struct Graphics<'clip, 'gfx, 'pass> { renderer: RenderContext<'clip, 'gfx, 'pass>, region: Rect, + current_font_family: &'clip mut Option, } enum RenderContext<'clip, 'gfx, 'pass> { @@ -29,10 +30,14 @@ enum RenderContext<'clip, 'gfx, 'pass> { impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> { /// Returns a new graphics context for the given [`Renderer`]. #[must_use] - pub fn new(renderer: Renderer<'gfx, 'pass>) -> Self { + pub fn new( + renderer: Renderer<'gfx, 'pass>, + current_font_family: &'clip mut Option, + ) -> Self { Self { region: renderer.clip_rect().into_signed(), renderer: RenderContext::Renderer(renderer), + current_font_family, } } @@ -86,8 +91,11 @@ impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> { /// Sets the font family to the first family in `list`. pub fn set_available_font_family(&mut self, list: &FontFamilyList) { - if let Some(family) = self.find_available_font_family(list) { - self.set_font_family(family); + if self.current_font_family.as_ref() != Some(list) { + *self.current_font_family = Some(list.clone()); + if let Some(family) = self.find_available_font_family(list) { + self.set_font_family(family); + } } } @@ -123,6 +131,7 @@ impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> { Graphics { renderer: RenderContext::Clipped(self.renderer.clipped_to(new_clip)), region, + current_font_family: &mut *self.current_font_family, } } diff --git a/src/value.rs b/src/value.rs index cf76caa..9d252e0 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,7 +1,7 @@ //! Types for storing and interacting with values in Widgets. use std::collections::HashMap; -use std::fmt::{Debug, Display}; +use std::fmt::{self, Debug, Display}; use std::future::Future; use std::hash::{BuildHasher, Hash}; use std::ops::{Deref, DerefMut, Not}; @@ -25,7 +25,6 @@ use crate::widgets::{Radio, Select, Space, Switcher}; /// An instance of a value that provides APIs to observe and react to its /// contents. -#[derive(Debug)] pub struct Dynamic(Arc>); impl Dynamic { @@ -598,18 +597,34 @@ impl Dynamic { } } +impl Debug for Dynamic +where + T: Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0.state() { + Ok(state) => f + .debug_struct("Dynamic") + .field("value", &state.wrapped.value) + .field("generation", &state.wrapped.generation) + .finish(), + Err(_) => f.debug_tuple("Dynamic").field(&"").finish(), + } + } +} + impl Dynamic { /// Returns a new [`Switcher`] widget whose contents is the value of this /// dynamic. #[must_use] - pub fn switcher(self) -> Switcher { + pub fn into_switcher(self) -> Switcher { Switcher::new(self) } } impl MakeWidgetWithId for Dynamic { fn make_with_id(self, id: crate::widget::WidgetTag) -> WidgetInstance { - self.switcher().make_with_id(id) + self.into_switcher().make_with_id(id) } } @@ -634,6 +649,8 @@ impl context::sealed::Trackable for Dynamic { } } +impl Eq for Dynamic {} + impl PartialEq for Dynamic { fn eq(&self, other: &Self) -> bool { Arc::ptr_eq(&self.0, &other.0) @@ -833,7 +850,7 @@ struct DeadlockError; impl std::error::Error for DeadlockError {} impl Display for DeadlockError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("a deadlock was detected") } } @@ -869,7 +886,7 @@ impl Debug for State where T: Debug, { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("State") .field("wrapped", &self.wrapped) .field("readers", &self.readers) @@ -988,6 +1005,17 @@ pub struct DynamicGuard<'a, T> { accessed_mut: bool, } +impl DynamicGuard<'_, T> { + /// Returns the generation of the value at the time of locking the dynamic. + /// + /// Even if this guard accesses the data through [`DerefMut`], this value + /// will remain unchanged while the guard is held. + #[must_use] + pub fn generation(&self) -> Generation { + self.guard.wrapped.generation + } +} + impl<'a, T> Deref for DynamicGuard<'a, T> { type Target = T; @@ -1896,6 +1924,19 @@ impl Validation { } } +impl IntoDynamic for Dynamic> +where + T: Send + 'static, + E: Display + Send + 'static, +{ + fn into_dynamic(self) -> Dynamic { + self.map_each(|result| match result { + Ok(_) => Validation::Valid, + Err(err) => Validation::Invalid(err.to_string()), + }) + } +} + /// A grouping of validations that can be checked simultaneously. #[derive(Debug, Default, Clone)] pub struct Validations { @@ -2135,13 +2176,17 @@ impl WhenValidation<'_> { /// The validation is linked with `self` such that checking `self`'s /// validation status will include this validation. #[must_use] - pub fn validate_result(&self, result: impl IntoDynamic>) -> Dynamic + pub fn validate_result( + &self, + result: impl IntoDynamic>, + ) -> Dynamic where + T: Send + 'static, E: Display + Send + 'static, { let result = result.into_dynamic(); let error_message = result.map_each(move |value| match value { - Ok(()) => None, + Ok(_) => None, Err(err) => Some(err.to_string()), }); diff --git a/src/widgets/grid.rs b/src/widgets/grid.rs index 4b147a8..74eb256 100644 --- a/src/widgets/grid.rs +++ b/src/widgets/grid.rs @@ -800,6 +800,15 @@ impl GridWidgets { } } +impl From> for GridWidgets +where + T: Into>, +{ + fn from(value: Vec) -> Self { + Self(value.into_iter().map(T::into).collect()) + } +} + impl From for GridWidgets where T: Into>, @@ -851,6 +860,18 @@ where } } +impl From<[T; N]> for GridSection +where + T: MakeWidget, +{ + fn from(values: [T; N]) -> Self { + let mut widgets = values.into_iter(); + Self(array::from_fn(|_| { + widgets.next().assert("length checked").make_widget() + })) + } +} + impl Deref for GridSection { type Target = [WidgetInstance; N]; diff --git a/src/widgets/input.rs b/src/widgets/input.rs index c4f21fe..7a750b1 100644 --- a/src/widgets/input.rs +++ b/src/widgets/input.rs @@ -1099,7 +1099,7 @@ where .translate_by(padding), ); } - } else if window_focused { + } else if window_focused && context.enabled() { let (location, _) = Self::point_from_cursor(cache.measured, cache.cursor, cache.bytes); if cursor_state.visible { diff --git a/src/window.rs b/src/window.rs index 192fe8f..5679547 100644 --- a/src/window.rs +++ b/src/window.rs @@ -637,6 +637,7 @@ where let root_mode = self.constrain_window_resizing(resizable, &mut window, graphics); let graphics = self.contents.new_frame(graphics); + let mut current_font_family = None; let mut context = GraphicsContext { widget: WidgetContext::new( self.root.clone(), @@ -646,7 +647,7 @@ where self.theme_mode.get(), &mut self.cursor, ), - gfx: Exclusive::Owned(Graphics::new(graphics)), + gfx: Exclusive::Owned(Graphics::new(graphics, &mut current_font_family)), }; self.theme_mode.redraw_when_changed(&context); let mut layout_context = LayoutContext::new(&mut context);