639 lines
22 KiB
C++
639 lines
22 KiB
C++
#pragma once
|
|
|
|
#include "foundation/result.h"
|
|
|
|
#include <cmath>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <utility>
|
|
|
|
namespace pp::app {
|
|
|
|
inline constexpr std::size_t document_layer_name_max_length = 128;
|
|
inline constexpr int document_layer_legacy_blend_mode_count = 5;
|
|
|
|
enum class DocumentLayerRenameAction {
|
|
no_op_same_name,
|
|
rename_and_record_undo,
|
|
};
|
|
|
|
enum class DocumentLayerOperation {
|
|
add,
|
|
duplicate,
|
|
select,
|
|
reorder,
|
|
remove,
|
|
set_opacity,
|
|
set_visibility,
|
|
set_alpha_lock,
|
|
set_blend_mode,
|
|
set_highlight,
|
|
};
|
|
|
|
enum class DocumentLayerMenuCommand {
|
|
clear,
|
|
rename,
|
|
merge_down,
|
|
};
|
|
|
|
enum class DocumentLayerMenuAction {
|
|
clear_current_layer,
|
|
show_rename_dialog,
|
|
merge_with_lower_layer,
|
|
show_merge_animated_not_supported,
|
|
no_op_select_layer,
|
|
no_op_select_upper_layer,
|
|
};
|
|
|
|
struct DocumentLayerRenamePlan {
|
|
std::string old_name;
|
|
std::string new_name;
|
|
DocumentLayerRenameAction action = DocumentLayerRenameAction::no_op_same_name;
|
|
};
|
|
|
|
struct DocumentLayerOperationPlan {
|
|
DocumentLayerOperation operation = DocumentLayerOperation::select;
|
|
int index = 0;
|
|
int from_index = 0;
|
|
int to_index = 0;
|
|
int insert_index = 0;
|
|
int source_index = 0;
|
|
std::string name;
|
|
float opacity = 1.0F;
|
|
bool flag = false;
|
|
int blend_mode = 0;
|
|
bool mutates_document = false;
|
|
bool marks_unsaved = false;
|
|
bool reloads_animation_layers = false;
|
|
bool updates_title = false;
|
|
};
|
|
|
|
struct DocumentLayerMenuPlan {
|
|
DocumentLayerMenuCommand command = DocumentLayerMenuCommand::clear;
|
|
DocumentLayerMenuAction action = DocumentLayerMenuAction::clear_current_layer;
|
|
std::string label;
|
|
int from_index = 0;
|
|
int to_index = 0;
|
|
};
|
|
|
|
struct DocumentLayerMergePlan {
|
|
int from_index = 0;
|
|
int to_index = 0;
|
|
bool create_history = true;
|
|
};
|
|
|
|
class DocumentLayerMenuServices {
|
|
public:
|
|
virtual ~DocumentLayerMenuServices() = default;
|
|
|
|
virtual void clear_current_layer() = 0;
|
|
virtual void show_rename_dialog() = 0;
|
|
virtual void merge_with_lower_layer(int from_index, int to_index) = 0;
|
|
virtual void show_merge_animated_not_supported() = 0;
|
|
};
|
|
|
|
class DocumentLayerRenameServices {
|
|
public:
|
|
virtual ~DocumentLayerRenameServices() = default;
|
|
|
|
virtual void rename_layer(std::string_view old_name, std::string_view new_name) = 0;
|
|
virtual void finish_layer_rename() = 0;
|
|
};
|
|
|
|
class DocumentLayerOperationServices {
|
|
public:
|
|
virtual ~DocumentLayerOperationServices() = default;
|
|
|
|
virtual void add_layer(std::string_view name, int insert_index) = 0;
|
|
virtual void duplicate_layer(int source_index, int insert_index) = 0;
|
|
virtual void select_layer(int index) = 0;
|
|
virtual void reorder_layer(int from_index, int to_index) = 0;
|
|
virtual void remove_layer(int index) = 0;
|
|
virtual void set_layer_opacity(int index, float opacity) = 0;
|
|
virtual void set_layer_visibility(int index, bool visible) = 0;
|
|
virtual void set_layer_alpha_lock(int index, bool locked) = 0;
|
|
virtual void set_layer_blend_mode(int index, int blend_mode) = 0;
|
|
virtual void set_layer_highlight(int index, bool highlighted) = 0;
|
|
virtual void mark_unsaved() = 0;
|
|
virtual void reload_animation_layers() = 0;
|
|
virtual void update_title() = 0;
|
|
};
|
|
|
|
class DocumentLayerMergeServices {
|
|
public:
|
|
virtual ~DocumentLayerMergeServices() = default;
|
|
|
|
virtual void merge_layers(int from_index, int to_index, bool create_history) = 0;
|
|
};
|
|
|
|
[[nodiscard]] inline pp::foundation::Status validate_layer_index(
|
|
int layer_count,
|
|
int index) noexcept
|
|
{
|
|
if (layer_count <= 0) {
|
|
return pp::foundation::Status::invalid_argument("document must contain at least one layer");
|
|
}
|
|
|
|
if (index < 0 || index >= layer_count) {
|
|
return pp::foundation::Status::out_of_range("layer index is outside the document");
|
|
}
|
|
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Status validate_layer_insert_index(
|
|
int layer_count,
|
|
int index) noexcept
|
|
{
|
|
if (layer_count < 0) {
|
|
return pp::foundation::Status::invalid_argument("layer count must not be negative");
|
|
}
|
|
|
|
if (index < 0 || index > layer_count) {
|
|
return pp::foundation::Status::out_of_range("layer insert index is outside the document");
|
|
}
|
|
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Result<DocumentLayerRenamePlan> plan_document_layer_rename(
|
|
std::string_view old_name,
|
|
std::string_view requested_name)
|
|
{
|
|
if (requested_name.empty()) {
|
|
return pp::foundation::Result<DocumentLayerRenamePlan>::failure(
|
|
pp::foundation::Status::invalid_argument("layer name must not be empty"));
|
|
}
|
|
|
|
if (requested_name.size() > document_layer_name_max_length) {
|
|
return pp::foundation::Result<DocumentLayerRenamePlan>::failure(
|
|
pp::foundation::Status::out_of_range("layer name length exceeds the configured limit"));
|
|
}
|
|
|
|
DocumentLayerRenamePlan plan;
|
|
plan.old_name = std::string(old_name);
|
|
plan.new_name = std::string(requested_name);
|
|
plan.action = old_name == requested_name
|
|
? DocumentLayerRenameAction::no_op_same_name
|
|
: DocumentLayerRenameAction::rename_and_record_undo;
|
|
return pp::foundation::Result<DocumentLayerRenamePlan>::success(std::move(plan));
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Result<DocumentLayerOperationPlan> plan_document_layer_add(
|
|
int layer_count,
|
|
int insert_index,
|
|
std::string_view name)
|
|
{
|
|
const auto index_status = validate_layer_insert_index(layer_count, insert_index);
|
|
if (!index_status.ok()) {
|
|
return pp::foundation::Result<DocumentLayerOperationPlan>::failure(index_status);
|
|
}
|
|
|
|
const auto rename = plan_document_layer_rename({}, name);
|
|
if (!rename) {
|
|
return pp::foundation::Result<DocumentLayerOperationPlan>::failure(rename.status());
|
|
}
|
|
|
|
DocumentLayerOperationPlan plan;
|
|
plan.operation = DocumentLayerOperation::add;
|
|
plan.insert_index = insert_index;
|
|
plan.name = std::string(name);
|
|
plan.mutates_document = true;
|
|
plan.marks_unsaved = true;
|
|
plan.reloads_animation_layers = true;
|
|
plan.updates_title = true;
|
|
return pp::foundation::Result<DocumentLayerOperationPlan>::success(std::move(plan));
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Result<DocumentLayerOperationPlan> plan_document_layer_duplicate(
|
|
int layer_count,
|
|
int source_index)
|
|
{
|
|
const auto index_status = validate_layer_index(layer_count, source_index);
|
|
if (!index_status.ok()) {
|
|
return pp::foundation::Result<DocumentLayerOperationPlan>::failure(index_status);
|
|
}
|
|
|
|
DocumentLayerOperationPlan plan;
|
|
plan.operation = DocumentLayerOperation::duplicate;
|
|
plan.source_index = source_index;
|
|
plan.insert_index = source_index + 1;
|
|
plan.mutates_document = true;
|
|
plan.marks_unsaved = true;
|
|
plan.reloads_animation_layers = true;
|
|
plan.updates_title = true;
|
|
return pp::foundation::Result<DocumentLayerOperationPlan>::success(plan);
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Result<DocumentLayerOperationPlan> plan_document_layer_select(
|
|
int layer_count,
|
|
int index)
|
|
{
|
|
const auto index_status = validate_layer_index(layer_count, index);
|
|
if (!index_status.ok()) {
|
|
return pp::foundation::Result<DocumentLayerOperationPlan>::failure(index_status);
|
|
}
|
|
|
|
DocumentLayerOperationPlan plan;
|
|
plan.operation = DocumentLayerOperation::select;
|
|
plan.index = index;
|
|
plan.reloads_animation_layers = true;
|
|
return pp::foundation::Result<DocumentLayerOperationPlan>::success(plan);
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Result<DocumentLayerOperationPlan> plan_document_layer_reorder(
|
|
int layer_count,
|
|
int from_index,
|
|
int to_index)
|
|
{
|
|
auto index_status = validate_layer_index(layer_count, from_index);
|
|
if (!index_status.ok()) {
|
|
return pp::foundation::Result<DocumentLayerOperationPlan>::failure(index_status);
|
|
}
|
|
|
|
index_status = validate_layer_index(layer_count, to_index);
|
|
if (!index_status.ok()) {
|
|
return pp::foundation::Result<DocumentLayerOperationPlan>::failure(index_status);
|
|
}
|
|
|
|
DocumentLayerOperationPlan plan;
|
|
plan.operation = DocumentLayerOperation::reorder;
|
|
plan.from_index = from_index;
|
|
plan.to_index = to_index;
|
|
plan.mutates_document = from_index != to_index;
|
|
plan.marks_unsaved = plan.mutates_document;
|
|
plan.reloads_animation_layers = plan.mutates_document;
|
|
plan.updates_title = plan.mutates_document;
|
|
return pp::foundation::Result<DocumentLayerOperationPlan>::success(plan);
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Result<DocumentLayerOperationPlan> plan_document_layer_remove(
|
|
int layer_count,
|
|
int index)
|
|
{
|
|
const auto index_status = validate_layer_index(layer_count, index);
|
|
if (!index_status.ok()) {
|
|
return pp::foundation::Result<DocumentLayerOperationPlan>::failure(index_status);
|
|
}
|
|
|
|
if (layer_count <= 1) {
|
|
return pp::foundation::Result<DocumentLayerOperationPlan>::failure(
|
|
pp::foundation::Status::invalid_argument("document must keep at least one layer"));
|
|
}
|
|
|
|
DocumentLayerOperationPlan plan;
|
|
plan.operation = DocumentLayerOperation::remove;
|
|
plan.index = index;
|
|
plan.mutates_document = true;
|
|
plan.marks_unsaved = true;
|
|
plan.reloads_animation_layers = true;
|
|
plan.updates_title = true;
|
|
return pp::foundation::Result<DocumentLayerOperationPlan>::success(plan);
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Result<DocumentLayerOperationPlan> plan_document_layer_opacity(
|
|
int layer_count,
|
|
int index,
|
|
float opacity)
|
|
{
|
|
const auto index_status = validate_layer_index(layer_count, index);
|
|
if (!index_status.ok()) {
|
|
return pp::foundation::Result<DocumentLayerOperationPlan>::failure(index_status);
|
|
}
|
|
|
|
if (!std::isfinite(opacity) || opacity < 0.0F || opacity > 1.0F) {
|
|
return pp::foundation::Result<DocumentLayerOperationPlan>::failure(
|
|
pp::foundation::Status::out_of_range("layer opacity must be finite and within 0..1"));
|
|
}
|
|
|
|
DocumentLayerOperationPlan plan;
|
|
plan.operation = DocumentLayerOperation::set_opacity;
|
|
plan.index = index;
|
|
plan.opacity = opacity;
|
|
plan.mutates_document = true;
|
|
plan.marks_unsaved = true;
|
|
plan.updates_title = true;
|
|
return pp::foundation::Result<DocumentLayerOperationPlan>::success(plan);
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Result<DocumentLayerOperationPlan> plan_document_layer_visibility(
|
|
int layer_count,
|
|
int index,
|
|
bool visible)
|
|
{
|
|
const auto index_status = validate_layer_index(layer_count, index);
|
|
if (!index_status.ok()) {
|
|
return pp::foundation::Result<DocumentLayerOperationPlan>::failure(index_status);
|
|
}
|
|
|
|
DocumentLayerOperationPlan plan;
|
|
plan.operation = DocumentLayerOperation::set_visibility;
|
|
plan.index = index;
|
|
plan.flag = visible;
|
|
plan.mutates_document = true;
|
|
plan.marks_unsaved = true;
|
|
plan.reloads_animation_layers = true;
|
|
plan.updates_title = true;
|
|
return pp::foundation::Result<DocumentLayerOperationPlan>::success(plan);
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Result<DocumentLayerOperationPlan> plan_document_layer_alpha_lock(
|
|
int layer_count,
|
|
int index,
|
|
bool locked)
|
|
{
|
|
const auto index_status = validate_layer_index(layer_count, index);
|
|
if (!index_status.ok()) {
|
|
return pp::foundation::Result<DocumentLayerOperationPlan>::failure(index_status);
|
|
}
|
|
|
|
DocumentLayerOperationPlan plan;
|
|
plan.operation = DocumentLayerOperation::set_alpha_lock;
|
|
plan.index = index;
|
|
plan.flag = locked;
|
|
plan.mutates_document = true;
|
|
plan.marks_unsaved = true;
|
|
plan.updates_title = true;
|
|
return pp::foundation::Result<DocumentLayerOperationPlan>::success(plan);
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Result<DocumentLayerOperationPlan> plan_document_layer_blend_mode(
|
|
int layer_count,
|
|
int index,
|
|
int blend_mode)
|
|
{
|
|
const auto index_status = validate_layer_index(layer_count, index);
|
|
if (!index_status.ok()) {
|
|
return pp::foundation::Result<DocumentLayerOperationPlan>::failure(index_status);
|
|
}
|
|
|
|
if (blend_mode < 0 || blend_mode >= document_layer_legacy_blend_mode_count) {
|
|
return pp::foundation::Result<DocumentLayerOperationPlan>::failure(
|
|
pp::foundation::Status::out_of_range("layer blend mode is outside the supported range"));
|
|
}
|
|
|
|
DocumentLayerOperationPlan plan;
|
|
plan.operation = DocumentLayerOperation::set_blend_mode;
|
|
plan.index = index;
|
|
plan.blend_mode = blend_mode;
|
|
plan.mutates_document = true;
|
|
plan.marks_unsaved = true;
|
|
plan.updates_title = true;
|
|
return pp::foundation::Result<DocumentLayerOperationPlan>::success(plan);
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Result<DocumentLayerOperationPlan> plan_document_layer_highlight(
|
|
int layer_count,
|
|
int index,
|
|
bool highlight)
|
|
{
|
|
const auto index_status = validate_layer_index(layer_count, index);
|
|
if (!index_status.ok()) {
|
|
return pp::foundation::Result<DocumentLayerOperationPlan>::failure(index_status);
|
|
}
|
|
|
|
DocumentLayerOperationPlan plan;
|
|
plan.operation = DocumentLayerOperation::set_highlight;
|
|
plan.index = index;
|
|
plan.flag = highlight;
|
|
return pp::foundation::Result<DocumentLayerOperationPlan>::success(plan);
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Result<DocumentLayerMenuPlan> plan_document_layer_menu(
|
|
DocumentLayerMenuCommand command,
|
|
bool has_current_layer,
|
|
int current_index,
|
|
int animation_duration,
|
|
std::string_view current_layer_name,
|
|
std::string_view lower_layer_name)
|
|
{
|
|
if (current_index < 0) {
|
|
return pp::foundation::Result<DocumentLayerMenuPlan>::failure(
|
|
pp::foundation::Status::out_of_range("current layer index must not be negative"));
|
|
}
|
|
if (animation_duration < 0) {
|
|
return pp::foundation::Result<DocumentLayerMenuPlan>::failure(
|
|
pp::foundation::Status::out_of_range("animation duration must not be negative"));
|
|
}
|
|
|
|
DocumentLayerMenuPlan plan;
|
|
plan.command = command;
|
|
plan.from_index = current_index;
|
|
plan.to_index = current_index > 0 ? current_index - 1 : 0;
|
|
|
|
switch (command) {
|
|
case DocumentLayerMenuCommand::clear:
|
|
plan.action = has_current_layer
|
|
? DocumentLayerMenuAction::clear_current_layer
|
|
: DocumentLayerMenuAction::no_op_select_layer;
|
|
plan.label = has_current_layer
|
|
? "Clear Layer " + std::string(current_layer_name)
|
|
: "Clear Layer (Select a layer)";
|
|
break;
|
|
case DocumentLayerMenuCommand::rename:
|
|
plan.action = has_current_layer
|
|
? DocumentLayerMenuAction::show_rename_dialog
|
|
: DocumentLayerMenuAction::no_op_select_layer;
|
|
plan.label = has_current_layer
|
|
? "Rename Layer " + std::string(current_layer_name)
|
|
: "Rename Layer (Select a layer)";
|
|
break;
|
|
case DocumentLayerMenuCommand::merge_down:
|
|
if (!has_current_layer) {
|
|
plan.action = DocumentLayerMenuAction::no_op_select_layer;
|
|
plan.label = "Merge Layer (Select a layer)";
|
|
} else if (animation_duration > 1) {
|
|
plan.action = DocumentLayerMenuAction::show_merge_animated_not_supported;
|
|
plan.label = "Merge Layer (Animation not supported)";
|
|
} else if (current_index <= 0) {
|
|
plan.action = DocumentLayerMenuAction::no_op_select_upper_layer;
|
|
plan.label = "Merge Layer (Select upper layers)";
|
|
} else {
|
|
plan.action = DocumentLayerMenuAction::merge_with_lower_layer;
|
|
plan.label = "Merge with " + std::string(lower_layer_name);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return pp::foundation::Result<DocumentLayerMenuPlan>::success(std::move(plan));
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Result<DocumentLayerMergePlan> plan_document_layer_merge(
|
|
int layer_count,
|
|
int from_index,
|
|
int to_index,
|
|
int animation_duration,
|
|
bool create_history = true)
|
|
{
|
|
auto index_status = validate_layer_index(layer_count, from_index);
|
|
if (!index_status.ok()) {
|
|
return pp::foundation::Result<DocumentLayerMergePlan>::failure(index_status);
|
|
}
|
|
|
|
index_status = validate_layer_index(layer_count, to_index);
|
|
if (!index_status.ok()) {
|
|
return pp::foundation::Result<DocumentLayerMergePlan>::failure(index_status);
|
|
}
|
|
|
|
if (animation_duration < 0) {
|
|
return pp::foundation::Result<DocumentLayerMergePlan>::failure(
|
|
pp::foundation::Status::out_of_range("animation duration must not be negative"));
|
|
}
|
|
|
|
if (animation_duration > 1) {
|
|
return pp::foundation::Result<DocumentLayerMergePlan>::failure(
|
|
pp::foundation::Status::invalid_argument("animated layer merge is not supported"));
|
|
}
|
|
|
|
if (from_index <= to_index) {
|
|
return pp::foundation::Result<DocumentLayerMergePlan>::failure(
|
|
pp::foundation::Status::invalid_argument("layer merge source must be above the destination"));
|
|
}
|
|
|
|
DocumentLayerMergePlan plan;
|
|
plan.from_index = from_index;
|
|
plan.to_index = to_index;
|
|
plan.create_history = create_history;
|
|
return pp::foundation::Result<DocumentLayerMergePlan>::success(plan);
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Status execute_document_layer_rename_plan(
|
|
const DocumentLayerRenamePlan& plan,
|
|
DocumentLayerRenameServices& services)
|
|
{
|
|
switch (plan.action) {
|
|
case DocumentLayerRenameAction::no_op_same_name:
|
|
services.finish_layer_rename();
|
|
return pp::foundation::Status::success();
|
|
case DocumentLayerRenameAction::rename_and_record_undo:
|
|
if (plan.new_name.empty()) {
|
|
return pp::foundation::Status::invalid_argument("layer rename plan must include a new name");
|
|
}
|
|
if (plan.old_name == plan.new_name) {
|
|
return pp::foundation::Status::invalid_argument("layer rename plan must change the name");
|
|
}
|
|
services.rename_layer(plan.old_name, plan.new_name);
|
|
services.finish_layer_rename();
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
return pp::foundation::Status::invalid_argument("unknown document layer rename action");
|
|
}
|
|
|
|
inline void execute_document_layer_operation_side_effects(
|
|
const DocumentLayerOperationPlan& plan,
|
|
DocumentLayerOperationServices& services)
|
|
{
|
|
if (plan.marks_unsaved)
|
|
services.mark_unsaved();
|
|
if (plan.reloads_animation_layers)
|
|
services.reload_animation_layers();
|
|
if (plan.updates_title)
|
|
services.update_title();
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Status execute_document_layer_operation_plan(
|
|
const DocumentLayerOperationPlan& plan,
|
|
DocumentLayerOperationServices& services)
|
|
{
|
|
switch (plan.operation) {
|
|
case DocumentLayerOperation::add:
|
|
if (!plan.mutates_document || plan.name.empty()) {
|
|
return pp::foundation::Status::invalid_argument("layer add plan must mutate with a name");
|
|
}
|
|
services.add_layer(plan.name, plan.insert_index);
|
|
break;
|
|
case DocumentLayerOperation::duplicate:
|
|
if (!plan.mutates_document) {
|
|
return pp::foundation::Status::invalid_argument("layer duplicate plan must mutate the document");
|
|
}
|
|
services.duplicate_layer(plan.source_index, plan.insert_index);
|
|
break;
|
|
case DocumentLayerOperation::select:
|
|
services.select_layer(plan.index);
|
|
break;
|
|
case DocumentLayerOperation::reorder:
|
|
if (plan.mutates_document)
|
|
services.reorder_layer(plan.from_index, plan.to_index);
|
|
break;
|
|
case DocumentLayerOperation::remove:
|
|
if (!plan.mutates_document) {
|
|
return pp::foundation::Status::invalid_argument("layer remove plan must mutate the document");
|
|
}
|
|
services.remove_layer(plan.index);
|
|
break;
|
|
case DocumentLayerOperation::set_opacity:
|
|
if (!plan.mutates_document) {
|
|
return pp::foundation::Status::invalid_argument("layer opacity plan must mutate the document");
|
|
}
|
|
services.set_layer_opacity(plan.index, plan.opacity);
|
|
break;
|
|
case DocumentLayerOperation::set_visibility:
|
|
if (!plan.mutates_document) {
|
|
return pp::foundation::Status::invalid_argument("layer visibility plan must mutate the document");
|
|
}
|
|
services.set_layer_visibility(plan.index, plan.flag);
|
|
break;
|
|
case DocumentLayerOperation::set_alpha_lock:
|
|
if (!plan.mutates_document) {
|
|
return pp::foundation::Status::invalid_argument("layer alpha-lock plan must mutate the document");
|
|
}
|
|
services.set_layer_alpha_lock(plan.index, plan.flag);
|
|
break;
|
|
case DocumentLayerOperation::set_blend_mode:
|
|
if (!plan.mutates_document) {
|
|
return pp::foundation::Status::invalid_argument("layer blend-mode plan must mutate the document");
|
|
}
|
|
services.set_layer_blend_mode(plan.index, plan.blend_mode);
|
|
break;
|
|
case DocumentLayerOperation::set_highlight:
|
|
services.set_layer_highlight(plan.index, plan.flag);
|
|
break;
|
|
}
|
|
|
|
execute_document_layer_operation_side_effects(plan, services);
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Status execute_document_layer_merge_plan(
|
|
const DocumentLayerMergePlan& plan,
|
|
DocumentLayerMergeServices& services)
|
|
{
|
|
if (plan.from_index <= plan.to_index) {
|
|
return pp::foundation::Status::invalid_argument(
|
|
"layer merge source must be above the destination");
|
|
}
|
|
|
|
services.merge_layers(plan.from_index, plan.to_index, plan.create_history);
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
[[nodiscard]] inline pp::foundation::Status execute_document_layer_menu_plan(
|
|
const DocumentLayerMenuPlan& plan,
|
|
DocumentLayerMenuServices& services)
|
|
{
|
|
switch (plan.action) {
|
|
case DocumentLayerMenuAction::clear_current_layer:
|
|
services.clear_current_layer();
|
|
return pp::foundation::Status::success();
|
|
case DocumentLayerMenuAction::show_rename_dialog:
|
|
services.show_rename_dialog();
|
|
return pp::foundation::Status::success();
|
|
case DocumentLayerMenuAction::merge_with_lower_layer:
|
|
services.merge_with_lower_layer(plan.from_index, plan.to_index);
|
|
return pp::foundation::Status::success();
|
|
case DocumentLayerMenuAction::show_merge_animated_not_supported:
|
|
services.show_merge_animated_not_supported();
|
|
return pp::foundation::Status::success();
|
|
case DocumentLayerMenuAction::no_op_select_layer:
|
|
case DocumentLayerMenuAction::no_op_select_upper_layer:
|
|
return pp::foundation::Status::success();
|
|
}
|
|
|
|
return pp::foundation::Status::invalid_argument("unknown document layer menu action");
|
|
}
|
|
|
|
}
|