forked from 1b-agent/opencadstudio-upstream
fix(xref): Change Path Type flyout with hover reveal
The Change Path Type row shows a right-triangle affordance and reveals the Make Absolute / Make Relative / Remove Path submenu beside it. The flyout opens on hover only and a shared submenu-row style removes dead code from the earlier iterations.
This commit is contained in:
1 parent
1c8e3ff3d7
commit
10758f3e59
4 files changed
+80
-36
No files matched your search
@@ -2403,6 +2403,8 @@ pub enum Message {
|
|||||||
/// Prefill the command line for Find & Replace across references
|
/// Prefill the command line for Find & Replace across references
|
||||||
/// (`XREF Path Find <old> <new>`); the CLI parses and runs it.
|
/// (`XREF Path Find <old> <new>`); the CLI parses and runs it.
|
||||||
XrefFindReplacePrompt,
|
XrefFindReplacePrompt,
|
||||||
|
XrefRowChangePathEnter,
|
||||||
|
XrefRowChangePathLeave,
|
||||||
LayerToggleVisible(usize),
|
LayerToggleVisible(usize),
|
||||||
LayerToggleLock(usize),
|
LayerToggleLock(usize),
|
||||||
LayerToggleFreeze(usize),
|
LayerToggleFreeze(usize),
|
||||||
|
|||||||
@@ -1191,8 +1191,8 @@ pub(super) fn on_ribbon_tool_click(&mut self, tool_id: String, event: ModuleEven
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
XrefPaletteOp::Open | XrefPaletteOp::Attach => {
|
XrefPaletteOp::Open => {
|
||||||
// Handled in update/mod.rs XrefRowOp dispatch (navigation / picker), not here.
|
// Handled in update/mod.rs XrefRowOp dispatch (navigation), not here.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if done > 0 {
|
if done > 0 {
|
||||||
|
|||||||
@@ -2333,9 +2333,18 @@ impl OpenCADStudio {
|
|||||||
}
|
}
|
||||||
// Row-scoped op: select the row first, then run the op.
|
// Row-scoped op: select the row first, then run the op.
|
||||||
self.xref_manager.right_click_select(index);
|
self.xref_manager.right_click_select(index);
|
||||||
|
self.xref_manager.row_change_path_open = false;
|
||||||
self.xref_manager_op(op);
|
self.xref_manager_op(op);
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
Message::XrefRowChangePathEnter => {
|
||||||
|
self.xref_manager.row_change_path_open = true;
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
Message::XrefRowChangePathLeave => {
|
||||||
|
self.xref_manager.row_change_path_open = false;
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
Message::XrefFindReplacePrompt => {
|
Message::XrefFindReplacePrompt => {
|
||||||
// No fields in the panel: prefill the command line so the
|
// No fields in the panel: prefill the command line so the
|
||||||
// existing `XREF Path Find <old> <new>` parsing runs it.
|
// existing `XREF Path Find <old> <new>` parsing runs it.
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ pub struct XrefManagerPanel {
|
|||||||
pub attach_open: bool,
|
pub attach_open: bool,
|
||||||
pub refresh_open: bool,
|
pub refresh_open: bool,
|
||||||
pub path_open: bool,
|
pub path_open: bool,
|
||||||
|
pub row_change_path_open: bool,
|
||||||
/// Decoded preview images keyed by `(entry key, resolved path)` — rebuilt
|
/// Decoded preview images keyed by `(entry key, resolved path)` — rebuilt
|
||||||
/// on every refresh for the anchor entry only, so the per-frame `view`
|
/// on every refresh for the anchor entry only, so the per-frame `view`
|
||||||
/// stays pure and file I/O never happens during rendering.
|
/// stays pure and file I/O never happens during rendering.
|
||||||
@@ -160,6 +161,7 @@ impl Default for XrefManagerPanel {
|
|||||||
attach_open: false,
|
attach_open: false,
|
||||||
refresh_open: false,
|
refresh_open: false,
|
||||||
path_open: false,
|
path_open: false,
|
||||||
|
row_change_path_open: false,
|
||||||
previews: HashMap::new(),
|
previews: HashMap::new(),
|
||||||
show_preview: false,
|
show_preview: false,
|
||||||
}
|
}
|
||||||
@@ -901,6 +903,7 @@ impl XrefManagerPanel {
|
|||||||
is_expanded,
|
is_expanded,
|
||||||
table_w,
|
table_w,
|
||||||
&self.col_widths,
|
&self.col_widths,
|
||||||
|
self.row_change_path_open,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
// ── Reference table: header + rows scroll together. A horizontal
|
// ── Reference table: header + rows scroll together. A horizontal
|
||||||
@@ -946,6 +949,7 @@ impl XrefManagerPanel {
|
|||||||
is_sel,
|
is_sel,
|
||||||
has_children,
|
has_children,
|
||||||
is_expanded,
|
is_expanded,
|
||||||
|
self.row_change_path_open,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
scrollable(tree_col).height(Length::Fixed(self.table_h)).into()
|
scrollable(tree_col).height(Length::Fixed(self.table_h)).into()
|
||||||
@@ -1485,15 +1489,6 @@ fn menu_row(label: String, msg: Message) -> Element<'static, Message> {
|
|||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn menu_header(label: String) -> Element<'static, Message> {
|
|
||||||
container(text(label).size(11).style(|theme: &Theme| iced::widget::text::Style {
|
|
||||||
color: Some(theme.palette().background.base.text.scale_alpha(0.55)),
|
|
||||||
}))
|
|
||||||
.padding([6, 12])
|
|
||||||
.width(Fill)
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn menu_separator() -> Element<'static, Message> {
|
fn menu_separator() -> Element<'static, Message> {
|
||||||
container(iced::widget::Space::new().height(Length::Fixed(1.0)))
|
container(iced::widget::Space::new().height(Length::Fixed(1.0)))
|
||||||
.style(|theme: &Theme| container::Style {
|
.style(|theme: &Theme| container::Style {
|
||||||
@@ -1507,25 +1502,34 @@ fn menu_separator() -> Element<'static, Message> {
|
|||||||
|
|
||||||
/// Per-row right-click menu. Eight items exactly: universal tools +
|
/// Per-row right-click menu. Eight items exactly: universal tools +
|
||||||
/// path tools. No Bind, no Attach/Overlay — those are DWG-only and
|
/// path tools. No Bind, no Attach/Overlay — those are DWG-only and
|
||||||
/// hidden entirely. Change Path Type renders as an indented group
|
/// hidden entirely. Change Path Type shows a right triangle and a
|
||||||
/// because iced_aw ContextMenu has no nested flyout.
|
/// second list in front with the 3 sub-options on hover.
|
||||||
fn row_menu_for(index: usize) -> Element<'static, Message> {
|
fn row_menu_for(index: usize, change_path_open: bool) -> Element<'static, Message> {
|
||||||
let item = |label: &str, op: XrefPaletteOp| -> Element<'static, Message> {
|
let item = |label: &str, op: XrefPaletteOp| -> Element<'static, Message> {
|
||||||
menu_row(label.to_string(), Message::XrefRowOp(index, op))
|
menu_row(label.to_string(), Message::XrefRowOp(index, op))
|
||||||
};
|
};
|
||||||
let pathtype_item = |label: &str, pt: Pathtype| -> Element<'static, Message> {
|
let pathtype_item = |label: &str, pt: Pathtype| -> Element<'static, Message> {
|
||||||
container(menu_row(
|
menu_row(
|
||||||
label.to_string(),
|
label.to_string(),
|
||||||
Message::XrefRowOp(index, XrefPaletteOp::Pathtype(pt)),
|
Message::XrefRowOp(index, XrefPaletteOp::Pathtype(pt)),
|
||||||
))
|
)
|
||||||
.padding(Padding {
|
|
||||||
left: 12.0,
|
|
||||||
..Padding::default()
|
|
||||||
})
|
|
||||||
.width(Fill)
|
|
||||||
.into()
|
|
||||||
};
|
};
|
||||||
container(
|
let change_path_row: Element<'static, Message> = mouse_area(
|
||||||
|
container(
|
||||||
|
row![
|
||||||
|
text(crate::t!("Change Path Type").into_owned()).size(12).width(Fill),
|
||||||
|
crate::ui::icons::themed_arrow_right(10.0),
|
||||||
|
]
|
||||||
|
.align_y(iced::Center)
|
||||||
|
.spacing(8),
|
||||||
|
)
|
||||||
|
.padding([4, 12])
|
||||||
|
.width(Fill),
|
||||||
|
)
|
||||||
|
.on_enter(Message::XrefRowChangePathEnter)
|
||||||
|
.on_exit(Message::XrefRowChangePathLeave)
|
||||||
|
.into();
|
||||||
|
let main: Element<'static, Message> = container(
|
||||||
column![
|
column![
|
||||||
item(&crate::t!("Open").into_owned(), XrefPaletteOp::Open),
|
item(&crate::t!("Open").into_owned(), XrefPaletteOp::Open),
|
||||||
item(&crate::t!("Attach...").into_owned(), XrefPaletteOp::Attach),
|
item(&crate::t!("Attach...").into_owned(), XrefPaletteOp::Attach),
|
||||||
@@ -1533,16 +1537,7 @@ fn row_menu_for(index: usize) -> Element<'static, Message> {
|
|||||||
item(&crate::t!("Reload").into_owned(), XrefPaletteOp::Reload),
|
item(&crate::t!("Reload").into_owned(), XrefPaletteOp::Reload),
|
||||||
item(&crate::t!("Detach").into_owned(), XrefPaletteOp::Detach),
|
item(&crate::t!("Detach").into_owned(), XrefPaletteOp::Detach),
|
||||||
menu_separator(),
|
menu_separator(),
|
||||||
menu_header(crate::t!("Change Path Type").into_owned()),
|
change_path_row,
|
||||||
pathtype_item(
|
|
||||||
&crate::t!("Make Absolute").into_owned(),
|
|
||||||
Pathtype::Full
|
|
||||||
),
|
|
||||||
pathtype_item(
|
|
||||||
&crate::t!("Make Relative").into_owned(),
|
|
||||||
Pathtype::Relative
|
|
||||||
),
|
|
||||||
pathtype_item(&crate::t!("Remove Path").into_owned(), Pathtype::None),
|
|
||||||
menu_separator(),
|
menu_separator(),
|
||||||
menu_row(
|
menu_row(
|
||||||
crate::t!("Select New Path").into_owned(),
|
crate::t!("Select New Path").into_owned(),
|
||||||
@@ -1568,7 +1563,43 @@ fn row_menu_for(index: usize) -> Element<'static, Message> {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.width(Length::Fixed(220.0))
|
.width(Length::Fixed(220.0))
|
||||||
.into()
|
.into();
|
||||||
|
if !change_path_open {
|
||||||
|
return main;
|
||||||
|
}
|
||||||
|
let flyout: Element<'static, Message> = mouse_area(
|
||||||
|
container(
|
||||||
|
column![
|
||||||
|
pathtype_item(
|
||||||
|
&crate::t!("Make Absolute").into_owned(),
|
||||||
|
Pathtype::Full
|
||||||
|
),
|
||||||
|
pathtype_item(
|
||||||
|
&crate::t!("Make Relative").into_owned(),
|
||||||
|
Pathtype::Relative
|
||||||
|
),
|
||||||
|
pathtype_item(&crate::t!("Remove Path").into_owned(), Pathtype::None),
|
||||||
|
]
|
||||||
|
.spacing(1)
|
||||||
|
.padding(4),
|
||||||
|
)
|
||||||
|
.style(|theme: &Theme| container::Style {
|
||||||
|
background: Some(Background::Color(
|
||||||
|
theme.palette().background.base.color,
|
||||||
|
)),
|
||||||
|
border: Border {
|
||||||
|
color: theme.palette().background.neutral.color,
|
||||||
|
width: 1.0,
|
||||||
|
radius: 3.0.into(),
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.width(Length::Fixed(180.0)),
|
||||||
|
)
|
||||||
|
.on_enter(Message::XrefRowChangePathEnter)
|
||||||
|
.on_exit(Message::XrefRowChangePathLeave)
|
||||||
|
.into();
|
||||||
|
row![main, flyout].spacing(4).align_y(iced::alignment::Vertical::Top).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn xref_row<'a>(
|
fn xref_row<'a>(
|
||||||
@@ -1579,6 +1610,7 @@ fn xref_row<'a>(
|
|||||||
is_expanded: bool,
|
is_expanded: bool,
|
||||||
table_w: f32,
|
table_w: f32,
|
||||||
cw: &[f32; 6],
|
cw: &[f32; 6],
|
||||||
|
change_path_open: bool,
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
let mut name = entry.name.clone();
|
let mut name = entry.name.clone();
|
||||||
if display.is_nested {
|
if display.is_nested {
|
||||||
@@ -1662,7 +1694,7 @@ fn xref_row<'a>(
|
|||||||
)
|
)
|
||||||
.on_press(Message::XrefManagerSelect(index))
|
.on_press(Message::XrefManagerSelect(index))
|
||||||
.on_right_press(Message::XrefRowRightClick(index));
|
.on_right_press(Message::XrefRowRightClick(index));
|
||||||
iced_aw::ContextMenu::new(row, move || row_menu_for(index)).into()
|
iced_aw::ContextMenu::new(row, move || row_menu_for(index, change_path_open)).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// One tree-mode row: indent + optional expand arrow + file icon + name.
|
/// One tree-mode row: indent + optional expand arrow + file icon + name.
|
||||||
@@ -1673,6 +1705,7 @@ fn tree_row(
|
|||||||
is_selected: bool,
|
is_selected: bool,
|
||||||
show_expand: bool,
|
show_expand: bool,
|
||||||
is_expanded: bool,
|
is_expanded: bool,
|
||||||
|
change_path_open: bool,
|
||||||
) -> Element<'_, Message> {
|
) -> Element<'_, Message> {
|
||||||
let mut cells = row![].spacing(2).align_y(iced::Center);
|
let mut cells = row![].spacing(2).align_y(iced::Center);
|
||||||
cells = cells.push(tree_indent(display.depth));
|
cells = cells.push(tree_indent(display.depth));
|
||||||
@@ -1722,7 +1755,7 @@ fn tree_row(
|
|||||||
)
|
)
|
||||||
.on_press(Message::XrefManagerSelect(index))
|
.on_press(Message::XrefManagerSelect(index))
|
||||||
.on_right_press(Message::XrefRowRightClick(index));
|
.on_right_press(Message::XrefRowRightClick(index));
|
||||||
iced_aw::ContextMenu::new(row, move || row_menu_for(index)).into()
|
iced_aw::ContextMenu::new(row, move || row_menu_for(index, change_path_open)).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tree-mode host root: home icon + name with the current-drawing marker.
|
/// Tree-mode host root: home icon + name with the current-drawing marker.
|
||||||
|
|||||||
Reference in new issue
Block a user