Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move LensWrap to widget module #1251

Merged
merged 1 commit into from
Sep 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ You can find its changes [documented below](#060---2020-06-01).
- `Movement::RightOfLine` to `Movement::NextLineBreak`, and `Movement::LeftOfLine` to `Movement::PrecedingLineBreak`. ([#1092] by [@sysint64])
- `AnimFrame` was moved from `lifecycle` to `event` ([#1155] by [@jneem])
- Contexts' `text()` methods return `&mut PietText` instead of cloning ([#1205] by [@cmyr])
- `LensWrap` widget moved into widget module ([#1251] by [@cmyr])

### Deprecated

Expand Down Expand Up @@ -461,6 +462,7 @@ Last release without a changelog :(
[#1220]: https://github.com/linebender/druid/pull/1220
[#1238]: https://github.com/linebender/druid/pull/1238
[#1241]: https://github.com/linebender/druid/pull/1241
[#1251]: https://github.com/linebender/druid/pull/1251

[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
6 changes: 4 additions & 2 deletions druid/examples/styled_text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@

//! Example of dynamic text styling

use druid::widget::{Checkbox, Flex, Label, MainAxisAlignment, Painter, Parse, Stepper, TextBox};
use druid::widget::{
Checkbox, Flex, Label, LensWrap, MainAxisAlignment, Painter, Parse, Stepper, TextBox,
};
use druid::{
theme, AppLauncher, Color, Data, FontDescriptor, FontFamily, Key, Lens, LensExt, LensWrap,
theme, AppLauncher, Color, Data, FontDescriptor, FontFamily, Key, Lens, LensExt,
LocalizedString, PlatformError, RenderContext, Widget, WidgetExt, WindowDesc,
};
use std::fmt::Display;
Expand Down
5 changes: 3 additions & 2 deletions druid/examples/switches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@
// limitations under the License.

use druid::widget::{
Checkbox, Flex, Label, MainAxisAlignment, Padding, Parse, Stepper, Switch, TextBox, WidgetExt,
Checkbox, Flex, Label, LensWrap, MainAxisAlignment, Padding, Parse, Stepper, Switch, TextBox,
WidgetExt,
};
use druid::{AppLauncher, Data, Lens, LensExt, LensWrap, LocalizedString, Widget, WindowDesc};
use druid::{AppLauncher, Data, Lens, LensExt, LocalizedString, Widget, WindowDesc};

#[derive(Clone, Data, Lens)]
struct DemoState {
Expand Down
92 changes: 0 additions & 92 deletions druid/src/lens/lens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ use std::marker::PhantomData;
use std::ops;
use std::sync::Arc;

use crate::kurbo::Size;
use crate::widget::prelude::*;
use crate::Data;

/// A lens is a datatype that gives access to a part of a larger
Expand Down Expand Up @@ -212,96 +210,6 @@ pub trait LensExt<A: ?Sized, B: ?Sized>: Lens<A, B> {

impl<A: ?Sized, B: ?Sized, T: Lens<A, B>> LensExt<A, B> for T {}

// A case can be made this should be in the `widget` module.

/// A wrapper for its widget subtree to have access to a part
/// of its parent's data.
///
/// Every widget in druid is instantiated with access to data of some
/// type; the root widget has access to the entire application data.
/// Often, a part of the widget hierarchy is only concerned with a part
/// of that data. The `LensWrap` widget is a way to "focus" the data
/// reference down, for the subtree. One advantage is performance;
/// data changes that don't intersect the scope of the lens aren't
/// propagated.
///
/// Another advantage is generality and reuse. If a widget (or tree of
/// widgets) is designed to work with some chunk of data, then with a
/// lens that same code can easily be reused across all occurrences of
/// that chunk within the application state.
///
/// This wrapper takes a [`Lens`] as an argument, which is a specification
/// of a struct field, or some other way of narrowing the scope.
///
/// [`Lens`]: trait.Lens.html
pub struct LensWrap<U, L, W> {
inner: W,
lens: L,
// The following is a workaround for otherwise getting E0207.
phantom: PhantomData<U>,
}

impl<U, L, W> LensWrap<U, L, W> {
/// Wrap a widget with a lens.
///
/// When the lens has type `Lens<T, U>`, the inner widget has data
/// of type `U`, and the wrapped widget has data of type `T`.
pub fn new(inner: W, lens: L) -> LensWrap<U, L, W> {
LensWrap {
inner,
lens,
phantom: Default::default(),
}
}
}

impl<T, U, L, W> Widget<T> for LensWrap<U, L, W>
where
T: Data,
U: Data,
L: Lens<T, U>,
W: Widget<U>,
{
fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
let inner = &mut self.inner;
self.lens
.with_mut(data, |data| inner.event(ctx, event, data, env))
}

fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &T, env: &Env) {
let inner = &mut self.inner;
self.lens
.with(data, |data| inner.lifecycle(ctx, event, data, env))
}

fn update(&mut self, ctx: &mut UpdateCtx, old_data: &T, data: &T, env: &Env) {
let inner = &mut self.inner;
let lens = &self.lens;
lens.with(old_data, |old_data| {
lens.with(data, |data| {
if ctx.has_requested_update() || !old_data.same(data) || ctx.env_changed() {
inner.update(ctx, old_data, data, env);
}
})
})
}

fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints, data: &T, env: &Env) -> Size {
let inner = &mut self.inner;
self.lens
.with(data, |data| inner.layout(ctx, bc, data, env))
}

fn paint(&mut self, ctx: &mut PaintCtx, data: &T, env: &Env) {
let inner = &mut self.inner;
self.lens.with(data, |data| inner.paint(ctx, data, env));
}

fn id(&self) -> Option<WidgetId> {
self.inner.id()
}
}

/// Lens accessing a member of some type using accessor functions
///
/// See also the `lens` macro.
Expand Down
2 changes: 1 addition & 1 deletion druid/src/lens/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,4 @@
mod lens;
pub use lens::{Deref, Field, Id, InArc, Index, Map, Ref, Then, Unit};
#[doc(hidden)]
pub use lens::{Lens, LensExt, LensWrap};
pub use lens::{Lens, LensExt};
6 changes: 5 additions & 1 deletion druid/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ pub use data::{ArcStr, Data};
pub use env::{Env, Key, KeyOrValue, Value, ValueType};
pub use event::{Event, InternalEvent, InternalLifeCycle, LifeCycle};
pub use ext_event::{ExtEventError, ExtEventSink};
pub use lens::{Lens, LensExt, LensWrap};
pub use lens::{Lens, LensExt};
pub use localization::LocalizedString;
pub use menu::{sys as platform_menus, ContextMenu, MenuDesc, MenuItem};
pub use mouse::MouseEvent;
Expand All @@ -206,6 +206,10 @@ pub use window::{Window, WindowId};
#[cfg(test)]
pub(crate) use event::{StateCell, StateCheckFn};

#[deprecated(since = "0.7.0", note = "use druid::widget::LensWrap instead")]
#[allow(missing_docs)]
pub type LensWrap<A, B, C> = widget::LensWrap<A, B, C>;

/// The meaning (mapped value) of a keypress.
///
/// Note that in previous versions, the `KeyCode` field referred to the
Expand Down
112 changes: 112 additions & 0 deletions druid/src/widget/lens_wrap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Copyright 2020 The Druid Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! A [`Widget`] that uses a [`Lens`] to change the [`Data`] of its child.
//!
//! [`Widget`]: ../trait.Widget.html
//! [`Lens`]: ../trait.Lens.html
//! [`Data`]: ../trait.Data.html

use std::marker::PhantomData;

use crate::widget::prelude::*;
use crate::{Data, Lens};

/// A wrapper for its widget subtree to have access to a part
/// of its parent's data.
///
/// Every widget in druid is instantiated with access to data of some
/// type; the root widget has access to the entire application data.
/// Often, a part of the widget hierarchy is only concerned with a part
/// of that data. The `LensWrap` widget is a way to "focus" the data
/// reference down, for the subtree. One advantage is performance;
/// data changes that don't intersect the scope of the lens aren't
/// propagated.
///
/// Another advantage is generality and reuse. If a widget (or tree of
/// widgets) is designed to work with some chunk of data, then with a
/// lens that same code can easily be reused across all occurrences of
/// that chunk within the application state.
///
/// This wrapper takes a [`Lens`] as an argument, which is a specification
/// of a struct field, or some other way of narrowing the scope.
///
/// [`Lens`]: trait.Lens.html
pub struct LensWrap<U, L, W> {
inner: W,
lens: L,
// The following is a workaround for otherwise getting E0207.
phantom: PhantomData<U>,
}

impl<U, L, W> LensWrap<U, L, W> {
/// Wrap a widget with a lens.
///
/// When the lens has type `Lens<T, U>`, the inner widget has data
/// of type `U`, and the wrapped widget has data of type `T`.
pub fn new(inner: W, lens: L) -> LensWrap<U, L, W> {
LensWrap {
inner,
lens,
phantom: Default::default(),
}
}
}

impl<T, U, L, W> Widget<T> for LensWrap<U, L, W>
where
T: Data,
U: Data,
L: Lens<T, U>,
W: Widget<U>,
{
fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
let inner = &mut self.inner;
self.lens
.with_mut(data, |data| inner.event(ctx, event, data, env))
}

fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &T, env: &Env) {
let inner = &mut self.inner;
self.lens
.with(data, |data| inner.lifecycle(ctx, event, data, env))
}

fn update(&mut self, ctx: &mut UpdateCtx, old_data: &T, data: &T, env: &Env) {
let inner = &mut self.inner;
let lens = &self.lens;
lens.with(old_data, |old_data| {
lens.with(data, |data| {
if ctx.has_requested_update() || !old_data.same(data) || ctx.env_changed() {
inner.update(ctx, old_data, data, env);
}
})
})
}

fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints, data: &T, env: &Env) -> Size {
let inner = &mut self.inner;
self.lens
.with(data, |data| inner.layout(ctx, bc, data, env))
}

fn paint(&mut self, ctx: &mut PaintCtx, data: &T, env: &Env) {
let inner = &mut self.inner;
self.lens.with(data, |data| inner.paint(ctx, data, env));
}

fn id(&self) -> Option<WidgetId> {
self.inner.id()
}
}
2 changes: 2 additions & 0 deletions druid/src/widget/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ mod identity_wrapper;
mod image;
mod invalidation;
mod label;
mod lens_wrap;
mod list;
mod padding;
mod painter;
Expand Down Expand Up @@ -64,6 +65,7 @@ pub use env_scope::EnvScope;
pub use flex::{CrossAxisAlignment, Flex, FlexParams, MainAxisAlignment};
pub use identity_wrapper::IdentityWrapper;
pub use label::{Label, LabelText, LineBreaking};
pub use lens_wrap::LensWrap;
pub use list::{List, ListIter};
pub use padding::Padding;
pub use painter::{BackgroundBrush, Painter};
Expand Down
4 changes: 2 additions & 2 deletions druid/src/widget/widget_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
use super::invalidation::DebugInvalidation;
use super::{
Align, BackgroundBrush, Click, Container, Controller, ControllerHost, EnvScope,
IdentityWrapper, Padding, Parse, SizedBox, WidgetId,
IdentityWrapper, LensWrap, Padding, Parse, SizedBox, WidgetId,
};
use crate::{Color, Data, Env, EventCtx, Insets, KeyOrValue, Lens, LensWrap, UnitPoint, Widget};
use crate::{Color, Data, Env, EventCtx, Insets, KeyOrValue, Lens, UnitPoint, Widget};

/// A trait that provides extra methods for combining `Widget`s.
pub trait WidgetExt<T: Data>: Widget<T> + Sized + 'static {
Expand Down