156 lines
6.8 KiB
Plaintext
156 lines
6.8 KiB
Plaintext
--- a/crates/oa-desktop/src/connection_edit.rs
|
|
+++ b/crates/oa-desktop/src/connection_edit.rs
|
|
@@ -37,6 +37,26 @@
|
|
let compatible = self.tool.targets(project, source);
|
|
self.candidates = visible(view).filter(|id| compatible.contains(id)).collect();
|
|
}
|
|
+}
|
|
+
|
|
+/// Contextual drawing follows the visible semantic source, not the palette layer.
|
|
+/// The caller separately validates the menu/context lifetime and pending drafts.
|
|
+pub fn can_begin(project: &Project, diagram: Id, tool: Tool, source: Id) -> bool {
|
|
+ let Some(diagram) = project.diagrams.get(&diagram) else {
|
|
+ return false;
|
|
+ };
|
|
+ let Some(element) = project.elements.get(&source) else {
|
|
+ return false;
|
|
+ };
|
|
+ diagram.projection.is_architecture()
|
|
+ && (element.layer() == diagram.layer
|
|
+ || diagram.projection == oa_core::ProjectionKind::Traceability)
|
|
+ && Tool::for_layer(element.layer()).contains(&tool)
|
|
+ && tool.can_start(project, source)
|
|
+ && (tool != Tool::Allocation
|
|
+ || tool.available_in(element.layer(), diagram.projection))
|
|
+ && visible(&oa_core::projection::project_view(project, diagram.id))
|
|
+ .any(|id| id == source)
|
|
}
|
|
|
|
pub fn visible(view: &ProjectedView) -> impl Iterator<Item = Id> + '_ {
|
|
--- a/crates/oa-desktop/src/model_menu.rs
|
|
+++ b/crates/oa-desktop/src/model_menu.rs
|
|
@@ -557,10 +557,7 @@
|
|
&format!("Draw {}", tool.label().to_lowercase()),
|
|
Message::Connection(crate::connection_edit::Event::Begin(*tool, id)),
|
|
app.active_diagram.is_some_and(|diagram| {
|
|
- crate::connection_edit::visible(&projection::project_view(
|
|
- p, diagram,
|
|
- ))
|
|
- .any(|element| element == id)
|
|
+ crate::connection_edit::can_begin(p, diagram, *tool, id)
|
|
}),
|
|
));
|
|
}
|
|
--- a/crates/oa-desktop/src/app.rs
|
|
+++ b/crates/oa-desktop/src/app.rs
|
|
@@ -2751,7 +2751,23 @@
|
|
e.kind() != Kind::Port && d.projection.includes(e.kind())
|
|
})
|
|
});
|
|
- if show_in_mixed_view || menu.request.canvas.is_some() {
|
|
+ // Draw acts on the visible current view even when opened from
|
|
+ // Explorer; explicit Select/Open actions still navigate.
|
|
+ let draw_in_current_view = match &*action {
|
|
+ Message::Connection(crate::connection_edit::Event::Begin(tool, source)) => {
|
|
+ *source == menu.request.object
|
|
+ && self.active_diagram.is_some_and(|diagram| {
|
|
+ crate::connection_edit::can_begin(
|
|
+ self.project(),
|
|
+ diagram,
|
|
+ *tool,
|
|
+ *source,
|
|
+ )
|
|
+ })
|
|
+ }
|
|
+ _ => false,
|
|
+ };
|
|
+ if show_in_mixed_view || draw_in_current_view || menu.request.canvas.is_some() {
|
|
if !self.inspect_canvas_target(menu.request.object) {
|
|
return Task::none();
|
|
}
|
|
@@ -5042,8 +5042,30 @@
|
|
epoch: self.canvas_epoch,
|
|
})
|
|
}
|
|
+ // Admission belongs to the caller: palette choice, contextual source, or
|
|
+ // an already admitted session whose complete context is still current.
|
|
+ fn arm_connection(
|
|
+ &mut self,
|
|
+ context: crate::route_edit::Context,
|
|
+ tool: crate::connection_tool::Tool,
|
|
+ ) -> crate::route_edit::Context {
|
|
+ self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
|
|
+ let context = crate::route_edit::Context {
|
|
+ epoch: self.canvas_epoch,
|
|
+ ..context
|
|
+ };
|
|
+ self.connection = Some(crate::connection_edit::Session::new(
|
|
+ self.project(),
|
|
+ &project_view(self.project(), context.diagram),
|
|
+ context,
|
|
+ tool,
|
|
+ ));
|
|
+ self.selected_bend = None;
|
|
+ self.status = tool.hint().into();
|
|
+ context
|
|
+ }
|
|
fn edit_connection(&mut self, event: crate::connection_edit::Event) {
|
|
- use crate::connection_edit::{Event, Session};
|
|
+ use crate::connection_edit::Event;
|
|
let Some(context) = self.route_context() else {
|
|
return;
|
|
};
|
|
@@ -5059,10 +5081,17 @@
|
|
}
|
|
match event {
|
|
Event::Begin(tool, source) => {
|
|
- self.edit_connection(Event::Choose(Some(tool)));
|
|
- if let Some(context) = self.route_context() {
|
|
- self.edit_connection(Event::Endpoint(context, source));
|
|
- }
|
|
+ if !crate::connection_edit::can_begin(
|
|
+ self.project(),
|
|
+ context.diagram,
|
|
+ tool,
|
|
+ source,
|
|
+ ) {
|
|
+ self.status = "Choose a visible compatible source in this diagram.".into();
|
|
+ return;
|
|
+ }
|
|
+ let context = self.arm_connection(context, tool);
|
|
+ self.edit_connection(Event::Endpoint(context, source));
|
|
}
|
|
Event::Choose(Some(tool)) => {
|
|
if !crate::connection_tool::Tool::for_layer(self.layer).contains(&tool) {
|
|
@@ -5076,16 +5105,7 @@
|
|
{
|
|
return;
|
|
}
|
|
- self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
|
|
- let context = self.route_context().unwrap();
|
|
- self.connection = Some(Session::new(
|
|
- self.project(),
|
|
- &project_view(self.project(), context.diagram),
|
|
- context,
|
|
- tool,
|
|
- ));
|
|
- self.selected_bend = None;
|
|
- self.status = tool.hint().into();
|
|
+ self.arm_connection(context, tool);
|
|
}
|
|
Event::Endpoint(captured, id) => {
|
|
if captured != context {
|
|
@@ -5164,8 +5184,11 @@
|
|
Event::Restart(captured) => {
|
|
if captured == context
|
|
&& let Some(session) = &self.connection
|
|
+ && session.context == context
|
|
{
|
|
- self.edit_connection(Event::Choose(Some(session.tool)));
|
|
+ // A contextual mixed-layer tool need not be in this layer's
|
|
+ // palette. Its unchanged session context retains admission.
|
|
+ self.arm_connection(context, session.tool);
|
|
}
|
|
}
|
|
Event::Choose(None) => {}
|