diff --git a/CHANGELOG.md b/CHANGELOG.md index 17599edd7c..343f2bd229 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ You can find its changes [documented below](#060---2020-06-01). - `request_update` in `EventCtx`. ([#1128] by [@raphlinus]) - `ExtEventSink`s can now be obtained from widget methods. ([#1152] by [@jneem]) - 'Scope' widget to allow encapsulation of reactive state. ([#1151] by [@rjwittams]) +- `Ref` lens that applies `AsRef` and thus allow indexing arrays. ([#1171] by [@finnerale]) ### Changed @@ -27,8 +28,8 @@ You can find its changes [documented below](#060---2020-06-01). - `Container::rounded` takes `KeyOrValue` instead of `f64`. ([#1054] by [@binomial0]) - `request_anim_frame` no longer invalidates the entire window. ([#1057] by [@jneem]) - Use new Piet text api ([#1143] by [@cmyr]) -- `Env::try_get` (and related methods) return a `Result` instead of an `Option`. - ([#1172] by [@cmyr]) +- `Env::try_get` (and related methods) return a `Result` instead of an `Option`. ([#1172] by [@cmyr]) +- `lens!` macro to use move semantics for the index. ([#1171] by [@finnerale]) ### Deprecated @@ -405,7 +406,8 @@ Last release without a changelog :( [#1151]: https://github.com/linebender/druid/pull/1151 [#1152]: https://github.com/linebender/druid/pull/1152 [#1157]: https://github.com/linebender/druid/pull/1157 -[#1172]: https://github.com/linebender/druid/pull/1157 +[#1171]: https://github.com/linebender/druid/pull/1171 +[#1172]: https://github.com/linebender/druid/pull/1172 [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 diff --git a/druid/src/lens/lens.rs b/druid/src/lens/lens.rs index 2f04c0668e..c9a408175b 100644 --- a/druid/src/lens/lens.rs +++ b/druid/src/lens/lens.rs @@ -125,6 +125,53 @@ pub trait LensExt: Lens { self.then(Deref) } + /// Invoke a type's `AsRef` and `AsMut` impl. + /// + /// It also allows indexing arrays with the [`index`] lens as shown in the example. + /// This is necessary, because the `Index` trait in Rust is only implemented + /// for slices (`[T]`), but not for arrays (`[T; N]`). + /// + /// # Examples + /// + /// Using `ref` this works: + /// + /// ``` + /// use druid::{widget::TextBox, Data, Lens, LensExt, Widget, WidgetExt}; + /// + /// #[derive(Clone, Default, Data, Lens)] + /// struct State { + /// data: [String; 2], + /// } + /// + /// fn with_ref() -> impl Widget { + /// TextBox::new().lens(State::data.as_ref().index(1)) + /// } + /// ``` + /// + /// While this fails: + /// + /// ```compile_fail + /// # use druid::*; + /// # #[derive(Clone, Default, Data, Lens)] + /// # struct State { + /// # data: [String; 2], + /// # } + /// fn without_ref() -> impl Widget { + /// // results in: `[std::string::String; 2]` cannot be mutably indexed by `usize` + /// TextBox::new().lens(State::data.index(1)) + /// } + /// ``` + /// + /// [`Lens`]: ./trait.Lens.html + /// [`index`]: #method.index + fn as_ref(self) -> Then + where + B: AsRef + AsMut, + Self: Sized, + { + self.then(Ref) + } + /// Access an index in a container /// /// ``` @@ -307,10 +354,10 @@ where #[macro_export] macro_rules! lens { ($ty:ty, [$index:expr]) => { - $crate::lens::Field::new::<$ty, _>(|x| &x[$index], |x| &mut x[$index]) + $crate::lens::Field::new::<$ty, _>(move |x| &x[$index], move |x| &mut x[$index]) }; ($ty:ty, $field:tt) => { - $crate::lens::Field::new::<$ty, _>(|x| &x.$field, |x| &mut x.$field) + $crate::lens::Field::new::<$ty, _>(move |x| &x.$field, move |x| &mut x.$field) }; } @@ -421,6 +468,28 @@ where } } +/// [`Lens`] for invoking `AsRef` and `AsMut` on a type. +/// +/// [`LensExt::ref`] offers an easy way to apply this, +/// as well as more information and examples. +/// +/// [`Lens`]: ../trait.Lens.html +/// [`LensExt::ref`]: ../trait.LensExt.html#method.as_ref +#[derive(Debug, Copy, Clone)] +pub struct Ref; + +impl Lens for Ref +where + T: AsRef + AsMut, +{ + fn with V>(&self, data: &T, f: F) -> V { + f(data.as_ref()) + } + fn with_mut V>(&self, data: &mut T, f: F) -> V { + f(data.as_mut()) + } +} + /// `Lens` for indexing containers #[derive(Debug, Copy, Clone)] pub struct Index { diff --git a/druid/src/lens/mod.rs b/druid/src/lens/mod.rs index 2a62ade9cf..f04d2c3ec8 100644 --- a/druid/src/lens/mod.rs +++ b/druid/src/lens/mod.rs @@ -48,6 +48,6 @@ #[allow(clippy::module_inception)] mod lens; -pub use lens::{Deref, Field, Id, InArc, Index, Map, Then}; +pub use lens::{Deref, Field, Id, InArc, Index, Map, Ref, Then}; #[doc(hidden)] pub use lens::{Lens, LensExt, LensWrap};