From 5506a24dae80a20ea5f82a246ad557784a4a0763 Mon Sep 17 00:00:00 2001 From: Jonathan Johnson Date: Wed, 15 Nov 2023 10:36:00 -0800 Subject: [PATCH] Added WidgetCacheKey --- src/context.rs | 56 +++++++++++++++++++++++++++++++++---------- src/tree.rs | 5 ++++ src/widget.rs | 4 ++++ src/widgets/button.rs | 18 +++++++------- 4 files changed, 62 insertions(+), 21 deletions(-) diff --git a/src/context.rs b/src/context.rs index b2c2037..94a301b 100644 --- a/src/context.rs +++ b/src/context.rs @@ -706,9 +706,8 @@ pub struct WidgetContext<'context, 'window> { window: &'context mut RunningWindow<'window>, theme: Cow<'context, ThemePair>, pending_state: PendingState<'context>, - theme_mode: ThemeMode, effective_styles: Styles, - enabled: bool, + cache: WidgetCacheKey, } impl<'context, 'window> WidgetContext<'context, 'window> { @@ -736,11 +735,14 @@ impl<'context, 'window> WidgetContext<'context, 'window> { focus_is_advancing: false, }), effective_styles: current_node.effective_styles(), - enabled, + cache: WidgetCacheKey { + theme_mode, + enabled, + invalidation: current_node.invalidation(), + }, current_node, redraw_status, theme: Cow::Borrowed(theme), - theme_mode, window, } } @@ -753,9 +755,8 @@ impl<'context, 'window> WidgetContext<'context, 'window> { window: &mut *self.window, theme: Cow::Borrowed(self.theme.as_ref()), pending_state: self.pending_state.borrowed(), - theme_mode: self.theme_mode, + cache: self.cache, effective_styles: self.effective_styles.clone(), - enabled: self.enabled, } } @@ -778,17 +779,20 @@ impl<'context, 'window> WidgetContext<'context, 'window> { let theme_mode = if let Some(mode) = theme_mode { mode.get_tracked(self) } else { - self.theme_mode + self.cache.theme_mode }; WidgetContext { effective_styles, - enabled: current_node.enabled(&self.handle()), + cache: WidgetCacheKey { + theme_mode, + enabled: current_node.enabled(&self.handle()), + invalidation: current_node.invalidation(), + }, current_node, redraw_status: self.redraw_status, window: &mut *self.window, theme, pending_state: self.pending_state.borrowed(), - theme_mode, } }) } @@ -796,7 +800,7 @@ impl<'context, 'window> WidgetContext<'context, 'window> { /// Returns true if this widget is enabled. #[must_use] pub const fn enabled(&self) -> bool { - self.enabled + self.cache.enabled } pub(crate) fn parent(&self) -> Option { @@ -1004,7 +1008,7 @@ impl<'context, 'window> WidgetContext<'context, 'window> { /// Returns the current theme in either light or dark mode. #[must_use] pub fn theme(&self) -> &Theme { - match self.theme_mode { + match self.cache.theme_mode { ThemeMode::Light => &self.theme.light, ThemeMode::Dark => &self.theme.dark, } @@ -1013,11 +1017,18 @@ impl<'context, 'window> WidgetContext<'context, 'window> { /// Returns the opposite theme of [`Self::theme()`]. #[must_use] pub fn inverse_theme(&self) -> &Theme { - match self.theme_mode { + match self.cache.theme_mode { ThemeMode::Light => &self.theme.dark, ThemeMode::Dark => &self.theme.light, } } + + /// Returns a key that can be checked to see if a widget should invalidate + /// caches it stores. + #[must_use] + pub fn cache_key(&self) -> WidgetCacheKey { + self.cache + } } #[derive(Clone)] @@ -1207,3 +1218,24 @@ impl MapManagedWidget for ManagedWidget { map(self) } } + +/// An type that contains information about the state of a widget. +/// +/// This value can be stored and compared in future widget events. If the cache +/// keys are not equal, the widget should clear all caches. +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub struct WidgetCacheKey { + theme_mode: ThemeMode, + enabled: bool, + invalidation: u64, +} + +impl Default for WidgetCacheKey { + fn default() -> Self { + Self { + theme_mode: ThemeMode::default().inverse(), + enabled: false, + invalidation: u64::MAX, + } + } +} diff --git a/src/tree.rs b/src/tree.rs index 68f9996..3aea4c8 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -277,6 +277,11 @@ impl Tree { data.widget_from_node(id, self) } + pub(crate) fn invalidation(&self, id: LotId) -> Option { + let data = self.data.lock().ignore_poison(); + data.nodes.get(id).map(|node| node.invalidation) + } + pub(crate) fn is_enabled(&self, mut id: LotId, context: &WindowHandle) -> bool { let data = self.data.lock().ignore_poison(); loop { diff --git a/src/widget.rs b/src/widget.rs index c063c23..4673918 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -1186,6 +1186,10 @@ impl ManagedWidget { self.tree.is_enabled(self.node_id, handle) } + pub(crate) fn invalidation(&self) -> u64 { + self.tree.invalidation(self.node_id).expect("missing node") + } + /// Returns true if this widget is currently the hovered widget. #[must_use] pub fn hovered(&self) -> bool { diff --git a/src/widgets/button.rs b/src/widgets/button.rs index 5737e07..5a03829 100644 --- a/src/widgets/button.rs +++ b/src/widgets/button.rs @@ -9,7 +9,9 @@ use kludgine::shapes::{Shape, StrokeOptions}; use kludgine::Color; use crate::animation::{AnimationHandle, AnimationTarget, LinearInterpolate, Spawn}; -use crate::context::{AsEventContext, EventContext, GraphicsContext, LayoutContext, WidgetContext}; +use crate::context::{ + AsEventContext, EventContext, GraphicsContext, LayoutContext, WidgetCacheKey, WidgetContext, +}; use crate::styles::components::{ AutoFocusableControls, Easing, HighlightColor, IntrinsicPadding, OpaqueWidgetColor, OutlineColor, SurfaceColor, TextColor, @@ -36,7 +38,7 @@ pub struct Button { #[derive(Debug, Eq, PartialEq, Clone, Copy)] struct CacheState { - enabled: bool, + key: WidgetCacheKey, kind: ButtonKind, } @@ -129,7 +131,7 @@ impl Button { content: content.widget_ref(), on_click: None, cached_state: CacheState { - enabled: true, + key: WidgetCacheKey::default(), kind: ButtonKind::default(), }, buttons_pressed: 0, @@ -213,11 +215,12 @@ impl Button { let visual_state = Self::visual_style(context); self.cached_state = CacheState { - enabled: !matches!(visual_state, VisualState::Disabled), + key: context.cache_key(), kind, }; - if !self.cached_state.enabled { + // TODO this should be genericized to happen automatically. + if !context.enabled() { context.blur(); } @@ -331,12 +334,9 @@ impl VisualState { impl Widget for Button { fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { #![allow(clippy::similar_names)] - let enabled = context.enabled(); - // TODO This seems ugly. It needs context, so it can't be moved into the - // dynamic system. let current_style = self.kind.get_tracked(context); - if self.cached_state.enabled != enabled || self.cached_state.kind != current_style { + if self.cached_state.key != context.cache_key() || self.cached_state.kind != current_style { self.update_colors(context, false); }