Route animation panel view through app core

This commit is contained in:
2026-06-05 00:37:11 +02:00
parent a9e12f2219
commit bd6cdc20c5
8 changed files with 469 additions and 28 deletions

View File

@@ -1268,6 +1268,24 @@ if(TARGET pano_cli)
LABELS "app;integration;desktop-fast;fuzz"
WILL_FAIL TRUE)
add_test(NAME pano_cli_plan_animation_panel_view_smoke
COMMAND pano_cli plan-animation-panel-view --layer-count 2 --frame-count 3 --frame-duration 2 --total-duration 6 --current-layer 1 --current-frame 4 --selected-layer-id 10 --selected-frame 2 --onion-size 2 --hidden-layer 1)
set_tests_properties(pano_cli_plan_animation_panel_view_smoke PROPERTIES
LABELS "app;ui;integration;desktop-fast"
PASS_REGULAR_EXPRESSION "\"command\":\"plan-animation-panel-view\".*\"layers\":2.*\"visibleLayers\":1.*\"selectedFrames\":1.*\"hasSelectedFrame\":true.*\"currentLayerId\":20.*\"firstLayerFrames\":3")
add_test(NAME pano_cli_plan_animation_panel_view_allows_stale_selection
COMMAND pano_cli plan-animation-panel-view --layer-count 1 --frame-count 1 --total-duration 1 --current-layer 0 --current-frame 0 --selected-layer-id 999 --selected-frame 42)
set_tests_properties(pano_cli_plan_animation_panel_view_allows_stale_selection PROPERTIES
LABELS "app;ui;integration;desktop-fast;fuzz"
PASS_REGULAR_EXPRESSION "\"command\":\"plan-animation-panel-view\".*\"layers\":1.*\"selectedFrames\":0.*\"hasSelectedFrame\":false")
add_test(NAME pano_cli_plan_animation_panel_view_rejects_empty_frames
COMMAND pano_cli plan-animation-panel-view --layer-count 1 --frame-count 0 --total-duration 1)
set_tests_properties(pano_cli_plan_animation_panel_view_rejects_empty_frames PROPERTIES
LABELS "app;ui;integration;desktop-fast;fuzz"
WILL_FAIL TRUE)
add_test(NAME pano_cli_plan_animation_timeline_scrub_smoke
COMMAND pano_cli plan-animation-timeline-scrub --total-duration 5 --cursor-x 174.99)
set_tests_properties(pano_cli_plan_animation_timeline_scrub_smoke PROPERTIES

View File

@@ -3,6 +3,7 @@
#include <limits>
#include <string>
#include <vector>
#define PP_REQUIRE(harness, expression) \
do { \
@@ -426,6 +427,114 @@ void onion_size_updates_canvas_without_document_mutation(pp::tests::Harness& har
PP_EXPECT(harness, !pp::app::plan_animation_onion_size(-1));
}
void panel_view_projects_layers_frames_and_timeline_state(pp::tests::Harness& harness)
{
std::vector<pp::app::DocumentAnimationLayerInput> layers;
layers.push_back(pp::app::DocumentAnimationLayerInput {
.layer_index = 0,
.layer_id = 10,
.name = "Base",
.visible = true,
.frame_durations = { 1, 2 },
});
layers.push_back(pp::app::DocumentAnimationLayerInput {
.layer_index = 1,
.layer_id = 20,
.name = "Ink",
.visible = false,
.frame_durations = { 3 },
});
const auto view = pp::app::plan_animation_panel_view(
layers,
4,
1,
2,
10,
1,
3);
PP_REQUIRE(harness, view);
PP_EXPECT(harness, view.value().total_duration == 4);
PP_EXPECT(harness, view.value().current_frame == 2);
PP_EXPECT(harness, view.value().onion_size == 3);
PP_EXPECT(harness, view.value().has_selected_frame);
PP_EXPECT(harness, view.value().layers.size() == 2);
PP_EXPECT(harness, !view.value().layers[0].current);
PP_EXPECT(harness, view.value().layers[1].current);
PP_EXPECT(harness, view.value().layers[0].name == "Base");
PP_EXPECT(harness, !view.value().layers[1].visible);
PP_EXPECT(harness, view.value().layers[0].frames.size() == 2);
PP_EXPECT(harness, view.value().layers[0].frames[0].duration == 1);
PP_EXPECT(harness, view.value().layers[0].frames[1].duration == 2);
PP_EXPECT(harness, !view.value().layers[0].frames[0].selected);
PP_EXPECT(harness, view.value().layers[0].frames[1].selected);
PP_EXPECT(harness, !view.value().layers[1].frames[0].selected);
}
void panel_view_preserves_stale_selection_as_no_active_frame(pp::tests::Harness& harness)
{
const std::vector<pp::app::DocumentAnimationLayerInput> layers {
pp::app::DocumentAnimationLayerInput {
.layer_index = 0,
.layer_id = 10,
.name = "Base",
.visible = true,
.frame_durations = { 1 },
},
};
const auto view = pp::app::plan_animation_panel_view(
layers,
1,
0,
0,
999,
42,
0);
PP_REQUIRE(harness, view);
PP_EXPECT(harness, !view.value().has_selected_frame);
PP_EXPECT(harness, !view.value().layers[0].frames[0].selected);
}
void panel_view_rejects_invalid_document_state(pp::tests::Harness& harness)
{
const std::vector<pp::app::DocumentAnimationLayerInput> valid_layers {
pp::app::DocumentAnimationLayerInput {
.layer_index = 0,
.layer_id = 10,
.name = "Base",
.visible = true,
.frame_durations = { 1 },
},
};
const std::vector<pp::app::DocumentAnimationLayerInput> empty_frames {
pp::app::DocumentAnimationLayerInput {
.layer_index = 0,
.layer_id = 10,
.name = "Base",
.visible = true,
.frame_durations = {},
},
};
const std::vector<pp::app::DocumentAnimationLayerInput> bad_duration {
pp::app::DocumentAnimationLayerInput {
.layer_index = 0,
.layer_id = 10,
.name = "Base",
.visible = true,
.frame_durations = { 0 },
},
};
PP_EXPECT(harness, !pp::app::plan_animation_panel_view({}, 1, 0, 0, 0, -1, 0));
PP_EXPECT(harness, !pp::app::plan_animation_panel_view(valid_layers, 0, 0, 0, 0, -1, 0));
PP_EXPECT(harness, !pp::app::plan_animation_panel_view(valid_layers, 1, 1, 0, 0, -1, 0));
PP_EXPECT(harness, !pp::app::plan_animation_panel_view(valid_layers, 1, 0, 1, 0, -1, 0));
PP_EXPECT(harness, !pp::app::plan_animation_panel_view(valid_layers, 1, 0, 0, 0, -1, -1));
PP_EXPECT(harness, !pp::app::plan_animation_panel_view(empty_frames, 1, 0, 0, 0, -1, 0));
PP_EXPECT(harness, !pp::app::plan_animation_panel_view(bad_duration, 1, 0, 0, 0, -1, 0));
}
void onion_frame_ranges_clamp_edges_and_alpha(pp::tests::Harness& harness)
{
const auto center = pp::app::plan_animation_onion_frame_range(5, 2, 1);
@@ -666,6 +775,9 @@ int main()
harness.run("panel actions plan timeline and playback intent", panel_actions_plan_timeline_and_playback_intent);
harness.run("panel actions reject invalid timeline state", panel_actions_reject_invalid_timeline_state);
harness.run("onion size updates canvas without document mutation", onion_size_updates_canvas_without_document_mutation);
harness.run("panel view projects layers frames and timeline state", panel_view_projects_layers_frames_and_timeline_state);
harness.run("panel view preserves stale selection as no active frame", panel_view_preserves_stale_selection_as_no_active_frame);
harness.run("panel view rejects invalid document state", panel_view_rejects_invalid_document_state);
harness.run("onion frame ranges clamp edges and alpha", onion_frame_ranges_clamp_edges_and_alpha);
harness.run("onion frame ranges reject invalid inputs", onion_frame_ranges_reject_invalid_inputs);
harness.run("timeline scrub clamps cursor to document frame", timeline_scrub_clamps_cursor_to_document_frame);