feat(iced): make toolbox expandable/collapsible to 2 columns

This commit is contained in:
2026-07-16 13:08:34 +03:00
parent baf77632dc
commit edae18fca0
2 changed files with 64 additions and 8 deletions
+12 -1
View File
@@ -264,6 +264,8 @@ pub struct HcieIcedApp {
pub bool_shape_a: Option<usize>, pub bool_shape_a: Option<usize>,
/// Boolean operation shape B selector index (geometry panel). /// Boolean operation shape B selector index (geometry panel).
pub bool_shape_b: Option<usize>, pub bool_shape_b: Option<usize>,
/// Whether the toolbox is expanded to 2 columns (false = single column 36px, true = 2 columns 72px).
pub sidebar_expanded: bool,
} }
/// A recently opened file entry. /// A recently opened file entry.
@@ -779,6 +781,9 @@ pub enum Message {
ViewerPrev, ViewerPrev,
ViewerNext, ViewerNext,
ViewerEnter, ViewerEnter,
// ── Toolbox expand/collapse ─────────────────────────
SidebarToggleExpanded,
} }
/// Convert a FilterType enum to the engine's string filter ID. /// Convert a FilterType enum to the engine's string filter ID.
@@ -1113,6 +1118,7 @@ impl HcieIcedApp {
vector_drag_last: None, vector_drag_last: None,
bool_shape_a: None, bool_shape_a: None,
bool_shape_b: None, bool_shape_b: None,
sidebar_expanded: false,
}; };
// Apply saved settings to tool state // Apply saved settings to tool state
@@ -5147,6 +5153,10 @@ impl HcieIcedApp {
} }
} }
Message::SidebarToggleExpanded => {
self.sidebar_expanded = !self.sidebar_expanded;
}
Message::NoOp => {} Message::NoOp => {}
Message::TabletTouch { x, y, pressed } => { Message::TabletTouch { x, y, pressed } => {
@@ -5257,7 +5267,7 @@ impl HcieIcedApp {
self.active_menu, self.active_menu,
); );
// Toolbox — fixed 36px strip on the left edge (outside dock) // Toolbox — 36px strip (single column) or 72px (2 columns) on the left edge
let toolbox = crate::sidebar::view( let toolbox = crate::sidebar::view(
&self.tool_state, &self.tool_state,
&self.fg_color, &self.fg_color,
@@ -5266,6 +5276,7 @@ impl HcieIcedApp {
self.active_tool_slot, self.active_tool_slot,
self.sub_tools_open, self.sub_tools_open,
&self.slot_last_used, &self.slot_last_used,
self.sidebar_expanded,
); );
// Merged toolbar: icon buttons + tool options in one row // Merged toolbar: icon buttons + tool options in one row
@@ -63,7 +63,10 @@ pub const TOOL_SLOTS: &[ToolSlot] = &[
}, },
]; ];
/// Build the Photopea-style fixed 36px sidebar element. /// Build the Photopea-style sidebar element.
///
/// When `expanded` is false, renders a single 36px column (default).
/// When `expanded` is true, renders a 2-column grid (72px) showing more tools.
pub fn view<'a>( pub fn view<'a>(
tool_state: &'a ToolState, tool_state: &'a ToolState,
fg_color: &'a [u8; 4], fg_color: &'a [u8; 4],
@@ -72,12 +75,30 @@ pub fn view<'a>(
active_slot: usize, active_slot: usize,
sub_tools_open: bool, sub_tools_open: bool,
slot_last_used: &'a [usize], slot_last_used: &'a [usize],
expanded: bool,
) -> Element<'a, Message> { ) -> Element<'a, Message> {
// Tool buttons — single column, with sub-tools popup support let sidebar_width = if expanded { 72 } else { 36 };
// Tool buttons — single column or 2-column grid depending on expanded state
let mut tool_list = column![].spacing(1); let mut tool_list = column![].spacing(1);
for (idx, slot) in TOOL_SLOTS.iter().enumerate() { if expanded {
let btn = tool_button(slot, tool_state, colors, idx, active_slot, sub_tools_open, slot_last_used); // 2-column layout: show tools in pairs
tool_list = tool_list.push(btn); let mut row_children = iced::widget::Row::new().spacing(1);
for (idx, slot) in TOOL_SLOTS.iter().enumerate() {
let btn = tool_button(slot, tool_state, colors, idx, active_slot, sub_tools_open, slot_last_used);
row_children = row_children.push(btn);
// Every 2 tools, start a new row
if idx % 2 == 1 || idx == TOOL_SLOTS.len() - 1 {
tool_list = tool_list.push(row_children);
row_children = iced::widget::Row::new().spacing(1);
}
}
} else {
// Single column layout (default)
for (idx, slot) in TOOL_SLOTS.iter().enumerate() {
let btn = tool_button(slot, tool_state, colors, idx, active_slot, sub_tools_open, slot_last_used);
tool_list = tool_list.push(btn);
}
} }
// Color swatches at bottom (Photopea-style: diagonal overlapping) // Color swatches at bottom (Photopea-style: diagonal overlapping)
@@ -173,7 +194,7 @@ pub fn view<'a>(
) )
) )
.padding([4, 2]) .padding([4, 2])
.width(36) .width(sidebar_width)
.height(32); .height(32);
// Separator line // Separator line
@@ -182,13 +203,37 @@ pub fn view<'a>(
..Default::default() ..Default::default()
}; };
// Toggle expand/collapse button (arrow icon)
let toggle_label = if expanded { "" } else { "" };
let toggle_btn = button(text(toggle_label).size(10).color(colors.text_secondary))
.on_press(Message::SidebarToggleExpanded)
.padding(2)
.width(sidebar_width)
.style(move |_theme: &iced::Theme, status: iced::widget::button::Status| {
match status {
iced::widget::button::Status::Hovered => iced::widget::button::Style {
background: Some(iced::Background::Color(colors.bg_hover)),
text_color: colors.text_primary,
border: iced::Border::default(),
..Default::default()
},
_ => iced::widget::button::Style {
background: Some(iced::Background::Color(iced::Color::from_rgba(0.0, 0.0, 0.0, 0.0))),
text_color: colors.text_secondary,
border: iced::Border::default(),
..Default::default()
},
}
});
let sidebar = column![ let sidebar = column![
container(tool_list).padding([2, 0]), container(tool_list).padding([2, 0]),
container(text("").height(1)).width(Length::Fill).style(sep_style), container(text("").height(1)).width(Length::Fill).style(sep_style),
color_section, color_section,
container(reset_btn).padding([2, 4]), container(reset_btn).padding([2, 4]),
container(toggle_btn).padding([2, 0]),
] ]
.width(36) .width(sidebar_width)
.spacing(0); .spacing(0);
container(sidebar) container(sidebar)