Remove obsolete canvas panel focus mode
This commit is contained in:
@@ -1181,27 +1181,14 @@ impl App {
|
||||
// This whitelist contains only the production presentation controls.
|
||||
// A stamped toolbar cannot smuggle an arbitrary authoring command.
|
||||
match &mut message {
|
||||
Message::ScenarioCanvas(Scenario::Camera(c, lifetime, Camera::Focus))
|
||||
if valid_scenario(*c) && *lifetime == scope.epoch =>
|
||||
{
|
||||
return canvas_workspace::focus_task(scope, Camera::Focus);
|
||||
}
|
||||
Message::TopologyCanvas(Process::Camera(c, lifetime, Camera::Focus))
|
||||
if valid_process(*c) && *lifetime == scope.epoch =>
|
||||
{
|
||||
return canvas_workspace::focus_task(scope, Camera::Focus);
|
||||
}
|
||||
Message::ScenarioCanvas(
|
||||
Scenario::Zoom(c, _)
|
||||
| Scenario::Choose(c, _)
|
||||
| Scenario::Focus(c)
|
||||
| Scenario::Shelf(c, _),
|
||||
Scenario::Zoom(c, _) | Scenario::Choose(c, _) | Scenario::Shelf(c, _),
|
||||
) if valid_scenario(*c) => {
|
||||
c.epoch = self.canvas_epoch;
|
||||
}
|
||||
Message::TopologyCanvas(
|
||||
Process::Zoom(c, _) | Process::Choose(c, _) | Process::Focus(c),
|
||||
) if valid_process(*c) => {
|
||||
Message::TopologyCanvas(Process::Zoom(c, _) | Process::Choose(c, _))
|
||||
if valid_process(*c) =>
|
||||
{
|
||||
c.epoch = self.canvas_epoch;
|
||||
}
|
||||
Message::Behavior(behavior::Event::Topology(behavior::topology::Event::Shelf(_)))
|
||||
@@ -1642,8 +1629,7 @@ impl App {
|
||||
return Task::none();
|
||||
}
|
||||
match input {
|
||||
Event::Focus
|
||||
| Event::Traverse(_)
|
||||
Event::Traverse(_)
|
||||
| Event::ControlFocus(_)
|
||||
| Event::ToggleFocus
|
||||
| Event::Leave => {
|
||||
@@ -1678,10 +1664,6 @@ impl App {
|
||||
self.begin_model_delete(None);
|
||||
}
|
||||
}
|
||||
CanvasEvent::Focus(_) => {
|
||||
self.behavior.scenario.toggle_focus();
|
||||
self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
|
||||
}
|
||||
CanvasEvent::Shelf(_, shelf) => {
|
||||
self.behavior.scenario.toggle_shelf(shelf);
|
||||
self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
|
||||
|
||||
@@ -74,11 +74,7 @@ impl App {
|
||||
return Task::none();
|
||||
}
|
||||
match input {
|
||||
Event::Focus
|
||||
| Event::Traverse(_)
|
||||
| Event::ControlFocus(_)
|
||||
| Event::ToggleFocus
|
||||
| Event::Leave => {
|
||||
Event::Traverse(_) | Event::ControlFocus(_) | Event::ToggleFocus | Event::Leave => {
|
||||
return canvas_workspace::focus_task(scope, input);
|
||||
}
|
||||
Event::CancelGesture => self.canvas_epoch = self.canvas_epoch.wrapping_add(1),
|
||||
@@ -163,10 +159,6 @@ impl App {
|
||||
self.behavior.topology.select_context(resolved.address);
|
||||
}
|
||||
}
|
||||
CanvasEvent::Focus(_) => {
|
||||
self.behavior.topology.focused = !self.behavior.topology.focused;
|
||||
self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
|
||||
}
|
||||
CanvasEvent::Select(_, id) => {
|
||||
if !self.behavior.has_pending()
|
||||
&& crate::behavior::selected_behavior(self.project(), id) == Some(context.owner)
|
||||
@@ -349,7 +341,7 @@ mod tests {
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn process_focus_retains_rejected_draft_and_guards_selection_until_panels_restore() {
|
||||
fn process_zoom_retains_rejected_draft_and_guards_selection() {
|
||||
let (mut app, f) = setup();
|
||||
let original = app.document.snapshot();
|
||||
let initial = app.selected;
|
||||
@@ -359,9 +351,14 @@ mod tests {
|
||||
&mut app,
|
||||
Event::Name(" unfinished contextual work ".into()),
|
||||
);
|
||||
let camera = app.behavior.topology.camera_boundary();
|
||||
let context = app.topology_context().unwrap();
|
||||
let _ = app.update(Message::TopologyCanvas(CanvasEvent::Focus(context)));
|
||||
assert!(app.behavior.topology.focused);
|
||||
let _ = app.update(Message::TopologyCanvas(CanvasEvent::Zoom(
|
||||
context,
|
||||
Some(0.75),
|
||||
)));
|
||||
assert_ne!(app.canvas_epoch, context.epoch);
|
||||
assert!(app.behavior.topology.camera_boundary() != camera);
|
||||
let raw = app.behavior.recovery_snapshot().unwrap();
|
||||
let context = app.topology_context().unwrap();
|
||||
let _ = app.update(Message::TopologyCanvas(CanvasEvent::Select(
|
||||
@@ -373,8 +370,8 @@ mod tests {
|
||||
assert_eq!(app.behavior.recovery_snapshot().unwrap(), raw);
|
||||
assert!(Arc::ptr_eq(&original, &app.document.snapshot()));
|
||||
let context = app.topology_context().unwrap();
|
||||
let _ = app.update(Message::TopologyCanvas(CanvasEvent::Focus(context)));
|
||||
assert!(!app.behavior.topology.focused);
|
||||
let _ = app.update(Message::TopologyCanvas(CanvasEvent::Zoom(context, None)));
|
||||
assert_ne!(app.canvas_epoch, context.epoch);
|
||||
assert_eq!(app.behavior.recovery_snapshot().unwrap(), raw);
|
||||
}
|
||||
#[test]
|
||||
|
||||
@@ -52,7 +52,9 @@ pub fn controls<'a>(
|
||||
text(format!("{:.0}%", scale * 100.)).size(12),
|
||||
control("ZoomIn", "+", Some(zoom(Some(scale * 1.25)))),
|
||||
control("ActualSize", "100%", Some(zoom(Some(1.)))),
|
||||
].spacing(4).align_y(iced::Center);
|
||||
]
|
||||
.spacing(4)
|
||||
.align_y(iced::Center);
|
||||
tooltip(
|
||||
controls,
|
||||
container(text("F6: enter/leave canvas keyboard controls · Tab: next · Enter/Space: activate · Esc: leave").size(12)).padding(7).style(crate::style::card),
|
||||
@@ -125,7 +127,6 @@ pub enum Event {
|
||||
CancelGesture,
|
||||
Pan(Vector),
|
||||
Zoom { factor: f32, at: Point, base: f32 },
|
||||
Focus,
|
||||
Traverse(bool),
|
||||
ControlFocus(&'static str),
|
||||
ToggleFocus,
|
||||
|
||||
@@ -70,7 +70,6 @@ impl<T: Send + 'static> Operation<T> for Collect {
|
||||
return operation::Outcome::None;
|
||||
}
|
||||
let target = match self.event {
|
||||
Event::Focus => Some(canvas),
|
||||
Event::ControlFocus(name) => Some(self.scope.control_id(name)),
|
||||
Event::ToggleFocus if self.focused.is_none() => Some(canvas),
|
||||
Event::ToggleFocus | Event::Leave if self.focused.is_some() => None,
|
||||
|
||||
@@ -130,7 +130,6 @@ pub enum CanvasEvent {
|
||||
FormShown(Context, Id),
|
||||
Zoom(Context, Option<f32>),
|
||||
Camera(Context, u64, super::canvas_workspace::Event),
|
||||
Focus(Context),
|
||||
Shelf(Context, Shelf),
|
||||
OpenUse(Context, Id),
|
||||
Back(Context),
|
||||
@@ -170,7 +169,6 @@ impl CanvasEvent {
|
||||
| Self::FormShown(context, _)
|
||||
| Self::Zoom(context, _)
|
||||
| Self::Camera(context, ..)
|
||||
| Self::Focus(context)
|
||||
| Self::Shelf(context, _)
|
||||
| Self::OpenUse(context, _)
|
||||
| Self::Back(context)
|
||||
@@ -320,7 +318,6 @@ impl State {
|
||||
pub(crate) fn camera_boundary(&self) -> impl PartialEq + use<> {
|
||||
(
|
||||
self.canvas_tool,
|
||||
self.presentation.focused,
|
||||
self.presentation.shelf,
|
||||
self.presentation.viewport,
|
||||
self.presentation.pan,
|
||||
@@ -359,7 +356,6 @@ impl State {
|
||||
if self.presentation.identity != Some((document, owner)) {
|
||||
self.presentation = workspace::Presentation {
|
||||
identity: Some((document, owner)),
|
||||
focused: self.presentation.focused,
|
||||
..Default::default()
|
||||
};
|
||||
self.details_offset = iced::Vector::ZERO;
|
||||
@@ -394,12 +390,6 @@ impl State {
|
||||
true
|
||||
}
|
||||
}
|
||||
pub(crate) fn focused(&self) -> bool {
|
||||
self.presentation.focused
|
||||
}
|
||||
pub(crate) fn toggle_focus(&mut self) {
|
||||
self.presentation.focused = !self.presentation.focused;
|
||||
}
|
||||
pub(crate) fn toggle_shelf(&mut self, shelf: Shelf) {
|
||||
self.presentation.shelf = (self.presentation.shelf != Some(shelf)).then_some(shelf);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ pub enum Shelf {
|
||||
pub(super) struct Presentation {
|
||||
pub(super) identity: Option<(Id, Id)>,
|
||||
pub(super) viewport: Option<Size>,
|
||||
pub(super) focused: bool,
|
||||
pub(super) shelf: Option<Shelf>,
|
||||
pub(super) pan: iced::Vector,
|
||||
pub(super) zoom: Option<f32>,
|
||||
|
||||
@@ -1040,7 +1040,9 @@ fn scenario_workspace_viewport_events_are_passive_guarded_and_do_not_apply_raw_f
|
||||
}
|
||||
app.busy = false;
|
||||
navigation_event(&mut app, |ctx| CanvasEvent::Shelf(ctx, Shelf::Selection));
|
||||
navigation_event(&mut app, CanvasEvent::Focus);
|
||||
let epoch = app.canvas_epoch;
|
||||
navigation_event(&mut app, |ctx| CanvasEvent::Zoom(ctx, Some(0.8)));
|
||||
assert_ne!(app.canvas_epoch, epoch);
|
||||
navigation_event(&mut app, |ctx| CanvasEvent::Zoom(ctx, None));
|
||||
assert_eq!(app.behavior.recovery_snapshot().unwrap(), raw);
|
||||
assert!(std::sync::Arc::ptr_eq(&accepted, &app.document.snapshot()));
|
||||
@@ -1341,7 +1343,7 @@ async fn production_scenario_camera_external_controls_bounds_and_retained_deltas
|
||||
let mut mounted = Mounted::new(&mut app).await;
|
||||
let snapshot = app.document.snapshot();
|
||||
let source = app.document.bundle().clone();
|
||||
for boundary in ["Fit", "A", "Details", "Panels", "Bounds"] {
|
||||
for boundary in ["Fit", "A", "Details", "ZoomIn", "Bounds"] {
|
||||
let toolbar = capture_toolbar(&mut mounted, &app, "Fit");
|
||||
let bounds = mounted.geometry(&app).bounds("scenario-viewport");
|
||||
let point = bounds.position() + iced::Vector::new(95., 90.);
|
||||
@@ -1371,11 +1373,6 @@ async fn production_scenario_camera_external_controls_bounds_and_retained_deltas
|
||||
let size = Size::new(app.window_size.width + 90., app.window_size.height + 35.);
|
||||
let task = app.update(Message::WindowResized(size));
|
||||
mounted.perform(&mut app, task).await;
|
||||
} else if boundary == "Panels" {
|
||||
// Panel layout remains a presentation command, not canvas activation.
|
||||
let context = app.scenario_context().unwrap();
|
||||
let task = app.update(Message::ScenarioCanvas(CanvasEvent::Focus(context)));
|
||||
mounted.perform(&mut app, task).await;
|
||||
} else {
|
||||
let id = scope(&app).control_id(boundary);
|
||||
let at = mounted.geometry(&app).containers[&id].center();
|
||||
@@ -1860,7 +1857,6 @@ async fn scenario_workspace_open_and_back_restore_owner_and_mounted_camera_after
|
||||
CanvasEvent::Choose(ctx, CanvasTool::Message(MessageStyle::Timer))
|
||||
});
|
||||
navigation_event(&mut app, |ctx| CanvasEvent::Shelf(ctx, Shelf::Selection));
|
||||
navigation_event(&mut app, CanvasEvent::Focus);
|
||||
let mut mounted = Mounted::new(&mut app).await;
|
||||
navigation_event(&mut app, |ctx| CanvasEvent::Zoom(ctx, Some(0.75)));
|
||||
let canvas = mounted.geometry(&app).bounds("scenario-viewport");
|
||||
@@ -1903,7 +1899,13 @@ async fn scenario_workspace_open_and_back_restore_owner_and_mounted_camera_after
|
||||
mounted.settle(&mut app).await;
|
||||
assert_eq!(app.behavior.scenario.presentation.pan, Vector::ZERO);
|
||||
navigation_event(&mut app, |ctx| CanvasEvent::Zoom(ctx, Some(2.)));
|
||||
navigation_event(&mut app, CanvasEvent::Focus);
|
||||
navigation_event(&mut app, |ctx| {
|
||||
CanvasEvent::Choose(ctx, CanvasTool::Add(Tool::Action))
|
||||
});
|
||||
assert_eq!(
|
||||
app.behavior.scenario.canvas_tool(b),
|
||||
CanvasTool::Add(Tool::Action)
|
||||
);
|
||||
let _ = app.update(Message::Command(Command::Rename(
|
||||
reference,
|
||||
"Renamed caller use".into(),
|
||||
@@ -1921,7 +1923,6 @@ async fn scenario_workspace_open_and_back_restore_owner_and_mounted_camera_after
|
||||
app.behavior.scenario.canvas_tool(a),
|
||||
CanvasTool::Message(MessageStyle::Timer)
|
||||
);
|
||||
assert!(app.behavior.scenario.focused());
|
||||
assert_eq!(
|
||||
app.behavior.scenario.presentation.shelf,
|
||||
Some(Shelf::Selection)
|
||||
|
||||
@@ -86,7 +86,6 @@ pub struct State {
|
||||
pan: iced::Vector,
|
||||
zoom: Option<f32>,
|
||||
fit: bool,
|
||||
pub(crate) focused: bool,
|
||||
contextual_expanded: BTreeSet<OccurrenceEndpoint>,
|
||||
selected_context: Option<oa_core::topology_context::ContextAddress>,
|
||||
shelf: Option<Shelf>,
|
||||
@@ -128,7 +127,6 @@ impl State {
|
||||
pub(crate) fn camera_boundary(&self) -> impl PartialEq + use<> {
|
||||
(
|
||||
self.tool,
|
||||
self.focused,
|
||||
self.shelf,
|
||||
self.viewport,
|
||||
self.pan,
|
||||
@@ -2148,7 +2146,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn process_fit_disclosures_and_focus_preserve_rejected_raw_draft_and_accepted_source() {
|
||||
fn process_zoom_and_disclosures_preserve_rejected_raw_draft_and_accepted_source() {
|
||||
let (p, owner, _, _, _, _) = fixture();
|
||||
let mut app = App::with_project(p);
|
||||
let _ = app.update(Message::Select(owner));
|
||||
@@ -2163,16 +2161,19 @@ mod tests {
|
||||
let before = app.behavior.recovery_snapshot().unwrap();
|
||||
let accepted = app.document.snapshot();
|
||||
assert!(app.behavior.prepare_pending(app.project()).is_err());
|
||||
for (shelf, expected) in [
|
||||
(Shelf::Selection, Some(Shelf::Selection)),
|
||||
(Shelf::Selection, None),
|
||||
for (shelf, expected, zoom) in [
|
||||
(Shelf::Selection, Some(Shelf::Selection), 0.6),
|
||||
(Shelf::Selection, None, 1.2),
|
||||
] {
|
||||
let focused = app.behavior.topology.focused;
|
||||
let _ = app.update(event(Event::Shelf(shelf)));
|
||||
assert_eq!(app.behavior.topology.shelf, expected);
|
||||
let context = app.topology_context().unwrap();
|
||||
let _ = app.update(Message::TopologyCanvas(CanvasEvent::Focus(context)));
|
||||
assert_eq!(app.behavior.topology.focused, !focused);
|
||||
let _ = app.update(Message::TopologyCanvas(CanvasEvent::Zoom(
|
||||
context,
|
||||
Some(zoom),
|
||||
)));
|
||||
assert_ne!(app.canvas_epoch, context.epoch);
|
||||
assert_eq!(app.behavior.topology.zoom, Some(zoom));
|
||||
assert_eq!(app.behavior.recovery_snapshot().unwrap(), before);
|
||||
assert!(app.behavior.prepare_pending(app.project()).is_err());
|
||||
assert!(std::sync::Arc::ptr_eq(&app.document.snapshot(), &accepted));
|
||||
|
||||
@@ -62,7 +62,6 @@ pub enum CanvasEvent {
|
||||
Back(Context),
|
||||
Zoom(Context, Option<f32>),
|
||||
Camera(Context, u64, super::super::canvas_workspace::Event),
|
||||
Focus(Context),
|
||||
Viewport(Context, Size),
|
||||
}
|
||||
impl CanvasEvent {
|
||||
@@ -81,7 +80,6 @@ impl CanvasEvent {
|
||||
| Self::AppendExchange(c)
|
||||
| Self::Zoom(c, ..)
|
||||
| Self::Camera(c, ..)
|
||||
| Self::Focus(c)
|
||||
| Self::Viewport(c, ..) => *c,
|
||||
Self::Begin { context, .. }
|
||||
| Self::Connect { context, .. }
|
||||
|
||||
@@ -218,7 +218,7 @@ async fn production_process_camera_external_controls_bounds_and_retained_deltas_
|
||||
let mut mounted = Mounted::new(&mut app).await;
|
||||
let snapshot = app.document.snapshot();
|
||||
let source = app.document.bundle().clone();
|
||||
for boundary in ["Fit", "OR", "Details", "Panels", "Bounds"] {
|
||||
for boundary in ["Fit", "OR", "Details", "ZoomIn", "Bounds"] {
|
||||
let toolbar = capture_toolbar(&mut mounted, &app, "Fit");
|
||||
let bounds = mounted.geometry(&app).bounds("process-viewport");
|
||||
let point = bounds.position() + iced::Vector::new(95., 90.);
|
||||
@@ -248,11 +248,6 @@ async fn production_process_camera_external_controls_bounds_and_retained_deltas_
|
||||
let size = Size::new(app.window_size.width + 90., app.window_size.height + 35.);
|
||||
let task = app.update(Message::WindowResized(size));
|
||||
mounted.perform(&mut app, task).await;
|
||||
} else if boundary == "Panels" {
|
||||
// Panel layout remains a presentation command, not canvas activation.
|
||||
let context = app.topology_context().unwrap();
|
||||
let task = app.update(Message::TopologyCanvas(CanvasEvent::Focus(context)));
|
||||
mounted.perform(&mut app, task).await;
|
||||
} else {
|
||||
let id = scope(&app).control_id(boundary);
|
||||
let at = mounted.geometry(&app).containers[&id].center();
|
||||
|
||||
@@ -463,7 +463,7 @@ fn article(topic: Topic) -> Article {
|
||||
),
|
||||
explain(
|
||||
"Navigate the canvas",
|
||||
"The header and palette stay available while Details and creation forms overlay the canvas. Fit frames the contents; −/+ changes zoom and 100% restores working scale. Focus the canvas to zoom with the wheel or pan with middle-button drag or Space + left drag. F6 enters or leaves canvas controls; Tab moves to the next control, Enter/Space activates it, and Escape leaves canvas controls or cancels a gesture.",
|
||||
"The header and palette stay available while Details and creation forms overlay the canvas. Fit frames the contents; −/+ changes zoom and 100% restores working scale. Click in the canvas to give it wheel and keyboard focus; pan with middle-button drag or Space + left drag. F6 enters or leaves canvas keyboard controls; Tab moves to the next control, Enter/Space activates it, and Escape leaves canvas controls or cancels a gesture.",
|
||||
),
|
||||
explain(
|
||||
"Choose the right behavioral description",
|
||||
@@ -476,7 +476,7 @@ fn article(topic: Topic) -> Article {
|
||||
walkthrough(
|
||||
"Connect a particular use of a procedure",
|
||||
&[
|
||||
"Choose Focus canvas for more working space; Restore panels brings the explorer and properties back. Select a contribution inside a reusable preview, then open Details to read its complete caller and reference identity.",
|
||||
"Select a contribution directly inside a reusable preview, then open Details to read its complete caller and reference identity. The explorer and properties remain available alongside the canvas.",
|
||||
"In Details, use Expand nested reference or Fold nested reference to inspect that instance. Open source or Open contribution definition visits the shared definition; Back to caller returns to the original caller context.",
|
||||
"Open Details → Qualified link / assembly... and choose its source and target. Use Enter reference to open an instance's contributions, Up one reference for its caller scope, or Root for the link owner. Search and page through all contributions in that scope, then select the exact endpoint. The first and second uses of a procedure remain different choices even when their work has the same name.",
|
||||
"Choose Precedence to describe order and its guard, Exchange assembly to identify a work exchange between the contributions, or Common work incidence to state that both use the same work. Select the engineering identity when required, then Apply link.",
|
||||
|
||||
@@ -227,35 +227,12 @@ pub fn view(app: &App) -> View<'_, Message> {
|
||||
Page::Validation => checks(app),
|
||||
Page::Guide => crate::guide::view(&app.guide),
|
||||
};
|
||||
let process_layout = app.topology_context().is_some();
|
||||
let scenario_layout = app.scenario_context().is_some();
|
||||
let focus_behavior = (process_layout && app.behavior.topology.focused)
|
||||
|| (scenario_layout && app.behavior.scenario.focused());
|
||||
let mut body = if app.page == Page::Home {
|
||||
row![container(main).width(Fill).height(Fill)].height(Fill)
|
||||
} else if process_layout || scenario_layout {
|
||||
// Keep main at the same widget-tree path across Focus toggles. The
|
||||
// collapsed slot has no controls, so it cannot paint over or hit the canvas.
|
||||
let process_sidebar: View<'_, Message> = if focus_behavior {
|
||||
Space::new().width(0).into()
|
||||
} else {
|
||||
sidebar.into()
|
||||
};
|
||||
// Row::push would discard the zero-width slot and shift main again.
|
||||
widget::Row::from_vec(vec![
|
||||
container(process_sidebar)
|
||||
.width(if focus_behavior { 0 } else { 260 })
|
||||
.height(Fill)
|
||||
.clip(true)
|
||||
.into(),
|
||||
container(main).width(Fill).height(Fill).into(),
|
||||
])
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
} else {
|
||||
row![sidebar, container(main).width(Fill).height(Fill)].height(Fill)
|
||||
};
|
||||
if !focus_behavior && !matches!(app.page, Page::Home | Page::Text | Page::Guide) {
|
||||
if !matches!(app.page, Page::Home | Page::Text | Page::Guide) {
|
||||
body = body.push(inspector(app));
|
||||
}
|
||||
let footer = container(
|
||||
|
||||
Reference in New Issue
Block a user