forked from 1b-agent/opencadstudio-upstream
refactor(statusbar): unify popup menus
This commit is contained in:
1 parent
d5be78a187
commit
b9de8940bc
14 files changed
+478
-557
No files matched your search
@@ -1,59 +1,43 @@
|
||||
//! Isolate action menu — Isolate / Hide / End Isolation. Opened from the
|
||||
//! ISO status pill, rendered as a floating overlay above the status bar
|
||||
//! (same pattern as the scale picker). The same actions are also offered
|
||||
//! in the viewport right-click menu.
|
||||
//! Isolate / Hide / End Isolation status menu.
|
||||
|
||||
use iced::widget::{button, column, container, mouse_area, row, text};
|
||||
use iced::{Background, Border, Color, Element, Fill, Length, Rectangle, Theme};
|
||||
use iced::widget::{button, row, text};
|
||||
use iced::{Background, Color, Element, Fill, Theme};
|
||||
|
||||
use crate::app::Message;
|
||||
use crate::ui::statusbar::status_menu::Entry;
|
||||
|
||||
/// Full-screen overlay: transparent click-catcher + action list pinned
|
||||
/// bottom-right, above the status bar.
|
||||
///
|
||||
/// - `has_selection`: enables Isolate / Hide (they act on the selection).
|
||||
/// - `isolation_active`: enables End Isolation (something is hidden).
|
||||
pub fn isolate_popup_overlay(
|
||||
pub fn menu_entries(
|
||||
has_selection: bool,
|
||||
isolation_active: bool,
|
||||
pill: Option<Rectangle>,
|
||||
win: (f32, f32),
|
||||
) -> Element<'static, Message> {
|
||||
let rows = column![
|
||||
action_row(
|
||||
) -> Vec<Entry<'static>> {
|
||||
vec![
|
||||
action_entry(
|
||||
"Isolate Objects",
|
||||
has_selection,
|
||||
Message::Command("ISOLATEOBJECTS".to_string()),
|
||||
),
|
||||
action_row(
|
||||
action_entry(
|
||||
"Hide Objects",
|
||||
has_selection,
|
||||
Message::Command("HIDEOBJECTS".to_string()),
|
||||
),
|
||||
action_row(
|
||||
action_entry(
|
||||
"End Isolation",
|
||||
isolation_active,
|
||||
Message::Command("UNISOLATEOBJECTS".to_string()),
|
||||
),
|
||||
];
|
||||
]
|
||||
}
|
||||
|
||||
let panel = container(rows)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(PANEL_BG)),
|
||||
border: Border {
|
||||
color: PANEL_BORDER,
|
||||
width: 1.0,
|
||||
radius: 3.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.width(Length::Fixed(160.0));
|
||||
|
||||
let positioned = super::position_statusbar_popup(panel.into(), pill, win, 160.0, true);
|
||||
|
||||
mouse_area(positioned)
|
||||
.on_press(Message::CloseIsolatePopup)
|
||||
.into()
|
||||
fn action_entry(label: &'static str, enabled: bool, msg: Message) -> Entry<'static> {
|
||||
let row = action_row(label, enabled, msg);
|
||||
if enabled {
|
||||
Entry::close(row)
|
||||
} else {
|
||||
Entry::stay(row)
|
||||
}
|
||||
}
|
||||
|
||||
fn action_row(label: &'static str, enabled: bool, msg: Message) -> Element<'static, Message> {
|
||||
@@ -80,18 +64,6 @@ fn action_row(label: &'static str, enabled: bool, msg: Message) -> Element<'stat
|
||||
|
||||
// ── Colours ───────────────────────────────────────────────────────────────
|
||||
|
||||
const PANEL_BG: Color = Color {
|
||||
r: 0.15,
|
||||
g: 0.15,
|
||||
b: 0.15,
|
||||
a: 1.0,
|
||||
};
|
||||
const PANEL_BORDER: Color = Color {
|
||||
r: 0.32,
|
||||
g: 0.32,
|
||||
b: 0.32,
|
||||
a: 1.0,
|
||||
};
|
||||
const ROW_HOVER: Color = Color {
|
||||
r: 0.22,
|
||||
g: 0.22,
|
||||
|
||||
+10
-46
@@ -1,12 +1,10 @@
|
||||
//! Polar-tracking angle picker — sets the polar increment used by POLAR
|
||||
//! tracking. Rendered as a floating overlay above the status bar, same pattern
|
||||
//! as the units / scale pickers. Offers the common AutoCAD-style increments
|
||||
//! plus a free-text field for any custom angle (#264).
|
||||
//! Polar-tracking angle status menu.
|
||||
|
||||
use iced::widget::{button, column, container, mouse_area, row, text, text_input};
|
||||
use iced::{Background, Border, Color, Element, Fill, Length, Rectangle, Theme};
|
||||
use iced::widget::{button, container, row, text, text_input};
|
||||
use iced::{Background, Color, Element, Fill, Length, Theme};
|
||||
|
||||
use crate::app::Message;
|
||||
use crate::ui::statusbar::status_menu::Entry;
|
||||
|
||||
/// Angle increments offered in the picker, in degrees. Matches the common
|
||||
/// drafting set and adds the fine 1° step requested in #264.
|
||||
@@ -21,20 +19,15 @@ pub fn angle_label(deg: f32) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
/// Full-screen overlay: transparent click-catcher + angle list pinned
|
||||
/// bottom-right, above the status bar. `custom` is the live text of the
|
||||
/// free-entry field.
|
||||
pub fn polar_popup_overlay<'a>(
|
||||
pub fn menu_entries<'a>(
|
||||
current: f32,
|
||||
custom: &'a str,
|
||||
pill: Option<Rectangle>,
|
||||
win: (f32, f32),
|
||||
) -> Element<'a, Message> {
|
||||
let mut rows: Vec<Element<'a, Message>> = PRESETS
|
||||
) -> Vec<Entry<'a>> {
|
||||
let mut entries: Vec<Entry<'a>> = PRESETS
|
||||
.iter()
|
||||
.map(|°| {
|
||||
let active = (current - deg).abs() < 1e-3;
|
||||
angle_row(deg, active)
|
||||
Entry::close(angle_row(deg, active))
|
||||
})
|
||||
.collect();
|
||||
|
||||
@@ -54,25 +47,8 @@ pub fn polar_popup_overlay<'a>(
|
||||
.align_y(iced::Center),
|
||||
)
|
||||
.padding([5, 10]);
|
||||
rows.push(custom_row.into());
|
||||
|
||||
let panel = container(column(rows))
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(PANEL_BG)),
|
||||
border: Border {
|
||||
color: PANEL_BORDER,
|
||||
width: 1.0,
|
||||
radius: 3.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.width(Length::Fixed(120.0));
|
||||
|
||||
let positioned = super::position_statusbar_popup(panel.into(), pill, win, 120.0, true);
|
||||
|
||||
mouse_area(positioned)
|
||||
.on_press(Message::ClosePolarPopup)
|
||||
.into()
|
||||
entries.push(Entry::stay(custom_row));
|
||||
entries
|
||||
}
|
||||
|
||||
fn angle_row<'a>(deg: f32, active: bool) -> Element<'a, Message> {
|
||||
@@ -100,18 +76,6 @@ fn angle_row<'a>(deg: f32, active: bool) -> Element<'a, Message> {
|
||||
|
||||
// ── Colours ───────────────────────────────────────────────────────────────
|
||||
|
||||
const PANEL_BG: Color = Color {
|
||||
r: 0.15,
|
||||
g: 0.15,
|
||||
b: 0.15,
|
||||
a: 1.0,
|
||||
};
|
||||
const PANEL_BORDER: Color = Color {
|
||||
r: 0.32,
|
||||
g: 0.32,
|
||||
b: 0.32,
|
||||
a: 1.0,
|
||||
};
|
||||
const ROW_HOVER: Color = Color {
|
||||
r: 0.22,
|
||||
g: 0.22,
|
||||
|
||||
+10
-46
@@ -1,29 +1,24 @@
|
||||
//! Scale picker dropdown — annotation scale (model space) or viewport scale (paper space).
|
||||
//!
|
||||
//! Rendered as a floating overlay above the status bar, same pattern as snap_popup.
|
||||
//! Annotation / viewport scale status menu.
|
||||
|
||||
use iced::widget::{button, column, container, mouse_area, row, text};
|
||||
use iced::{Background, Border, Color, Element, Fill, Length, Rectangle, Theme};
|
||||
use iced::widget::{button, row, text};
|
||||
use iced::{Background, Color, Element, Fill, Theme};
|
||||
|
||||
use crate::app::Message;
|
||||
use crate::ui::statusbar::status_menu::Entry;
|
||||
|
||||
/// Full-screen overlay: transparent click-catcher + scale list panel pinned bottom-right.
|
||||
///
|
||||
/// - `is_model`: true = model space (dispatches SetAnnotationScale), false = paper space (SetViewportScale).
|
||||
/// - `current_anno_scale`: current annotation_scale from Scene (used to highlight active row in model space).
|
||||
/// - `viewport_scale`: current effective vp scale, view_height-first (used to highlight in paper space).
|
||||
/// - `file_scales`: scale list read from the drawing (`ACAD_SCALELIST`). Only
|
||||
/// scales actually stored in the file are shown; the picker never injects
|
||||
/// scales of its own.
|
||||
pub fn scale_popup_overlay(
|
||||
pub fn menu_entries(
|
||||
is_model: bool,
|
||||
current_anno_scale: f32,
|
||||
viewport_scale: Option<f64>,
|
||||
file_scales: Vec<(String, f32, f64)>,
|
||||
pill: Option<Rectangle>,
|
||||
win: (f32, f32),
|
||||
) -> Element<'static, Message> {
|
||||
let mut rows: Vec<Element<'static, Message>> = file_scales
|
||||
) -> Vec<Entry<'static>> {
|
||||
let mut entries: Vec<Entry<'static>> = file_scales
|
||||
.into_iter()
|
||||
.map(|(label, anno_scale, vp_scale)| {
|
||||
let active = if is_model {
|
||||
@@ -38,33 +33,14 @@ pub fn scale_popup_overlay(
|
||||
} else {
|
||||
Message::SetViewportScale(vp_scale)
|
||||
};
|
||||
scale_row(label, active, msg)
|
||||
Entry::close(scale_row(label, active, msg))
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Model space gets a "Manage scales..." row that opens the scale manager.
|
||||
if is_model {
|
||||
rows.push(manage_row());
|
||||
entries.push(Entry::close(manage_row()));
|
||||
}
|
||||
|
||||
let width = if is_model { 150.0 } else { 120.0 };
|
||||
let panel = container(column(rows))
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(PANEL_BG)),
|
||||
border: Border {
|
||||
color: PANEL_BORDER,
|
||||
width: 1.0,
|
||||
radius: 3.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.width(Length::Fixed(width));
|
||||
|
||||
let positioned = super::position_statusbar_popup(panel.into(), pill, win, width, true);
|
||||
|
||||
mouse_area(positioned)
|
||||
.on_press(Message::CloseScalePopup)
|
||||
.into()
|
||||
entries
|
||||
}
|
||||
|
||||
fn scale_row(label: String, active: bool, msg: Message) -> Element<'static, Message> {
|
||||
@@ -107,18 +83,6 @@ fn manage_row() -> Element<'static, Message> {
|
||||
|
||||
// ── Colours ───────────────────────────────────────────────────────────────
|
||||
|
||||
const PANEL_BG: Color = Color {
|
||||
r: 0.15,
|
||||
g: 0.15,
|
||||
b: 0.15,
|
||||
a: 1.0,
|
||||
};
|
||||
const PANEL_BORDER: Color = Color {
|
||||
r: 0.32,
|
||||
g: 0.32,
|
||||
b: 0.32,
|
||||
a: 1.0,
|
||||
};
|
||||
const ROW_HOVER: Color = Color {
|
||||
r: 0.22,
|
||||
g: 0.22,
|
||||
|
||||
@@ -1,25 +1,21 @@
|
||||
//! Selection-filter type picker — choose which entity types are selectable.
|
||||
//! Selection-filter status menu — choose which entity types are selectable.
|
||||
//! A checked row means that type can be picked; unchecking it excludes the
|
||||
//! type from interactive selection. Opened from the FILTER status pill.
|
||||
|
||||
use rustc_hash::FxHashSet as HashSet;
|
||||
|
||||
use iced::widget::{button, column, container, mouse_area, row, text};
|
||||
use iced::{Background, Border, Color, Element, Fill, Length, Rectangle, Theme};
|
||||
use iced::widget::{button, container, row, text};
|
||||
use iced::{Background, Border, Color, Element, Fill, Theme};
|
||||
|
||||
use crate::app::Message;
|
||||
use crate::ui::statusbar::status_menu::Entry;
|
||||
|
||||
/// Full-screen overlay: transparent click-catcher + type list pinned
|
||||
/// bottom-right, above the status bar.
|
||||
///
|
||||
/// - `types`: entity-type names present in the current layout.
|
||||
/// - `excluded`: types currently filtered out (unchecked).
|
||||
pub fn selection_filter_popup_overlay(
|
||||
pub fn menu_entries(
|
||||
types: Vec<String>,
|
||||
excluded: &HashSet<String>,
|
||||
pill: Option<Rectangle>,
|
||||
win: (f32, f32),
|
||||
) -> Element<'static, Message> {
|
||||
) -> Vec<Entry<'static>> {
|
||||
// "Select All / Clear All" header, mirroring the OSNAP popup: Select All
|
||||
// clears every exclusion, Clear All excludes every present type.
|
||||
let has_types = !types.is_empty();
|
||||
@@ -48,35 +44,21 @@ pub fn selection_filter_popup_overlay(
|
||||
.width(Fill)
|
||||
.padding([0, 4]);
|
||||
|
||||
let rows: Vec<Element<'static, Message>> = if types.is_empty() {
|
||||
vec![empty_row()]
|
||||
let rows: Vec<Entry<'static>> = if types.is_empty() {
|
||||
vec![Entry::stay(empty_row())]
|
||||
} else {
|
||||
types
|
||||
.into_iter()
|
||||
.map(|name| {
|
||||
let included = !excluded.contains(&name);
|
||||
type_row(name, included)
|
||||
Entry::stay(type_row(name, included))
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
|
||||
let panel = container(column![header, divider, column(rows)])
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(PANEL_BG)),
|
||||
border: Border {
|
||||
color: PANEL_BORDER,
|
||||
width: 1.0,
|
||||
radius: 3.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.width(Length::Fixed(180.0));
|
||||
|
||||
let positioned = super::position_statusbar_popup(panel.into(), pill, win, 180.0, true);
|
||||
|
||||
mouse_area(positioned)
|
||||
.on_press(Message::CloseSelectionFilterPopup)
|
||||
.into()
|
||||
let mut entries = vec![Entry::stay(header), Entry::stay(divider)];
|
||||
entries.extend(rows);
|
||||
entries
|
||||
}
|
||||
|
||||
fn type_row(name: String, included: bool) -> Element<'static, Message> {
|
||||
@@ -134,12 +116,6 @@ fn header_btn(label: &str, msg: Message, enabled: bool) -> Element<'_, Message>
|
||||
|
||||
// ── Colours ───────────────────────────────────────────────────────────────
|
||||
|
||||
const PANEL_BG: Color = Color {
|
||||
r: 0.15,
|
||||
g: 0.15,
|
||||
b: 0.15,
|
||||
a: 1.0,
|
||||
};
|
||||
const PANEL_BORDER: Color = Color {
|
||||
r: 0.32,
|
||||
g: 0.32,
|
||||
|
||||
+15
-52
@@ -1,27 +1,16 @@
|
||||
//! OpenCADStudio-style OSNAP dropdown popup panel.
|
||||
//!
|
||||
//! Rendered as a floating overlay above the status bar. The popup is only
|
||||
//! inserted into the view stack when `snap_popup_open` is true.
|
||||
//! OpenCADStudio-style OSNAP status menu.
|
||||
|
||||
use iced::widget::{button, column, container, mouse_area, row, text};
|
||||
use iced::{Background, Border, Color, Element, Fill, Length, Rectangle, Theme};
|
||||
use iced::widget::{button, container, row, text};
|
||||
use iced::{Background, Border, Color, Element, Fill, Length, Theme};
|
||||
|
||||
use crate::app::Message;
|
||||
use crate::snap::{SnapType, Snapper, ALL_SNAP_MODES};
|
||||
use crate::ui::statusbar::status_menu::Entry;
|
||||
|
||||
/// Returns a full-screen overlay element:
|
||||
/// - a transparent click-catcher that closes the popup on outside click
|
||||
/// - the popup panel pinned to the bottom-right (above the status bar)
|
||||
pub fn snap_popup_overlay<'a>(
|
||||
snapper: &'a Snapper,
|
||||
pill: Option<Rectangle>,
|
||||
win: (f32, f32),
|
||||
) -> Element<'a, Message> {
|
||||
// ── Panel content ─────────────────────────────────────────────────────
|
||||
pub fn menu_entries<'a>(snapper: &'a Snapper) -> Vec<Entry<'a>> {
|
||||
let all_on = snapper.all_on();
|
||||
let none_on = snapper.none_on();
|
||||
|
||||
// "Select All / Clear All" header row
|
||||
let header = row![
|
||||
header_btn("Select All", Message::SnapSelectAll, !all_on),
|
||||
header_btn("Clear All", Message::SnapClearAll, !none_on),
|
||||
@@ -38,34 +27,15 @@ pub fn snap_popup_overlay<'a>(
|
||||
.width(Fill)
|
||||
.padding([0, 4]);
|
||||
|
||||
// Snap mode rows
|
||||
let mut rows: Vec<Element<'_, Message>> = Vec::new();
|
||||
let mut entries = vec![Entry::stay(header), Entry::stay(divider)];
|
||||
for &(snap_type, _glyph, label) in ALL_SNAP_MODES {
|
||||
rows.push(snap_row(snap_type, label, snapper.is_on(snap_type)));
|
||||
entries.push(Entry::stay(snap_row(
|
||||
snap_type,
|
||||
label,
|
||||
snapper.is_on(snap_type),
|
||||
)));
|
||||
}
|
||||
|
||||
let panel_content = column![header, divider, column(rows)];
|
||||
|
||||
const PANEL_W: f32 = 210.0;
|
||||
let panel = container(panel_content)
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(PANEL_BG)),
|
||||
border: Border {
|
||||
color: PANEL_BORDER,
|
||||
width: 1.0,
|
||||
radius: 3.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.width(Length::Fixed(PANEL_W));
|
||||
|
||||
// Anchored just above its pill, flipping horizontally to stay on screen.
|
||||
let positioned = super::position_statusbar_popup(panel.into(), pill, win, PANEL_W, true);
|
||||
|
||||
// ── Click-catcher: closes popup on any outside click ──────────────────
|
||||
mouse_area(positioned)
|
||||
.on_press(Message::CloseSnapPopup)
|
||||
.into()
|
||||
entries
|
||||
}
|
||||
|
||||
// ── Individual snap row ───────────────────────────────────────────────────
|
||||
@@ -140,12 +110,6 @@ fn header_btn(label: &str, msg: Message, enabled: bool) -> Element<'_, Message>
|
||||
|
||||
// ── Colours ───────────────────────────────────────────────────────────────
|
||||
|
||||
const PANEL_BG: Color = Color {
|
||||
r: 0.16,
|
||||
g: 0.16,
|
||||
b: 0.16,
|
||||
a: 0.98,
|
||||
};
|
||||
const PANEL_BORDER: Color = Color {
|
||||
r: 0.32,
|
||||
g: 0.32,
|
||||
@@ -171,11 +135,11 @@ const BTN_BG: Color = Color {
|
||||
a: 1.0,
|
||||
};
|
||||
const CHECK_COLOR: Color = Color {
|
||||
r: 0.20,
|
||||
r: 0.35,
|
||||
g: 0.75,
|
||||
b: 0.35,
|
||||
b: 1.00,
|
||||
a: 1.0,
|
||||
}; // green ✓
|
||||
};
|
||||
const ICON_COLOR: Color = Color {
|
||||
r: 0.25,
|
||||
g: 0.75,
|
||||
@@ -194,4 +158,3 @@ const LABEL_OFF: Color = Color {
|
||||
b: 0.52,
|
||||
a: 1.0,
|
||||
};
|
||||
|
||||
+13
-43
@@ -1,10 +1,10 @@
|
||||
//! Drawing-units picker — sets the INSUNITS header value. Rendered as a
|
||||
//! floating overlay above the status bar, same pattern as the scale picker.
|
||||
//! Drawing-units status menu.
|
||||
|
||||
use iced::widget::{button, column, container, mouse_area, row, text};
|
||||
use iced::{Background, Border, Color, Element, Fill, Length, Rectangle, Theme};
|
||||
use iced::widget::{button, row, text};
|
||||
use iced::{Background, Color, Element, Fill, Theme};
|
||||
|
||||
use crate::app::Message;
|
||||
use crate::ui::statusbar::status_menu::Entry;
|
||||
|
||||
/// Units offered in the picker: (INSUNITS code, menu label).
|
||||
const UNITS: &[(i16, &str)] = &[
|
||||
@@ -35,35 +35,17 @@ pub fn unit_short(code: i16) -> &'static str {
|
||||
}
|
||||
}
|
||||
|
||||
/// Full-screen overlay: transparent click-catcher + units list pinned
|
||||
/// bottom-right, above the status bar.
|
||||
pub fn units_popup_overlay(
|
||||
current: i16,
|
||||
pill: Option<Rectangle>,
|
||||
win: (f32, f32),
|
||||
) -> Element<'static, Message> {
|
||||
let rows: Vec<Element<'static, Message>> = UNITS
|
||||
pub fn menu_entries(current: i16) -> Vec<Entry<'static>> {
|
||||
UNITS
|
||||
.iter()
|
||||
.map(|&(code, label)| unit_row(label, code == current, Message::SetDrawingUnits(code)))
|
||||
.collect();
|
||||
|
||||
let panel = container(column(rows))
|
||||
.style(|_: &Theme| container::Style {
|
||||
background: Some(Background::Color(PANEL_BG)),
|
||||
border: Border {
|
||||
color: PANEL_BORDER,
|
||||
width: 1.0,
|
||||
radius: 3.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
.map(|&(code, label)| {
|
||||
Entry::close(unit_row(
|
||||
label,
|
||||
code == current,
|
||||
Message::SetDrawingUnits(code),
|
||||
))
|
||||
})
|
||||
.width(Length::Fixed(140.0));
|
||||
|
||||
let positioned = super::position_statusbar_popup(panel.into(), pill, win, 140.0, true);
|
||||
|
||||
mouse_area(positioned)
|
||||
.on_press(Message::CloseUnitsPopup)
|
||||
.into()
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn unit_row(label: &'static str, active: bool, msg: Message) -> Element<'static, Message> {
|
||||
@@ -91,18 +73,6 @@ fn unit_row(label: &'static str, active: bool, msg: Message) -> Element<'static,
|
||||
|
||||
// ── Colours ───────────────────────────────────────────────────────────────
|
||||
|
||||
const PANEL_BG: Color = Color {
|
||||
r: 0.15,
|
||||
g: 0.15,
|
||||
b: 0.15,
|
||||
a: 1.0,
|
||||
};
|
||||
const PANEL_BORDER: Color = Color {
|
||||
r: 0.32,
|
||||
g: 0.32,
|
||||
b: 0.32,
|
||||
a: 1.0,
|
||||
};
|
||||
const ROW_HOVER: Color = Color {
|
||||
r: 0.22,
|
||||
g: 0.22,
|
||||
|
||||
Reference in new issue
Block a user