Route brush preset list planning
This commit is contained in:
@@ -28,6 +28,14 @@ enum class BrushTextureListOperation {
|
||||
move_texture,
|
||||
};
|
||||
|
||||
enum class BrushPresetListOperation {
|
||||
add_current_brush,
|
||||
remove_preset,
|
||||
move_preset,
|
||||
select_preset,
|
||||
clear_presets,
|
||||
};
|
||||
|
||||
enum class BrushStrokeControlOperation {
|
||||
set_float,
|
||||
set_bool,
|
||||
@@ -139,6 +147,20 @@ struct BrushTextureListPlan {
|
||||
bool no_op = false;
|
||||
};
|
||||
|
||||
struct BrushPresetListPlan {
|
||||
BrushPresetListOperation operation = BrushPresetListOperation::select_preset;
|
||||
int item_count = 0;
|
||||
int current_index = -1;
|
||||
int target_index = -1;
|
||||
int move_offset = 0;
|
||||
bool saves_list = false;
|
||||
bool updates_empty_notification = false;
|
||||
bool selects_target = false;
|
||||
bool clears_selection = false;
|
||||
bool notifies_brush_changed = false;
|
||||
bool no_op = false;
|
||||
};
|
||||
|
||||
struct BrushStrokeControlPlan {
|
||||
BrushStrokeControlOperation operation = BrushStrokeControlOperation::set_float;
|
||||
BrushStrokeFloatSetting float_setting = BrushStrokeFloatSetting::tip_size;
|
||||
@@ -474,6 +496,122 @@ public:
|
||||
return pp::foundation::Result<BrushTextureListPlan>::success(plan);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Result<BrushPresetListPlan> plan_brush_preset_list_add(
|
||||
int item_count,
|
||||
bool has_current_brush)
|
||||
{
|
||||
if (item_count < 0) {
|
||||
return pp::foundation::Result<BrushPresetListPlan>::failure(
|
||||
pp::foundation::Status::out_of_range("brush preset item count must not be negative"));
|
||||
}
|
||||
if (!has_current_brush) {
|
||||
return pp::foundation::Result<BrushPresetListPlan>::failure(
|
||||
pp::foundation::Status::invalid_argument("current brush must be available to add a preset"));
|
||||
}
|
||||
|
||||
BrushPresetListPlan plan;
|
||||
plan.operation = BrushPresetListOperation::add_current_brush;
|
||||
plan.item_count = item_count;
|
||||
plan.target_index = item_count;
|
||||
plan.saves_list = true;
|
||||
plan.updates_empty_notification = true;
|
||||
return pp::foundation::Result<BrushPresetListPlan>::success(plan);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Result<BrushPresetListPlan> plan_brush_preset_list_remove(
|
||||
int item_count,
|
||||
int current_index)
|
||||
{
|
||||
if (item_count <= 0) {
|
||||
return pp::foundation::Result<BrushPresetListPlan>::failure(
|
||||
pp::foundation::Status::invalid_argument("brush preset list must contain an item to remove"));
|
||||
}
|
||||
if (current_index < 0 || current_index >= item_count) {
|
||||
return pp::foundation::Result<BrushPresetListPlan>::failure(
|
||||
pp::foundation::Status::out_of_range("selected brush preset index is outside the list"));
|
||||
}
|
||||
|
||||
BrushPresetListPlan plan;
|
||||
plan.operation = BrushPresetListOperation::remove_preset;
|
||||
plan.item_count = item_count;
|
||||
plan.current_index = current_index;
|
||||
plan.target_index = item_count > 1 ? std::min(current_index, item_count - 2) : -1;
|
||||
plan.saves_list = true;
|
||||
plan.updates_empty_notification = true;
|
||||
plan.selects_target = plan.target_index >= 0;
|
||||
plan.clears_selection = plan.target_index < 0;
|
||||
return pp::foundation::Result<BrushPresetListPlan>::success(plan);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Result<BrushPresetListPlan> plan_brush_preset_list_move(
|
||||
int item_count,
|
||||
int current_index,
|
||||
int offset)
|
||||
{
|
||||
if (item_count <= 0) {
|
||||
return pp::foundation::Result<BrushPresetListPlan>::failure(
|
||||
pp::foundation::Status::invalid_argument("brush preset list must contain an item to move"));
|
||||
}
|
||||
if (current_index < 0 || current_index >= item_count) {
|
||||
return pp::foundation::Result<BrushPresetListPlan>::failure(
|
||||
pp::foundation::Status::out_of_range("selected brush preset index is outside the list"));
|
||||
}
|
||||
if (offset == 0) {
|
||||
return pp::foundation::Result<BrushPresetListPlan>::failure(
|
||||
pp::foundation::Status::invalid_argument("brush preset move offset must not be zero"));
|
||||
}
|
||||
|
||||
BrushPresetListPlan plan;
|
||||
plan.operation = BrushPresetListOperation::move_preset;
|
||||
plan.item_count = item_count;
|
||||
plan.current_index = current_index;
|
||||
plan.target_index = std::clamp(current_index + offset, 0, item_count - 1);
|
||||
plan.move_offset = offset;
|
||||
plan.saves_list = true;
|
||||
plan.no_op = plan.target_index == current_index;
|
||||
return pp::foundation::Result<BrushPresetListPlan>::success(plan);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Result<BrushPresetListPlan> plan_brush_preset_list_select(
|
||||
int item_count,
|
||||
int index)
|
||||
{
|
||||
if (item_count <= 0) {
|
||||
return pp::foundation::Result<BrushPresetListPlan>::failure(
|
||||
pp::foundation::Status::invalid_argument("brush preset list must contain an item to select"));
|
||||
}
|
||||
if (index < 0 || index >= item_count) {
|
||||
return pp::foundation::Result<BrushPresetListPlan>::failure(
|
||||
pp::foundation::Status::out_of_range("selected brush preset index is outside the list"));
|
||||
}
|
||||
|
||||
BrushPresetListPlan plan;
|
||||
plan.operation = BrushPresetListOperation::select_preset;
|
||||
plan.item_count = item_count;
|
||||
plan.current_index = index;
|
||||
plan.target_index = index;
|
||||
plan.selects_target = true;
|
||||
plan.notifies_brush_changed = true;
|
||||
return pp::foundation::Result<BrushPresetListPlan>::success(plan);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Result<BrushPresetListPlan> plan_brush_preset_list_clear(int item_count)
|
||||
{
|
||||
if (item_count < 0) {
|
||||
return pp::foundation::Result<BrushPresetListPlan>::failure(
|
||||
pp::foundation::Status::out_of_range("brush preset item count must not be negative"));
|
||||
}
|
||||
|
||||
BrushPresetListPlan plan;
|
||||
plan.operation = BrushPresetListOperation::clear_presets;
|
||||
plan.item_count = item_count;
|
||||
plan.saves_list = true;
|
||||
plan.updates_empty_notification = true;
|
||||
plan.clears_selection = true;
|
||||
plan.no_op = item_count == 0;
|
||||
return pp::foundation::Result<BrushPresetListPlan>::success(plan);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Status execute_brush_ui_plan(
|
||||
const BrushUiPlan& plan,
|
||||
BrushUiServices& services)
|
||||
|
||||
@@ -407,25 +407,97 @@ Node* NodePanelBrushPreset::clone_instantiate() const
|
||||
return new NodePanelBrushPreset();
|
||||
}
|
||||
|
||||
void NodePanelBrushPreset::execute_preset_list_plan(const pp::app::BrushPresetListPlan& plan)
|
||||
{
|
||||
switch (plan.operation)
|
||||
{
|
||||
case pp::app::BrushPresetListOperation::add_current_brush:
|
||||
if (!Canvas::I || !Canvas::I->m_current_brush)
|
||||
return;
|
||||
for (auto p : s_panels)
|
||||
p->add_brush(std::make_shared<Brush>(*Canvas::I->m_current_brush));
|
||||
break;
|
||||
|
||||
case pp::app::BrushPresetListOperation::move_preset:
|
||||
for (auto p : s_panels)
|
||||
{
|
||||
if (plan.current_index >= 0 && plan.current_index < static_cast<int>(p->m_container->m_children.size()))
|
||||
p->m_container->move_child(p->m_container->m_children[plan.current_index].get(), plan.target_index);
|
||||
}
|
||||
break;
|
||||
|
||||
case pp::app::BrushPresetListOperation::remove_preset:
|
||||
for (auto p : s_panels)
|
||||
{
|
||||
if (plan.current_index < 0 || plan.current_index >= static_cast<int>(p->m_container->m_children.size()))
|
||||
continue;
|
||||
bool new_current = !p->m_current || p->m_container->get_child_index(p->m_current) == plan.current_index;
|
||||
p->m_container->m_children[plan.current_index]->destroy();
|
||||
if (plan.clears_selection)
|
||||
{
|
||||
p->m_current = nullptr;
|
||||
}
|
||||
else if (new_current && plan.selects_target)
|
||||
{
|
||||
p->m_current = static_cast<NodeBrushPresetItem*>(p->m_container->m_children[plan.target_index].get());
|
||||
p->m_current->m_selected = true;
|
||||
}
|
||||
p->m_notification->SetVisibility(p->m_container->m_children.size() == 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case pp::app::BrushPresetListOperation::select_preset:
|
||||
for (auto p : s_panels)
|
||||
{
|
||||
if (p->m_current)
|
||||
p->m_current->m_selected = false;
|
||||
p->m_current = static_cast<NodeBrushPresetItem*>(p->m_container->get_child_at(plan.target_index));
|
||||
p->m_current->m_selected = true;
|
||||
p->m_interacted = true;
|
||||
}
|
||||
if (plan.notifies_brush_changed && on_brush_changed)
|
||||
on_brush_changed(this, m_current->m_brush);
|
||||
break;
|
||||
|
||||
case pp::app::BrushPresetListOperation::clear_presets:
|
||||
for (auto p : s_panels)
|
||||
{
|
||||
p->m_container->remove_all_children();
|
||||
p->m_current = nullptr;
|
||||
p->m_notification->SetVisibility(p->m_container->m_children.size() == 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (plan.saves_list)
|
||||
save();
|
||||
}
|
||||
|
||||
void NodePanelBrushPreset::init()
|
||||
{
|
||||
init_template_file("data/dialogs/panel-brushes.xml", "tpl-panel-brush-preset");
|
||||
m_container = find<Node>("brushes");
|
||||
m_btn_add = find<NodeButtonCustom>("btn-add");
|
||||
m_btn_add->on_click = [this] (Node*) {
|
||||
for (auto p : s_panels)
|
||||
p->add_brush(std::make_shared<Brush>(*Canvas::I->m_current_brush));
|
||||
save();
|
||||
const auto plan = pp::app::plan_brush_preset_list_add(
|
||||
static_cast<int>(m_container->m_children.size()),
|
||||
Canvas::I && Canvas::I->m_current_brush);
|
||||
if (plan) {
|
||||
execute_preset_list_plan(plan.value());
|
||||
}
|
||||
};
|
||||
m_btn_up = find<NodeButtonCustom>("btn-up");
|
||||
m_btn_up->on_click = [this](Node*) {
|
||||
if (m_current)
|
||||
{
|
||||
int idx = m_container->get_child_index(m_current);
|
||||
int to = std::max(idx - 1, 0);
|
||||
for (auto p : s_panels)
|
||||
p->m_container->move_child(p->m_container->m_children[idx].get(), to);
|
||||
save();
|
||||
const auto plan = pp::app::plan_brush_preset_list_move(
|
||||
static_cast<int>(m_container->m_children.size()),
|
||||
idx,
|
||||
-1);
|
||||
if (plan) {
|
||||
execute_preset_list_plan(plan.value());
|
||||
}
|
||||
}
|
||||
};
|
||||
m_btn_down = find<NodeButtonCustom>("btn-down");
|
||||
@@ -433,10 +505,13 @@ void NodePanelBrushPreset::init()
|
||||
if (m_current)
|
||||
{
|
||||
int idx = m_container->get_child_index(m_current);
|
||||
int to = std::min(idx + 1, (int)m_container->m_children.size() - 1);
|
||||
for (auto p : s_panels)
|
||||
p->m_container->move_child(p->m_container->m_children[idx].get(), to);
|
||||
save();
|
||||
const auto plan = pp::app::plan_brush_preset_list_move(
|
||||
static_cast<int>(m_container->m_children.size()),
|
||||
idx,
|
||||
1);
|
||||
if (plan) {
|
||||
execute_preset_list_plan(plan.value());
|
||||
}
|
||||
}
|
||||
};
|
||||
/*
|
||||
@@ -456,23 +531,12 @@ void NodePanelBrushPreset::init()
|
||||
if (!m_current)
|
||||
return;
|
||||
int index = m_container->get_child_index(m_current);
|
||||
for (auto p : s_panels)
|
||||
{
|
||||
bool new_current = !p->m_current || p->m_container->get_child_index(p->m_current) == index;
|
||||
p->m_container->m_children[index]->destroy();
|
||||
if (p->m_container->m_children.empty())
|
||||
{
|
||||
p->m_current = nullptr;
|
||||
}
|
||||
else if (new_current)
|
||||
{
|
||||
int next = std::min<int>((int)p->m_container->m_children.size() - 1, index);
|
||||
p->m_current = (NodeBrushPresetItem*)p->m_container->m_children[next].get();
|
||||
p->m_current->m_selected = true;
|
||||
}
|
||||
p->m_notification->SetVisibility(p->m_container->m_children.size() == 0);
|
||||
const auto plan = pp::app::plan_brush_preset_list_remove(
|
||||
static_cast<int>(m_container->m_children.size()),
|
||||
index);
|
||||
if (plan) {
|
||||
execute_preset_list_plan(plan.value());
|
||||
}
|
||||
save();
|
||||
};
|
||||
m_btn_menu = find<NodeButtonCustom>("btn-menu");
|
||||
m_btn_menu->on_click = [this](Node* b) {
|
||||
@@ -508,8 +572,11 @@ void NodePanelBrushPreset::init()
|
||||
mb->btn_ok->m_text->set_text("Yes");
|
||||
mb->btn_cancel->m_text->set_text("No");
|
||||
mb->btn_ok->on_click = mb->on_submit = [this, mb](Node*) {
|
||||
App::I->presets->clear_brushes();
|
||||
App::I->presets->save();
|
||||
const auto plan = pp::app::plan_brush_preset_list_clear(
|
||||
static_cast<int>(m_container->m_children.size()));
|
||||
if (plan) {
|
||||
execute_preset_list_plan(plan.value());
|
||||
}
|
||||
mb->destroy();
|
||||
};
|
||||
break;
|
||||
@@ -576,16 +643,12 @@ kEventResult NodePanelBrushPreset::handle_event(Event* e)
|
||||
void NodePanelBrushPreset::handle_click(Node* target)
|
||||
{
|
||||
int idx = m_container->get_child_index(target);
|
||||
for (auto p : s_panels)
|
||||
{
|
||||
if (p->m_current)
|
||||
p->m_current->m_selected = false;
|
||||
p->m_current = (NodeBrushPresetItem*)p->m_container->get_child_at(idx);
|
||||
p->m_current->m_selected = true;
|
||||
p->m_interacted = true;
|
||||
const auto plan = pp::app::plan_brush_preset_list_select(
|
||||
static_cast<int>(m_container->m_children.size()),
|
||||
idx);
|
||||
if (plan) {
|
||||
execute_preset_list_plan(plan.value());
|
||||
}
|
||||
if (on_brush_changed)
|
||||
on_brush_changed(this, m_current->m_brush);
|
||||
}
|
||||
|
||||
bool NodePanelBrushPreset::save()
|
||||
@@ -1067,10 +1130,10 @@ bool NodePanelBrushPreset::import_brush(const std::string& path)
|
||||
|
||||
void NodePanelBrushPreset::clear_brushes()
|
||||
{
|
||||
for (auto p : s_panels)
|
||||
{
|
||||
p->m_container->remove_all_children();
|
||||
p->m_notification->SetVisibility(p->m_container->m_children.size() == 0);
|
||||
const auto plan = pp::app::plan_brush_preset_list_clear(
|
||||
static_cast<int>(m_container->m_children.size()));
|
||||
if (plan) {
|
||||
execute_preset_list_plan(plan.value());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
namespace pp::app {
|
||||
struct BrushTextureListPlan;
|
||||
struct BrushPresetListPlan;
|
||||
}
|
||||
namespace pp::panopainter {
|
||||
class LegacyBrushTextureListServices;
|
||||
@@ -99,6 +100,7 @@ class NodePanelBrushPreset : public Node
|
||||
NodeButton* m_btn_import;
|
||||
NodeButton* m_btn_download;
|
||||
Node* m_notification;
|
||||
void execute_preset_list_plan(const pp::app::BrushPresetListPlan& plan);
|
||||
public:
|
||||
struct PPBRInfo
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user