pixcil/widget/
tool_box.rs1use super::{FixedSizeWidget, Widget, button::ButtonWidget, select_box::SelectBoxWidget};
2use crate::{
3 app::App,
4 asset::{ButtonKind, IconId},
5 canvas_ext::CanvasExt,
6 color,
7 event::Event,
8 model::tool::ToolKind,
9};
10use orfail::{OrFail, Result};
11use pagurus::spatial::{Position, Region, Size};
12use pagurus::{event::Key, image::Canvas};
13
14const MARGIN: u32 = 8;
15
16#[derive(Debug)]
17pub struct ToolBoxWidget {
18 region: Region,
19 tools: SelectBoxWidget,
20 current: ToolKind,
21}
22
23impl ToolBoxWidget {
24 fn handle_tool_change(&mut self, app: &mut App) -> Result<()> {
25 self.tools
26 .on_selected(|state, button| {
27 if state.is_selected() {
28 let next = ToolKind::from_icon(button.icon()).or_fail()?;
29 button.set_kind(ButtonKind::BasicPressed);
30
31 self.current = next;
32 app.models_mut().tool.current = next;
33 } else {
34 button.set_kind(ButtonKind::Basic);
35 }
36 app.request_redraw(button.region());
37 Ok(())
38 })
39 .or_fail()
40 }
41
42 fn handle_key_event(&mut self, app: &mut App, event: &mut Event) -> Result<bool> {
43 let Event::Key { event, consumed } = event else {
44 return Ok(false);
45 };
46 let index = match event.key {
47 Key::Tab => {
48 let n = self.tools.buttons().len();
49 (self.tools.selected() + 1) % n
50 }
51 Key::BackTab => {
52 let n = self.tools.buttons().len();
53 (self.tools.selected() + n - 1) % n
54 }
55 Key::Char('p') => 0, Key::Char('d') => 1, Key::Char('f') => 2, Key::Char('e') => 3, Key::Char('s') => 4, Key::Char('m') => 5, _ => {
62 return Ok(false);
63 }
64 };
65 self.tools.select(app, index).or_fail()?;
66 *consumed = true;
67 Ok(true)
68 }
69}
70
71impl Default for ToolBoxWidget {
72 fn default() -> Self {
73 let mut buttons = vec![
74 ButtonWidget::new(ButtonKind::Basic, IconId::Pick),
75 ButtonWidget::new(ButtonKind::Basic, IconId::PenStroke),
76 ButtonWidget::new(ButtonKind::Basic, IconId::Bucket),
77 ButtonWidget::new(ButtonKind::Basic, IconId::Erase),
78 ButtonWidget::new(ButtonKind::Basic, IconId::Lasso),
79 ButtonWidget::new(ButtonKind::Basic, IconId::Move),
80 ];
81
82 buttons[0].set_disabled_callback(|app| app.models().tool.current == ToolKind::Pick);
83 buttons[1].set_disabled_callback(|app| app.models().tool.current == ToolKind::Draw);
84 buttons[2].set_disabled_callback(|app| app.models().tool.current == ToolKind::Fill);
85 buttons[3].set_disabled_callback(|app| app.models().tool.current == ToolKind::Erase);
86 buttons[4].set_disabled_callback(|app| app.models().tool.current == ToolKind::Select);
87 buttons[5].set_disabled_callback(|app| app.models().tool.current == ToolKind::Move);
88
89 Self {
90 region: Default::default(),
91 tools: SelectBoxWidget::new(buttons, 1).expect("unreachable"),
92 current: ToolKind::default(),
93 }
94 }
95}
96
97impl Widget for ToolBoxWidget {
98 fn region(&self) -> Region {
99 self.region
100 }
101
102 fn render(&self, app: &App, canvas: &mut Canvas) {
103 canvas.fill_rectangle(self.region, color::BUTTONS_BACKGROUND);
104 canvas.draw_rectangle(self.region, color::WINDOW_BORDER);
105 self.tools.render_if_need(app, canvas);
106 }
107
108 fn handle_event(&mut self, app: &mut App, event: &mut Event) -> Result<()> {
109 if !self.handle_key_event(app, event).or_fail()? {
110 self.tools.handle_event(app, event).or_fail()?;
111 }
112 self.handle_tool_change(app).or_fail()?;
113 event.consume_if_contained(self.region);
114 Ok(())
115 }
116
117 fn handle_event_after(&mut self, app: &mut App) -> Result<()> {
118 if self.current != app.models().tool.current {
119 let next = app.models().tool.current;
120 let i = self
121 .tools
122 .buttons()
123 .iter()
124 .position(|b| Some(next) == ToolKind::from_icon(b.icon()).ok())
125 .or_fail()?;
126 self.tools.select(app, i).or_fail()?;
127 self.handle_tool_change(app).or_fail()?;
128 }
129
130 for child in self.children() {
131 child.handle_event_after(app).or_fail()?;
132 }
133 Ok(())
134 }
135
136 fn children(&mut self) -> Vec<&mut dyn Widget> {
137 self.tools.children()
138 }
139}
140
141impl FixedSizeWidget for ToolBoxWidget {
142 fn requiring_size(&self, app: &App) -> Size {
143 self.tools.requiring_size(app) + (MARGIN * 2)
144 }
145
146 fn set_position(&mut self, app: &App, position: Position) {
147 self.region = Region::new(position, self.requiring_size(app));
148 self.tools.set_position(app, position + MARGIN as i32);
149 }
150}