pixcil/widget/
tool_box.rs

1use 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, // ToolKind::Pick
56            Key::Char('d') => 1, // ToolKind::Draw
57            Key::Char('f') => 2, // ToolKind::Fill
58            Key::Char('e') => 3, // ToolKind::Erase
59            Key::Char('s') => 4, // ToolKind::Select
60            Key::Char('m') => 5, // ToolKind::Move
61            _ => {
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}