Add brush preset list executor bridge

This commit is contained in:
2026-06-04 15:25:54 +02:00
parent 47c35fb859
commit 0c7bc98d5b
7 changed files with 356 additions and 54 deletions

View File

@@ -201,6 +201,23 @@ public:
virtual void save_texture_list() = 0;
};
class BrushPresetListServices {
public:
virtual ~BrushPresetListServices() = default;
virtual pp::foundation::Status add_current_brush_preset(int target_index) = 0;
virtual void remove_brush_preset(
int current_index,
int target_index,
bool selects_target,
bool clears_selection) = 0;
virtual void move_brush_preset(int from_index, int to_index) = 0;
virtual void select_brush_preset(int index, bool notify_brush_changed) = 0;
virtual void clear_brush_presets(bool clears_selection) = 0;
virtual void update_preset_empty_notification() = 0;
virtual void save_preset_list() = 0;
};
class BrushStrokeControlServices {
public:
virtual ~BrushStrokeControlServices() = default;
@@ -763,4 +780,83 @@ public:
return pp::foundation::Status::invalid_argument("unknown brush texture list operation");
}
[[nodiscard]] inline pp::foundation::Status execute_brush_preset_list_plan(
const BrushPresetListPlan& plan,
BrushPresetListServices& services)
{
switch (plan.operation) {
case BrushPresetListOperation::add_current_brush:
{
if (plan.item_count < 0 || plan.target_index != plan.item_count) {
return pp::foundation::Status::out_of_range("brush preset add plan has invalid target");
}
const auto add_status = services.add_current_brush_preset(plan.target_index);
if (!add_status.ok()) {
return add_status;
}
if (plan.updates_empty_notification) {
services.update_preset_empty_notification();
}
if (plan.saves_list) {
services.save_preset_list();
}
return pp::foundation::Status::success();
}
case BrushPresetListOperation::remove_preset:
if (plan.item_count <= 0 || plan.current_index < 0 || plan.current_index >= plan.item_count) {
return pp::foundation::Status::out_of_range("brush preset remove plan has invalid selection");
}
if (plan.selects_target && (plan.target_index < 0 || plan.target_index >= plan.item_count - 1)) {
return pp::foundation::Status::out_of_range("brush preset remove plan has invalid target");
}
services.remove_brush_preset(
plan.current_index,
plan.target_index,
plan.selects_target,
plan.clears_selection);
if (plan.updates_empty_notification) {
services.update_preset_empty_notification();
}
if (plan.saves_list) {
services.save_preset_list();
}
return pp::foundation::Status::success();
case BrushPresetListOperation::move_preset:
if (plan.item_count <= 0 || plan.current_index < 0 || plan.current_index >= plan.item_count
|| plan.target_index < 0 || plan.target_index >= plan.item_count) {
return pp::foundation::Status::out_of_range("brush preset move plan has invalid indices");
}
services.move_brush_preset(plan.current_index, plan.target_index);
if (plan.saves_list) {
services.save_preset_list();
}
return pp::foundation::Status::success();
case BrushPresetListOperation::select_preset:
if (plan.item_count <= 0 || plan.target_index < 0 || plan.target_index >= plan.item_count) {
return pp::foundation::Status::out_of_range("brush preset select plan has invalid target");
}
services.select_brush_preset(plan.target_index, plan.notifies_brush_changed);
return pp::foundation::Status::success();
case BrushPresetListOperation::clear_presets:
if (plan.item_count < 0) {
return pp::foundation::Status::out_of_range("brush preset clear plan has invalid item count");
}
services.clear_brush_presets(plan.clears_selection);
if (plan.updates_empty_notification) {
services.update_preset_empty_notification();
}
if (plan.saves_list) {
services.save_preset_list();
}
return pp::foundation::Status::success();
}
return pp::foundation::Status::invalid_argument("unknown brush preset list operation");
}
} // namespace pp::app

View File

@@ -407,70 +407,111 @@ Node* NodePanelBrushPreset::clone_instantiate() const
return new NodePanelBrushPreset();
}
void NodePanelBrushPreset::execute_preset_list_plan(const pp::app::BrushPresetListPlan& plan)
{
switch (plan.operation)
namespace pp::panopainter {
class LegacyBrushPresetListServices final : public pp::app::BrushPresetListServices {
public:
explicit LegacyBrushPresetListServices(NodePanelBrushPreset& owner)
: owner_(owner)
{
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);
pp::foundation::Status add_current_brush_preset(int target_index) override
{
if (target_index < 0) {
return pp::foundation::Status::out_of_range("brush preset add target is invalid");
}
if (!Canvas::I || !Canvas::I->m_current_brush) {
return pp::foundation::Status::invalid_argument("current brush must be available to add a preset");
}
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()))
for (auto p : NodePanelBrushPreset::s_panels) {
p->add_brush(std::make_shared<Brush>(*Canvas::I->m_current_brush));
}
return pp::foundation::Status::success();
}
void remove_brush_preset(
int current_index,
int target_index,
bool selects_target,
bool clears_selection) override
{
for (auto p : NodePanelBrushPreset::s_panels) {
if (current_index < 0 || 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());
const bool new_current = !p->m_current || p->m_container->get_child_index(p->m_current) == current_index;
p->m_container->m_children[current_index]->destroy();
if (clears_selection) {
p->m_current = nullptr;
} else if (new_current && selects_target) {
p->m_current = static_cast<NodeBrushPresetItem*>(p->m_container->m_children[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)
void move_brush_preset(int from_index, int to_index) override
{
for (auto p : NodePanelBrushPreset::s_panels) {
if (from_index >= 0 && from_index < static_cast<int>(p->m_container->m_children.size())) {
p->m_container->move_child(p->m_container->m_children[from_index].get(), to_index);
}
}
}
void select_brush_preset(int index, bool notify_brush_changed) override
{
for (auto p : NodePanelBrushPreset::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 = static_cast<NodeBrushPresetItem*>(p->m_container->get_child_at(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);
if (notify_brush_changed && owner_.on_brush_changed) {
owner_.on_brush_changed(&owner_, owner_.m_current->m_brush);
}
break;
}
if (plan.saves_list)
save();
void clear_brush_presets(bool clears_selection) override
{
for (auto p : NodePanelBrushPreset::s_panels) {
p->m_container->remove_all_children();
if (clears_selection) {
p->m_current = nullptr;
}
}
}
void update_preset_empty_notification() override
{
for (auto p : NodePanelBrushPreset::s_panels) {
p->m_notification->SetVisibility(p->m_container->m_children.size() == 0);
}
}
void save_preset_list() override
{
owner_.save();
}
private:
NodePanelBrushPreset& owner_;
};
} // namespace pp::panopainter
void NodePanelBrushPreset::execute_preset_list_plan(const pp::app::BrushPresetListPlan& plan)
{
pp::panopainter::LegacyBrushPresetListServices services(*this);
const auto status = pp::app::execute_brush_preset_list_plan(plan, services);
if (!status.ok()) {
LOG("Brush preset list action failed: %s", status.message);
}
}
void NodePanelBrushPreset::init()

View File

@@ -15,6 +15,7 @@ struct BrushPresetListPlan;
}
namespace pp::panopainter {
class LegacyBrushTextureListServices;
class LegacyBrushPresetListServices;
}
class NodeButtonBrush : public NodeButtonCustom, public Serializer::Type
@@ -88,6 +89,8 @@ public:
class NodePanelBrushPreset : public Node
{
friend class pp::panopainter::LegacyBrushPresetListServices;
static std::vector<NodePanelBrushPreset*> s_panels;
bool m_interacted = false;
NodeBrushPresetItem* m_current = nullptr;