use crate::runtime::auto_pan::{AutoPanRequest, SelectionAutoPanRequest};
use crate::runtime::gesture::ViewportDragPanSession;
use crate::runtime::store::NodeGraphStore;
use crate::runtime::viewport::{
ViewportAnimationFrame, ViewportAnimationPlan, ViewportAnimationRequest,
ViewportDoubleClickZoomInput, ViewportDragPanInput, ViewportGestureContext,
ViewportGestureIntent, ViewportGestureRejection, ViewportPanInertiaFrame,
ViewportPanInertiaPlan, ViewportPanInertiaRequest, ViewportPanRequest, ViewportScrollInput,
ViewportZoomRequest, plan_viewport_animation_with_options, plan_viewport_pan_inertia,
resolve_viewport_double_click_zoom, resolve_viewport_drag_pan_gesture,
resolve_viewport_scroll_gesture,
};
use jellyflow_core::core::CanvasSize;
use super::super::super::scenario::ConformanceAction;
pub(super) fn execute_action(
store: &mut NodeGraphStore,
action: &ConformanceAction,
) -> Option<Result<(), String>> {
Some(match action {
ConformanceAction::ApplyAutoPan { request } => apply_auto_pan(store, *request),
ConformanceAction::ApplySelectionAutoPan { request } => {
apply_selection_auto_pan(store, *request)
}
ConformanceAction::ApplyViewportPan { request } => apply_pan(store, *request),
ConformanceAction::ApplyViewportPanConstrained {
request,
viewport_size,
} => apply_pan_constrained(store, *request, *viewport_size),
ConformanceAction::ApplyViewportZoom { request } => apply_zoom(store, *request),
ConformanceAction::ApplyViewportZoomConstrained {
request,
viewport_size,
} => apply_zoom_constrained(store, *request, *viewport_size),
ConformanceAction::ApplyViewportAnimationFrame {
request,
elapsed_seconds,
} => apply_animation_frame(store, *request, *elapsed_seconds),
ConformanceAction::ApplyViewportAnimationFrames {
request,
elapsed_seconds,
} => apply_animation_frames(store, *request, elapsed_seconds),
ConformanceAction::AssertViewportAnimationFrame {
request,
elapsed_seconds,
expected,
} => assert_animation_frame(*request, *elapsed_seconds, *expected),
ConformanceAction::ApplyViewportPanInertiaFrame {
request,
elapsed_seconds,
} => apply_pan_inertia_frame(store, request, *elapsed_seconds),
ConformanceAction::ApplyViewportPanInertiaFrames {
request,
elapsed_seconds,
} => apply_pan_inertia_frames(store, request, elapsed_seconds),
ConformanceAction::AssertViewportPanInertiaFrame {
request,
elapsed_seconds,
expected,
} => assert_pan_inertia_frame(request, *elapsed_seconds, *expected),
ConformanceAction::ExpectViewportPanInertiaRejected { request } => {
expect_pan_inertia_rejected(request)
}
ConformanceAction::AssertViewportDoubleClickZoom {
input,
expected,
expect_rejection,
} => assert_double_click_zoom(store, *input, *expected, *expect_rejection),
ConformanceAction::ApplyViewportScrollGesture {
context,
input,
expect_rejection,
} => apply_scroll_gesture(store, *context, *input, *expect_rejection),
ConformanceAction::ApplyViewportDragPanGesture {
context,
input,
expect_rejection,
} => apply_drag_pan_gesture(store, *context, *input, *expect_rejection),
ConformanceAction::ApplyViewportDragPanSession {
context,
input,
expect_rejection,
} => apply_drag_pan_session(store, *context, *input, *expect_rejection),
_ => return None,
})
}
pub(super) fn apply_auto_pan(
store: &mut NodeGraphStore,
request: AutoPanRequest,
) -> Result<(), String> {
store
.apply_auto_pan(request)
.map(|_| ())
.ok_or_else(|| "auto-pan request was rejected".to_owned())
}
pub(super) fn apply_selection_auto_pan(
store: &mut NodeGraphStore,
request: SelectionAutoPanRequest,
) -> Result<(), String> {
store
.apply_selection_auto_pan(request)
.map(|_| ())
.ok_or_else(|| "selection auto-pan request was rejected".to_owned())
}
pub(super) fn apply_pan(
store: &mut NodeGraphStore,
request: ViewportPanRequest,
) -> Result<(), String> {
store
.apply_viewport_pan(request)
.map(|_| ())
.ok_or_else(|| "viewport pan request was rejected".to_owned())
}
pub(super) fn apply_pan_constrained(
store: &mut NodeGraphStore,
request: ViewportPanRequest,
viewport_size: CanvasSize,
) -> Result<(), String> {
store
.apply_viewport_pan_constrained(request, viewport_size)
.map(|_| ())
.ok_or_else(|| "viewport constrained pan request was rejected".to_owned())
}
pub(super) fn apply_zoom(
store: &mut NodeGraphStore,
request: ViewportZoomRequest,
) -> Result<(), String> {
store
.apply_viewport_zoom(request)
.map(|_| ())
.ok_or_else(|| "viewport zoom request was rejected".to_owned())
}
pub(super) fn apply_zoom_constrained(
store: &mut NodeGraphStore,
request: ViewportZoomRequest,
viewport_size: CanvasSize,
) -> Result<(), String> {
store
.apply_viewport_zoom_constrained(request, viewport_size)
.map(|_| ())
.ok_or_else(|| "viewport constrained zoom request was rejected".to_owned())
}
pub(super) fn apply_animation_frame(
store: &mut NodeGraphStore,
request: ViewportAnimationRequest,
elapsed_seconds: f32,
) -> Result<(), String> {
let plan = plan_viewport_animation_with_options(request)
.ok_or_else(|| "viewport animation request was rejected".to_owned())?;
apply_viewport_animation_frame(store, plan, elapsed_seconds)
}
pub(super) fn apply_animation_frames(
store: &mut NodeGraphStore,
request: ViewportAnimationRequest,
elapsed_seconds: &[f32],
) -> Result<(), String> {
if elapsed_seconds.is_empty() {
return Err("viewport animation frame list was empty".to_owned());
}
let plan = plan_viewport_animation_with_options(request)
.ok_or_else(|| "viewport animation request was rejected".to_owned())?;
for elapsed_seconds in elapsed_seconds {
apply_viewport_animation_frame(store, plan, *elapsed_seconds)?;
}
Ok(())
}
pub(super) fn assert_animation_frame(
request: ViewportAnimationRequest,
elapsed_seconds: f32,
expected: ViewportAnimationFrame,
) -> Result<(), String> {
let plan = plan_viewport_animation_with_options(request)
.ok_or_else(|| "viewport animation request was rejected".to_owned())?;
let actual = plan
.frame_at(elapsed_seconds)
.ok_or_else(|| "viewport animation frame was rejected".to_owned())?;
if actual == expected {
Ok(())
} else {
Err(format!(
"viewport animation frame resolved to {actual:?}, expected {expected:?}"
))
}
}
pub(super) fn apply_pan_inertia_frame(
store: &mut NodeGraphStore,
request: &ViewportPanInertiaRequest,
elapsed_seconds: f32,
) -> Result<(), String> {
let plan = plan_viewport_pan_inertia(request.clone())
.ok_or_else(|| "viewport pan inertia request was rejected".to_owned())?;
apply_viewport_pan_inertia_frame(store, plan, elapsed_seconds)
}
pub(super) fn apply_pan_inertia_frames(
store: &mut NodeGraphStore,
request: &ViewportPanInertiaRequest,
elapsed_seconds: &[f32],
) -> Result<(), String> {
if elapsed_seconds.is_empty() {
return Err("viewport pan inertia frame list was empty".to_owned());
}
let plan = plan_viewport_pan_inertia(request.clone())
.ok_or_else(|| "viewport pan inertia request was rejected".to_owned())?;
for elapsed_seconds in elapsed_seconds {
apply_viewport_pan_inertia_frame(store, plan, *elapsed_seconds)?;
}
Ok(())
}
pub(super) fn assert_pan_inertia_frame(
request: &ViewportPanInertiaRequest,
elapsed_seconds: f32,
expected: ViewportPanInertiaFrame,
) -> Result<(), String> {
let plan = plan_viewport_pan_inertia(request.clone())
.ok_or_else(|| "viewport pan inertia request was rejected".to_owned())?;
let actual = plan
.frame_at(elapsed_seconds)
.ok_or_else(|| "viewport pan inertia frame was rejected".to_owned())?;
if actual == expected {
Ok(())
} else {
Err(format!(
"viewport pan inertia frame resolved to {actual:?}, expected {expected:?}"
))
}
}
pub(super) fn expect_pan_inertia_rejected(
request: &ViewportPanInertiaRequest,
) -> Result<(), String> {
match plan_viewport_pan_inertia(request.clone()) {
Some(actual) => Err(format!(
"viewport pan inertia was accepted as {actual:?}, expected rejection"
)),
None => Ok(()),
}
}
pub(super) fn assert_double_click_zoom(
store: &NodeGraphStore,
input: ViewportDoubleClickZoomInput,
expected: Option<ViewportAnimationPlan>,
expect_rejection: Option<ViewportGestureRejection>,
) -> Result<(), String> {
let interaction = store.resolved_interaction_state();
let result = resolve_viewport_double_click_zoom(&interaction.zoom_interaction(), input);
assert_viewport_double_click_zoom_result(result, expected, expect_rejection)
}
pub(super) fn apply_scroll_gesture(
store: &mut NodeGraphStore,
context: ViewportGestureContext,
input: ViewportScrollInput,
expect_rejection: Option<ViewportGestureRejection>,
) -> Result<(), String> {
let interaction = store.resolved_interaction_state();
let result = resolve_viewport_scroll_gesture(
&interaction.pan_interaction(),
&interaction.zoom_interaction(),
context,
input,
);
apply_viewport_gesture_result(store, result, expect_rejection)
}
pub(super) fn apply_drag_pan_gesture(
store: &mut NodeGraphStore,
context: ViewportGestureContext,
input: ViewportDragPanInput,
expect_rejection: Option<ViewportGestureRejection>,
) -> Result<(), String> {
let interaction = store.resolved_interaction_state();
let result = resolve_viewport_drag_pan_gesture(&interaction.pan_interaction(), context, input);
apply_viewport_gesture_result(store, result, expect_rejection)
}
pub(super) fn apply_drag_pan_session(
store: &mut NodeGraphStore,
context: ViewportGestureContext,
input: ViewportDragPanInput,
expect_rejection: Option<ViewportGestureRejection>,
) -> Result<(), String> {
match (
store.apply_viewport_drag_pan_session(ViewportDragPanSession::new(context, input)),
expect_rejection,
) {
(Ok(_), None) => Ok(()),
(Ok(actual), Some(expected)) => Err(format!(
"viewport drag-pan session was accepted as {:?}, expected rejection {:?}",
actual.kind, expected
)),
(Err(actual), Some(expected)) if actual == expected => Ok(()),
(Err(actual), Some(expected)) => Err(format!(
"viewport drag-pan session rejected with {:?}, expected {:?}",
actual, expected
)),
(Err(actual), None) => Err(format!(
"viewport drag-pan session was rejected: {:?}",
actual
)),
}
}
fn apply_viewport_animation_frame(
store: &mut NodeGraphStore,
plan: ViewportAnimationPlan,
elapsed_seconds: f32,
) -> Result<(), String> {
let frame = plan
.frame_at(elapsed_seconds)
.ok_or_else(|| "viewport animation frame was rejected".to_owned())?;
store.set_viewport(frame.transform.pan, frame.transform.zoom);
Ok(())
}
fn apply_viewport_pan_inertia_frame(
store: &mut NodeGraphStore,
plan: ViewportPanInertiaPlan,
elapsed_seconds: f32,
) -> Result<(), String> {
let frame = plan
.frame_at(elapsed_seconds)
.ok_or_else(|| "viewport pan inertia frame was rejected".to_owned())?;
store.set_viewport(frame.transform.pan, frame.transform.zoom);
Ok(())
}
fn assert_viewport_double_click_zoom_result(
result: Result<ViewportAnimationPlan, ViewportGestureRejection>,
expected: Option<ViewportAnimationPlan>,
expect_rejection: Option<ViewportGestureRejection>,
) -> Result<(), String> {
match (result, expected, expect_rejection) {
(Ok(actual), Some(expected), None) if actual == expected => Ok(()),
(Ok(actual), Some(expected), None) => Err(format!(
"viewport double-click zoom resolved to {actual:?}, expected {expected:?}"
)),
(Ok(actual), None, Some(expected_rejection)) => Err(format!(
"viewport double-click zoom was accepted as {actual:?}, expected rejection {expected_rejection:?}"
)),
(Err(actual), None, Some(expected)) if actual == expected => Ok(()),
(Err(actual), None, Some(expected)) => Err(format!(
"viewport double-click zoom rejected with {actual:?}, expected {expected:?}"
)),
(Err(actual), Some(_), None) => Err(format!(
"viewport double-click zoom was rejected: {actual:?}"
)),
(Ok(actual), None, None) => Err(format!(
"viewport double-click zoom accepted as {actual:?}, but no expected plan was provided"
)),
(Err(actual), None, None) => Err(format!(
"viewport double-click zoom was rejected: {actual:?}, but no expected rejection was provided"
)),
(_, Some(_), Some(_)) => Err(
"viewport double-click zoom action cannot expect both a plan and a rejection"
.to_owned(),
),
}
}
fn apply_viewport_gesture_result(
store: &mut NodeGraphStore,
result: Result<ViewportGestureIntent, ViewportGestureRejection>,
expect_rejection: Option<ViewportGestureRejection>,
) -> Result<(), String> {
match (result, expect_rejection) {
(Ok(intent), None) => apply_viewport_gesture_intent(store, intent),
(Ok(intent), Some(expected)) => Err(format!(
"viewport gesture was accepted as {:?}, expected rejection {:?}",
intent.move_kind(),
expected
)),
(Err(actual), Some(expected)) if actual == expected => Ok(()),
(Err(actual), Some(expected)) => Err(format!(
"viewport gesture rejected with {:?}, expected {:?}",
actual, expected
)),
(Err(actual), None) => Err(format!("viewport gesture was rejected: {:?}", actual)),
}
}
fn apply_viewport_gesture_intent(
store: &mut NodeGraphStore,
intent: ViewportGestureIntent,
) -> Result<(), String> {
if intent.apply_to_store(store) {
Ok(())
} else {
match intent {
ViewportGestureIntent::Pan { .. } => {
Err("viewport pan gesture request was rejected".to_owned())
}
ViewportGestureIntent::Zoom { .. } => {
Err("viewport zoom gesture request was rejected".to_owned())
}
}
}
}