forked from 1b-agent/opencadstudio-upstream
ui: fit the context menu above the command area; right-button control actions
Rows are 22 px (as the grip popup), the edit menu drops Select All and Zoom Extents (both stay on the idle menu, matching the commercial solutions' edit menu), and cursor-anchored panels now reserve the overlaid prompt lines (CLIPROMPTLINES) as well as the input row, so a long menu is never hidden behind the command area — it scrolls inside the visible canvas instead. The automation port gains `pointer_right_press` / `pointer_right_release` actions so scripts and MCP clients can exercise the context menu. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
426af2d3a5
commit
96df4928c7
5 files changed
+39
-10
No files matched your search
@@ -267,7 +267,8 @@ impl OpenCADStudio {
|
||||
"mtext_cancel" => Message::MTextCancel,
|
||||
"text_input" => Message::TextInlineInput(string(req, "value")?.into()),
|
||||
"text_commit" => Message::TextInlineOk,
|
||||
"pointer_move" | "pointer_press" | "pointer_release" => {
|
||||
"pointer_move" | "pointer_press" | "pointer_release" | "pointer_right_press"
|
||||
| "pointer_right_release" => {
|
||||
let x = req["x"]
|
||||
.as_f64()
|
||||
.filter(|v| v.is_finite())
|
||||
@@ -289,6 +290,10 @@ impl OpenCADStudio {
|
||||
let event = match name {
|
||||
"pointer_press" => self.update(Message::ViewportLeftPress),
|
||||
"pointer_release" => self.update(Message::ViewportLeftRelease),
|
||||
// Right button: the context menu / Enter behaviour chosen in
|
||||
// Options (see `Message::ViewportRightRelease`).
|
||||
"pointer_right_press" => self.update(Message::ViewportRightPress),
|
||||
"pointer_right_release" => self.update(Message::ViewportRightRelease),
|
||||
_ => Task::none(),
|
||||
};
|
||||
return Ok(Task::batch([move_task, event]));
|
||||
|
||||
+1
-1
@@ -1603,7 +1603,7 @@ bg={bg_ms:.1}ms n={view_count}"
|
||||
crate::ui::command_line::history_max_height(self.win_size.1),
|
||||
) + 72.0
|
||||
} else {
|
||||
34.0
|
||||
34.0 + self.command_line.overlay_lines_height()
|
||||
};
|
||||
// Quick Properties: stay near the selection cursor, flipping around
|
||||
// it as needed to remain inside the visible drawing area.
|
||||
|
||||
@@ -994,6 +994,7 @@ impl Widget<Message, Theme, iced::Renderer> for ClampedPin<'_> {
|
||||
&layout::Limits::new(Size::ZERO, safe),
|
||||
);
|
||||
let content = node.size();
|
||||
let bottom_inset = self.bottom_inset;
|
||||
|
||||
let max_x = (max.width - MARGIN - content.width).max(MARGIN);
|
||||
let right = self.anchor.x + self.gap + self.offset.x;
|
||||
@@ -1006,8 +1007,7 @@ impl Widget<Message, Theme, iced::Renderer> for ClampedPin<'_> {
|
||||
max_x
|
||||
};
|
||||
|
||||
let max_y =
|
||||
(max.height - self.bottom_inset - MARGIN - content.height).max(MARGIN);
|
||||
let max_y = (max.height - bottom_inset - MARGIN - content.height).max(MARGIN);
|
||||
let below = self.anchor.y + self.gap + self.offset.y;
|
||||
let above = self.anchor.y - self.gap - content.height;
|
||||
let y = if below <= max_y {
|
||||
@@ -1182,7 +1182,7 @@ pub(super) fn viewport_context_menu_overlay(
|
||||
.align_y(iced::Center);
|
||||
let enabled = !children.is_empty();
|
||||
let mut btn = button(content)
|
||||
.padding([4, 12])
|
||||
.padding([3, 12])
|
||||
.width(Fill)
|
||||
.height(Length::Fixed(MENU_ROW_H))
|
||||
.style(move |theme: &Theme, status| context_menu_row_style(theme, status, is_hl));
|
||||
@@ -1263,9 +1263,9 @@ fn context_menu_row(item: &MenuItem, indent: f32, highlighted: bool) -> Element<
|
||||
}
|
||||
let mut btn = button(content)
|
||||
.padding(iced::Padding {
|
||||
top: 4.0,
|
||||
top: 3.0,
|
||||
right: 12.0,
|
||||
bottom: 4.0,
|
||||
bottom: 3.0,
|
||||
left: 12.0 + indent,
|
||||
})
|
||||
.width(Fill)
|
||||
|
||||
@@ -500,6 +500,24 @@ impl CommandLine {
|
||||
e.pinned || (self.fade_ms > 0 && e.created_at.elapsed().as_secs_f32() < self.fade_secs())
|
||||
}
|
||||
|
||||
/// Height of the overlaid prompt lines currently shown above the input
|
||||
/// row (CLIPROMPTLINES, minus lines that have faded out), in pixels. Lets
|
||||
/// cursor-anchored panels stay clear of the whole command area, not just
|
||||
/// the input row.
|
||||
pub fn overlay_lines_height(&self) -> f32 {
|
||||
if self.cliprompt_lines == 0 || self.history_open {
|
||||
return 0.0;
|
||||
}
|
||||
let visible = self
|
||||
.history
|
||||
.iter()
|
||||
.filter(|e| self.entry_visible(e))
|
||||
.count()
|
||||
.min(self.cliprompt_lines as usize);
|
||||
// Text size 12 (line height ≈ 15.6) plus the 1 px padding each side.
|
||||
visible as f32 * 18.0
|
||||
}
|
||||
|
||||
/// Visible overlay count respecting CLIPROMPTLINES (0–50). Used in tests.
|
||||
#[cfg(test)]
|
||||
pub fn visible_history_count(&self) -> usize {
|
||||
|
||||
@@ -20,7 +20,7 @@ use crate::t;
|
||||
|
||||
/// Fixed row height used by the renderer, so `ContextMenu::default_row_y`
|
||||
/// (which anchors the default row under the cursor) agrees with the layout.
|
||||
pub const MENU_ROW_H: f32 = 24.0;
|
||||
pub const MENU_ROW_H: f32 = 22.0;
|
||||
/// Height of a separator row (1 px line plus its vertical padding).
|
||||
pub const MENU_SEP_H: f32 = 7.0;
|
||||
/// Vertical padding inside the menu panel above the first row.
|
||||
@@ -576,7 +576,11 @@ fn idle_rows(
|
||||
// ── Navigation ─────────────────────────────────────────────────────
|
||||
rows.push(MenuRow::Item(cmd(t!("Pan").into_owned(), "PAN")));
|
||||
rows.push(MenuRow::Item(cmd(t!("Zoom").into_owned(), "ZOOM")));
|
||||
rows.push(MenuRow::Item(cmd(t!("Zoom Extents").into_owned(), "ZOOM EXTENTS")));
|
||||
// The edit menu is long already; Zoom Extents stays on the idle menu
|
||||
// (and on the middle button's double-click), as in AutoCAD.
|
||||
if !has_selection {
|
||||
rows.push(MenuRow::Item(cmd(t!("Zoom Extents").into_owned(), "ZOOM EXTENTS")));
|
||||
}
|
||||
rows.push(MenuRow::Separator);
|
||||
|
||||
// ── Selection tools ────────────────────────────────────────────────
|
||||
@@ -594,7 +598,9 @@ fn idle_rows(
|
||||
MenuAction::DeselectAll,
|
||||
)));
|
||||
}
|
||||
rows.push(MenuRow::Item(cmd(t!("Select All").into_owned(), "SELECTALL")));
|
||||
if !has_selection {
|
||||
rows.push(MenuRow::Item(cmd(t!("Select All").into_owned(), "SELECTALL")));
|
||||
}
|
||||
rows.push(MenuRow::Item(MenuItem::new(
|
||||
t!("Quick Select...").into_owned(),
|
||||
MenuAction::QuickSelect,
|
||||
|
||||
Reference in new issue
Block a user