Extract brush UI operation planning

This commit is contained in:
2026-06-03 10:40:12 +02:00
parent 4f0909f30c
commit efd568a416
8 changed files with 511 additions and 52 deletions

122
src/app_core/brush_ui.h Normal file
View File

@@ -0,0 +1,122 @@
#pragma once
#include "foundation/result.h"
#include <cmath>
#include <string>
#include <string_view>
namespace pp::app {
enum class BrushUiTextureSlot {
tip,
pattern,
dual,
};
enum class BrushUiOperation {
set_tip_color,
set_texture,
replace_brush_from_preset,
stroke_settings_changed,
};
struct BrushUiPlan {
BrushUiOperation operation = BrushUiOperation::stroke_settings_changed;
BrushUiTextureSlot texture_slot = BrushUiTextureSlot::tip;
std::string path;
std::string thumbnail_path;
float r = 0.0F;
float g = 0.0F;
float b = 0.0F;
float a = 1.0F;
bool mutates_brush = false;
bool preserves_existing_color = false;
bool loads_brush_resources = false;
bool update_color_ui = false;
bool update_brush_ui = false;
};
[[nodiscard]] inline pp::foundation::Status validate_brush_ui_color_channel(float value) noexcept
{
if (!std::isfinite(value) || value < 0.0F || value > 1.0F) {
return pp::foundation::Status::out_of_range("brush color channels must be finite and within 0..1");
}
return pp::foundation::Status::success();
}
[[nodiscard]] inline pp::foundation::Result<BrushUiPlan> plan_brush_ui_color(
float r,
float g,
float b,
float a)
{
for (const auto value : { r, g, b, a }) {
const auto channel_status = validate_brush_ui_color_channel(value);
if (!channel_status.ok()) {
return pp::foundation::Result<BrushUiPlan>::failure(channel_status);
}
}
BrushUiPlan plan;
plan.operation = BrushUiOperation::set_tip_color;
plan.r = r;
plan.g = g;
plan.b = b;
plan.a = a;
plan.mutates_brush = true;
plan.update_color_ui = true;
return pp::foundation::Result<BrushUiPlan>::success(plan);
}
[[nodiscard]] inline pp::foundation::Result<BrushUiPlan> plan_brush_ui_texture(
BrushUiTextureSlot slot,
std::string_view path,
std::string_view thumbnail_path)
{
if (path.empty()) {
return pp::foundation::Result<BrushUiPlan>::failure(
pp::foundation::Status::invalid_argument("brush texture path must not be empty"));
}
BrushUiPlan plan;
plan.operation = BrushUiOperation::set_texture;
plan.texture_slot = slot;
plan.path = std::string(path);
plan.thumbnail_path = std::string(thumbnail_path);
plan.mutates_brush = true;
plan.loads_brush_resources = true;
plan.update_color_ui = true;
plan.update_brush_ui = true;
return pp::foundation::Result<BrushUiPlan>::success(std::move(plan));
}
[[nodiscard]] inline pp::foundation::Result<BrushUiPlan> plan_brush_ui_preset_replace(bool has_preset_brush)
{
if (!has_preset_brush) {
return pp::foundation::Result<BrushUiPlan>::failure(
pp::foundation::Status::invalid_argument("preset brush must be available"));
}
BrushUiPlan plan;
plan.operation = BrushUiOperation::replace_brush_from_preset;
plan.mutates_brush = true;
plan.preserves_existing_color = true;
plan.loads_brush_resources = true;
plan.update_color_ui = true;
plan.update_brush_ui = true;
return pp::foundation::Result<BrushUiPlan>::success(plan);
}
[[nodiscard]] inline constexpr BrushUiPlan plan_brush_ui_stroke_settings_changed() noexcept
{
BrushUiPlan plan;
plan.operation = BrushUiOperation::stroke_settings_changed;
plan.mutates_brush = true;
plan.update_color_ui = true;
plan.update_brush_ui = true;
return plan;
}
} // namespace pp::app

View File

@@ -7,6 +7,7 @@
#include "node_dialog_picker.h"
#include "node_panel_floating.h"
#include "app_core/app_preferences.h"
#include "app_core/brush_ui.h"
#include "app_core/document_layer.h"
#include "app_core/app_status.h"
#include "settings.h"
@@ -17,6 +18,64 @@
#include <vector>
namespace {
bool apply_brush_color_plan(App& app, glm::vec4 color, bool update_quick, bool update_color_panel)
{
const auto plan = pp::app::plan_brush_ui_color(color.r, color.g, color.b, color.a);
if (!plan)
return false;
Canvas::I->m_current_brush->m_tip_color = glm::vec4(
plan.value().r,
plan.value().g,
plan.value().b,
plan.value().a);
if (update_quick)
app.quick->set_color(Canvas::I->m_current_brush->m_tip_color);
if (update_color_panel)
app.color->set_color(Canvas::I->m_current_brush->m_tip_color);
app.brush_update(plan.value().update_color_ui, plan.value().update_brush_ui);
return true;
}
bool apply_brush_texture_plan(pp::app::BrushUiTextureSlot slot, const std::string& path, const std::string& thumb)
{
const auto plan = pp::app::plan_brush_ui_texture(slot, path, thumb);
if (!plan)
return false;
switch (plan.value().texture_slot)
{
case pp::app::BrushUiTextureSlot::tip:
Canvas::I->m_current_brush->load_tip(plan.value().path, plan.value().thumbnail_path);
break;
case pp::app::BrushUiTextureSlot::pattern:
Canvas::I->m_current_brush->load_pattern(plan.value().path, plan.value().thumbnail_path);
break;
case pp::app::BrushUiTextureSlot::dual:
Canvas::I->m_current_brush->load_dual(plan.value().path, plan.value().thumbnail_path);
break;
}
App::I->brush_update(plan.value().update_color_ui, plan.value().update_brush_ui);
return true;
}
bool apply_brush_preset_plan(App& app, const std::shared_ptr<Brush>& brush)
{
const auto plan = pp::app::plan_brush_ui_preset_replace(static_cast<bool>(brush));
if (!plan)
return false;
auto color = Canvas::I->m_current_brush->m_tip_color;
*Canvas::I->m_current_brush = *brush;
if (plan.value().preserves_existing_color)
Canvas::I->m_current_brush->m_tip_color = color;
if (plan.value().loads_brush_resources)
Canvas::I->m_current_brush->load();
app.brush_update(plan.value().update_color_ui, plan.value().update_brush_ui);
return true;
}
} // namespace
void App::title_update()
{
if (auto docname = layout[main_id]->find<NodeText>("txt-docname"))
@@ -144,30 +203,25 @@ void App::init_sidebar()
brush_update(true, true);
};
color->on_color_changed = [this](Node* target, glm::vec4 color) {
Canvas::I->m_current_brush->m_tip_color = color;
quick->set_color(color);
brush_update(true, false);
apply_brush_color_plan(*this, color, true, false);
};
stroke->on_brush_changed = [this](Node* target, const std::string& path, const std::string& thumb) {
Canvas::I->m_current_brush->load_tip(path, thumb);
brush_update(true, true);
apply_brush_texture_plan(pp::app::BrushUiTextureSlot::tip, path, thumb);
};
stroke->on_pattern_changed = [this](Node*target, const std::string& path, const std::string& thumb) {
Canvas::I->m_current_brush->load_pattern(path, thumb);
brush_update(true, true);
apply_brush_texture_plan(pp::app::BrushUiTextureSlot::pattern, path, thumb);
};
stroke->on_dual_changed = [this](Node*target, const std::string& path, const std::string& thumb) {
Canvas::I->m_current_brush->load_dual(path, thumb);
brush_update(true, true);
apply_brush_texture_plan(pp::app::BrushUiTextureSlot::dual, path, thumb);
};
stroke->on_stroke_change = [this](Node*) {
brush_update(true, true);
const auto plan = pp::app::plan_brush_ui_stroke_settings_changed();
brush_update(plan.update_color_ui, plan.update_brush_ui);
};
quick->on_color_change = [this](Node*, glm::vec3 c) {
Canvas::I->m_current_brush->m_tip_color = glm::vec4(c, 1.f);
color->set_color(c);
apply_brush_color_plan(*this, glm::vec4(c, 1.f), false, true);
};
quick->on_flow_change = [this](Node*, float value) {
stroke->set_flow(value, true, true);
@@ -176,10 +230,7 @@ void App::init_sidebar()
stroke->set_size(value, true, true);
};
quick->on_brush_change = [this](Node*, std::shared_ptr<Brush> b) {
auto c = Canvas::I->m_current_brush->m_tip_color;
*Canvas::I->m_current_brush = *b;
Canvas::I->m_current_brush->m_tip_color = c;
brush_update(true, true);
apply_brush_preset_plan(*this, b);
};
layers->on_layer_add = [this](Node*, std::shared_ptr<class Layer> layer, int index) {
@@ -853,11 +904,7 @@ void App::init_menu_tools()
//floating_presets->SetFlexGrow(1);
//floating_presets->find("toolbar")->destroy();
floating_presets->on_brush_changed = [this](Node* target, std::shared_ptr<Brush>& b) {
auto c = Canvas::I->m_current_brush->m_tip_color;
*Canvas::I->m_current_brush = *b;
Canvas::I->m_current_brush->m_tip_color = c;
Canvas::I->m_current_brush->load();
brush_update(true, true);
apply_brush_preset_plan(*this, b);
};
}
else
@@ -883,8 +930,7 @@ void App::init_menu_tools()
//floating_color->SetMinHeight(300);
floating_color->find("title")->SetVisibility(false);
floating_color->on_color_changed = [this](Node* target, glm::vec4 color) {
Canvas::I->m_current_brush->m_tip_color = color;
brush_update(true, false);
apply_brush_color_plan(*this, color, false, false);
};
}
else
@@ -910,8 +956,7 @@ void App::init_menu_tools()
//floating_picker->SetWidth(250);
floating_picker->m_autohide = false;
floating_picker->on_color_change = [this](Node* target, glm::vec3 color) {
Canvas::I->m_current_brush->m_tip_color = glm::vec4(color, 1.f);
brush_update(true, false);
apply_brush_color_plan(*this, glm::vec4(color, 1.f), false, false);
};
}
else
@@ -1624,11 +1669,7 @@ void App::ui_restore()
floating_presets->SetHeightP(100);
//floating_presets->find("toolbar")->destroy();
floating_presets->on_brush_changed = [this](Node* target, std::shared_ptr<Brush>& b) {
auto c = Canvas::I->m_current_brush->m_tip_color;
*Canvas::I->m_current_brush = *b;
Canvas::I->m_current_brush->m_tip_color = c;
Canvas::I->m_current_brush->load();
brush_update(true, true);
apply_brush_preset_plan(*this, b);
};
break;
}
@@ -1638,8 +1679,7 @@ void App::ui_restore()
floating_color->SetHeightP(100);
floating_color->find("title")->SetVisibility(false);
floating_color->on_color_changed = [this](Node* target, glm::vec4 color) {
Canvas::I->m_current_brush->m_tip_color = color;
brush_update(true, false);
apply_brush_color_plan(*this, color, false, false);
};
break;
}
@@ -1648,8 +1688,7 @@ void App::ui_restore()
floating_picker = f->m_container->add_child_ref<NodeColorPicker>();
floating_picker->m_autohide = false;
floating_picker->on_color_change = [this](Node* target, glm::vec3 color) {
Canvas::I->m_current_brush->m_tip_color = glm::vec4(color, 1.f);
brush_update(true, false);
apply_brush_color_plan(*this, glm::vec4(color, 1.f), false, false);
};
break;
}
@@ -1717,11 +1756,7 @@ void App::ui_restore()
floating_presets->SetHeightP(100);
//floating_presets->find("toolbar")->destroy();
floating_presets->on_brush_changed = [this](Node* target, std::shared_ptr<Brush>& b) {
auto c = Canvas::I->m_current_brush->m_tip_color;
*Canvas::I->m_current_brush = *b;
Canvas::I->m_current_brush->m_tip_color = c;
Canvas::I->m_current_brush->load();
brush_update(true, true);
apply_brush_preset_plan(*this, b);
};
break;
}
@@ -1731,8 +1766,7 @@ void App::ui_restore()
floating_color->SetHeightP(100);
floating_color->find("title")->destroy();
floating_color->on_color_changed = [this](Node* target, glm::vec4 color) {
Canvas::I->m_current_brush->m_tip_color = color;
brush_update(true, false);
apply_brush_color_plan(*this, color, false, false);
};
break;
}
@@ -1741,8 +1775,7 @@ void App::ui_restore()
floating_picker = f->m_container->add_child_ref<NodeColorPicker>();
floating_picker->m_autohide = false;
floating_picker->on_color_change = [this](Node* target, glm::vec3 color) {
Canvas::I->m_current_brush->m_tip_color = glm::vec4(color, 1.f);
brush_update(true, false);
apply_brush_color_plan(*this, glm::vec4(color, 1.f), false, false);
};
break;
}
@@ -1797,11 +1830,7 @@ void App::ui_restore()
floating_presets->SetHeightP(100);
//floating_presets->find("toolbar")->destroy();
floating_presets->on_brush_changed = [this](Node* target, std::shared_ptr<Brush>& b) {
auto c = Canvas::I->m_current_brush->m_tip_color;
*Canvas::I->m_current_brush = *b;
Canvas::I->m_current_brush->m_tip_color = c;
Canvas::I->m_current_brush->load();
brush_update(true, true);
apply_brush_preset_plan(*this, b);
};
break;
}
@@ -1811,8 +1840,7 @@ void App::ui_restore()
floating_color->SetHeightP(100);
floating_color->find("title")->destroy();
floating_color->on_color_changed = [this](Node* target, glm::vec4 color) {
Canvas::I->m_current_brush->m_tip_color = color;
brush_update(true, false);
apply_brush_color_plan(*this, color, false, false);
};
break;
}
@@ -1821,8 +1849,7 @@ void App::ui_restore()
floating_picker = f->m_container->add_child_ref<NodeColorPicker>();
floating_picker->m_autohide = false;
floating_picker->on_color_change = [this](Node* target, glm::vec3 color) {
Canvas::I->m_current_brush->m_tip_color = glm::vec4(color, 1.f);
brush_update(true, false);
apply_brush_color_plan(*this, glm::vec4(color, 1.f), false, false);
};
break;
}