~raph/interp-toy

723dadf0f20faf02734927cafb628a052b3dee40 — Raph Levien 2 years ago 1fc3ed6
Checkpoint while reworking the lens stuff
6 files changed, 153 insertions(+), 23 deletions(-)

M src/app_state.rs
M src/interp_pane.rs
M src/lens2.rs
M src/list.rs
M src/main.rs
A src/master.rs
M src/app_state.rs => src/app_state.rs +21 -9
@@ 14,8 14,7 @@ pub struct AppState {
    // TODO: increasingly aware that we're hardcoding two parameters,
    // this needs to be a variable number, but we're trying to keep
    // complexity down for now.
    pub width: f64,
    pub weight: f64,
    pub shared: Shared,

    pub pts: Arc<Vec<InterpPt>>,



@@ 24,18 23,30 @@ pub struct AppState {

impl Data for AppState {
    fn same(&self, other: &AppState) -> bool {
        self.width.same(&other.width)
            && self.weight.same(&other.weight)
        self.shared.same(&other.shared)
            && self.pts.same(&other.pts)
            && self.masters.same(&other.masters)
    }
}

impl Data for Shared {
    fn same(&self, other: &Shared) -> bool {
        self.width.same(&other.width) && self.weight.same(&other.weight)
    }
}

#[derive(Clone)]
pub struct InterpPt {
    samples: Vec<InterpSample>,
}

/// This is data that's made available to individual master entries
#[derive(Clone, Default)]
pub struct Shared {
    pub width: f64,
    pub weight: f64,
}

#[derive(Clone)]
pub struct Master {
    pub weight: f64,


@@ 66,23 77,24 @@ pub mod lenses {
        pub struct Weight;
        pub struct Masters;

        // Note: this lens isn't quite right.
        impl Lens<AppState, f64> for Width {
            fn get<'a>(&self, data: &'a AppState) -> &'a f64 {
                &data.width
                &data.shared.width
            }

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

        impl Lens<AppState, f64> for Weight {
            fn get<'a>(&self, data: &'a AppState) -> &'a f64 {
                &data.weight
                &data.shared.weight
            }

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



@@ 106,7 118,7 @@ impl AppState {
    pub fn add_new_master(&mut self) {
        let mut masters = self.masters.deref().to_owned();
        masters.push(Master {
            weight: self.weight,
            weight: self.shared.weight,
        });
        self.masters = masters.into();
        println!("adding new master");

M src/interp_pane.rs => src/interp_pane.rs +5 -5
@@ 24,8 24,8 @@ impl Widget<AppState> for InterpPane {
        data: &AppState,
        _env: &Env,
    ) {
        let width = data.width;
        let weight = data.weight;
        let width = data.shared.width;
        let weight = data.shared.weight;
        for pt in data.pts.deref() {
            let interp = pt.eval(width, weight);
            let circle = Circle::new(interp, 5.0);


@@ 51,8 51,8 @@ impl Widget<AppState> for InterpPane {
        data: &mut AppState,
        _env: &Env,
    ) -> Option<Action> {
        let width = data.width;
        let weight = data.weight;
        let width = data.shared.width;
        let weight = data.shared.weight;
        match event {
            Event::MouseDown(e) => {
                println!("mouse down {:?}!", e);


@@ 77,7 77,7 @@ impl Widget<AppState> for InterpPane {
            Event::MouseMoved(e) => {
                if let Some(drag_ix) = self.drag_ix {
                    let mut pts = data.pts.deref().clone();
                    pts[drag_ix].update(e.pos, data.width, data.weight);
                    pts[drag_ix].update(e.pos, data.shared.width, data.shared.weight);
                    data.pts = Arc::new(pts);
                    ctx.invalidate();
                }

M src/lens2.rs => src/lens2.rs +9 -6
@@ 63,25 63,28 @@ pub struct Pair<L1, L2> {
    lens2: L2,
}

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

impl<T: Data, U1: Data, U2: Data, L1: Lens2<T, U1>, L2: Lens2<T, U2>> Lens2<T, (U1, U2)>
    for Pair<L1, L2>
{
    fn get<V, F: FnOnce(&(U1, U2)) -> V>(&self, data: &T, f: F) -> V {
        self.lens1.get(data, |data1|
        self.lens1.get(data, |data1| {
            self.lens2.get(data, |data2| {
                let data = (data1.to_owned(), data2.to_owned());
                f(&data)
            }))
            })
        })
    }

    fn with_mut<V, F: FnOnce(&mut (U1, U2)) -> V>(&self, data: &mut T, f: F) -> V {
        let ((data1, data2), val, delta1, delta2) = self.lens1.get(data, |data1|
        let ((data1, data2), val, delta1, delta2) = self.lens1.get(data, |data1| {
            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);
                (data, val, delta1, delta2)
            }));
            })
        });
        if delta1 {
            self.lens1.with_mut(data, |d1| *d1 = data1);
        }

M src/list.rs => src/list.rs +105 -0
@@ 105,3 105,108 @@ impl<T: Data, F: FnMut() -> Box<dyn Widget<T>>> Widget<Arc<Vec<T>>> for List<T, 
        }
    }
}

// This is cut'n'paste for now to support both plain lists and lists paired with
// shared data, but it should migrate to a list-iteration trait.

impl<T1: Data, T: Data, F: FnMut() -> Box<dyn Widget<(T1, T)>>> Widget<(T1, Arc<Vec<T>>)>
    for List<(T1, T), F>
{
    fn paint(
        &mut self,
        paint_ctx: &mut PaintCtx,
        _base_state: &BaseState,
        data: &(T1, Arc<Vec<T>>),
        env: &Env,
    ) {
        for (child, child_data) in self.children.iter_mut().zip(data.1.iter()) {
            let d = (data.0.clone(), child_data.to_owned());
            child.paint_with_offset(paint_ctx, &d, env);
        }
    }

    fn layout(
        &mut self,
        layout_ctx: &mut LayoutCtx,
        bc: &BoxConstraints,
        data: &(T1, Arc<Vec<T>>),
        env: &Env,
    ) -> Size {
        let mut width = bc.min().width;
        let mut y = 0.0;
        for (child, child_data) in self.children.iter_mut().zip(data.1.iter()) {
            let d = (data.0.clone(), child_data.to_owned());
            let child_bc = BoxConstraints::new(
                Size::new(bc.min().width, 0.0),
                Size::new(bc.max().width, std::f64::INFINITY),
            );
            let child_size = child.layout(layout_ctx, &child_bc, &d, env);
            let rect = Rect::from_origin_size(Point::new(0.0, y), child_size);
            child.set_layout_rect(rect);
            width = width.max(child_size.width);
            y += child_size.height;
        }
        bc.constrain(Size::new(width, y))
    }

    fn event(
        &mut self,
        event: &Event,
        ctx: &mut EventCtx,
        data: &mut (T1, Arc<Vec<T>>),
        env: &Env,
    ) -> Option<Action> {
        let mut action = None;
        let mut new_shared = data.0.to_owned();
        let mut new_data = Vec::with_capacity(data.1.len());
        let mut any_shared_changed = false;
        let mut any_el_changed = false;
        for (child, child_data) in self.children.iter_mut().zip(data.1.iter()) {
            let mut d = (new_shared.clone(), child_data.to_owned());
            action = Action::merge(action, child.event(event, ctx, &mut d, env));
            if !any_shared_changed && !new_shared.same(&d.0) {
                any_shared_changed = true;
            }
            if any_shared_changed {
                new_shared = d.0;
            }
            if !any_el_changed && !child_data.same(&d.1) {
                any_el_changed = true;
            }
            new_data.push(d.1);
        }
        // It's not clear we need to track this; it's possible it would
        // be slightly more efficient to just update data.0 in place.
        if any_shared_changed {
            data.0 = new_shared;
        }
        if any_el_changed {
            data.1 = Arc::new(new_data);
        }
        action
    }

    fn update(
        &mut self,
        ctx: &mut UpdateCtx,
        _old_data: Option<&(T1, Arc<Vec<T>>)>,
        data: &(T1, Arc<Vec<T>>),
        env: &Env,
    ) {
        for (child, child_data) in self.children.iter_mut().zip(data.1.iter()) {
            let d = (data.0.clone(), child_data.to_owned());
            child.update(ctx, &d, env);
        }
        let len = self.children.len();
        if len > data.1.len() {
            self.children.truncate(data.1.len())
        } else if len < data.1.len() {
            for child_data in &data.1[len..] {
                let mut child = WidgetPod::new((self.closure)());
                let d = (data.0.clone(), child_data.to_owned());
                child.update(ctx, &d, env);
                self.children.push(child);
            }
        }
    }
}

M src/main.rs => src/main.rs +4 -3
@@ 6,6 6,7 @@ mod app_state;
mod interp_pane;
mod lens2;
mod list;
mod master;

use app_state::{lenses, AppState, InterpPt, Master};
use interp_pane::InterpPane;


@@ 29,13 30,13 @@ fn build_ui() -> impl Widget<AppState> {
        ),
        0.0,
    );
    let label = DynLabel::new(|data: &AppState, _env| format!("weight: {:.2}", data.weight));
    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)),
        0.0,
    );
    let label_wdth = DynLabel::new(|data: &AppState, _env| format!("width: {:.2}", data.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| {


@@ 50,7 51,7 @@ fn build_ui() -> impl Widget<AppState> {
        .vertical(),
        1.0,
    );
    let col = ActionWrapper::new(col, |data: &mut AppState, _env| data.width += 0.1);
    let col = ActionWrapper::new(col, |data: &mut AppState, _env| data.shared.width += 0.1);
    let mut row = Row::new();
    row.add_child(pane, 2.0);
    row.add_child(col, 1.0);

A src/master.rs => src/master.rs +9 -0
@@ 0,0 1,9 @@
//! UI for the master in a list view

use druid::BoxedWidget;

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

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