feat(ui): render BPT popup and authoring selection state

This commit is contained in:
Karim Jerbi committed 2026-10-18 19:07:20 +01:00
1 parent 286bdf6426
commit 29d903d5d2
2 files changed
+116 -20

No files matched your search

+68 -11
View File
@@ -1114,8 +1114,14 @@ bg={bg_ms:.1}ms n={view_count}"
);
let point = tab.scene.camera.borrow().project(location, vp_bounds)?;
let point = iced::Point::new(vp_bounds.x + point.x, vp_bounds.y + point.y);
(point.x.is_finite() && point.y.is_finite())
.then(|| (point, parameter.name.clone(), vp_bounds))
(point.x.is_finite() && point.y.is_finite()).then(|| {
(
point,
parameter.name.clone(),
vp_bounds,
tab.selected_authoring == Some(parameter.handle),
)
})
});
let authoring_points =
tab.active_block_edit_session()
@@ -1125,7 +1131,7 @@ bg={bg_ms:.1}ms n={view_count}"
session.br_handle,
)
.into_iter()
.filter_map(|(_, parameter)| {
.filter_map(|(handle, parameter)| {
let project = |point: codec::types::Vector3| {
let point = tab.scene.camera.borrow().project(
glam::DVec3::new(point.x, point.y, point.z),
@@ -1140,6 +1146,7 @@ bg={bg_ms:.1}ms n={view_count}"
project(parameter.definition_label_point)?,
parameter.position_name.clone(),
vp_bounds,
tab.selected_authoring == Some(handle),
))
})
.collect()
@@ -1153,8 +1160,9 @@ bg={bg_ms:.1}ms n={view_count}"
session.br_handle,
)
.iter()
.map(|p| {
.map(|p| {
(
p.handle,
crate::scene::block_distance_parameter::graphic(p),
p.native.definition_base_point,
)
@@ -1167,6 +1175,7 @@ bg={bg_ms:.1}ms n={view_count}"
.iter()
.map(|p| {
(
p.handle,
crate::scene::block_orientation_parameter::graphic(p),
crate::scene::block_orientation_parameter::marker_tail(p),
)
@@ -1177,10 +1186,29 @@ bg={bg_ms:.1}ms n={view_count}"
&tab.scene.document,
session.br_handle,
)
.map(|(_, p)| {
.map(|(handle, p)| (handle, p.parameter.definition_point))
.or_else(|| {
let name = tab
.scene
.document
.block_records
.iter()
.find(|record| record.handle == session.br_handle)?
.name
.as_str();
Some((
session.br_handle,
crate::scene::render_graph::block_base_point(
&tab.scene.document,
name,
),
))
})
.map(|(handle, point)| {
let mut g = crate::scene::block_distance_parameter::Graphic::default();
g.base_grips.push(p.parameter.definition_point);
(g, p.parameter.definition_point)
g.base_grips.push(point);
g.labels.push((point, "Basepoint".into()));
(handle, g, point)
}),
)
.chain(
@@ -1189,12 +1217,12 @@ bg={bg_ms:.1}ms n={view_count}"
session.br_handle,
)
.into_iter()
.map(|(_, p)| {
.map(|(handle, p)| {
let mut g = crate::scene::block_distance_parameter::Graphic::default();
g.lookup_grips.push(p.parameter.definition_point);
g.labels
.push((p.parameter.definition_point, p.lookup_name.clone()));
(g, p.parameter.definition_point)
(handle, g, p.parameter.definition_point)
}),
)
.chain(
@@ -1219,10 +1247,10 @@ bg={bg_ms:.1}ms n={view_count}"
g.grips.push((action.display_location, false));
g.labels
.push((action.display_location, action.element.name.clone()));
(g, action.display_location)
(handle, g, action.display_location)
}),
)
.filter_map(|(graphic, marker_tail)| {
.filter_map(|(handle, graphic, marker_tail)| {
let project = |p: codec::types::Vector3| {
let p = tab
.scene
@@ -1257,6 +1285,7 @@ bg={bg_ms:.1}ms n={view_count}"
})
.collect::<Option<Vec<_>>>()?;
Some(crate::ui::overlay::AuthoringDimension {
selected: tab.selected_authoring == Some(handle),
lines,
base_grips: graphic
.base_grips
@@ -2052,6 +2081,34 @@ bg={bg_ms:.1}ms n={view_count}"
viewport_stack.push(position_canvas_overlay(anchor, panel.into()));
}
}
if let Some(popup) = self.table_popup.as_ref() {
if !tab.is_start {
let mut col = column![text(popup.header.clone()).size(12)].spacing(2);
for (idx, name) in popup.rows.iter().enumerate() {
let mark: Element<'_, Message> = if popup.current == Some(idx) {
crate::ui::icons::themed_check_cell(true)
} else {
Space::new().width(11).into()
};
col = col.push(
button(
row![container(mark).width(16), text(name).size(12)]
.spacing(2)
.align_y(iced::Center),
)
.on_press(Message::TableRowPick(idx))
.padding([3, 10])
.width(Fill),
);
}
let panel =
container(iced::widget::scrollable(col).height(iced::Length::Shrink))
.padding(2);
let anchor = iced::Point::new(popup.anchor.x + 12.0, popup.anchor.y + 12.0);
viewport_stack =
viewport_stack.push(position_canvas_overlay(anchor, panel.into()));
}
}
// Selection actions: with only PDF underlays or only xrefs
// selected, their tools take the right edge (over the paper-space
+48 -9
View File
@@ -1037,8 +1037,8 @@ pub(crate) fn selection_overlay<'a>(
constraint_glyph_selected: Arc<[bool]>,
constraint_glyph_tooltip: Option<String>,
constraint_cursor_badge: Option<String>,
authoring_visibility: Option<(Point, String, iced::Rectangle)>,
authoring_points: Vec<(Point, Point, String, iced::Rectangle)>,
authoring_visibility: Option<(Point, String, iced::Rectangle, bool)>,
authoring_points: Vec<(Point, Point, String, iced::Rectangle, bool)>,
authoring_dimensions: Vec<AuthoringDimension>,
) -> Element<'a, Message> {
canvas(SelectionCanvas {
@@ -1079,6 +1079,7 @@ pub(crate) fn selection_overlay<'a>(
}
pub(crate) struct AuthoringDimension {
pub selected: bool,
pub lines: Vec<[Point; 2]>,
pub round_grips: Vec<Point>,
pub base_grips: Vec<Point>,
@@ -1160,8 +1161,8 @@ struct SelectionCanvas {
/// Symbol shown beside the cursor while it targets an entity that already
/// participates in an enabled geometric constraint.
constraint_cursor_badge: Option<String>,
authoring_visibility: Option<(Point, String, iced::Rectangle)>,
authoring_points: Vec<(Point, Point, String, iced::Rectangle)>,
authoring_visibility: Option<(Point, String, iced::Rectangle, bool)>,
authoring_points: Vec<(Point, Point, String, iced::Rectangle, bool)>,
authoring_dimensions: Vec<AuthoringDimension>,
}
@@ -2388,15 +2389,25 @@ impl canvas::Program<Message> for SelectionCanvas {
frame.stroke(&b1, stroke.clone());
frame.stroke(&b2, stroke);
}
if let Some((point, label, clip)) = &self.authoring_visibility {
if let Some((point, label, clip, selected)) = &self.authoring_visibility {
frame.with_clip(*clip, |frame| {
let color = theme.palette().primary.base.color;
let color = if *selected {
theme.palette().primary.strong.color
} else {
theme.palette().primary.base.color
};
let triangle = canvas::Path::new(|builder| {
builder.move_to(Point::new(point.x - 5.0, point.y - 3.0));
builder.line_to(Point::new(point.x + 5.0, point.y - 3.0));
builder.line_to(Point::new(point.x, point.y + 5.0));
builder.close();
});
if *selected {
frame.stroke(
&canvas::Path::circle(*point, 10.0),
canvas::Stroke::default().with_color(color).with_width(2.0),
);
}
frame.fill(&triangle, color);
frame.fill_text(canvas::Text {
content: label.clone(),
@@ -2407,9 +2418,19 @@ impl canvas::Program<Message> for SelectionCanvas {
});
});
}
for (point, label_point, label, clip) in &self.authoring_points {
for (point, label_point, label, clip, selected) in &self.authoring_points {
frame.with_clip(*clip, |frame| {
let color = theme.palette().primary.base.color;
let color = if *selected {
theme.palette().primary.strong.color
} else {
theme.palette().primary.base.color
};
if *selected {
frame.stroke(
&canvas::Path::circle(*point, 10.0),
canvas::Stroke::default().with_color(color).with_width(2.0),
);
}
frame.stroke(
&canvas::Path::line(*point, *label_point),
canvas::Stroke::default().with_color(color),
@@ -2432,7 +2453,25 @@ impl canvas::Program<Message> for SelectionCanvas {
}
for dimension in &self.authoring_dimensions {
frame.with_clip(dimension.clip, |frame| {
let color = theme.palette().primary.base.color;
let color = if dimension.selected {
theme.palette().primary.strong.color
} else {
theme.palette().primary.base.color
};
if dimension.selected {
for point in dimension
.base_grips
.iter()
.chain(dimension.lookup_grips.iter())
.chain(dimension.round_grips.iter())
.chain(dimension.grips.iter().map(|(point, _)| point))
{
frame.stroke(
&canvas::Path::circle(*point, 10.0),
canvas::Stroke::default().with_color(color).with_width(2.0),
);
}
}
for [a, b] in &dimension.lines {
frame.stroke(
&canvas::Path::line(*a, *b),