Add animation playback toggle boundary

This commit is contained in:
2026-06-03 16:47:02 +02:00
parent 5752bc6ae9
commit 603bb0c4e7
7 changed files with 217 additions and 26 deletions

View File

@@ -21,6 +21,7 @@ enum class DocumentAnimationOperation {
goto_next,
goto_previous,
playback_step,
toggle_playback,
set_onion_size,
};
@@ -36,11 +37,15 @@ struct DocumentAnimationOperationPlan {
int onion_size = 1;
int layer_index = 0;
std::uint32_t layer_id = 0;
int playback_idle_ms = 100;
bool requires_selected_frame = false;
bool mutates_document = false;
bool reloads_animation_layers = false;
bool updates_canvas_animation = false;
bool marks_unsaved = false;
bool playback_was_active = false;
bool playback_active = false;
bool resets_playback_timer = false;
};
class DocumentAnimationServices {
@@ -57,6 +62,12 @@ public:
virtual void goto_frame(int target_frame) = 0;
virtual void set_timeline_frame(int target_frame) = 0;
virtual void set_onion_size(int onion_size) = 0;
virtual void capture_playback_restore_mode() = 0;
virtual void enter_playback_camera_mode() = 0;
virtual void restore_playback_canvas_mode() = 0;
virtual void set_playback_active(bool active) = 0;
virtual void reset_playback_timer() = 0;
virtual void set_playback_idle_ms(int idle_ms) = 0;
virtual void update_canvas_animation() = 0;
virtual void update_frame_status() = 0;
virtual void reload_animation_layers() = 0;
@@ -343,6 +354,18 @@ public:
return pp::foundation::Result<DocumentAnimationOperationPlan>::success(plan);
}
[[nodiscard]] inline pp::foundation::Result<DocumentAnimationOperationPlan> plan_animation_playback_toggle(
bool playback_active)
{
DocumentAnimationOperationPlan plan;
plan.operation = DocumentAnimationOperation::toggle_playback;
plan.playback_was_active = playback_active;
plan.playback_active = !playback_active;
plan.playback_idle_ms = playback_active ? 100 : 10;
plan.resets_playback_timer = !playback_active;
return pp::foundation::Result<DocumentAnimationOperationPlan>::success(plan);
}
[[nodiscard]] inline pp::foundation::Result<DocumentAnimationOperationPlan> plan_animation_onion_size(int onion_size)
{
if (onion_size < 0) {
@@ -419,6 +442,18 @@ public:
}
return validate_animation_frame_index(plan.frame_count, plan.target_frame);
case DocumentAnimationOperation::toggle_playback:
if (plan.playback_active == plan.playback_was_active) {
return pp::foundation::Status::invalid_argument("animation playback toggle must change state");
}
if (plan.playback_idle_ms <= 0) {
return pp::foundation::Status::invalid_argument("animation playback idle interval must be positive");
}
if (plan.playback_active && !plan.resets_playback_timer) {
return pp::foundation::Status::invalid_argument("animation playback start must reset timer");
}
return pp::foundation::Status::success();
case DocumentAnimationOperation::set_onion_size:
if (plan.onion_size < 0) {
return pp::foundation::Status::invalid_argument("animation onion size must not be negative");
@@ -517,6 +552,20 @@ public:
services.update_frame_status();
return pp::foundation::Status::success();
case DocumentAnimationOperation::toggle_playback:
if (plan.playback_active) {
services.capture_playback_restore_mode();
services.enter_playback_camera_mode();
if (plan.resets_playback_timer) {
services.reset_playback_timer();
}
} else {
services.restore_playback_canvas_mode();
}
services.set_playback_active(plan.playback_active);
services.set_playback_idle_ms(plan.playback_idle_ms);
return pp::foundation::Status::success();
case DocumentAnimationOperation::set_onion_size:
services.set_onion_size(plan.onion_size);
if (plan.updates_canvas_animation) {

View File

@@ -96,6 +96,36 @@ void NodePanelAnimation::execute_animation_plan(const pp::app::DocumentAnimation
panel_.m_timeline->m_onion_size = onion_size;
}
void capture_playback_restore_mode() override
{
playback_restore_mode() = Canvas::I->m_current_mode;
}
void enter_playback_camera_mode() override
{
Canvas::set_mode(kCanvasMode::Camera);
}
void restore_playback_canvas_mode() override
{
Canvas::set_mode(playback_restore_mode());
}
void set_playback_active(bool active) override
{
panel_.btn_play->set_active(active);
}
void reset_playback_timer() override
{
panel_.m_playback_timer = 0;
}
void set_playback_idle_ms(int idle_ms) override
{
App::I->idle_ms = idle_ms;
}
void update_canvas_animation() override
{
Canvas::I->anim_update();
@@ -117,6 +147,12 @@ void NodePanelAnimation::execute_animation_plan(const pp::app::DocumentAnimation
}
private:
static kCanvasMode& playback_restore_mode()
{
static auto mode = Canvas::I->m_current_mode;
return mode;
}
NodePanelAnimation& panel_;
Layer* layer_ = nullptr;
};
@@ -258,23 +294,10 @@ void NodePanelAnimation::init_controls()
return;
execute_animation_plan(plan.value());
};
btn_play->on_click = [this] (Node* target) {
static auto mode = Canvas::I->m_current_mode;
auto b = static_cast<NodeButtonCustom*>(target);
if (b->is_active())
{
Canvas::set_mode(mode);
b->set_active(false);
App::I->idle_ms = 100;
}
else
{
mode = Canvas::I->m_current_mode;
Canvas::set_mode(kCanvasMode::Camera);
m_playback_timer = 0;
b->set_active(true);
App::I->idle_ms = 10;
}
btn_play->on_click = [this] (Node*) {
const auto plan = pp::app::plan_animation_playback_toggle(btn_play->is_active());
if (plan)
execute_animation_plan(plan.value());
};
}