Added WidgetCacheKey

This commit is contained in:
Jonathan Johnson 2023-11-15 10:36:00 -08:00
parent 534f676ef0
commit 5506a24dae
No known key found for this signature in database
GPG key ID: A66D6A34D6620579
4 changed files with 62 additions and 21 deletions

View file

@ -706,9 +706,8 @@ pub struct WidgetContext<'context, 'window> {
window: &'context mut RunningWindow<'window>, window: &'context mut RunningWindow<'window>,
theme: Cow<'context, ThemePair>, theme: Cow<'context, ThemePair>,
pending_state: PendingState<'context>, pending_state: PendingState<'context>,
theme_mode: ThemeMode,
effective_styles: Styles, effective_styles: Styles,
enabled: bool, cache: WidgetCacheKey,
} }
impl<'context, 'window> WidgetContext<'context, 'window> { impl<'context, 'window> WidgetContext<'context, 'window> {
@ -736,11 +735,14 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
focus_is_advancing: false, focus_is_advancing: false,
}), }),
effective_styles: current_node.effective_styles(), effective_styles: current_node.effective_styles(),
enabled, cache: WidgetCacheKey {
theme_mode,
enabled,
invalidation: current_node.invalidation(),
},
current_node, current_node,
redraw_status, redraw_status,
theme: Cow::Borrowed(theme), theme: Cow::Borrowed(theme),
theme_mode,
window, window,
} }
} }
@ -753,9 +755,8 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
window: &mut *self.window, window: &mut *self.window,
theme: Cow::Borrowed(self.theme.as_ref()), theme: Cow::Borrowed(self.theme.as_ref()),
pending_state: self.pending_state.borrowed(), pending_state: self.pending_state.borrowed(),
theme_mode: self.theme_mode, cache: self.cache,
effective_styles: self.effective_styles.clone(), 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 { let theme_mode = if let Some(mode) = theme_mode {
mode.get_tracked(self) mode.get_tracked(self)
} else { } else {
self.theme_mode self.cache.theme_mode
}; };
WidgetContext { WidgetContext {
effective_styles, effective_styles,
enabled: current_node.enabled(&self.handle()), cache: WidgetCacheKey {
theme_mode,
enabled: current_node.enabled(&self.handle()),
invalidation: current_node.invalidation(),
},
current_node, current_node,
redraw_status: self.redraw_status, redraw_status: self.redraw_status,
window: &mut *self.window, window: &mut *self.window,
theme, theme,
pending_state: self.pending_state.borrowed(), 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. /// Returns true if this widget is enabled.
#[must_use] #[must_use]
pub const fn enabled(&self) -> bool { pub const fn enabled(&self) -> bool {
self.enabled self.cache.enabled
} }
pub(crate) fn parent(&self) -> Option<ManagedWidget> { pub(crate) fn parent(&self) -> Option<ManagedWidget> {
@ -1004,7 +1008,7 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
/// Returns the current theme in either light or dark mode. /// Returns the current theme in either light or dark mode.
#[must_use] #[must_use]
pub fn theme(&self) -> &Theme { pub fn theme(&self) -> &Theme {
match self.theme_mode { match self.cache.theme_mode {
ThemeMode::Light => &self.theme.light, ThemeMode::Light => &self.theme.light,
ThemeMode::Dark => &self.theme.dark, ThemeMode::Dark => &self.theme.dark,
} }
@ -1013,11 +1017,18 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
/// Returns the opposite theme of [`Self::theme()`]. /// Returns the opposite theme of [`Self::theme()`].
#[must_use] #[must_use]
pub fn inverse_theme(&self) -> &Theme { pub fn inverse_theme(&self) -> &Theme {
match self.theme_mode { match self.cache.theme_mode {
ThemeMode::Light => &self.theme.dark, ThemeMode::Light => &self.theme.dark,
ThemeMode::Dark => &self.theme.light, 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)] #[derive(Clone)]
@ -1207,3 +1218,24 @@ impl<T> MapManagedWidget<T> for ManagedWidget {
map(self) 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,
}
}
}

View file

@ -277,6 +277,11 @@ impl Tree {
data.widget_from_node(id, self) data.widget_from_node(id, self)
} }
pub(crate) fn invalidation(&self, id: LotId) -> Option<u64> {
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 { pub(crate) fn is_enabled(&self, mut id: LotId, context: &WindowHandle) -> bool {
let data = self.data.lock().ignore_poison(); let data = self.data.lock().ignore_poison();
loop { loop {

View file

@ -1186,6 +1186,10 @@ impl ManagedWidget {
self.tree.is_enabled(self.node_id, handle) 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. /// Returns true if this widget is currently the hovered widget.
#[must_use] #[must_use]
pub fn hovered(&self) -> bool { pub fn hovered(&self) -> bool {

View file

@ -9,7 +9,9 @@ use kludgine::shapes::{Shape, StrokeOptions};
use kludgine::Color; use kludgine::Color;
use crate::animation::{AnimationHandle, AnimationTarget, LinearInterpolate, Spawn}; 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::{ use crate::styles::components::{
AutoFocusableControls, Easing, HighlightColor, IntrinsicPadding, OpaqueWidgetColor, AutoFocusableControls, Easing, HighlightColor, IntrinsicPadding, OpaqueWidgetColor,
OutlineColor, SurfaceColor, TextColor, OutlineColor, SurfaceColor, TextColor,
@ -36,7 +38,7 @@ pub struct Button {
#[derive(Debug, Eq, PartialEq, Clone, Copy)] #[derive(Debug, Eq, PartialEq, Clone, Copy)]
struct CacheState { struct CacheState {
enabled: bool, key: WidgetCacheKey,
kind: ButtonKind, kind: ButtonKind,
} }
@ -129,7 +131,7 @@ impl Button {
content: content.widget_ref(), content: content.widget_ref(),
on_click: None, on_click: None,
cached_state: CacheState { cached_state: CacheState {
enabled: true, key: WidgetCacheKey::default(),
kind: ButtonKind::default(), kind: ButtonKind::default(),
}, },
buttons_pressed: 0, buttons_pressed: 0,
@ -213,11 +215,12 @@ impl Button {
let visual_state = Self::visual_style(context); let visual_state = Self::visual_style(context);
self.cached_state = CacheState { self.cached_state = CacheState {
enabled: !matches!(visual_state, VisualState::Disabled), key: context.cache_key(),
kind, kind,
}; };
if !self.cached_state.enabled { // TODO this should be genericized to happen automatically.
if !context.enabled() {
context.blur(); context.blur();
} }
@ -331,12 +334,9 @@ impl VisualState {
impl Widget for Button { impl Widget for Button {
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) {
#![allow(clippy::similar_names)] #![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); 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); self.update_colors(context, false);
} }