Add exact State multiselection, atomic movement and placement Hide
Preserve the desktop-2 captured source (1286 passed, 6 existing ignored). Three menu payload blobs are staged from that verified capture without replacing working files; the strict-lint size correction follows in a separate commit. Native acceptance remains pending.
This commit is contained in:
@@ -12,6 +12,7 @@ pub struct Request {
|
||||
pub anchor: Point,
|
||||
pub object: Id,
|
||||
pub placement: Option<Id>,
|
||||
pub selection_target: Option<crate::diagram_usage::Target>,
|
||||
pub bend: Option<usize>,
|
||||
pub route_point: Option<Position>,
|
||||
pub route_segment: Option<(usize, Position)>,
|
||||
@@ -25,6 +26,7 @@ impl Request {
|
||||
anchor,
|
||||
object,
|
||||
placement: None,
|
||||
selection_target: None,
|
||||
bend: None,
|
||||
route_point: None,
|
||||
route_segment: None,
|
||||
@@ -55,6 +57,7 @@ pub struct Opened {
|
||||
impl Opened {
|
||||
pub fn new(app: &App, request: Request, token: u64) -> Option<Self> {
|
||||
if !app.project().contains(request.object)
|
||||
|| !target_matches(app, &request)
|
||||
|| request
|
||||
.canvas
|
||||
.is_some_and(|c| Some(c) != current_canvas(app))
|
||||
@@ -80,6 +83,7 @@ impl Opened {
|
||||
&& self.layer == app.layer
|
||||
&& self.diagram == current_diagram(app)
|
||||
&& app.project().contains(self.request.object)
|
||||
&& target_matches(app, &self.request)
|
||||
&& self
|
||||
.request
|
||||
.canvas
|
||||
@@ -92,6 +96,32 @@ impl Opened {
|
||||
}
|
||||
}
|
||||
|
||||
fn target_matches(app: &App, request: &Request) -> bool {
|
||||
use crate::diagram_usage::Target;
|
||||
let Some(target) = &request.selection_target else {
|
||||
return true;
|
||||
};
|
||||
if request.canvas.is_none() || !app.multiselect_target_visible(target) {
|
||||
return false;
|
||||
}
|
||||
match target {
|
||||
Target::Placement(id) => {
|
||||
request.placement == Some(*id)
|
||||
&& request
|
||||
.canvas
|
||||
.and_then(|context| app.project().diagrams.get(&context.diagram))
|
||||
.and_then(|diagram| diagram.nodes.get(id))
|
||||
.is_some_and(|node| node.element == request.object || node.id == request.object)
|
||||
}
|
||||
Target::Port { port, .. } => request.placement.is_none() && *port == request.object,
|
||||
Target::Edge(id) => request.placement.is_none() && *id == request.object,
|
||||
Target::StateRow { relation, .. } => {
|
||||
request.placement.is_none() && *relation == request.object
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn current_canvas(app: &App) -> Option<route_edit::Context> {
|
||||
if app.page == Page::Behavior {
|
||||
app.state_context()
|
||||
@@ -126,7 +156,22 @@ pub fn trigger(app: &App) -> iced::Element<'_, Message> {
|
||||
request.canvas = app.route_context();
|
||||
} else if app.page == Page::Behavior {
|
||||
request.canvas = app.state_context();
|
||||
if let Some(context) = request.canvas
|
||||
if let Some(target) = app.diagram_selection.as_ref().and_then(|s| s.primary()) {
|
||||
request.selection_target = Some(target.clone());
|
||||
request.placement = match target {
|
||||
crate::diagram_usage::Target::Placement(id) => {
|
||||
if let Some(node) = request
|
||||
.canvas
|
||||
.and_then(|c| app.project().diagrams.get(&c.diagram))
|
||||
.and_then(|d| d.nodes.get(id))
|
||||
{
|
||||
request.object = node.element;
|
||||
}
|
||||
Some(*id)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
} else if let Some(context) = request.canvas
|
||||
&& let Some(node) = app.project().diagrams[&context.diagram]
|
||||
.nodes
|
||||
.values()
|
||||
@@ -187,24 +232,20 @@ pub fn popup(app: &App) -> Option<Menu<Message>> {
|
||||
on_close: Message::Menu(Event::Close),
|
||||
});
|
||||
}
|
||||
if app.page == Page::Behavior
|
||||
&& let Some(menu) = crate::behavior::state_menu::popup(app, opened, clean)
|
||||
{
|
||||
return Some(menu);
|
||||
}
|
||||
let mut entries = vec![Entry::title(crate::views::short(p.named(id), 38))];
|
||||
if app.page == Page::Diagram
|
||||
&& request.canvas.is_some()
|
||||
if request.canvas.is_some()
|
||||
&& let Some(context) = app.multiselect_context()
|
||||
{
|
||||
use crate::app::multiselect::{Choice, Event as SelectionEvent};
|
||||
use crate::diagram_usage::Target;
|
||||
let view = app
|
||||
.active_diagram
|
||||
let view = request
|
||||
.canvas
|
||||
.map(|context| context.diagram)
|
||||
.map(|d| projection::project_view_with_index(p, d, app.document.view_index()));
|
||||
let target = request
|
||||
.placement
|
||||
.map(Target::Placement)
|
||||
.selection_target
|
||||
.clone()
|
||||
.or_else(|| request.placement.map(Target::Placement))
|
||||
.or_else(|| {
|
||||
view.as_ref()?
|
||||
.ports
|
||||
@@ -287,6 +328,15 @@ pub fn popup(app: &App) -> Option<Menu<Message>> {
|
||||
entries.push(Entry::separator());
|
||||
}
|
||||
}
|
||||
if app.page == Page::Behavior
|
||||
&& let Some(mut menu) = crate::behavior::state_menu::popup(app, opened, clean)
|
||||
{
|
||||
if entries.len() > 1 {
|
||||
entries.extend(menu.entries.into_iter().skip(1));
|
||||
menu.entries = entries;
|
||||
}
|
||||
return Some(menu);
|
||||
}
|
||||
if !clean {
|
||||
entries.push(Entry::title("Apply or revert pending edits first"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user