~raph/interp-toy

5aab980756d28b53d1816cd9a35240328e826c73 — Raph Levien 2 years ago 5412ba6
Add list widget

WIP
2 files changed, 108 insertions(+), 0 deletions(-)

A src/list.rs
M src/main.rs
A src/list.rs => src/list.rs +107 -0
@@ 0,0 1,107 @@
//! Simple list view widget.

use std::sync::Arc;

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

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

pub struct List<T: Data, F: FnMut() -> Box<dyn Widget<T>>> {
    closure: F,
    children: Vec<WidgetPod<T, Box<dyn Widget<T>>>>,
}

impl<T: Data, F: FnMut() -> Box<dyn Widget<T>>> List<T, F> {
    pub fn new(closure: F) -> Self {
        List {
            closure,
            children: Vec::new(),
        }
    }
}

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

    fn layout(
        &mut self,
        layout_ctx: &mut LayoutCtx,
        bc: &BoxConstraints,
        data: &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.iter()) {
            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, child_data, 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 Arc<Vec<T>>,
        env: &Env,
    ) -> Option<Action> {
        let mut action = None;
        let mut new_data = Vec::with_capacity(data.len());
        let mut any_changed = false;
        for (child, child_data) in self.children.iter_mut().zip(data.iter()) {
            let mut d = child_data.to_owned();
            action = Action::merge(action, child.event(event, ctx, &mut d, env));
            if !any_changed && !child_data.same(&d) {
                any_changed = true;
            }
            new_data.push(d);
        }
        if any_changed {
            *data = Arc::new(new_data);
        }
        action
    }

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

M src/main.rs => src/main.rs +1 -0
@@ 4,6 4,7 @@ use druid::{LensWrap, UiMain, UiState};

mod app_state;
mod interp_pane;
mod list;

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