diff --git a/hcie-iced-app/crates/hcie-iced-gui/src/app.rs b/hcie-iced-app/crates/hcie-iced-gui/src/app.rs index c6bf103..e6f8c6a 100644 --- a/hcie-iced-app/crates/hcie-iced-gui/src/app.rs +++ b/hcie-iced-app/crates/hcie-iced-gui/src/app.rs @@ -264,6 +264,8 @@ pub struct HcieIcedApp { pub bool_shape_a: Option, /// Boolean operation shape B selector index (geometry panel). pub bool_shape_b: Option, + /// 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. @@ -779,6 +781,9 @@ pub enum Message { ViewerPrev, ViewerNext, ViewerEnter, + + // ── Toolbox expand/collapse ───────────────────────── + SidebarToggleExpanded, } /// Convert a FilterType enum to the engine's string filter ID. @@ -1113,6 +1118,7 @@ impl HcieIcedApp { vector_drag_last: None, bool_shape_a: None, bool_shape_b: None, + sidebar_expanded: false, }; // Apply saved settings to tool state @@ -5147,6 +5153,10 @@ impl HcieIcedApp { } } + Message::SidebarToggleExpanded => { + self.sidebar_expanded = !self.sidebar_expanded; + } + Message::NoOp => {} Message::TabletTouch { x, y, pressed } => { @@ -5257,7 +5267,7 @@ impl HcieIcedApp { 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( &self.tool_state, &self.fg_color, @@ -5266,6 +5276,7 @@ impl HcieIcedApp { self.active_tool_slot, self.sub_tools_open, &self.slot_last_used, + self.sidebar_expanded, ); // Merged toolbar: icon buttons + tool options in one row diff --git a/hcie-iced-app/crates/hcie-iced-gui/src/sidebar/mod.rs b/hcie-iced-app/crates/hcie-iced-gui/src/sidebar/mod.rs index cdbea25..24d605f 100644 --- a/hcie-iced-app/crates/hcie-iced-gui/src/sidebar/mod.rs +++ b/hcie-iced-app/crates/hcie-iced-gui/src/sidebar/mod.rs @@ -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>( tool_state: &'a ToolState, fg_color: &'a [u8; 4], @@ -72,12 +75,30 @@ pub fn view<'a>( active_slot: usize, sub_tools_open: bool, slot_last_used: &'a [usize], + expanded: bool, ) -> 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); - 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); + if expanded { + // 2-column layout: show tools in pairs + 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) @@ -173,7 +194,7 @@ pub fn view<'a>( ) ) .padding([4, 2]) - .width(36) + .width(sidebar_width) .height(32); // Separator line @@ -182,13 +203,37 @@ pub fn view<'a>( ..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![ container(tool_list).padding([2, 0]), container(text("").height(1)).width(Length::Fill).style(sep_style), color_section, container(reset_btn).padding([2, 4]), + container(toggle_btn).padding([2, 0]), ] - .width(36) + .width(sidebar_width) .spacing(0); container(sidebar)