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>,
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<ManagedWidget> {
@ -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<T> MapManagedWidget<T> 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,
}
}
}

View file

@ -277,6 +277,11 @@ impl Tree {
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 {
let data = self.data.lock().ignore_poison();
loop {

View file

@ -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 {

View file

@ -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);
}