use druid::{ widget::{Container, Controller, Flex, Label, Padding, Painter, Svg, SvgData}, Color, Data, LifeCycle, RenderContext, Widget, WidgetExt, }; use crate::AppData; pub fn app_header_ui() -> impl Widget { let open_svg = include_str!("../assets/folder-open.svg") .parse::() .unwrap_or_default(); let disk_svg = include_str!("../assets/floppy-disk.svg") .parse::() .unwrap_or_default(); let run_svg = include_str!("../assets/play.svg") .parse::() .unwrap_or_default(); let plus_svg = include_str!("../assets/plus.svg") .parse::() .unwrap_or_default(); let abacus_svg = include_str!("../assets/abacus.svg") .parse::() .unwrap_or_default(); Container::new( Flex::row() .must_fill_main_axis(true) .with_spacer(10.0) .with_child( Container::new(Padding::new(5.0, Svg::new(abacus_svg).fix_width(20.0))) .controller(ToolbarButtonController::new(Color::rgb8(50, 50, 50))) .on_click(|_ctx, _, _| {}), ) .with_spacer(20.0) .with_child( Container::new(Padding::new(10.0, Svg::new(open_svg).fix_width(10.0))) .controller(ToolbarButtonController::new(Color::rgb8(50, 50, 50))) .on_click(|ctx, _, _| { let abacus = druid::FileSpec::new("Abacus File", &["abacus"]); let json = druid::FileSpec::new("JSON File", &["json"]); let open_dialog_options = druid::FileDialogOptions::new() .allowed_types(vec![abacus, json]) .default_type(abacus) .name_label("Source") .title("Open Workbook") .button_text("Open"); ctx.submit_command( druid::commands::SHOW_OPEN_PANEL.with(open_dialog_options), ); }), ) .with_spacer(10.0) .with_child( Container::new(Padding::new(10.0, Svg::new(disk_svg).fix_width(10.0))) .controller(ToolbarButtonController::new(Color::rgb8(50, 50, 50))) .on_click(|ctx, data: &mut AppData, _| { let abacus = druid::FileSpec::new("Abacus File", &["abacus"]); let json = druid::FileSpec::new("JSON File", &["json"]); if data.filename.is_some() { data.save(); } else { let save_dialog_options = druid::FileDialogOptions::new() .allowed_types(vec![abacus, json]) .default_type(abacus) .name_label("Target") .title("Save workbook") .button_text("Save"); ctx.submit_command( druid::commands::SHOW_SAVE_PANEL.with(save_dialog_options), ); } }), ) .with_spacer(20.0) .with_flex_child( Padding::new( 5.0, Container::new(Padding::new( 3.0, Label::dynamic(|data: &AppData, _| { data.filename .clone() .unwrap_or_else(|| String::from("Scratch file")) }) .with_text_size(12.0) .center(), )) .background(Color::rgb8(35, 35, 35)) .rounded(4.0) .border(Color::rgb8(10, 10, 10), 1.0) .expand_width(), ), 1.0, ) .with_spacer(20.0) .with_child( Container::new(Padding::new(10.0, Svg::new(plus_svg).fix_width(10.0))) .controller(ToolbarButtonController::new(Color::rgb8(50, 50, 50))) .on_click(|_ctx, data: &mut AppData, _env| { data.blocks.push_back(crate::Block::new( &format!("Block #{}", data.blocks.len() + 1), data.blocks.len(), )); }), ) .with_spacer(10.0) .with_child( Container::new(Padding::new(10.0, Svg::new(run_svg).fix_width(10.0))) .controller(ToolbarButtonController::new(Color::rgb8(50, 50, 50))) .on_click(|_ctx, data: &mut AppData, _env| data.process()), ) .with_spacer(20.0) .expand_width(), ) .background(Color::rgb8(20, 20, 20)) } pub fn header_separater() -> impl Widget { Painter::new(|ctx, _data, _env| { let rect = ctx.size().to_rect(); ctx.fill(rect, &Color::rgb8(0, 0, 0)) }) .fix_height(1.0) } pub struct ToolbarButtonController { color: Color, } impl ToolbarButtonController { pub fn new(color: Color) -> Self { Self { color } } } impl Controller> for ToolbarButtonController { fn event( &mut self, child: &mut Container, ctx: &mut druid::EventCtx, event: &druid::Event, data: &mut T, env: &druid::Env, ) { ctx.set_cursor(&druid::Cursor::Pointer); child.event(ctx, event, data, env) } fn lifecycle( &mut self, child: &mut Container, ctx: &mut druid::LifeCycleCtx, event: &druid::LifeCycle, data: &T, env: &druid::Env, ) { match event { LifeCycle::HotChanged(true) => { child.set_background(self.color.clone()); ctx.request_paint(); } LifeCycle::HotChanged(false) => { child.set_background(Color::TRANSPARENT); ctx.request_paint(); } _ => {} } child.lifecycle(ctx, event, data, env) } fn update( &mut self, child: &mut Container, ctx: &mut druid::UpdateCtx, old_data: &T, data: &T, env: &druid::Env, ) { child.update(ctx, old_data, data, env) } }