Skip to content

Commit

Permalink
Add UpdateCtx::env_changed and UpdateCtx::env_key_changed
Browse files Browse the repository at this point in the history
These methods can be used by widgets and other components to check
if particular items in the env have been modified, so they may
invalidate and rebuild resources as needed.

- closes #1167
  • Loading branch information
cmyr committed Sep 9, 2020
1 parent 22ae5c7 commit ebcb978
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 4 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ You can find its changes [documented below](#060---2020-06-01).
- `Menu` commands can now choose a custom target. ([#1185] by [@finnerale])
- `Movement::StartOfDocument`, `Movement::EndOfDocument`. ([#1092] by [@sysint64])
- `TextLayout` type simplifies drawing text ([#1182] by [@cmyr])
- `UpdateCtx` gets `env_changed` and `env_key_changed` methods ([#1207] by [@cmyr])

### Changed

Expand Down Expand Up @@ -425,6 +426,7 @@ Last release without a changelog :(
[#1185]: https://github.com/linebender/druid/pull/1185
[#1092]: https://github.com/linebender/druid/pull/1092
[#1204]: https://github.com/linebender/druid/pull/1204
[#1207]: https://github.com/linebender/druid/pull/1207

[Unreleased]: https://github.com/linebender/druid/compare/v0.6.0...master
[0.6.0]: https://github.com/linebender/druid/compare/v0.5.0...v0.6.0
Expand Down
31 changes: 29 additions & 2 deletions druid/src/contexts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ use std::{
};

use crate::core::{CommandQueue, FocusChange, WidgetState};
use crate::env::EnvResolveable;
use crate::piet::{Piet, PietText, RenderContext};
use crate::shell::Region;
use crate::{
commands, Affine, Command, ContextMenu, Cursor, ExtEventSink, Insets, MenuDesc, Point, Rect,
SingleUse, Size, Target, TimerToken, WidgetId, WindowDesc, WindowHandle, WindowId,
commands, Affine, Command, ContextMenu, Cursor, Env, ExtEventSink, Insets, MenuDesc, Point,
Rect, SingleUse, Size, Target, TimerToken, WidgetId, WindowDesc, WindowHandle, WindowId,
};

/// A macro for implementing methods on multiple contexts.
Expand Down Expand Up @@ -90,6 +91,8 @@ pub struct LifeCycleCtx<'a, 'b> {
pub struct UpdateCtx<'a, 'b> {
pub(crate) state: &'a mut ContextState<'b>,
pub(crate) widget_state: &'a mut WidgetState,
pub(crate) prev_env: Option<&'a Env>,
pub(crate) env: &'a Env,
}

/// A context provided to layout handling methods of widgets.
Expand Down Expand Up @@ -528,6 +531,30 @@ impl UpdateCtx<'_, '_> {
pub fn has_requested_update(&mut self) -> bool {
self.widget_state.request_update
}

/// Returns `true` if the current [`Env`] has changed since the previous
/// [`update`] call.
///
/// [`Env`]: struct.Env.html
/// [`update`]: trait.Widget.html#tymethod.update
pub fn env_changed(&self) -> bool {
self.prev_env.is_some()
}

/// Returns `true` if the given key has changed since the last [`update`]
/// call. The argument can be anything that is resolveable from the [`Env`],
/// such as a [`Key`] or a [`KeyOrValue`].
///
/// [`update`]: trait.Widget.html#tymethod.update
/// [`Env`]: struct.Env.html
/// [`Key`]: struct.Key.html
/// [`KeyOrValue`]: enum.KeyOrValue.html
pub fn env_key_changed<T>(&self, key: &impl EnvResolveable<T>) -> bool {
match self.prev_env.as_ref() {
Some(prev) => key.changed(prev, self.env),
None => false,
}
}
}

impl LifeCycleCtx<'_, '_> {
Expand Down
4 changes: 4 additions & 0 deletions druid/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -854,9 +854,13 @@ impl<T: Data, W: Widget<T>> WidgetPod<T, W> {
}
}

let prev_env = self.env.as_ref().filter(|p| !p.same(env));

let mut child_ctx = UpdateCtx {
state: ctx.state,
widget_state: &mut self.state,
prev_env,
env,
};

self.inner
Expand Down
33 changes: 31 additions & 2 deletions druid/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,35 @@ pub enum KeyOrValue<T> {
Key(Key<T>),
}

/// A trait for anything that can resolve a value of some type from the [`Env`].
///
/// This is a generalization of the idea of [`KeyOrValue`], mostly motivated
/// by wanting to improve the API used for checking if items in the [`Env`] have changed.
///
/// [`Env`]: struct.Env.html
/// [`KeyOrValue`]: enum.KeyOrValue.html
pub trait EnvResolveable<T> {
/// Returns `true` if this item has changed between the old and new [`Env`].
///
/// [`Env`]: struct.Env.html
fn changed(&self, old: &Env, new: &Env) -> bool;
}

impl<T: ValueType> EnvResolveable<T> for Key<T> {
fn changed(&self, old: &Env, new: &Env) -> bool {
!old.get_untyped(self).same(new.get_untyped(self))
}
}

impl<T> EnvResolveable<T> for KeyOrValue<T> {
fn changed(&self, old: &Env, new: &Env) -> bool {
match self {
KeyOrValue::Concrete(_) => false,
KeyOrValue::Key(key) => !old.get_untyped(key).same(new.get_untyped(key)),
}
}
}

/// Values which can be stored in an environment.
pub trait ValueType: Sized + Into<Value> {
/// Attempt to convert the generic `Value` into this type.
Expand Down Expand Up @@ -243,7 +272,7 @@ impl Env {
/// Panics if the key is not found.
///
/// [`Value`]: enum.Value.html
pub fn get_untyped(&self, key: impl Borrow<Key<()>>) -> &Value {
pub fn get_untyped<V>(&self, key: impl Borrow<Key<V>>) -> &Value {
match self.try_get_untyped(key) {
Ok(val) => val,
Err(err) => panic!("{}", err),
Expand All @@ -258,7 +287,7 @@ impl Env {
/// e.g. for debugging, theme editing, and theme loading.
///
/// [`Value`]: enum.Value.html
pub fn try_get_untyped(&self, key: impl Borrow<Key<()>>) -> Result<&Value, MissingKeyError> {
pub fn try_get_untyped<V>(&self, key: impl Borrow<Key<V>>) -> Result<&Value, MissingKeyError> {
self.0.map.get(key.borrow().key).ok_or(MissingKeyError {
key: key.borrow().key.into(),
})
Expand Down
2 changes: 2 additions & 0 deletions druid/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,8 @@ impl<T: Data> Window<T> {
let mut update_ctx = UpdateCtx {
widget_state: &mut widget_state,
state: &mut state,
prev_env: None,
env,
};

self.root.update(&mut update_ctx, data, env);
Expand Down

0 comments on commit ebcb978

Please # to comment.