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 = tab.scene.camera.borrow().project(location, vp_bounds)?;
let point = iced::Point::new(vp_bounds.x + point.x, vp_bounds.y + point.y); let point = iced::Point::new(vp_bounds.x + point.x, vp_bounds.y + point.y);
(point.x.is_finite() && point.y.is_finite()) (point.x.is_finite() && point.y.is_finite()).then(|| {
.then(|| (point, parameter.name.clone(), vp_bounds)) (
point,
parameter.name.clone(),
vp_bounds,
tab.selected_authoring == Some(parameter.handle),
)
})
}); });
let authoring_points = let authoring_points =
tab.active_block_edit_session() tab.active_block_edit_session()
@@ -1125,7 +1131,7 @@ bg={bg_ms:.1}ms n={view_count}"
session.br_handle, session.br_handle,
) )
.into_iter() .into_iter()
.filter_map(|(_, parameter)| { .filter_map(|(handle, parameter)| {
let project = |point: codec::types::Vector3| { let project = |point: codec::types::Vector3| {
let point = tab.scene.camera.borrow().project( let point = tab.scene.camera.borrow().project(
glam::DVec3::new(point.x, point.y, point.z), 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)?, project(parameter.definition_label_point)?,
parameter.position_name.clone(), parameter.position_name.clone(),
vp_bounds, vp_bounds,
tab.selected_authoring == Some(handle),
)) ))
}) })
.collect() .collect()
@@ -1153,8 +1160,9 @@ bg={bg_ms:.1}ms n={view_count}"
session.br_handle, session.br_handle,
) )
.iter() .iter()
.map(|p| { .map(|p| {
( (
p.handle,
crate::scene::block_distance_parameter::graphic(p), crate::scene::block_distance_parameter::graphic(p),
p.native.definition_base_point, p.native.definition_base_point,
) )
@@ -1167,6 +1175,7 @@ bg={bg_ms:.1}ms n={view_count}"
.iter() .iter()
.map(|p| { .map(|p| {
( (
p.handle,
crate::scene::block_orientation_parameter::graphic(p), crate::scene::block_orientation_parameter::graphic(p),
crate::scene::block_orientation_parameter::marker_tail(p), crate::scene::block_orientation_parameter::marker_tail(p),
) )
@@ -1177,10 +1186,29 @@ bg={bg_ms:.1}ms n={view_count}"
&tab.scene.document, &tab.scene.document,
session.br_handle, 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(); let mut g = crate::scene::block_distance_parameter::Graphic::default();
g.base_grips.push(p.parameter.definition_point); g.base_grips.push(point);
(g, p.parameter.definition_point) g.labels.push((point, "Basepoint".into()));
(handle, g, point)
}), }),
) )
.chain( .chain(
@@ -1189,12 +1217,12 @@ bg={bg_ms:.1}ms n={view_count}"
session.br_handle, session.br_handle,
) )
.into_iter() .into_iter()
.map(|(_, p)| { .map(|(handle, p)| {
let mut g = crate::scene::block_distance_parameter::Graphic::default(); let mut g = crate::scene::block_distance_parameter::Graphic::default();
g.lookup_grips.push(p.parameter.definition_point); g.lookup_grips.push(p.parameter.definition_point);
g.labels g.labels
.push((p.parameter.definition_point, p.lookup_name.clone())); .push((p.parameter.definition_point, p.lookup_name.clone()));
(g, p.parameter.definition_point) (handle, g, p.parameter.definition_point)
}), }),
) )
.chain( .chain(
@@ -1219,10 +1247,10 @@ bg={bg_ms:.1}ms n={view_count}"
g.grips.push((action.display_location, false)); g.grips.push((action.display_location, false));
g.labels g.labels
.push((action.display_location, action.element.name.clone())); .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 project = |p: codec::types::Vector3| {
let p = tab let p = tab
.scene .scene
@@ -1257,6 +1285,7 @@ bg={bg_ms:.1}ms n={view_count}"
}) })
.collect::<Option<Vec<_>>>()?; .collect::<Option<Vec<_>>>()?;
Some(crate::ui::overlay::AuthoringDimension { Some(crate::ui::overlay::AuthoringDimension {
selected: tab.selected_authoring == Some(handle),
lines, lines,
base_grips: graphic base_grips: graphic
.base_grips .base_grips
@@ -2052,6 +2081,34 @@ bg={bg_ms:.1}ms n={view_count}"
viewport_stack.push(position_canvas_overlay(anchor, panel.into())); 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 // Selection actions: with only PDF underlays or only xrefs
// selected, their tools take the right edge (over the paper-space // 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_selected: Arc<[bool]>,
constraint_glyph_tooltip: Option<String>, constraint_glyph_tooltip: Option<String>,
constraint_cursor_badge: Option<String>, constraint_cursor_badge: Option<String>,
authoring_visibility: Option<(Point, String, iced::Rectangle)>, authoring_visibility: Option<(Point, String, iced::Rectangle, bool)>,
authoring_points: Vec<(Point, Point, String, iced::Rectangle)>, authoring_points: Vec<(Point, Point, String, iced::Rectangle, bool)>,
authoring_dimensions: Vec<AuthoringDimension>, authoring_dimensions: Vec<AuthoringDimension>,
) -> Element<'a, Message> { ) -> Element<'a, Message> {
canvas(SelectionCanvas { canvas(SelectionCanvas {
@@ -1079,6 +1079,7 @@ pub(crate) fn selection_overlay<'a>(
} }
pub(crate) struct AuthoringDimension { pub(crate) struct AuthoringDimension {
pub selected: bool,
pub lines: Vec<[Point; 2]>, pub lines: Vec<[Point; 2]>,
pub round_grips: Vec<Point>, pub round_grips: Vec<Point>,
pub base_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 /// Symbol shown beside the cursor while it targets an entity that already
/// participates in an enabled geometric constraint. /// participates in an enabled geometric constraint.
constraint_cursor_badge: Option<String>, constraint_cursor_badge: Option<String>,
authoring_visibility: Option<(Point, String, iced::Rectangle)>, authoring_visibility: Option<(Point, String, iced::Rectangle, bool)>,
authoring_points: Vec<(Point, Point, String, iced::Rectangle)>, authoring_points: Vec<(Point, Point, String, iced::Rectangle, bool)>,
authoring_dimensions: Vec<AuthoringDimension>, authoring_dimensions: Vec<AuthoringDimension>,
} }
@@ -2388,15 +2389,25 @@ impl canvas::Program<Message> for SelectionCanvas {
frame.stroke(&b1, stroke.clone()); frame.stroke(&b1, stroke.clone());
frame.stroke(&b2, stroke); 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| { 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| { let triangle = canvas::Path::new(|builder| {
builder.move_to(Point::new(point.x - 5.0, point.y - 3.0)); 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 + 5.0, point.y - 3.0));
builder.line_to(Point::new(point.x, point.y + 5.0)); builder.line_to(Point::new(point.x, point.y + 5.0));
builder.close(); 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(&triangle, color);
frame.fill_text(canvas::Text { frame.fill_text(canvas::Text {
content: label.clone(), 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| { 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( frame.stroke(
&canvas::Path::line(*point, *label_point), &canvas::Path::line(*point, *label_point),
canvas::Stroke::default().with_color(color), canvas::Stroke::default().with_color(color),
@@ -2432,7 +2453,25 @@ impl canvas::Program<Message> for SelectionCanvas {
} }
for dimension in &self.authoring_dimensions { for dimension in &self.authoring_dimensions {
frame.with_clip(dimension.clip, |frame| { 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 { for [a, b] in &dimension.lines {
frame.stroke( frame.stroke(
&canvas::Path::line(*a, *b), &canvas::Path::line(*a, *b),