From 29d903d5d2bc2e6a185583bbec85638d6219fe6e Mon Sep 17 00:00:00 2001 From: Karim Jerbi Date: Sun, 18 Oct 2026 19:07:20 +0100 Subject: [PATCH] feat(ui): render BPT popup and authoring selection state --- src/app/view/mod.rs | 79 ++++++++++++++++++++++++++++++++++++++------- src/ui/overlay.rs | 57 ++++++++++++++++++++++++++------ 2 files changed, 116 insertions(+), 20 deletions(-) diff --git a/src/app/view/mod.rs b/src/app/view/mod.rs index d6f4696ae..55000c964 100644 --- a/src/app/view/mod.rs +++ b/src/app/view/mod.rs @@ -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::>>()?; 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 diff --git a/src/ui/overlay.rs b/src/ui/overlay.rs index f209223ba..f8b339b6c 100644 --- a/src/ui/overlay.rs +++ b/src/ui/overlay.rs @@ -1037,8 +1037,8 @@ pub(crate) fn selection_overlay<'a>( constraint_glyph_selected: Arc<[bool]>, constraint_glyph_tooltip: Option, constraint_cursor_badge: Option, - 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, ) -> 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, pub base_grips: Vec, @@ -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, - 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, } @@ -2388,15 +2389,25 @@ impl canvas::Program 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 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 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),