Retire obsolete behavior canvas scroll and tool paths
This commit is contained in:
@@ -96,7 +96,6 @@ impl std::fmt::Display for CanvasTool {
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum CanvasEvent {
|
||||
First(Context, Tool),
|
||||
Scrolled(Context, ScrollArea, iced::Vector),
|
||||
Restored(Context, Id, ScrollArea, iced::Vector),
|
||||
Viewport(Context, Size),
|
||||
@@ -130,15 +129,13 @@ pub enum CanvasEvent {
|
||||
}
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum ScrollArea {
|
||||
Canvas,
|
||||
// Retained navigation channel, now the independent Selection/outline shelf.
|
||||
// The independent Details/outline scroller; viewport motion uses Camera.
|
||||
Page,
|
||||
}
|
||||
impl CanvasEvent {
|
||||
pub fn context(&self) -> Context {
|
||||
match self {
|
||||
Self::First(context, _)
|
||||
| Self::Scrolled(context, _, _)
|
||||
Self::Scrolled(context, _, _)
|
||||
| Self::Restored(context, ..)
|
||||
| Self::Viewport(context, _)
|
||||
| Self::FormShown(context, _)
|
||||
@@ -211,9 +208,9 @@ pub struct State {
|
||||
form_focus_pending: bool,
|
||||
canvas_tool: Option<(Id, CanvasTool)>,
|
||||
call_choices: RefCell<Option<(CallChoiceContext, Vec<Choice>)>>,
|
||||
scroll_offsets: [iced::Vector; 2],
|
||||
details_offset: iced::Vector,
|
||||
return_to: Vec<Caller>,
|
||||
restore: Option<ViewportRestore>,
|
||||
restore: Option<DetailsRestore>,
|
||||
presentation: workspace::Presentation,
|
||||
}
|
||||
const CALLER_LIMIT: usize = 32;
|
||||
@@ -223,15 +220,14 @@ pub(crate) struct Caller {
|
||||
pub occurrence: Id,
|
||||
target: Id,
|
||||
tool: CanvasTool,
|
||||
offsets: [iced::Vector; 2],
|
||||
details_offset: iced::Vector,
|
||||
presentation: workspace::Presentation,
|
||||
}
|
||||
struct ViewportRestore {
|
||||
struct DetailsRestore {
|
||||
document: Id,
|
||||
owner: Id,
|
||||
token: Id,
|
||||
offsets: [iced::Vector; 2],
|
||||
pending: [bool; 2],
|
||||
offset: iced::Vector,
|
||||
}
|
||||
#[derive(Clone, Copy)]
|
||||
pub(super) struct ScrollRestore {
|
||||
@@ -336,7 +332,7 @@ impl State {
|
||||
focused: self.presentation.focused,
|
||||
..Default::default()
|
||||
};
|
||||
self.scroll_offsets = [iced::Vector::ZERO; 2];
|
||||
self.details_offset = iced::Vector::ZERO;
|
||||
self.restore = None;
|
||||
}
|
||||
}
|
||||
@@ -396,11 +392,11 @@ impl State {
|
||||
occurrence,
|
||||
target,
|
||||
tool: self.canvas_tool(context.scenario),
|
||||
offsets: self.scroll_offsets,
|
||||
details_offset: self.details_offset,
|
||||
presentation: self.presentation.clone(),
|
||||
});
|
||||
self.canvas_tool = Some((target, CanvasTool::Select));
|
||||
self.scroll_offsets = [iced::Vector::ZERO; 2];
|
||||
self.details_offset = iced::Vector::ZERO;
|
||||
self.restore = None;
|
||||
Ok(target)
|
||||
}
|
||||
@@ -427,34 +423,30 @@ impl State {
|
||||
return;
|
||||
}
|
||||
self.canvas_tool = Some((frame.owner, frame.tool));
|
||||
self.scroll_offsets = frame.offsets;
|
||||
self.details_offset = frame.details_offset;
|
||||
self.presentation = frame.presentation;
|
||||
self.presentation.identity = Some((context.document, context.scenario));
|
||||
self.restore = Some(ViewportRestore {
|
||||
self.restore = Some(DetailsRestore {
|
||||
document: context.document,
|
||||
owner: frame.owner,
|
||||
token: Id::new_v4(),
|
||||
offsets: frame.offsets,
|
||||
pending: [false, true],
|
||||
offset: frame.details_offset,
|
||||
});
|
||||
}
|
||||
pub(super) fn scroll_restore(
|
||||
&self,
|
||||
context: Context,
|
||||
area: ScrollArea,
|
||||
ScrollArea::Page: ScrollArea,
|
||||
) -> Option<ScrollRestore> {
|
||||
let index = match area {
|
||||
ScrollArea::Canvas => 0,
|
||||
ScrollArea::Page => 1,
|
||||
};
|
||||
let restore = self.restore.as_ref().filter(|r| {
|
||||
r.document == context.document && r.owner == context.scenario && r.pending[index]
|
||||
})?;
|
||||
let restore = self
|
||||
.restore
|
||||
.as_ref()
|
||||
.filter(|r| r.document == context.document && r.owner == context.scenario)?;
|
||||
Some(ScrollRestore {
|
||||
document: restore.document,
|
||||
owner: restore.owner,
|
||||
token: restore.token,
|
||||
offset: restore.offsets[index],
|
||||
offset: restore.offset,
|
||||
})
|
||||
}
|
||||
pub(crate) fn restored(
|
||||
@@ -464,22 +456,12 @@ impl State {
|
||||
area: ScrollArea,
|
||||
offset: iced::Vector,
|
||||
) -> bool {
|
||||
let index = match area {
|
||||
ScrollArea::Canvas => 0,
|
||||
ScrollArea::Page => 1,
|
||||
};
|
||||
let Some(restore) = self.restore.as_mut().filter(|r| {
|
||||
r.document == context.document
|
||||
&& r.owner == context.scenario
|
||||
&& r.token == token
|
||||
&& r.pending[index]
|
||||
}) else {
|
||||
if !self.restore.as_ref().is_some_and(|r| {
|
||||
r.document == context.document && r.owner == context.scenario && r.token == token
|
||||
}) {
|
||||
return false;
|
||||
};
|
||||
restore.pending[index] = false;
|
||||
if !restore.pending.contains(&true) {
|
||||
self.restore = None;
|
||||
}
|
||||
self.restore = None;
|
||||
self.scrolled(area, offset)
|
||||
}
|
||||
pub(super) fn recovery_snapshot(&self) -> Result<Option<serde_json::Value>, String> {
|
||||
@@ -535,23 +517,14 @@ impl State {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn scrolled(&mut self, area: ScrollArea, offset: iced::Vector) -> bool {
|
||||
pub(crate) fn scrolled(&mut self, ScrollArea::Page: ScrollArea, offset: iced::Vector) -> bool {
|
||||
if !offset.x.is_finite() || !offset.y.is_finite() || offset.x < 0. || offset.y < 0. {
|
||||
return false;
|
||||
}
|
||||
let index = match area {
|
||||
ScrollArea::Canvas => 0,
|
||||
ScrollArea::Page => 1,
|
||||
};
|
||||
let changed = self.scroll_offsets[index] != offset;
|
||||
self.scroll_offsets[index] = offset;
|
||||
let changed = self.details_offset != offset;
|
||||
self.details_offset = offset;
|
||||
changed
|
||||
}
|
||||
#[cfg(test)]
|
||||
fn scroll_offset(&self) -> iced::Vector {
|
||||
// The former page scroll now belongs to a sibling outline shelf.
|
||||
self.scroll_offsets[0]
|
||||
}
|
||||
fn call_choices(&self, p: &Project, context: Context, draft: &Draft) -> Vec<Choice> {
|
||||
let key = CallChoiceContext {
|
||||
document: context.document,
|
||||
@@ -2079,20 +2052,31 @@ pub(crate) mod direct_tests {
|
||||
navigation_event(&mut app, |ctx| {
|
||||
CanvasEvent::Choose(ctx, CanvasTool::Add(Tool::Reuse))
|
||||
});
|
||||
navigation_event(&mut app, |ctx| CanvasEvent::Zoom(ctx, None));
|
||||
let camera_epoch = app.camera_epoch;
|
||||
navigation_event(&mut app, |ctx| {
|
||||
CanvasEvent::Scrolled(ctx, ScrollArea::Canvas, iced::Vector::new(25., 170.))
|
||||
CanvasEvent::Camera(
|
||||
ctx,
|
||||
camera_epoch,
|
||||
super::super::canvas_workspace::Event::Pan(iced::Vector::new(25., 170.)),
|
||||
)
|
||||
});
|
||||
navigation_event(&mut app, |ctx| {
|
||||
CanvasEvent::Scrolled(ctx, ScrollArea::Page, iced::Vector::new(0., 80.))
|
||||
});
|
||||
navigation_event(&mut app, |ctx| CanvasEvent::OpenUse(ctx, caller));
|
||||
assert_eq!(app.selected, b);
|
||||
assert_eq!(app.behavior.scenario.scroll_offset(), iced::Vector::ZERO);
|
||||
assert_eq!(app.behavior.scenario.presentation.pan, iced::Vector::ZERO);
|
||||
navigation_event(&mut app, |ctx| {
|
||||
CanvasEvent::Choose(ctx, CanvasTool::Message(MessageStyle::Timer))
|
||||
});
|
||||
let camera_epoch = app.camera_epoch;
|
||||
navigation_event(&mut app, |ctx| {
|
||||
CanvasEvent::Scrolled(ctx, ScrollArea::Canvas, iced::Vector::new(0., 115.))
|
||||
CanvasEvent::Camera(
|
||||
ctx,
|
||||
camera_epoch,
|
||||
super::super::canvas_workspace::Event::Pan(iced::Vector::new(0., 115.)),
|
||||
)
|
||||
});
|
||||
navigation_event(&mut app, |ctx| CanvasEvent::OpenUse(ctx, nested));
|
||||
assert_eq!(app.selected, c);
|
||||
@@ -2103,7 +2087,7 @@ pub(crate) mod direct_tests {
|
||||
CanvasTool::Message(MessageStyle::Timer)
|
||||
);
|
||||
assert_eq!(
|
||||
app.behavior.scenario.scroll_offset(),
|
||||
app.behavior.scenario.presentation.pan,
|
||||
iced::Vector::new(0., 115.)
|
||||
);
|
||||
navigation_event(&mut app, CanvasEvent::Back);
|
||||
@@ -2114,11 +2098,11 @@ pub(crate) mod direct_tests {
|
||||
CanvasTool::Add(Tool::Reuse)
|
||||
);
|
||||
assert_eq!(
|
||||
app.behavior.scenario.scroll_offset(),
|
||||
app.behavior.scenario.presentation.pan,
|
||||
iced::Vector::new(25., 170.)
|
||||
);
|
||||
assert_eq!(
|
||||
app.behavior.scenario.scroll_offsets[1],
|
||||
app.behavior.scenario.details_offset,
|
||||
iced::Vector::new(0., 80.),
|
||||
"The caller's independent outline scroll is restored separately"
|
||||
);
|
||||
@@ -2296,22 +2280,24 @@ pub(crate) mod direct_tests {
|
||||
let before = app.document.bundle().clone();
|
||||
let mut scene = Scene::sequence(app.project(), f.owner, f.call);
|
||||
scene.interaction = Some((context, CanvasTool::Add(tool), true));
|
||||
scene.scroll_offset = iced::Vector::new(23., 80.);
|
||||
let bounds = Rectangle::new(Point::new(20., 40.), scene.size);
|
||||
scene.camera_pan = iced::Vector::new(23., 80.);
|
||||
let bounds = Rectangle::new(
|
||||
Point::new(20., 40.),
|
||||
Size::new(scene.size.width + 46., scene.size.height + 160.),
|
||||
);
|
||||
let y = scene.slots.iter().find(|s| s.slot == slot).unwrap().y;
|
||||
let x = if on_lifeline {
|
||||
scene.lanes[&f.sender]
|
||||
} else {
|
||||
scene.size.width - 35.
|
||||
};
|
||||
let point = Point::new(bounds.x + x, bounds.y + y);
|
||||
let point = Point::new(bounds.x + x, bounds.y + y) + scene.camera_pan;
|
||||
assert!(bounds.contains(point));
|
||||
let mut input = SequenceInput::default();
|
||||
canvas_mouse(
|
||||
&scene,
|
||||
&mut input,
|
||||
mouse::Event::CursorMoved {
|
||||
position: point - scene.scroll_offset,
|
||||
},
|
||||
mouse::Event::CursorMoved { position: point },
|
||||
point,
|
||||
bounds,
|
||||
);
|
||||
@@ -2487,10 +2473,10 @@ pub(crate) mod direct_tests {
|
||||
assert!(!app.behavior.has_pending());
|
||||
}
|
||||
let old_context = app.scenario_context().unwrap();
|
||||
let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Scrolled(
|
||||
let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Camera(
|
||||
old_context,
|
||||
ScrollArea::Canvas,
|
||||
iced::Vector::new(0., 100.),
|
||||
app.camera_epoch,
|
||||
super::super::canvas_workspace::Event::Pan(iced::Vector::new(0., 100.)),
|
||||
)));
|
||||
let _ = app.update(Message::ScenarioCanvas(event(old_context)));
|
||||
assert!(!app.behavior.has_pending());
|
||||
@@ -2681,26 +2667,29 @@ pub(crate) mod direct_tests {
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn direct_busy_save_keeps_observed_scroll_current_for_the_next_pointer_gesture() {
|
||||
fn direct_busy_save_keeps_details_current_and_camera_for_the_next_pointer_gesture() {
|
||||
let f = fixture();
|
||||
let mut app = App::with_project(f.project.clone());
|
||||
let _ = app.update(Message::Select(f.owner));
|
||||
let _ = app.update(Message::Page(crate::app::Page::Behavior));
|
||||
let context = app.scenario_context().unwrap();
|
||||
let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Camera(
|
||||
context,
|
||||
app.camera_epoch,
|
||||
super::super::canvas_workspace::Event::Pan(iced::Vector::new(-25., -100.)),
|
||||
)));
|
||||
let context = app.scenario_context().unwrap();
|
||||
let original = app.document.snapshot();
|
||||
app.busy = true;
|
||||
for (area, offset) in [
|
||||
(ScrollArea::Canvas, iced::Vector::new(25., 100.)),
|
||||
(ScrollArea::Page, iced::Vector::new(0., 40.)),
|
||||
] {
|
||||
let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Scrolled(
|
||||
context, area, offset,
|
||||
)));
|
||||
}
|
||||
let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Scrolled(
|
||||
context,
|
||||
ScrollArea::Page,
|
||||
iced::Vector::new(0., 40.),
|
||||
)));
|
||||
assert!(app.busy);
|
||||
assert_eq!(
|
||||
app.behavior.scenario.scroll_offset(),
|
||||
iced::Vector::new(25., 100.)
|
||||
app.behavior.scenario.details_offset,
|
||||
iced::Vector::new(0., 40.)
|
||||
);
|
||||
assert_ne!(
|
||||
app.scenario_context(),
|
||||
@@ -2711,14 +2700,14 @@ pub(crate) mod direct_tests {
|
||||
stale.document = Id::new_v4();
|
||||
let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Scrolled(
|
||||
stale,
|
||||
ScrollArea::Canvas,
|
||||
ScrollArea::Page,
|
||||
iced::Vector::new(0., 900.),
|
||||
)));
|
||||
assert_eq!(
|
||||
app.behavior.scenario.scroll_offset(),
|
||||
iced::Vector::new(25., 100.)
|
||||
app.behavior.scenario.details_offset,
|
||||
iced::Vector::new(0., 40.)
|
||||
);
|
||||
let root = std::path::PathBuf::from("scenario-scroll-save-fixture");
|
||||
let root = std::path::PathBuf::from("scenario-details-save-fixture");
|
||||
let receipt = crate::app::FileSnapshot {
|
||||
document: context.document,
|
||||
revision: context.revision,
|
||||
@@ -2739,12 +2728,17 @@ pub(crate) mod direct_tests {
|
||||
let context = app.scenario_context().unwrap();
|
||||
let mut scene = Scene::sequence(app.project(), f.owner, f.call);
|
||||
scene.interaction = Some((context, CanvasTool::Message(MessageStyle::CallReply), true));
|
||||
scene.scroll_offset = app.behavior.scenario.scroll_offset();
|
||||
assert_eq!(
|
||||
app.behavior.scenario.presentation.pan,
|
||||
iced::Vector::new(-25., -100.)
|
||||
);
|
||||
scene.camera_pan = app.behavior.scenario.presentation.pan;
|
||||
let slot = scene.slots.last().unwrap().slot;
|
||||
let bounds = Rectangle::new(Point::new(20., 250.), scene.size);
|
||||
let y = bounds.y + scene.slots.last().unwrap().y;
|
||||
let a = Point::new(bounds.x + scene.lanes[&f.sender], y) - scene.scroll_offset;
|
||||
let b = Point::new(bounds.x + scene.lanes[&f.receiver], y) - scene.scroll_offset;
|
||||
let a = Point::new(bounds.x + scene.lanes[&f.sender], y) + scene.camera_pan;
|
||||
let b = Point::new(bounds.x + scene.lanes[&f.receiver], y) + scene.camera_pan;
|
||||
assert!(bounds.contains(a) && bounds.contains(b));
|
||||
let mut input = super::super::SequenceInput::default();
|
||||
let mut published = vec![];
|
||||
for event in [
|
||||
@@ -2758,7 +2752,7 @@ pub(crate) mod direct_tests {
|
||||
&mut input,
|
||||
&canvas::Event::Mouse(event),
|
||||
bounds,
|
||||
mouse::Cursor::Available(b + scene.scroll_offset),
|
||||
mouse::Cursor::Available(b),
|
||||
)
|
||||
.and_then(|action| action.into_inner().0)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user