Thin brush panel, preview, and node utility shells
This commit is contained in:
@@ -81,14 +81,14 @@ Current hotspot files:
|
|||||||
|
|
||||||
- `src/canvas.cpp`: 17 lines
|
- `src/canvas.cpp`: 17 lines
|
||||||
- `src/app_layout.cpp`: 125 lines
|
- `src/app_layout.cpp`: 125 lines
|
||||||
- `src/canvas_modes.cpp`: 176 lines
|
- `src/canvas_modes.cpp`: 191 lines
|
||||||
- `src/node.cpp`: 222 lines
|
- `src/node.cpp`: 257 lines
|
||||||
- `src/main.cpp`: 130 lines
|
- `src/main.cpp`: 156 lines
|
||||||
- `src/node_panel_brush.cpp`: 182 lines
|
- `src/node_panel_brush.cpp`: 2 lines
|
||||||
- `src/node_stroke_preview.cpp`: 192 lines
|
- `src/node_stroke_preview.cpp`: 160 lines
|
||||||
- `src/node_canvas.cpp`: 69 lines
|
- `src/node_canvas.cpp`: 85 lines
|
||||||
- `src/app.cpp`: 105 lines
|
- `src/app.cpp`: 125 lines
|
||||||
- `src/app_dialogs.cpp`: 106 lines
|
- `src/app_dialogs.cpp`: 127 lines
|
||||||
|
|
||||||
Latest slice:
|
Latest slice:
|
||||||
|
|
||||||
@@ -115,6 +115,22 @@ Latest slice:
|
|||||||
`SetMinSize(...)`, `SetMaxSize(...)`, and `SetPosition(const glm::vec2)` now
|
`SetMinSize(...)`, `SetMaxSize(...)`, and `SetPosition(const glm::vec2)` now
|
||||||
lives in `src/legacy_ui_node_style.*` instead of staying inline in
|
lives in `src/legacy_ui_node_style.*` instead of staying inline in
|
||||||
`src/node.cpp`.
|
`src/node.cpp`.
|
||||||
|
- `Node::app_redraw()` and `Node::watch(...)` now live in
|
||||||
|
`src/legacy_ui_node_execution.cpp` instead of staying inline in `src/node.cpp`.
|
||||||
|
- `NodeStrokePreview::draw_stroke_immediate()` now routes through
|
||||||
|
`execute_legacy_node_stroke_preview_immediate_draw(...)` in
|
||||||
|
`src/legacy_node_stroke_preview_runtime_services.*`, which moves render-target
|
||||||
|
validation, viewport/clear-color save-restore, and immediate-runtime request
|
||||||
|
assembly out of the live node file.
|
||||||
|
- The remaining `NodePanelBrush` and `NodePanelBrushPreset` member bodies now
|
||||||
|
live in the existing retained helper layers
|
||||||
|
(`src/legacy_brush_panel_item_ui.*`,
|
||||||
|
`src/legacy_brush_panel_ui.*`,
|
||||||
|
`src/legacy_brush_panel_services.*`,
|
||||||
|
`src/legacy_brush_preset_panel_ui.*`,
|
||||||
|
`src/legacy_brush_preset_list_services.*`, and
|
||||||
|
`src/legacy_brush_preset_services.*`), leaving
|
||||||
|
`src/node_panel_brush.cpp` as a thin translation unit.
|
||||||
|
|
||||||
Current architecture mismatches that must be treated as real blockers:
|
Current architecture mismatches that must be treated as real blockers:
|
||||||
|
|
||||||
|
|||||||
@@ -385,6 +385,12 @@ Current slice:
|
|||||||
`src/legacy_node_stroke_preview_sample_services.*` instead of staying inline
|
`src/legacy_node_stroke_preview_sample_services.*` instead of staying inline
|
||||||
in `src/node_stroke_preview.cpp`, which trims another coherent preview
|
in `src/node_stroke_preview.cpp`, which trims another coherent preview
|
||||||
execution pocket while preserving the live draw path.
|
execution pocket while preserving the live draw path.
|
||||||
|
- `NodeStrokePreview::draw_stroke_immediate()` now also routes through
|
||||||
|
`execute_legacy_node_stroke_preview_immediate_draw(...)` in
|
||||||
|
`src/legacy_node_stroke_preview_runtime_services.*`, which moves
|
||||||
|
render-target validation, viewport/clear-color save-restore, and immediate
|
||||||
|
runtime request assembly out of the live node file and leaves
|
||||||
|
`src/node_stroke_preview.cpp` at 160 lines.
|
||||||
- `NodeCanvas::init()` plus the remaining `NodeCanvas::draw()` outer shell now
|
- `NodeCanvas::init()` plus the remaining `NodeCanvas::draw()` outer shell now
|
||||||
also live in `src/legacy_node_canvas_draw_services.*` instead of staying
|
also live in `src/legacy_node_canvas_draw_services.*` instead of staying
|
||||||
inline in `src/node_canvas.cpp`, which materially reduces the live node to a
|
inline in `src/node_canvas.cpp`, which materially reduces the live node to a
|
||||||
@@ -394,10 +400,22 @@ Current slice:
|
|||||||
now also lives in `src/legacy_ui_node_style.*` instead of staying inline in
|
now also lives in `src/legacy_ui_node_style.*` instead of staying inline in
|
||||||
`src/node.cpp`, which trims another coherent base scene-graph shell pocket
|
`src/node.cpp`, which trims another coherent base scene-graph shell pocket
|
||||||
without changing the public surface.
|
without changing the public surface.
|
||||||
|
- `Node::app_redraw()` and `Node::watch(...)` now also live in
|
||||||
|
`src/legacy_ui_node_execution.cpp` instead of staying inline in `src/node.cpp`,
|
||||||
|
which trims another small generic node utility pocket from the live file.
|
||||||
- `NodePanelBrushPreset` global panel registration now also lives in
|
- `NodePanelBrushPreset` global panel registration now also lives in
|
||||||
`src/legacy_brush_preset_list_services.*` instead of staying on the live
|
`src/legacy_brush_preset_list_services.*` instead of staying on the live
|
||||||
node type as a static registry field, which trims another retained
|
node type as a static registry field, which trims another retained
|
||||||
controller-state pocket from `src/node_panel_brush.cpp`.
|
controller-state pocket from `src/node_panel_brush.cpp`.
|
||||||
|
- The remaining `NodePanelBrush` and `NodePanelBrushPreset` member bodies now
|
||||||
|
also live in the existing retained helper layers
|
||||||
|
(`src/legacy_brush_panel_item_ui.*`,
|
||||||
|
`src/legacy_brush_panel_ui.*`,
|
||||||
|
`src/legacy_brush_panel_services.*`,
|
||||||
|
`src/legacy_brush_preset_panel_ui.*`,
|
||||||
|
`src/legacy_brush_preset_list_services.*`, and
|
||||||
|
`src/legacy_brush_preset_services.*`), which leaves
|
||||||
|
`src/node_panel_brush.cpp` as a 2-line translation unit.
|
||||||
|
|
||||||
Write scope:
|
Write scope:
|
||||||
- `src/node_stroke_preview.cpp`
|
- `src/node_stroke_preview.cpp`
|
||||||
|
|||||||
@@ -5,6 +5,51 @@
|
|||||||
#include "app.h"
|
#include "app.h"
|
||||||
#include "texture.h"
|
#include "texture.h"
|
||||||
|
|
||||||
|
Node* NodeButtonBrush::clone_instantiate() const
|
||||||
|
{
|
||||||
|
return pp::panopainter::clone_legacy_brush_button(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeButtonBrush::init()
|
||||||
|
{
|
||||||
|
pp::panopainter::init_legacy_brush_button(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeButtonBrush::set_icon(const char* path)
|
||||||
|
{
|
||||||
|
pp::panopainter::set_legacy_brush_button_icon(*this, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeButtonBrush::draw()
|
||||||
|
{
|
||||||
|
pp::panopainter::draw_legacy_brush_button(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NodeButtonBrush::read(BinaryStreamReader& r)
|
||||||
|
{
|
||||||
|
return pp::panopainter::read_legacy_brush_button(*this, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeButtonBrush::write(BinaryStreamWriter& w) const
|
||||||
|
{
|
||||||
|
pp::panopainter::write_legacy_brush_button(*this, w);
|
||||||
|
}
|
||||||
|
|
||||||
|
Node* NodeBrushPresetItem::clone_instantiate() const
|
||||||
|
{
|
||||||
|
return pp::panopainter::clone_legacy_brush_preset_item(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeBrushPresetItem::init()
|
||||||
|
{
|
||||||
|
pp::panopainter::init_legacy_brush_preset_item(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeBrushPresetItem::draw()
|
||||||
|
{
|
||||||
|
pp::panopainter::draw_legacy_brush_preset_item(*this);
|
||||||
|
}
|
||||||
|
|
||||||
namespace pp::panopainter {
|
namespace pp::panopainter {
|
||||||
|
|
||||||
Node* clone_legacy_brush_button(const NodeButtonBrush&)
|
Node* clone_legacy_brush_button(const NodeButtonBrush&)
|
||||||
|
|||||||
@@ -4,12 +4,61 @@
|
|||||||
|
|
||||||
#include "app.h"
|
#include "app.h"
|
||||||
#include "asset.h"
|
#include "asset.h"
|
||||||
|
#include "legacy_brush_ui_services.h"
|
||||||
#include "node_panel_brush.h"
|
#include "node_panel_brush.h"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
void NodePanelBrush::execute_texture_list_plan(const pp::app::BrushTextureListPlan& plan)
|
||||||
|
{
|
||||||
|
const auto status = pp::panopainter::execute_legacy_brush_texture_list_plan(*this, plan);
|
||||||
|
if (!status.ok()) {
|
||||||
|
LOG("Brush texture list action failed: %s", status.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int NodePanelBrush::find_brush(const std::string& name) const
|
||||||
|
{
|
||||||
|
return pp::panopainter::LegacyBrushPanelServices(const_cast<NodePanelBrush&>(*this)).find_brush(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string NodePanelBrush::get_texture_path(int index) const
|
||||||
|
{
|
||||||
|
return pp::panopainter::LegacyBrushPanelServices(const_cast<NodePanelBrush&>(*this)).get_texture_path(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string NodePanelBrush::get_thumb_path(int index) const
|
||||||
|
{
|
||||||
|
return pp::panopainter::LegacyBrushPanelServices(const_cast<NodePanelBrush&>(*this)).get_thumb_path(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NodePanelBrush::save()
|
||||||
|
{
|
||||||
|
return pp::panopainter::LegacyBrushPanelServices(*this).save();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NodePanelBrush::restore()
|
||||||
|
{
|
||||||
|
return pp::panopainter::LegacyBrushPanelServices(*this).restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodePanelBrush::clear()
|
||||||
|
{
|
||||||
|
pp::panopainter::LegacyBrushPanelServices(*this).clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodePanelBrush::scan()
|
||||||
|
{
|
||||||
|
pp::panopainter::LegacyBrushPanelServices(*this).scan();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodePanelBrush::reload()
|
||||||
|
{
|
||||||
|
pp::panopainter::LegacyBrushPanelServices(*this).reload();
|
||||||
|
}
|
||||||
|
|
||||||
namespace pp::panopainter {
|
namespace pp::panopainter {
|
||||||
|
|
||||||
LegacyBrushPanelServices::LegacyBrushPanelServices(NodePanelBrush& panel) noexcept
|
LegacyBrushPanelServices::LegacyBrushPanelServices(NodePanelBrush& panel) noexcept
|
||||||
|
|||||||
@@ -7,6 +7,32 @@
|
|||||||
#include "asset.h"
|
#include "asset.h"
|
||||||
#include "legacy_ui_overlay_services.h"
|
#include "legacy_ui_overlay_services.h"
|
||||||
|
|
||||||
|
Node* NodePanelBrush::clone_instantiate() const
|
||||||
|
{
|
||||||
|
return new NodePanelBrush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodePanelBrush::init()
|
||||||
|
{
|
||||||
|
pp::panopainter::LegacyBrushPanelUi::init(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
kEventResult NodePanelBrush::handle_event(Event* e)
|
||||||
|
{
|
||||||
|
return pp::panopainter::LegacyBrushPanelUi::handle_event(*this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodePanelBrush::handle_click(Node* target)
|
||||||
|
{
|
||||||
|
pp::panopainter::LegacyBrushPanelUi::handle_click(*this, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodePanelBrush::added(Node* parent)
|
||||||
|
{
|
||||||
|
(void)parent;
|
||||||
|
pp::panopainter::LegacyBrushPanelUi::added(*this);
|
||||||
|
}
|
||||||
|
|
||||||
namespace pp::panopainter {
|
namespace pp::panopainter {
|
||||||
|
|
||||||
void LegacyBrushPanelUi::init(NodePanelBrush& owner)
|
void LegacyBrushPanelUi::init(NodePanelBrush& owner)
|
||||||
|
|||||||
@@ -5,12 +5,30 @@
|
|||||||
#include "canvas.h"
|
#include "canvas.h"
|
||||||
#include "legacy_ui_overlay_services.h"
|
#include "legacy_ui_overlay_services.h"
|
||||||
|
|
||||||
namespace pp::panopainter {
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
std::vector<NodePanelBrushPreset*> s_legacy_brush_preset_panels;
|
std::vector<NodePanelBrushPreset*> s_legacy_brush_preset_panels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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::clear_brushes()
|
||||||
|
{
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace pp::panopainter {
|
||||||
|
|
||||||
void register_legacy_brush_preset_panel(NodePanelBrushPreset& panel)
|
void register_legacy_brush_preset_panel(NodePanelBrushPreset& panel)
|
||||||
{
|
{
|
||||||
s_legacy_brush_preset_panels.push_back(&panel);
|
s_legacy_brush_preset_panels.push_back(&panel);
|
||||||
|
|||||||
@@ -6,10 +6,52 @@
|
|||||||
#include "app_core/brush_ui.h"
|
#include "app_core/brush_ui.h"
|
||||||
#include "asset.h"
|
#include "asset.h"
|
||||||
#include "canvas.h"
|
#include "canvas.h"
|
||||||
|
#include "legacy_brush_preset_list_services.h"
|
||||||
#include "legacy_ui_overlay_services.h"
|
#include "legacy_ui_overlay_services.h"
|
||||||
#include "node_message_box.h"
|
#include "node_message_box.h"
|
||||||
#include "node_popup_menu.h"
|
#include "node_popup_menu.h"
|
||||||
|
|
||||||
|
NodePanelBrushPreset::NodePanelBrushPreset()
|
||||||
|
{
|
||||||
|
pp::panopainter::register_legacy_brush_preset_panel(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
NodePanelBrushPreset::~NodePanelBrushPreset()
|
||||||
|
{
|
||||||
|
pp::panopainter::unregister_legacy_brush_preset_panel(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Node* NodePanelBrushPreset::clone_instantiate() const
|
||||||
|
{
|
||||||
|
return new NodePanelBrushPreset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodePanelBrushPreset::init()
|
||||||
|
{
|
||||||
|
pp::panopainter::LegacyBrushPresetPanelUi::init(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
kEventResult NodePanelBrushPreset::handle_event(Event* e)
|
||||||
|
{
|
||||||
|
return pp::panopainter::LegacyBrushPresetPanelUi::handle_event(*this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodePanelBrushPreset::handle_click(Node* target)
|
||||||
|
{
|
||||||
|
pp::panopainter::LegacyBrushPresetPanelUi::handle_click(*this, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodePanelBrushPreset::add_brush(std::shared_ptr<Brush> brush)
|
||||||
|
{
|
||||||
|
pp::panopainter::LegacyBrushPresetPanelUi::add_brush(*this, std::move(brush));
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodePanelBrushPreset::added(Node* parent)
|
||||||
|
{
|
||||||
|
(void)parent;
|
||||||
|
pp::panopainter::LegacyBrushPresetPanelUi::added(*this);
|
||||||
|
}
|
||||||
|
|
||||||
namespace pp::panopainter {
|
namespace pp::panopainter {
|
||||||
|
|
||||||
void LegacyBrushPresetPanelUi::init(NodePanelBrushPreset& owner)
|
void LegacyBrushPresetPanelUi::init(NodePanelBrushPreset& owner)
|
||||||
|
|||||||
@@ -14,13 +14,43 @@
|
|||||||
#include <regex>
|
#include <regex>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
namespace pp::panopainter {
|
pp::panopainter::LegacyBrushPresetServices::LegacyBrushPresetServices(NodePanelBrushPreset& owner) noexcept
|
||||||
|
|
||||||
LegacyBrushPresetServices::LegacyBrushPresetServices(NodePanelBrushPreset& owner) noexcept
|
|
||||||
: owner_(owner)
|
: owner_(owner)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NodePanelBrushPreset::save()
|
||||||
|
{
|
||||||
|
return pp::panopainter::LegacyBrushPresetServices(*this).save();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NodePanelBrushPreset::restore()
|
||||||
|
{
|
||||||
|
return pp::panopainter::LegacyBrushPresetServices(*this).restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NodePanelBrushPreset::export_ppbr(const std::string& path_in, const PPBRInfo& info_data)
|
||||||
|
{
|
||||||
|
return pp::panopainter::LegacyBrushPresetServices(*this).export_ppbr(path_in, info_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NodePanelBrushPreset::import_ppbr(const std::string& path)
|
||||||
|
{
|
||||||
|
return pp::panopainter::LegacyBrushPresetServices(*this).import_ppbr(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NodePanelBrushPreset::import_abr(const std::string& path)
|
||||||
|
{
|
||||||
|
return pp::panopainter::LegacyBrushPresetServices(*this).import_abr(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NodePanelBrushPreset::import_brush(const std::string& path)
|
||||||
|
{
|
||||||
|
return pp::panopainter::LegacyBrushPresetServices(*this).import_brush(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace pp::panopainter {
|
||||||
|
|
||||||
bool LegacyBrushPresetServices::save()
|
bool LegacyBrushPresetServices::save()
|
||||||
{
|
{
|
||||||
auto path = App::I->data_path + "/settings/presets.bin";
|
auto path = App::I->data_path + "/settings/presets.bin";
|
||||||
|
|||||||
@@ -136,6 +136,96 @@ void copy_legacy_node_stroke_preview_destination_texture_region(
|
|||||||
copy_framebuffer_to_texture_2d(src_x, src_y, dst_x, dst_y, width, height);
|
copy_framebuffer_to_texture_2d(src_x, src_y, dst_x, dst_y, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool execute_legacy_node_stroke_preview_immediate_draw(
|
||||||
|
const LegacyNodeStrokePreviewImmediateDrawRequest& request)
|
||||||
|
{
|
||||||
|
if (!request.brush ||
|
||||||
|
!request.prepare_render_target ||
|
||||||
|
!request.finish_render_target ||
|
||||||
|
!request.compute_frames ||
|
||||||
|
!request.draw_samples ||
|
||||||
|
!request.draw_mix ||
|
||||||
|
!request.draw_checkerboard ||
|
||||||
|
!request.draw_composite) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ensure_legacy_node_stroke_preview_render_targets(
|
||||||
|
LegacyNodeStrokePreviewRenderTargetSetup {
|
||||||
|
.preview_rtt = request.preview_rtt,
|
||||||
|
.preview_rtt_mixer = request.preview_rtt_mixer,
|
||||||
|
.preview_stroke_texture = request.preview_stroke_texture,
|
||||||
|
.preview_dual_texture = request.preview_dual_texture,
|
||||||
|
.preview_background_texture = request.preview_background_texture,
|
||||||
|
.preview_image_texture = request.preview_image_texture,
|
||||||
|
.size = request.preview_size,
|
||||||
|
})) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto vp = query_legacy_node_stroke_preview_viewport();
|
||||||
|
const auto cc = query_legacy_node_stroke_preview_clear_color();
|
||||||
|
const glm::vec2 size = {
|
||||||
|
static_cast<float>(request.preview_rtt.getWidth()),
|
||||||
|
static_cast<float>(request.preview_rtt.getHeight()),
|
||||||
|
};
|
||||||
|
const bool sequence_ok = execute_legacy_node_stroke_preview_immediate_runtime(
|
||||||
|
LegacyNodeStrokePreviewImmediateRuntimeRequest {
|
||||||
|
.brush = request.brush,
|
||||||
|
.preview_size = request.preview_size,
|
||||||
|
.zoom = request.zoom,
|
||||||
|
.min_flow = request.min_flow,
|
||||||
|
.stroke_max_size_override = request.stroke_max_size_override,
|
||||||
|
.pad_override = request.pad_override,
|
||||||
|
.camera_fov = request.camera_fov,
|
||||||
|
.camera_rot = request.camera_rot,
|
||||||
|
.render_device_features = request.render_device_features,
|
||||||
|
.preview_rtt = request.preview_rtt,
|
||||||
|
.preview_rtt_mixer = request.preview_rtt_mixer,
|
||||||
|
.preview_stroke_texture = request.preview_stroke_texture,
|
||||||
|
.preview_dual_texture = request.preview_dual_texture,
|
||||||
|
.preview_background_texture = request.preview_background_texture,
|
||||||
|
.preview_image_texture = request.preview_image_texture,
|
||||||
|
.linear_sampler = request.linear_sampler,
|
||||||
|
.repeat_sampler = request.repeat_sampler,
|
||||||
|
.prepare_render_target = request.prepare_render_target,
|
||||||
|
.finish_render_target = request.finish_render_target,
|
||||||
|
.set_blend_enabled = [](bool enabled) {
|
||||||
|
apply_legacy_node_stroke_preview_capability(pp::renderer::gl::blend_state(), enabled);
|
||||||
|
},
|
||||||
|
.setup_stroke_shader = [](const LegacyStrokeShaderSetupUniforms& uniforms) {
|
||||||
|
setup_legacy_stroke_shader(uniforms);
|
||||||
|
},
|
||||||
|
.bind_dual_pass_textures = [](const Brush& dual_brush) {
|
||||||
|
bind_legacy_node_stroke_preview_dual_pass_textures(dual_brush);
|
||||||
|
},
|
||||||
|
.capture_background = [&](bool colorize) {
|
||||||
|
execute_legacy_node_stroke_preview_background_capture_pass(
|
||||||
|
LegacyNodeStrokePreviewBackgroundCaptureRequest {
|
||||||
|
.size = size,
|
||||||
|
.colorize = colorize,
|
||||||
|
.background_texture = request.preview_background_texture,
|
||||||
|
.draw_checkerboard = [&] {
|
||||||
|
request.draw_checkerboard();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
.compute_frames = request.compute_frames,
|
||||||
|
.draw_samples = request.draw_samples,
|
||||||
|
.draw_mix = request.draw_mix,
|
||||||
|
.unbind_mixer_texture = [&] {
|
||||||
|
unbind_legacy_node_stroke_preview_mixer_texture(request.preview_rtt_mixer);
|
||||||
|
},
|
||||||
|
.bind_pattern_texture = [&] {
|
||||||
|
bind_legacy_node_stroke_preview_pattern_texture(*request.brush);
|
||||||
|
},
|
||||||
|
.draw_composite = request.draw_composite,
|
||||||
|
});
|
||||||
|
apply_legacy_node_stroke_preview_viewport(vp.x, vp.y, vp.width, vp.height);
|
||||||
|
apply_legacy_node_stroke_preview_clear_color(cc);
|
||||||
|
return sequence_ok;
|
||||||
|
}
|
||||||
|
|
||||||
void execute_legacy_node_stroke_preview_background_capture_pass(
|
void execute_legacy_node_stroke_preview_background_capture_pass(
|
||||||
const LegacyNodeStrokePreviewBackgroundCaptureRequest& request)
|
const LegacyNodeStrokePreviewBackgroundCaptureRequest& request)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -95,6 +95,36 @@ struct LegacyNodeStrokePreviewImmediateRuntimeRequest {
|
|||||||
std::function<void()> draw_composite;
|
std::function<void()> draw_composite;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct LegacyNodeStrokePreviewImmediateDrawRequest {
|
||||||
|
const std::shared_ptr<Brush>& brush;
|
||||||
|
glm::vec2 preview_size {};
|
||||||
|
float zoom = 1.0f;
|
||||||
|
float min_flow = 0.0f;
|
||||||
|
float stroke_max_size_override = 0.0f;
|
||||||
|
float pad_override = NAN;
|
||||||
|
float camera_fov = 0.0f;
|
||||||
|
glm::mat4 camera_rot { 1.0f };
|
||||||
|
pp::renderer::RenderDeviceFeatures render_device_features {};
|
||||||
|
RTT& preview_rtt;
|
||||||
|
RTT& preview_rtt_mixer;
|
||||||
|
Texture2D& preview_stroke_texture;
|
||||||
|
Texture2D& preview_dual_texture;
|
||||||
|
Texture2D& preview_background_texture;
|
||||||
|
Texture2D& preview_image_texture;
|
||||||
|
Sampler& linear_sampler;
|
||||||
|
Sampler& repeat_sampler;
|
||||||
|
std::function<void()> prepare_render_target;
|
||||||
|
std::function<void()> finish_render_target;
|
||||||
|
std::function<std::vector<LegacyNodeStrokePreviewFrame>(const Stroke&, float)> compute_frames;
|
||||||
|
std::function<glm::vec4(std::array<vertex_t, 4>&, Texture2D&, bool)> draw_samples;
|
||||||
|
std::function<void(const glm::vec2&, const glm::vec2&)> draw_mix;
|
||||||
|
std::function<void()> draw_checkerboard;
|
||||||
|
std::function<void()> draw_composite;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] bool execute_legacy_node_stroke_preview_immediate_draw(
|
||||||
|
const LegacyNodeStrokePreviewImmediateDrawRequest& request);
|
||||||
|
|
||||||
[[nodiscard]] bool execute_legacy_node_stroke_preview_immediate_runtime(
|
[[nodiscard]] bool execute_legacy_node_stroke_preview_immediate_runtime(
|
||||||
const LegacyNodeStrokePreviewImmediateRuntimeRequest& request);
|
const LegacyNodeStrokePreviewImmediateRuntimeRequest& request);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
#include "app.h"
|
||||||
#include "node.h"
|
#include "node.h"
|
||||||
|
|
||||||
void Node::restore_context()
|
void Node::restore_context()
|
||||||
@@ -13,6 +14,25 @@ void Node::clear_context()
|
|||||||
c->clear_context();
|
c->clear_context();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Node::app_redraw()
|
||||||
|
{
|
||||||
|
App::I->redraw = true;
|
||||||
|
App::I->runtime().notify_render_worker();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Node::watch(std::function<bool(Node*)> observer)
|
||||||
|
{
|
||||||
|
bool cont = observer(this);
|
||||||
|
if (cont)
|
||||||
|
{
|
||||||
|
for (auto& c : m_children)
|
||||||
|
{
|
||||||
|
//if (!glm::any(glm::lessThanEqual(zw(c->m_clip), { 0, 0 })))
|
||||||
|
c->watch(observer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Node::update(float width, float height, float zoom)
|
void Node::update(float width, float height, float zoom)
|
||||||
{
|
{
|
||||||
m_zoom = zoom;
|
m_zoom = zoom;
|
||||||
|
|||||||
19
src/node.cpp
19
src/node.cpp
@@ -10,25 +10,6 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "asset.h"
|
#include "asset.h"
|
||||||
|
|
||||||
void Node::app_redraw()
|
|
||||||
{
|
|
||||||
App::I->redraw = true;
|
|
||||||
App::I->runtime().notify_render_worker();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Node::watch(std::function<bool(Node*)> observer)
|
|
||||||
{
|
|
||||||
bool cont = observer(this);
|
|
||||||
if (cont)
|
|
||||||
{
|
|
||||||
for (auto& c : m_children)
|
|
||||||
{
|
|
||||||
//if (!glm::any(glm::lessThanEqual(zw(c->m_clip), { 0, 0 })))
|
|
||||||
c->watch(observer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
kEventResult Node::on_event(Event* e)
|
kEventResult Node::on_event(Event* e)
|
||||||
{
|
{
|
||||||
return pp::panopainter::handle_legacy_ui_node_event(*this, e);
|
return pp::panopainter::handle_legacy_ui_node_event(*this, e);
|
||||||
|
|||||||
@@ -1,223 +1,2 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include "log.h"
|
|
||||||
#include "node_panel_brush.h"
|
#include "node_panel_brush.h"
|
||||||
#include "legacy_brush_panel_item_ui.h"
|
|
||||||
#include "legacy_brush_panel_ui.h"
|
|
||||||
#include "legacy_brush_preset_panel_ui.h"
|
|
||||||
#include "legacy_brush_preset_list_services.h"
|
|
||||||
#include "legacy_brush_panel_services.h"
|
|
||||||
#include "app_core/brush_ui.h"
|
|
||||||
#include "legacy_brush_ui_services.h"
|
|
||||||
#include "legacy_brush_preset_services.h"
|
|
||||||
|
|
||||||
Node* NodeButtonBrush::clone_instantiate() const
|
|
||||||
{
|
|
||||||
return pp::panopainter::clone_legacy_brush_button(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeButtonBrush::init()
|
|
||||||
{
|
|
||||||
pp::panopainter::init_legacy_brush_button(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeButtonBrush::set_icon(const char* path)
|
|
||||||
{
|
|
||||||
pp::panopainter::set_legacy_brush_button_icon(*this, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeButtonBrush::draw()
|
|
||||||
{
|
|
||||||
pp::panopainter::draw_legacy_brush_button(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NodeButtonBrush::read(BinaryStreamReader& r)
|
|
||||||
{
|
|
||||||
return pp::panopainter::read_legacy_brush_button(*this, r);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeButtonBrush::write(BinaryStreamWriter& w) const
|
|
||||||
{
|
|
||||||
pp::panopainter::write_legacy_brush_button(*this, w);
|
|
||||||
}
|
|
||||||
|
|
||||||
Node* NodePanelBrush::clone_instantiate() const
|
|
||||||
{
|
|
||||||
return new NodePanelBrush();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodePanelBrush::execute_texture_list_plan(const pp::app::BrushTextureListPlan& plan)
|
|
||||||
{
|
|
||||||
const auto status = pp::panopainter::execute_legacy_brush_texture_list_plan(*this, plan);
|
|
||||||
if (!status.ok()) {
|
|
||||||
LOG("Brush texture list action failed: %s", status.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodePanelBrush::init()
|
|
||||||
{
|
|
||||||
pp::panopainter::LegacyBrushPanelUi::init(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
kEventResult NodePanelBrush::handle_event(Event* e)
|
|
||||||
{
|
|
||||||
return pp::panopainter::LegacyBrushPanelUi::handle_event(*this, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodePanelBrush::handle_click(Node* target)
|
|
||||||
{
|
|
||||||
pp::panopainter::LegacyBrushPanelUi::handle_click(*this, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
int NodePanelBrush::find_brush(const std::string & name) const
|
|
||||||
{
|
|
||||||
return pp::panopainter::LegacyBrushPanelServices(const_cast<NodePanelBrush&>(*this)).find_brush(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string NodePanelBrush::get_texture_path(int index) const
|
|
||||||
{
|
|
||||||
return pp::panopainter::LegacyBrushPanelServices(const_cast<NodePanelBrush&>(*this)).get_texture_path(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string NodePanelBrush::get_thumb_path(int index) const
|
|
||||||
{
|
|
||||||
return pp::panopainter::LegacyBrushPanelServices(const_cast<NodePanelBrush&>(*this)).get_thumb_path(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NodePanelBrush::save()
|
|
||||||
{
|
|
||||||
return pp::panopainter::LegacyBrushPanelServices(*this).save();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NodePanelBrush::restore()
|
|
||||||
{
|
|
||||||
return pp::panopainter::LegacyBrushPanelServices(*this).restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodePanelBrush::clear()
|
|
||||||
{
|
|
||||||
pp::panopainter::LegacyBrushPanelServices(*this).clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodePanelBrush::scan()
|
|
||||||
{
|
|
||||||
pp::panopainter::LegacyBrushPanelServices(*this).scan();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodePanelBrush::reload()
|
|
||||||
{
|
|
||||||
pp::panopainter::LegacyBrushPanelServices(*this).reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodePanelBrush::added(Node* parent)
|
|
||||||
{
|
|
||||||
(void)parent;
|
|
||||||
pp::panopainter::LegacyBrushPanelUi::added(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
|
||||||
|
|
||||||
Node* NodeBrushPresetItem::clone_instantiate() const
|
|
||||||
{
|
|
||||||
return pp::panopainter::clone_legacy_brush_preset_item(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeBrushPresetItem::init()
|
|
||||||
{
|
|
||||||
pp::panopainter::init_legacy_brush_preset_item(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeBrushPresetItem::draw()
|
|
||||||
{
|
|
||||||
pp::panopainter::draw_legacy_brush_preset_item(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
NodePanelBrushPreset::NodePanelBrushPreset()
|
|
||||||
{
|
|
||||||
pp::panopainter::register_legacy_brush_preset_panel(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
NodePanelBrushPreset::~NodePanelBrushPreset()
|
|
||||||
{
|
|
||||||
pp::panopainter::unregister_legacy_brush_preset_panel(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
//---
|
|
||||||
|
|
||||||
Node* NodePanelBrushPreset::clone_instantiate() const
|
|
||||||
{
|
|
||||||
return new NodePanelBrushPreset();
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
|
||||||
{
|
|
||||||
pp::panopainter::LegacyBrushPresetPanelUi::init(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
kEventResult NodePanelBrushPreset::handle_event(Event* e)
|
|
||||||
{
|
|
||||||
return pp::panopainter::LegacyBrushPresetPanelUi::handle_event(*this, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodePanelBrushPreset::handle_click(Node* target)
|
|
||||||
{
|
|
||||||
pp::panopainter::LegacyBrushPresetPanelUi::handle_click(*this, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NodePanelBrushPreset::save()
|
|
||||||
{
|
|
||||||
return pp::panopainter::LegacyBrushPresetServices(*this).save();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NodePanelBrushPreset::restore()
|
|
||||||
{
|
|
||||||
return pp::panopainter::LegacyBrushPresetServices(*this).restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodePanelBrushPreset::add_brush(std::shared_ptr<Brush> brush)
|
|
||||||
{
|
|
||||||
pp::panopainter::LegacyBrushPresetPanelUi::add_brush(*this, std::move(brush));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NodePanelBrushPreset::export_ppbr(const std::string& path_in, const PPBRInfo& info_data)
|
|
||||||
{
|
|
||||||
return pp::panopainter::LegacyBrushPresetServices(*this).export_ppbr(path_in, info_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NodePanelBrushPreset::import_ppbr(const std::string& path)
|
|
||||||
{
|
|
||||||
return pp::panopainter::LegacyBrushPresetServices(*this).import_ppbr(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NodePanelBrushPreset::import_abr(const std::string& path)
|
|
||||||
{
|
|
||||||
return pp::panopainter::LegacyBrushPresetServices(*this).import_abr(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NodePanelBrushPreset::import_brush(const std::string& path)
|
|
||||||
{
|
|
||||||
return pp::panopainter::LegacyBrushPresetServices(*this).import_brush(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodePanelBrushPreset::clear_brushes()
|
|
||||||
{
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodePanelBrushPreset::added(Node* parent)
|
|
||||||
{
|
|
||||||
(void)parent;
|
|
||||||
pp::panopainter::LegacyBrushPresetPanelUi::added(*this);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -109,29 +109,11 @@ void NodeStrokePreview::draw_stroke_immediate()
|
|||||||
{
|
{
|
||||||
if (m_size.x == 0 || m_size.y == 0)
|
if (m_size.x == 0 || m_size.y == 0)
|
||||||
return;
|
return;
|
||||||
|
const bool sequence_ok = pp::panopainter::execute_legacy_node_stroke_preview_immediate_draw(
|
||||||
if (!pp::panopainter::ensure_legacy_node_stroke_preview_render_targets(
|
pp::panopainter::LegacyNodeStrokePreviewImmediateDrawRequest {
|
||||||
pp::panopainter::LegacyNodeStrokePreviewRenderTargetSetup {
|
|
||||||
.preview_rtt = m_rtt,
|
|
||||||
.preview_rtt_mixer = m_rtt_mixer,
|
|
||||||
.preview_stroke_texture = m_tex,
|
|
||||||
.preview_dual_texture = m_tex_dual,
|
|
||||||
.preview_background_texture = m_tex_background,
|
|
||||||
.preview_image_texture = m_tex_preview,
|
|
||||||
.size = m_preview_size,
|
|
||||||
})) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto vp = pp::panopainter::query_legacy_node_stroke_preview_viewport();
|
|
||||||
const auto cc = pp::panopainter::query_legacy_node_stroke_preview_clear_color();
|
|
||||||
const glm::vec2 size = { m_rtt.getWidth(), m_rtt.getHeight() };
|
|
||||||
const float zoom = root()->m_zoom;
|
|
||||||
const bool sequence_ok = pp::panopainter::execute_legacy_node_stroke_preview_immediate_runtime(
|
|
||||||
pp::panopainter::LegacyNodeStrokePreviewImmediateRuntimeRequest {
|
|
||||||
.brush = m_brush,
|
.brush = m_brush,
|
||||||
.preview_size = m_size,
|
.preview_size = m_size,
|
||||||
.zoom = zoom,
|
.zoom = root()->m_zoom,
|
||||||
.min_flow = m_min_flow,
|
.min_flow = m_min_flow,
|
||||||
.stroke_max_size_override = m_max_size,
|
.stroke_max_size_override = m_max_size,
|
||||||
.pad_override = m_pad_override,
|
.pad_override = m_pad_override,
|
||||||
@@ -158,26 +140,6 @@ void NodeStrokePreview::draw_stroke_immediate()
|
|||||||
.finish_render_target = [&] {
|
.finish_render_target = [&] {
|
||||||
m_rtt.unbindFramebuffer();
|
m_rtt.unbindFramebuffer();
|
||||||
},
|
},
|
||||||
.set_blend_enabled = [&](bool enabled) {
|
|
||||||
pp::panopainter::apply_legacy_node_stroke_preview_capability(pp::renderer::gl::blend_state(), enabled);
|
|
||||||
},
|
|
||||||
.setup_stroke_shader = [](const pp::panopainter::LegacyStrokeShaderSetupUniforms& uniforms) {
|
|
||||||
pp::panopainter::setup_legacy_stroke_shader(uniforms);
|
|
||||||
},
|
|
||||||
.bind_dual_pass_textures = [](const Brush& dual_brush) {
|
|
||||||
pp::panopainter::bind_legacy_node_stroke_preview_dual_pass_textures(dual_brush);
|
|
||||||
},
|
|
||||||
.capture_background = [&](bool colorize) {
|
|
||||||
pp::panopainter::execute_legacy_node_stroke_preview_background_capture_pass(
|
|
||||||
pp::panopainter::LegacyNodeStrokePreviewBackgroundCaptureRequest {
|
|
||||||
.size = size,
|
|
||||||
.colorize = colorize,
|
|
||||||
.background_texture = m_tex_background,
|
|
||||||
.draw_checkerboard = [&] {
|
|
||||||
m_plane.draw_fill();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
.compute_frames = [&](const Stroke& stroke, float frame_zoom) {
|
.compute_frames = [&](const Stroke& stroke, float frame_zoom) {
|
||||||
return stroke_draw_compute(stroke, frame_zoom);
|
return stroke_draw_compute(stroke, frame_zoom);
|
||||||
},
|
},
|
||||||
@@ -187,18 +149,12 @@ void NodeStrokePreview::draw_stroke_immediate()
|
|||||||
.draw_mix = [&](const glm::vec2& bb_min, const glm::vec2& bb_sz) {
|
.draw_mix = [&](const glm::vec2& bb_min, const glm::vec2& bb_sz) {
|
||||||
stroke_draw_mix(bb_min, bb_sz);
|
stroke_draw_mix(bb_min, bb_sz);
|
||||||
},
|
},
|
||||||
.unbind_mixer_texture = [&] {
|
.draw_checkerboard = [&] {
|
||||||
pp::panopainter::unbind_legacy_node_stroke_preview_mixer_texture(m_rtt_mixer);
|
m_plane.draw_fill();
|
||||||
},
|
|
||||||
.bind_pattern_texture = [&] {
|
|
||||||
pp::panopainter::bind_legacy_node_stroke_preview_pattern_texture(*m_brush);
|
|
||||||
},
|
},
|
||||||
.draw_composite = [&] {
|
.draw_composite = [&] {
|
||||||
m_plane.draw_fill();
|
m_plane.draw_fill();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
assert(sequence_ok);
|
assert(sequence_ok);
|
||||||
|
|
||||||
pp::panopainter::apply_legacy_node_stroke_preview_viewport(vp.x, vp.y, vp.width, vp.height);
|
|
||||||
pp::panopainter::apply_legacy_node_stroke_preview_clear_color(cc);
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user