#pragma once #include "foundation/result.h" #include #include #include 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 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::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::success(plan); } [[nodiscard]] inline pp::foundation::Result plan_brush_ui_texture( BrushUiTextureSlot slot, std::string_view path, std::string_view thumbnail_path) { if (path.empty()) { return pp::foundation::Result::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::success(std::move(plan)); } [[nodiscard]] inline pp::foundation::Result plan_brush_ui_preset_replace(bool has_preset_brush) { if (!has_preset_brush) { return pp::foundation::Result::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::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