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)>,
+}