From c2df63c4ccd38e30d8ceb77abd9174e216f8a664 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Thu, 23 Apr 2026 09:18:01 +0300 Subject: [PATCH] feat: add clickable space-mode button to status bar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the static MODEL/LAYOUT pill with an interactive button that shows and toggles the current editing context: Model tab → "MODEL" (non-clickable, informational) Layout PSPACE → "PAPER" (click → MspaceCommand: enter MSPACE) Layout MSPACE → "MODEL" (click → ExitViewport: return to PSPACE) Highlights in blue when in MSPACE to signal active model editing. Mirrors the AutoCAD status-bar convention for MODEL/PAPER switching. Co-Authored-By: Claude Sonnet 4.6 --- src/app/view.rs | 1 + src/ui/statusbar.rs | 61 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/app/view.rs b/src/app/view.rs index 4c63d5f00..4ea0c513c 100644 --- a/src/app/view.rs +++ b/src/app/view.rs @@ -313,6 +313,7 @@ impl H7CAD { self.layout_rename_state.as_ref(), tab.scene.first_viewport_scale(), tab.scene.viewport_count(), + tab.scene.active_viewport.is_some(), ) ] .width(Fill) diff --git a/src/ui/statusbar.rs b/src/ui/statusbar.rs index 895ba0a53..dd8d01c58 100644 --- a/src/ui/statusbar.rs +++ b/src/ui/statusbar.rs @@ -38,6 +38,8 @@ impl StatusBar { viewport_scale: Option, // Number of user viewports in the current paper layout (0 = model space). viewport_count: usize, + // True when the user is editing inside a paper-space viewport (MSPACE). + in_mspace: bool, ) -> Element<'a, Message> { let menu_btn = button(text("≡").size(14).color(ICON_COLOR)) .on_press(Message::Command("MENU".into())) @@ -59,11 +61,6 @@ impl StatusBar { let osnap_active = snapper.is_active(); let snap_grid_on = snapper.is_on(crate::snap::SnapType::Grid) && snapper.snap_enabled; - let space_label = if current_layout == "Model" { - "MODEL" - } else { - "LAYOUT" - }; let scale_label = format_scale(viewport_scale); let vp_label = if viewport_count > 0 { format!("{} VP", viewport_count) @@ -93,7 +90,10 @@ impl StatusBar { "Object Snap Tracking\nF11" ), osnap_btn(osnap_active, snapper.snap_enabled, popup_open), - status_pill(space_label), + tip( + space_mode_btn(¤t_layout, in_mspace), + "Toggle Model/Paper space\nDouble-click viewport to enter MSPACE", + ), status_pill(scale_label), ] .spacing(2); @@ -448,6 +448,55 @@ fn space_tab<'a>(label: String, is_active: bool, rename_edit: Option<&'a str>) - } } +/// Space-mode toggle button in the status bar. +/// +/// - Model tab → "MODEL" (non-clickable, informational) +/// - Layout, PSPACE → "PAPER" (click → MspaceCommand: enter MSPACE) +/// - Layout, MSPACE → "MODEL" (click → ExitViewport: return to PSPACE) +fn space_mode_btn(current_layout: &str, in_mspace: bool) -> Element<'static, Message> { + let is_model_tab = current_layout == "Model"; + + // Labels and styling follow AutoCAD convention: + // PAPER = currently in paper-space editing + // MODEL = currently in model-space editing (either the Model tab or MSPACE) + let (label, active, on_press) = if is_model_tab { + ("MODEL", false, None) + } else if in_mspace { + ("MODEL", true, Some(Message::ExitViewport)) + } else { + ("PAPER", false, Some(Message::MspaceCommand)) + }; + + let text_color = if active { SNAP_BORDER_ON } else { OSNAP_OFF_TEXT }; + let bg_normal = if active { SNAP_ON_BG } else { SNAP_OFF_BG }; + let bg_hover = if active { SNAP_ON_HOVER } else { SNAP_OFF_HOVER }; + let border_color = if active { SNAP_BORDER_ON } else { BORDER_COLOR }; + + let clickable = on_press.is_some(); + let mut btn = button(text(label).size(10).color(text_color)) + .style(move |_: &Theme, status| button::Style { + background: Some(Background::Color(match status { + button::Status::Hovered if clickable => bg_hover, + _ => bg_normal, + })), + border: Border { + color: border_color, + width: 1.0, + radius: 2.0.into(), + }, + text_color, + shadow: iced::Shadow::default(), + snap: false, + }) + .padding([2, 6]); + + if let Some(msg) = on_press { + btn = btn.on_press(msg); + } + + btn.into() +} + fn status_pill(label: impl Into) -> Element<'static, Message> { container(text(label.into()).size(10).color(Color { r: 0.65,