~raph/interp-toy

0f8cb8e3c10c43e369d7b407fe405d6ca4eaa784 — Raph Levien 2 years ago 723dadf
Wire up lensing for masters

Clicking a master now sets the weight for the app.
4 files changed, 124 insertions(+), 23 deletions(-)

M src/app_state.rs
M src/lens2.rs
M src/main.rs
M src/master.rs
M src/app_state.rs => src/app_state.rs +31 -1
@@ 69,12 69,14 @@ impl Data for Master {
pub mod lenses {
    // Discussion: if the inner type were listed first, then
    // the capitalization wouldn't have to be twizzled.
    pub mod calc_state {
    pub mod app_state {
        use super::super::{AppState, Master};
        use crate::lens2::Lens2;
        use druid::Lens;
        use std::sync::Arc;
        pub struct Width;
        pub struct Weight;
        pub struct Shared;
        pub struct Masters;

        // Note: this lens isn't quite right.


@@ 111,6 113,34 @@ pub mod lenses {
                f(&mut data.masters)
            }
        }

        impl Lens2<AppState, super::super::Shared> for Shared {
            fn get<V, F: FnOnce(&super::super::Shared) -> V>(&self, data: &AppState, f: F) -> V {
                f(&data.shared)
            }

            fn with_mut<V, F: FnOnce(&mut super::super::Shared) -> V>(
                &self,
                data: &mut AppState,
                f: F,
            ) -> V {
                f(&mut data.shared)
            }
        }

        impl Lens2<AppState, Arc<Vec<Master>>> for Masters {
            fn get<V, F: FnOnce(&Arc<Vec<Master>>) -> V>(&self, data: &AppState, f: F) -> V {
                f(&data.masters)
            }

            fn with_mut<V, F: FnOnce(&mut Arc<Vec<Master>>) -> V>(
                &self,
                data: &mut AppState,
                f: F,
            ) -> V {
                f(&mut data.masters)
            }
        }
    }
}


M src/lens2.rs => src/lens2.rs +9 -2
@@ 58,11 58,18 @@ pub trait Lens2<T, U> {
    fn with_mut<V, F: FnOnce(&mut U) -> V>(&self, data: &mut T, f: F) -> V;
}

// Discussion: it might be even better to make this a blanket impl for tuples.
pub struct Pair<L1, L2> {
    lens1: L1,
    lens2: L2,
}

impl<L1, L2> Pair<L1, L2> {
    pub fn new(lens1: L1, lens2: L2) -> Pair<L1, L2> {
        Pair { lens1, lens2 }
    }
}

impl<T: Data, U1: Data, U2: Data, L1: Lens2<T, U1>, L2: Lens2<T, U2>> Lens2<T, (U1, U2)>
    for Pair<L1, L2>
{


@@ 80,8 87,8 @@ impl<T: Data, U1: Data, U2: Data, L1: Lens2<T, U1>, L2: Lens2<T, U2>> Lens2<T, (
            self.lens2.get(data, |data2| {
                let mut data = (data1.to_owned(), data2.to_owned());
                let val = f(&mut data);
                let delta1 = data1.same(&data.0);
                let delta2 = data2.same(&data.1);
                let delta1 = !data1.same(&data.0);
                let delta2 = !data2.same(&data.1);
                (data, val, delta1, delta2)
            })
        });

M src/main.rs => src/main.rs +10 -18
@@ 8,35 8,27 @@ mod lens2;
mod list;
mod master;

use app_state::{lenses, AppState, InterpPt, Master};
use app_state::{lenses, AppState, InterpPt};
use interp_pane::InterpPane;
use lens2::{Lens2Wrap, Pair};
use list::List;

fn build_master_ui() -> impl Widget<Master> {
    // Discussion: we might want spacing as a separate param for the list widget.
    Padding::uniform(
        3.0,
        DynLabel::new(|data: &Master, _env| format!("weight {:.2}", data.weight)),
    )
}
use master::MasterItem;

fn build_ui() -> impl Widget<AppState> {
    let pane = InterpPane::default();
    let mut col = Column::new();
    col.add_child(
        Padding::uniform(
            5.0,
            LensWrap::new(Slider::new(), lenses::calc_state::Weight),
        ),
        Padding::uniform(5.0, LensWrap::new(Slider::new(), lenses::app_state::Weight)),
        0.0,
    );
    let label = DynLabel::new(|data: &AppState, _env| format!("weight: {:.2}", data.shared.weight));
    col.add_child(Padding::uniform(5.0, label), 0.0);
    col.add_child(
        Padding::uniform(5.0, LensWrap::new(Slider::new(), lenses::calc_state::Width)),
        Padding::uniform(5.0, LensWrap::new(Slider::new(), lenses::app_state::Width)),
        0.0,
    );
    let label_wdth = DynLabel::new(|data: &AppState, _env| format!("width: {:.2}", data.shared.width));
    let label_wdth =
        DynLabel::new(|data: &AppState, _env| format!("width: {:.2}", data.shared.width));
    col.add_child(Padding::uniform(5.0, label_wdth), 0.0);
    let new_master_button = Button::new("New Master");
    let new_master_button = ActionWrapper::new(new_master_button, |data: &mut AppState, _env| {


@@ 44,9 36,9 @@ fn build_ui() -> impl Widget<AppState> {
    });
    col.add_child(Padding::uniform(5.0, new_master_button), 0.0);
    col.add_child(
        Scroll::new(LensWrap::new(
            List::new(|| Box::new(build_master_ui())),
            lenses::calc_state::Masters,
        Scroll::new(Lens2Wrap::new(
            List::new(|| Box::new(MasterItem::new())),
            Pair::new(lenses::app_state::Shared, lenses::app_state::Masters),
        ))
        .vertical(),
        1.0,

M src/master.rs => src/master.rs +74 -2
@@ 1,9 1,81 @@
//! UI for the master in a list view

use druid::BoxedWidget;
use druid::kurbo::{Point, Rect, Size};

use druid::{
    Action, BaseState, BoxConstraints, BoxedWidget, Env, Event, EventCtx, LayoutCtx, PaintCtx,
    UpdateCtx, Widget, WidgetPod,
};

use druid::widget::{DynLabel, Padding};

use crate::app_state::{Master, Shared};

struct MasterWrapper {
pub struct MasterItem {
    child: BoxedWidget<(Shared, Master)>,
}

impl MasterItem {
    pub fn new() -> Self {
        // Discussion: we might want spacing as a separate param for the list widget.
        let child = Padding::uniform(
            3.0,
            DynLabel::new(|data: &(Shared, Master), _env| format!("weight {:.2}", data.1.weight)),
        );
        MasterItem {
            child: WidgetPod::new(child).boxed(),
        }
    }
}

impl Widget<(Shared, Master)> for MasterItem {
    fn paint(
        &mut self,
        paint_ctx: &mut PaintCtx,
        _base_state: &BaseState,
        data: &(Shared, Master),
        env: &Env,
    ) {
        self.child.paint_with_offset(paint_ctx, data, env);
    }

    fn layout(
        &mut self,
        layout_ctx: &mut LayoutCtx,
        bc: &BoxConstraints,
        data: &(Shared, Master),
        env: &Env,
    ) -> Size {
        let size = self.child.layout(layout_ctx, &bc, data, env);
        self.child
            .set_layout_rect(Rect::from_origin_size(Point::ORIGIN, size));
        size
    }

    fn event(
        &mut self,
        event: &Event,
        ctx: &mut EventCtx,
        data: &mut (Shared, Master),
        env: &Env,
    ) -> Option<Action> {
        match event {
            Event::MouseDown(_) => {
                data.0.weight = data.1.weight;
                return None;
            }
            _ => (),
        }
        self.child.event(event, ctx, data, env)
    }

    fn update(
        &mut self,
        ctx: &mut UpdateCtx,
        _old_data: Option<&(Shared, Master)>,
        data: &(Shared, Master),
        env: &Env,
    ) {
        self.child.update(ctx, data, env);
    }
}