126 lines
4.1 KiB
Rust
Executable File
126 lines
4.1 KiB
Rust
Executable File
use std::collections::HashMap;
|
|
|
|
use druid::{BoxConstraints, Data, Env, Event, EventCtx, LayoutCtx, LensExt, LifeCycle, LifeCycleCtx, PaintCtx, Point, Rect, RenderContext, Size, UpdateCtx, Widget, WidgetExt, WidgetPod};
|
|
use druid::widget::{CrossAxisAlignment, Flex, MainAxisAlignment};
|
|
|
|
use crate::app::App;
|
|
use crate::Notes;
|
|
use crate::util::markdown_renderer::MarkdownRenderer;
|
|
|
|
#[derive(Default)]
|
|
pub struct NotesViewer {
|
|
children: Vec<((usize, usize), WidgetPod<App, Box<dyn Widget<App>>>)>,
|
|
rects: HashMap<(usize, usize), Rect>,
|
|
highlighted: Option<(usize, usize)>,
|
|
}
|
|
|
|
impl NotesViewer {
|
|
pub fn new() -> Self {
|
|
Self::default()
|
|
}
|
|
|
|
pub fn rect_for_step(&self, step: usize, area: usize) -> Option<Rect> {
|
|
self.rects.get(&(step, area)).cloned()
|
|
}
|
|
|
|
fn rerender(&mut self, notes: &Option<(Notes, String)>) {
|
|
self.children.clear();
|
|
|
|
let (notes, path) = match notes {
|
|
Some(notes) => notes,
|
|
None => return,
|
|
};
|
|
|
|
for step_idx in 0..notes.steps.len() {
|
|
let step = ¬es.steps[step_idx];
|
|
|
|
for area_idx in 0..step.areas.len() {
|
|
let area = &step.areas[area_idx];
|
|
|
|
let mut flex: Flex<App> = Flex::column()
|
|
.main_axis_alignment(MainAxisAlignment::Start)
|
|
.cross_axis_alignment(CrossAxisAlignment::Start);
|
|
let location_name = crate::util::location_name(area.area);
|
|
for child in Self::render_markdown(&format!("#### {}\n{}", location_name, area.steps), path, (step_idx, area_idx)) {
|
|
flex.add_child(child);
|
|
}
|
|
|
|
self.children.push((
|
|
(step_idx, area_idx),
|
|
WidgetPod::new(flex).boxed(),
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
fn render_markdown(src: &str, path: &str, pos: (usize, usize)) -> Vec<Box<dyn Widget<App>>> {
|
|
MarkdownRenderer::new(src, path, pos).render()
|
|
}
|
|
}
|
|
|
|
impl Widget<App> for NotesViewer {
|
|
fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut App, env: &Env) {
|
|
for (_, child) in &mut self.children {
|
|
child.event(ctx, event, data, env);
|
|
}
|
|
}
|
|
|
|
fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &App, env: &Env) {
|
|
for (_, child) in &mut self.children {
|
|
child.lifecycle(ctx, event, data, env);
|
|
}
|
|
|
|
match event {
|
|
LifeCycle::WidgetAdded => {
|
|
self.rerender(&data.notes);
|
|
ctx.children_changed();
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
fn update(&mut self, ctx: &mut UpdateCtx, old_data: &App, data: &App, env: &Env) {
|
|
for (_, child) in &mut self.children {
|
|
child.update(ctx, data, env);
|
|
}
|
|
|
|
// if the notes have changed, we need to rerender them
|
|
if !old_data.notes.same(&data.notes) {
|
|
self.rerender(&data.notes);
|
|
ctx.children_changed();
|
|
}
|
|
|
|
// if the note state has changed, a new section needs to be highlighted
|
|
if !old_data.notes_state.same(&data.notes_state) {
|
|
self.highlighted = data.notes_state
|
|
.as_ref()
|
|
.and_then(|state| state.step_idx.map(|step_idx| (step_idx, state.area_idx)));
|
|
ctx.request_paint();
|
|
}
|
|
}
|
|
|
|
fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints, data: &App, env: &Env) -> Size {
|
|
self.rects.clear();
|
|
|
|
let mut pos = Point::ZERO;
|
|
let mut size = bc.min();
|
|
|
|
for ((step, area), child) in &mut self.children {
|
|
let child_size = child.layout(ctx, bc, data, env);
|
|
child.set_origin(ctx, data, env, pos);
|
|
self.rects.insert((*step, *area), Rect::from_origin_size(pos, child_size));
|
|
pos.y += child_size.height;
|
|
size.height += child_size.height;
|
|
size.width = child_size.width.max(size.width);
|
|
}
|
|
|
|
size
|
|
}
|
|
|
|
fn paint(&mut self, ctx: &mut PaintCtx, data: &App, env: &Env) {
|
|
for (_, child) in &mut self.children {
|
|
child.paint(ctx, data, env);
|
|
}
|
|
}
|
|
}
|