Centralize quick and grid UI bridges
This commit is contained in:
@@ -90,6 +90,10 @@ set(PP_PANOPAINTER_UI_SOURCES
|
||||
src/legacy_brush_ui_services.h
|
||||
src/legacy_document_animation_services.cpp
|
||||
src/legacy_document_animation_services.h
|
||||
src/legacy_grid_ui_services.cpp
|
||||
src/legacy_grid_ui_services.h
|
||||
src/legacy_quick_ui_services.cpp
|
||||
src/legacy_quick_ui_services.h
|
||||
src/node_about.cpp
|
||||
src/node_canvas.cpp
|
||||
src/node_changelog.cpp
|
||||
|
||||
@@ -185,6 +185,16 @@ Known local toolchain state:
|
||||
legacy `Brush`, `Canvas::I`, image load/save, `NodePanelBrush`,
|
||||
`NodePanelStroke`, quick/color refreshes, and the temporary
|
||||
`NodePanelBrush` friend adapter remain tracked by `DEBT-0023`.
|
||||
- `src/legacy_grid_ui_services.*` is the current UI-shell bridge for grid
|
||||
heightmap picker/load/reload/clear, lightmap render, and heightmap commit
|
||||
execution. It keeps those live paths on the `pp_app_core` contracts while
|
||||
legacy image loading, OpenGL texture updates, nanort baking/progress, and
|
||||
`Canvas::draw_objects` execution remain tracked by `DEBT-0024`.
|
||||
- `src/legacy_quick_ui_services.*` is the current UI-shell bridge for quick
|
||||
brush/color slot selection, popup routing, mini-state restore, and mini-state
|
||||
reset execution. It keeps those live paths on the `pp_app_core` contracts
|
||||
while legacy quick widgets, brush previews, color picker state, and preset
|
||||
popup execution remain tracked by `DEBT-0025`.
|
||||
- `pano_cli simulate-image-import` decodes an embedded tiny PNG through
|
||||
`pp_assets`, attaches it to `pp_document`, and is covered by
|
||||
`pano_cli_simulate_image_import_smoke`.
|
||||
|
||||
@@ -41,8 +41,8 @@ agent or engineer to remove them without reconstructing context from chat.
|
||||
| DEBT-0021 | Open | Modernization | Layer rename planning/execution dispatch and layer panel operation planning/execution dispatch now consume pure `pp_app_core` through `App::dialog_layer_rename`, `App::init_sidebar` layer callbacks, `pano_cli plan-layer-rename`, `pano_cli plan-layer-operation`, `DocumentLayerRenameServices`, and `DocumentLayerOperationServices`, and the live execution adapters are centralized in `src/legacy_document_layer_services.*`, but that shared bridge still mutates legacy `Canvas` layer state, `NodeLayer`/`NodePanelLayer`, and `ActionManager` undo entries | Preserve existing UI/canvas behavior while document layer commands and undo history are extracted incrementally | `pp_app_core_document_layer_tests`; `pano_cli plan-layer-rename --old-name Base --new-name Paint`; `pano_cli plan-layer-operation --kind add --layer-count 2 --index 1 --name Paint`; `ctest --preset desktop-fast --build-config Debug` | Layer command execution is owned by the document/app command boundary with legacy `Canvas`/UI nodes acting only as adapters or removed entirely |
|
||||
| DEBT-0022 | Open | Modernization | Animation panel frame command planning, panel action planning, panel-control/timeline execution dispatch, selected-frame click dispatch, playback tick stepping, and play-mode toggles now consume pure `pp_app_core` through `NodePanelAnimation`, `pano_cli plan-animation-operation`, `pano_cli plan-animation-panel-action`, and `DocumentAnimationServices`; live execution is centralized in `src/legacy_document_animation_services.*`, but that bridge still mutates or reads legacy `Canvas`/`Layer` frame state, canvas mode, animation-panel timeline/playback fields, and uses a temporary `NodePanelAnimation` friend adapter | Preserve existing animation panel behavior while timeline/frame commands move toward the document/app command boundary | `pp_app_core_document_animation_tests`; `pano_cli plan-animation-operation --kind add --frame-count 2 --current-frame 0`; `pano_cli plan-animation-operation --kind select --frame-count 3 --selected-frame 1 --layer-index 2 --layer-id 42`; `pano_cli plan-animation-operation --kind playback --total-duration 5 --current-frame 4 --offset 1`; `pano_cli plan-animation-operation --kind toggle-playback --playing`; `pano_cli plan-animation-panel-action --action next --total-duration 5 --current-frame 4`; `ctest --preset desktop-fast --build-config Debug` | Animation frame/timeline/playback execution is owned by injected document/app timeline services with no legacy `Canvas`/`Layer`/canvas-mode adapter and UI nodes acting only as adapters or removed entirely |
|
||||
| DEBT-0023 | Open | Modernization | Brush/color/preset/stroke-settings UI planning, texture-list add/remove/reorder planning, stroke-panel slider/toggle/blend/reset planning, and execution dispatch now consume pure `pp_app_core` through `App::init_sidebar`, `NodePanelBrush`, `NodePanelStroke`, restored/docked floating-panel callbacks, `pano_cli plan-brush-operation`, `pano_cli plan-brush-texture-list`, `pano_cli plan-brush-stroke-control`, `BrushUiServices`, `BrushTextureListServices`, and `BrushStrokeControlServices`, and live execution is centralized in `src/legacy_brush_ui_services.*`, but the bridge still mutates legacy `Brush`/`Canvas::I`, loads/saves legacy brush texture images, refreshes legacy quick/stroke/color widgets, and uses a temporary `NodePanelBrush` friend adapter to reach private list state | Preserve existing brush UI behavior while brush commands move toward a brush/app/asset command boundary and asset-managed texture selection | `pp_app_core_brush_ui_tests`; `pano_cli plan-brush-operation --kind color --r 0.25 --g 0.5 --b 0.75 --a 1`; `pano_cli plan-brush-operation --kind pattern --path data/patterns/noise.png --thumb data/patterns/thumbs/noise.png`; `pano_cli plan-brush-texture-list --kind add --dir brushes --data-path data --source C:/Temp/soft.png`; `pano_cli plan-brush-stroke-control --kind float --setting tip-size --value 42.5`; `pano_cli plan-brush-stroke-control --kind blend --setting pattern --blend-mode 3`; `ctest --preset desktop-fast --build-config Debug` | Brush color/texture/preset/stroke-settings, texture-list, and stroke-control execution are owned by injected brush/app/asset/UI services with no legacy brush/canvas adapter or `NodePanelBrush` friend access |
|
||||
| DEBT-0024 | Open | Modernization | Grid/heightmap/lightmap UI planning now consumes pure `pp_app_core` through `NodePanelGrid` and `pano_cli plan-grid-operation`, but live execution still performs legacy image loading, OpenGL texture updates, nanort lightmap baking, progress UI, and `Canvas::draw_objects` commit directly | Preserve grid/lightmap behavior while moving renderable grid commands toward app/renderer/document boundaries | `pp_app_core_grid_ui_tests`; `pano_cli plan-grid-operation --kind render --float32 --texture-resolution 1024 --samples 32`; `ctest --preset desktop-fast --build-config Debug` | Grid heightmap/lightmap execution is owned by app/renderer/document services with `NodePanelGrid` acting only as UI adapter |
|
||||
| DEBT-0025 | Open | Modernization | Quick brush/color slot and mini-state planning and execution dispatch now consume pure `pp_app_core` through `NodePanelQuick`, `pano_cli plan-quick-operation`, and the `QuickUiServices` boundary, but the live adapter still mutates legacy quick UI widgets, `Brush` previews, color picker popup state, and preset popup state | Preserve quick-panel behavior while quick brush/color commands move toward a brush/app command boundary with safer automation coverage | `pp_app_core_quick_ui_tests`; `pano_cli plan-quick-operation --kind brush --current-index 0 --slot-index 2`; `pano_cli plan-quick-operation --kind restore --brush-index 2 --color-index 1 --fire-event`; `ctest --preset desktop-fast --build-config Debug` | Quick-panel selection, popup, restore, reset, brush preview, and color execution are owned by injected app/brush/UI services with no legacy quick-panel adapter |
|
||||
| DEBT-0024 | Open | Modernization | Grid/heightmap/lightmap UI planning and execution dispatch now consume pure `pp_app_core` through `NodePanelGrid`, `pano_cli plan-grid-operation`, and the `GridUiServices` boundary; live execution is centralized in `src/legacy_grid_ui_services.*`, but the bridge still performs legacy image loading, OpenGL texture updates, nanort lightmap baking/progress, and `Canvas::draw_objects` commit execution | Preserve grid/lightmap behavior while moving renderable grid commands toward app/renderer/document boundaries | `pp_app_core_grid_ui_tests`; `pano_cli plan-grid-operation --kind render --float32 --texture-resolution 1024 --samples 32`; `ctest --preset desktop-fast --build-config Debug` | Grid heightmap/lightmap execution is owned by app/renderer/document services with `NodePanelGrid` acting only as UI adapter |
|
||||
| DEBT-0025 | Open | Modernization | Quick brush/color slot and mini-state planning and execution dispatch now consume pure `pp_app_core` through `NodePanelQuick`, `pano_cli plan-quick-operation`, and the `QuickUiServices` boundary; live execution is centralized in `src/legacy_quick_ui_services.*`, but the bridge still mutates legacy quick UI widgets, `Brush` previews, color picker popup state, and preset popup state | Preserve quick-panel behavior while quick brush/color commands move toward a brush/app command boundary with safer automation coverage | `pp_app_core_quick_ui_tests`; `pano_cli plan-quick-operation --kind brush --current-index 0 --slot-index 2`; `pano_cli plan-quick-operation --kind restore --brush-index 2 --color-index 1 --fire-event`; `ctest --preset desktop-fast --build-config Debug` | Quick-panel selection, popup, restore, reset, brush preview, and color execution are owned by injected app/brush/UI services with no legacy quick-panel adapter |
|
||||
| DEBT-0026 | Open | Modernization | Toolbar history command planning and canvas hotkey history dispatch now consume pure `pp_app_core` through `App::init_toolbar_main`, `NodeCanvas`, `pano_cli plan-history-operation`, and the `HistoryUiServices` boundary, and both live callers share `src/legacy_history_services.*` for saturated legacy history metrics and execution, but the shared live bridge still mutates legacy `ActionManager` stacks directly | Preserve undo/redo/clear behavior while moving action history toward document/app command services | `pp_app_core_history_ui_tests`; `pano_cli plan-history-operation --kind undo --undo-count 2`; `pano_cli plan-history-operation --kind clear --undo-count 2 --redo-count 1 --memory-bytes 4096`; `ctest --preset desktop-fast --build-config Debug` | Undo/redo/clear execution is owned by injected document/app history services with no legacy `ActionManager` adapter |
|
||||
| DEBT-0027 | Open | Modernization | Canvas draw-tool toolbar command, canvas input mode switching, active-state planning/execution dispatch, and canvas keyboard/touch command planning now consume pure `pp_app_core` through `App::init_toolbar_draw`, `App::update`, `NodeCanvas`, `pano_cli plan-canvas-tool`, `pano_cli plan-canvas-tool-state`, `pano_cli plan-canvas-hotkey`, `CanvasToolServices`, and `CanvasHotkeyServices`, and live toolbar/input/hotkey execution is centralized in `src/legacy_canvas_tool_services.*`, but the bridge still mutates or reads legacy `Canvas` mode state, pen picking state, touch-lock state, transform copy/cut action objects, `ActionManager`, legacy save UI, legacy stroke size controls, and cursor/UI singletons | Preserve current toolbar, stylus eraser, keyboard, and touch command behavior while canvas input/tools move toward an app/document command boundary | `pp_app_core_canvas_tool_ui_tests`; `pp_app_core_canvas_hotkey_tests`; `pano_cli plan-canvas-tool --kind copy`; `pano_cli plan-canvas-tool-state --mode draw --picking --touch-lock`; `pano_cli plan-canvas-hotkey --event key-up --key z --ctrl --undo-count 2`; `pano_cli plan-canvas-hotkey --event key-up --key s --ctrl --shift`; `ctest --preset desktop-fast --build-config Debug` | Canvas tool selection, toolbar state refresh, picking, touch lock, stylus eraser/key mode switching, hotkey/touch command dispatch, save hotkeys, history hotkeys, brush-size hotkeys, and transform action execution are owned by injected app/document/canvas services with no legacy toolbar/canvas adapter |
|
||||
| DEBT-0028 | Open | Modernization | Canvas clear command planning and execution dispatch now consume pure `pp_app_core` through `App::init_toolbar_main`, Layer menu clear, `pano_cli plan-canvas-clear`, and the `DocumentCanvasClearServices` boundary, and toolbar/Layer-menu clear share `src/legacy_document_canvas_services.*`, but the shared live bridge still calls legacy `Canvas::clear`, which records `ActionLayerClear`, clears the current layer/frame, and marks legacy `Canvas::I` unsaved | Preserve clear-current-layer behavior while canvas/document commands move toward document/app command services | `pp_app_core_document_canvas_tests`; `pano_cli plan-canvas-clear --r 0 --g 0.1 --b 0.2 --a 0.3`; `pano_cli plan-canvas-clear --no-canvas`; `pano_cli plan-layer-menu --command clear --current-index 1 --current-name Paint`; `ctest --preset desktop-fast --build-config Debug` | Canvas clear execution, undo recording, dirty-state updates, and clear color handling are owned by injected document/app services with no legacy canvas-clear adapter |
|
||||
|
||||
@@ -570,8 +570,10 @@ dispatch through `DocumentExportMenuServices` in the shared app-shell bridge
|
||||
before legacy export dialogs and renderer/video execution continue.
|
||||
`pano_cli plan-grid-operation` exposes app-core planning for grid heightmap
|
||||
pick/load/reload/clear, lightmap render capability/limit checks, and heightmap
|
||||
commit used by the live grid panel before legacy image loading, OpenGL texture
|
||||
updates, nanort lightmap baking, and `Canvas::draw_objects` execution continue.
|
||||
commit used by the live grid panel. Grid execution now dispatches through
|
||||
`GridUiServices` in `src/legacy_grid_ui_services.*` before legacy image loading,
|
||||
OpenGL texture updates, nanort lightmap baking, and `Canvas::draw_objects`
|
||||
execution continue.
|
||||
`pano_cli plan-history-operation` exposes app-core planning for undo, redo, and
|
||||
clear-history availability used by toolbar buttons and canvas shortcuts; live
|
||||
toolbar and canvas-hotkey execution now dispatch through a shared app-shell
|
||||
@@ -588,8 +590,8 @@ history/canvas adapters, and settings UI execution continue.
|
||||
`pano_cli plan-quick-operation` exposes app-core planning for quick brush/color
|
||||
slot selection versus popup opening, plus quick mini-state restore/reset
|
||||
validation used by the live quick panel. Quick-panel execution now dispatches
|
||||
through `QuickUiServices` before the legacy `Brush`, color picker, stroke
|
||||
preview, and preset popup adapter continues.
|
||||
through `QuickUiServices` in `src/legacy_quick_ui_services.*` before the legacy
|
||||
`Brush`, color picker, stroke preview, and preset popup adapter continues.
|
||||
`pano_cli plan-tools-menu` and `pano_cli plan-tools-panel` expose app-core
|
||||
planning for top-level Tools commands and floating-panel requests, including
|
||||
already-visible no-ops, panel chrome metadata, shortcuts, camera reset,
|
||||
|
||||
@@ -35,6 +35,17 @@ struct GridUiPlan {
|
||||
bool mutates_grid_state = false;
|
||||
};
|
||||
|
||||
class GridUiServices {
|
||||
public:
|
||||
virtual ~GridUiServices() = default;
|
||||
|
||||
virtual void request_heightmap_pick() = 0;
|
||||
virtual pp::foundation::Status load_heightmap(std::string_view path, bool raise_ground_opacity) = 0;
|
||||
virtual void clear_heightmap(bool updates_preview) = 0;
|
||||
virtual void render_lightmap(bool shows_unsupported_message, bool renders_lightmap) = 0;
|
||||
virtual void commit_heightmap(bool updates_ground_opacity) = 0;
|
||||
};
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Status validate_grid_texture_resolution(int texture_resolution) noexcept
|
||||
{
|
||||
if (texture_resolution <= 0 || texture_resolution > 16384) {
|
||||
@@ -142,4 +153,57 @@ struct GridUiPlan {
|
||||
return plan;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline pp::foundation::Status execute_grid_ui_plan(
|
||||
const GridUiPlan& plan,
|
||||
GridUiServices& services)
|
||||
{
|
||||
switch (plan.operation) {
|
||||
case GridUiOperation::request_heightmap_pick:
|
||||
if (!plan.opens_picker) {
|
||||
return pp::foundation::Status::invalid_argument("grid heightmap pick plan must open a picker");
|
||||
}
|
||||
services.request_heightmap_pick();
|
||||
return pp::foundation::Status::success();
|
||||
|
||||
case GridUiOperation::load_heightmap:
|
||||
case GridUiOperation::reload_heightmap:
|
||||
if (!plan.loads_heightmap || plan.path.empty()) {
|
||||
return pp::foundation::Status::invalid_argument("grid heightmap load plan must provide a path");
|
||||
}
|
||||
return services.load_heightmap(plan.path, plan.updates_ground_opacity);
|
||||
|
||||
case GridUiOperation::clear_heightmap:
|
||||
if (!plan.clears_heightmap) {
|
||||
return pp::foundation::Status::invalid_argument("grid heightmap clear plan must clear heightmap state");
|
||||
}
|
||||
services.clear_heightmap(plan.updates_preview);
|
||||
return pp::foundation::Status::success();
|
||||
|
||||
case GridUiOperation::render_lightmap:
|
||||
{
|
||||
const auto texture_status = validate_grid_texture_resolution(plan.texture_resolution);
|
||||
if (!texture_status.ok()) {
|
||||
return texture_status;
|
||||
}
|
||||
const auto sample_status = validate_grid_lightmap_samples(plan.sample_count);
|
||||
if (!sample_status.ok()) {
|
||||
return sample_status;
|
||||
}
|
||||
if (!plan.shows_unsupported_message && !plan.renders_lightmap) {
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
services.render_lightmap(plan.shows_unsupported_message, plan.renders_lightmap);
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
case GridUiOperation::commit_heightmap:
|
||||
if (plan.commits_heightmap) {
|
||||
services.commit_heightmap(plan.updates_ground_opacity);
|
||||
}
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
return pp::foundation::Status::invalid_argument("unknown grid UI operation");
|
||||
}
|
||||
|
||||
} // namespace pp::app
|
||||
|
||||
101
src/legacy_grid_ui_services.cpp
Normal file
101
src/legacy_grid_ui_services.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include "legacy_grid_ui_services.h"
|
||||
|
||||
#include "app.h"
|
||||
#include "canvas.h"
|
||||
#include "image.h"
|
||||
#include "node_panel_grid.h"
|
||||
|
||||
namespace pp::panopainter {
|
||||
namespace {
|
||||
|
||||
class LegacyGridUiServices final : public pp::app::GridUiServices {
|
||||
public:
|
||||
explicit LegacyGridUiServices(NodePanelGrid& panel) noexcept
|
||||
: panel_(panel)
|
||||
{
|
||||
}
|
||||
|
||||
void request_heightmap_pick() override
|
||||
{
|
||||
auto* panel = &panel_;
|
||||
App::I->pick_image([panel](std::string path) {
|
||||
panel->load_heightmap_file(path, true);
|
||||
});
|
||||
}
|
||||
|
||||
pp::foundation::Status load_heightmap(std::string_view path, bool raise_ground_opacity) override
|
||||
{
|
||||
Image img;
|
||||
if (!img.load_file(std::string(path)))
|
||||
return pp::foundation::Status::invalid_argument("heightmap image could not be loaded");
|
||||
|
||||
panel_.m_file_path = std::string(path);
|
||||
panel_.m_hm_image = img.resize(128, 128);
|
||||
panel_.m_hm_preview->tex = std::make_shared<Texture2D>();
|
||||
panel_.m_hm_preview->tex->create(panel_.m_hm_image);
|
||||
panel_.m_hm_preview->tex->create_mipmaps();
|
||||
auto sz = panel_.m_hm_preview->tex->size();
|
||||
panel_.m_hm_preview->SetAspectRatio(sz.x / sz.y);
|
||||
panel_.m_hm_plane.create(1, 1, panel_.m_hm_image, panel_.get_resolution(), panel_.get_height());
|
||||
panel_.m_hm_preview->SetHeight(100);
|
||||
if (raise_ground_opacity && panel_.m_groud_opacity->get_value() == 0.f)
|
||||
panel_.m_groud_opacity->set_value(1.f);
|
||||
panel_.m_rt_dirty = true;
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
void clear_heightmap(bool updates_preview) override
|
||||
{
|
||||
panel_.m_hm_plane.create(1, 1, 100 * panel_.get_resolution());
|
||||
panel_.m_hm_image.destroy();
|
||||
panel_.m_hm_preview->tex.reset();
|
||||
panel_.m_hm_preview->SetHeight(0);
|
||||
}
|
||||
|
||||
void render_lightmap(bool shows_unsupported_message, bool renders_lightmap) override
|
||||
{
|
||||
if (shows_unsupported_message)
|
||||
{
|
||||
App::I->message_box("Rendering failed",
|
||||
"Your hardware does not support lightmap rendering.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!renders_lightmap)
|
||||
return;
|
||||
|
||||
auto* panel = &panel_;
|
||||
std::thread([panel] {
|
||||
BT_SetTerminate();
|
||||
panel->bake_uvs();
|
||||
panel->m_hm_shading->set_index(3);
|
||||
panel->m_shade_mode = NodePanelGrid::ShadeMode::Textured;
|
||||
}).detach();
|
||||
}
|
||||
|
||||
void commit_heightmap(bool updates_ground_opacity) override
|
||||
{
|
||||
Canvas::I->draw_objects([this](const glm::mat4& camera, const glm::mat4& proj, int i) {
|
||||
panel_.draw_heightmap(proj, camera, true);
|
||||
}, Canvas::I->layer().m_frame_index, true);
|
||||
if (updates_ground_opacity)
|
||||
panel_.m_groud_opacity->set_value(0);
|
||||
}
|
||||
|
||||
private:
|
||||
NodePanelGrid& panel_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
pp::foundation::Status execute_legacy_grid_ui_plan(
|
||||
NodePanelGrid& panel,
|
||||
const pp::app::GridUiPlan& plan)
|
||||
{
|
||||
LegacyGridUiServices services(panel);
|
||||
return pp::app::execute_grid_ui_plan(plan, services);
|
||||
}
|
||||
|
||||
} // namespace pp::panopainter
|
||||
14
src/legacy_grid_ui_services.h
Normal file
14
src/legacy_grid_ui_services.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "app_core/grid_ui.h"
|
||||
#include "foundation/result.h"
|
||||
|
||||
class NodePanelGrid;
|
||||
|
||||
namespace pp::panopainter {
|
||||
|
||||
[[nodiscard]] pp::foundation::Status execute_legacy_grid_ui_plan(
|
||||
NodePanelGrid& panel,
|
||||
const pp::app::GridUiPlan& plan);
|
||||
|
||||
} // namespace pp::panopainter
|
||||
207
src/legacy_quick_ui_services.cpp
Normal file
207
src/legacy_quick_ui_services.cpp
Normal file
@@ -0,0 +1,207 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include "legacy_quick_ui_services.h"
|
||||
|
||||
#include "app.h"
|
||||
#include "node_image.h"
|
||||
#include "node_stroke_preview.h"
|
||||
|
||||
namespace pp::panopainter {
|
||||
namespace {
|
||||
|
||||
class LegacyQuickUiServices final : public pp::app::QuickUiServices {
|
||||
public:
|
||||
LegacyQuickUiServices(NodePanelQuick& panel, const NodePanelQuick::MiniState* restore_state = nullptr) noexcept
|
||||
: panel_(panel)
|
||||
, restore_state_(restore_state)
|
||||
{
|
||||
}
|
||||
|
||||
void select_slot(pp::app::QuickUiSlotKind slot_kind, int slot_index, bool fire_event) override
|
||||
{
|
||||
if (slot_kind == pp::app::QuickUiSlotKind::brush) {
|
||||
panel_.set_selected_brush_index(slot_index, fire_event);
|
||||
return;
|
||||
}
|
||||
|
||||
panel_.set_selected_color_index(slot_index, fire_event);
|
||||
}
|
||||
|
||||
void open_slot_popup(pp::app::QuickUiSlotKind slot_kind, int slot_index) override
|
||||
{
|
||||
if (slot_kind == pp::app::QuickUiSlotKind::brush) {
|
||||
open_brush_popup(slot_index);
|
||||
return;
|
||||
}
|
||||
|
||||
open_color_picker(slot_index);
|
||||
}
|
||||
|
||||
void restore_state(int brush_index, int color_index, bool fire_event) override
|
||||
{
|
||||
if (!restore_state_)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < static_cast<int>(panel_.m_button_brushes.size()); i++)
|
||||
{
|
||||
auto b = static_cast<NodeStrokePreview*>(panel_.m_button_brushes[i]->m_children[0].get());
|
||||
b->m_brush = restore_state_->brushes[i];
|
||||
b->draw_stroke();
|
||||
auto c = static_cast<NodeBorder*>(panel_.m_button_colors[i]->m_children[0].get());
|
||||
c->m_color = restore_state_->colors[i];
|
||||
}
|
||||
panel_.set_selected_color_index(color_index, fire_event);
|
||||
panel_.set_selected_brush_index(brush_index, fire_event);
|
||||
}
|
||||
|
||||
void reset_state(bool fire_event) override
|
||||
{
|
||||
for (int i = 0; i < static_cast<int>(panel_.m_button_brushes.size()); i++)
|
||||
{
|
||||
auto b = static_cast<NodeStrokePreview*>(panel_.m_button_brushes[i]->m_children[0].get());
|
||||
b->m_brush = std::make_shared<Brush>();
|
||||
b->m_brush->load_tip("data/brushes/Round-Hard.png", "data/brushes/thumbs/Round-Hard.png");
|
||||
b->draw_stroke();
|
||||
}
|
||||
static_cast<NodeBorder*>(panel_.m_button_colors[0]->m_children[0].get())->m_color = glm::vec4(0, 0, 0, 1);
|
||||
static_cast<NodeBorder*>(panel_.m_button_colors[1]->m_children[0].get())->m_color = glm::vec4(.5, .5, .5, 1);
|
||||
static_cast<NodeBorder*>(panel_.m_button_colors[2]->m_children[0].get())->m_color = glm::vec4(1, 1, 1, 1);
|
||||
panel_.set_selected_brush_index(0, fire_event);
|
||||
panel_.set_selected_color_index(0, fire_event);
|
||||
}
|
||||
|
||||
private:
|
||||
void open_brush_popup(int slot_index)
|
||||
{
|
||||
auto button = panel_.m_button_brushes[slot_index];
|
||||
if (!button)
|
||||
return;
|
||||
|
||||
auto popup = App::I->presets;
|
||||
auto screen = panel_.root()->m_size;
|
||||
glm::vec2 tick_sz = { 16, 32 };
|
||||
glm::vec2 tick_pos = button->m_pos + glm::vec2(button->m_size.x, 0);
|
||||
glm::vec2 popup_pos = { tick_pos.x + tick_sz.x, tick_pos.y };
|
||||
|
||||
auto tick = panel_.root()->add_child<NodeImage>();
|
||||
tick->SetPositioning(YGPositionTypeAbsolute);
|
||||
tick->SetPosition(tick_pos.x, tick_pos.y + (button->m_size.y - tick_sz.y) * 0.5f);
|
||||
tick->SetSize(tick_sz);
|
||||
tick->set_image("data/ui/popup-tick.png");
|
||||
tick->m_scale = { 1, 1 };
|
||||
|
||||
float hh = popup->m_container->m_children.size() > 10 ? (screen.y - 90.f) : 400.f;
|
||||
popup->SetWidth(350);
|
||||
popup->SetHeight(glm::max(hh, 400.f));
|
||||
popup->SetPositioning(YGPositionTypeAbsolute);
|
||||
popup->SetPosition(popup_pos);
|
||||
panel_.root()->add_child(popup);
|
||||
|
||||
panel_.root()->update();
|
||||
popup->tick(0);
|
||||
popup->update();
|
||||
|
||||
if (tick_pos.x + popup->m_size.x > screen.x)
|
||||
{
|
||||
tick_pos = button->m_pos - glm::vec2(tick_sz.x, 0);
|
||||
popup_pos = { tick_pos.x - popup->GetWidth(), tick_pos.y };
|
||||
tick->m_scale.x = -1.f;
|
||||
}
|
||||
popup_pos = glm::clamp(popup_pos, { 0, 0 }, screen - popup->m_size);
|
||||
popup->SetPosition(popup_pos);
|
||||
tick->SetPosition(tick_pos.x, tick_pos.y + (button->m_size.y - tick_sz.y) * 0.5f);
|
||||
popup->update();
|
||||
|
||||
popup->m_mouse_ignore = false;
|
||||
popup->m_flood_events = true;
|
||||
popup->m_capture_children = false;
|
||||
popup->mouse_capture();
|
||||
|
||||
popup->on_popup_close = [tick](Node*) {
|
||||
tick->destroy();
|
||||
};
|
||||
|
||||
auto* panel = &panel_;
|
||||
popup->on_brush_changed = [panel, button](Node*, std::shared_ptr<Brush>& b) {
|
||||
auto pr = static_cast<NodeStrokePreview*>(button->m_children[0].get());
|
||||
*pr->m_brush = *b;
|
||||
pr->m_brush->load();
|
||||
pr->draw_stroke();
|
||||
if (panel->on_brush_change)
|
||||
panel->on_brush_change(button, pr->m_brush);
|
||||
};
|
||||
}
|
||||
|
||||
void open_color_picker(int slot_index)
|
||||
{
|
||||
auto target = panel_.m_button_colors[slot_index];
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
auto popup = panel_.m_picker;
|
||||
auto screen = panel_.root()->m_size;
|
||||
glm::vec2 tick_sz = { 16, 32 };
|
||||
glm::vec2 tick_pos = target->m_pos + glm::vec2(target->m_size.x, 0);
|
||||
glm::vec2 popup_pos = { tick_pos.x + tick_sz.x, tick_pos.y - 140.f };
|
||||
|
||||
auto tick = panel_.root()->add_child<NodeImage>();
|
||||
tick->SetPositioning(YGPositionTypeAbsolute);
|
||||
tick->SetPosition(tick_pos.x, tick_pos.y + (target->m_size.y - tick_sz.y) * 0.5f);
|
||||
tick->SetSize(tick_sz);
|
||||
tick->set_image("data/ui/popup-tick.png");
|
||||
tick->m_scale = { 1, 1 };
|
||||
|
||||
popup->SetPositioning(YGPositionTypeAbsolute);
|
||||
popup->SetPosition(popup_pos);
|
||||
panel_.root()->add_child(popup);
|
||||
|
||||
panel_.root()->update();
|
||||
popup->tick(0);
|
||||
popup->update();
|
||||
|
||||
if (tick_pos.x + popup->m_size.x > screen.x)
|
||||
{
|
||||
tick_pos = target->m_pos - glm::vec2(tick_sz.x, 0);
|
||||
popup_pos = { tick_pos.x - popup->GetWidth(), tick_pos.y - 140.f };
|
||||
tick->m_scale.x = -1.f;
|
||||
}
|
||||
popup_pos = glm::clamp(popup_pos, { 0, 0 }, screen - popup->m_size);
|
||||
popup->SetPosition(popup_pos);
|
||||
tick->SetPosition(tick_pos.x, tick_pos.y + (target->m_size.y - tick_sz.y) * 0.5f);
|
||||
popup->update();
|
||||
|
||||
popup->m_mouse_ignore = false;
|
||||
popup->m_flood_events = true;
|
||||
popup->m_capture_children = false;
|
||||
popup->mouse_capture();
|
||||
|
||||
auto c = static_cast<NodeBorder*>(target->m_children[0].get());
|
||||
panel_.m_picker->set_color(c->m_color);
|
||||
panel_.m_picker->on_popup_close = [tick](Node*) {
|
||||
tick->destroy();
|
||||
};
|
||||
|
||||
auto* panel = &panel_;
|
||||
panel_.m_picker->on_color_change = [panel, c](Node*, glm::vec3 rgb) {
|
||||
c->m_color = glm::vec4(rgb, 1.f);
|
||||
if (panel->on_color_change)
|
||||
panel->on_color_change(panel, rgb);
|
||||
};
|
||||
}
|
||||
|
||||
NodePanelQuick& panel_;
|
||||
const NodePanelQuick::MiniState* restore_state_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
pp::foundation::Status execute_legacy_quick_ui_plan(
|
||||
NodePanelQuick& panel,
|
||||
const pp::app::QuickUiPlan& plan,
|
||||
const NodePanelQuick::MiniState* restore_state)
|
||||
{
|
||||
LegacyQuickUiServices services(panel, restore_state);
|
||||
return pp::app::execute_quick_ui_plan(plan, services);
|
||||
}
|
||||
|
||||
} // namespace pp::panopainter
|
||||
14
src/legacy_quick_ui_services.h
Normal file
14
src/legacy_quick_ui_services.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "app_core/quick_ui.h"
|
||||
#include "foundation/result.h"
|
||||
#include "node_panel_quick.h"
|
||||
|
||||
namespace pp::panopainter {
|
||||
|
||||
[[nodiscard]] pp::foundation::Status execute_legacy_quick_ui_plan(
|
||||
NodePanelQuick& panel,
|
||||
const pp::app::QuickUiPlan& plan,
|
||||
const NodePanelQuick::MiniState* restore_state = nullptr);
|
||||
|
||||
} // namespace pp::panopainter
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "pch.h"
|
||||
#include "app_core/grid_ui.h"
|
||||
#include "legacy_grid_ui_services.h"
|
||||
#include "log.h"
|
||||
#include "node_panel_grid.h"
|
||||
#include "canvas.h"
|
||||
@@ -79,22 +80,17 @@ void NodePanelGrid::init_controls()
|
||||
|
||||
m_hm_load->on_click = [this](Node*) {
|
||||
const auto plan = pp::app::plan_grid_heightmap_pick();
|
||||
if (!plan.opens_picker)
|
||||
return;
|
||||
App::I->pick_image([this](std::string path) {
|
||||
load_heightmap_file(path, true);
|
||||
});
|
||||
const auto status = pp::panopainter::execute_legacy_grid_ui_plan(*this, plan);
|
||||
if (!status.ok())
|
||||
LOG("Grid heightmap pick action failed: %s", status.message);
|
||||
};
|
||||
|
||||
m_hm_clear->on_click = [this](Node*)
|
||||
{
|
||||
const auto plan = pp::app::plan_grid_heightmap_clear(static_cast<bool>(m_hm_image.data()));
|
||||
if (!plan.clears_heightmap)
|
||||
return;
|
||||
m_hm_plane.create(1, 1, 100 * get_resolution());
|
||||
m_hm_image.destroy();
|
||||
m_hm_preview->tex.reset();
|
||||
m_hm_preview->SetHeight(0);
|
||||
const auto status = pp::panopainter::execute_legacy_grid_ui_plan(*this, plan);
|
||||
if (!status.ok())
|
||||
LOG("Grid heightmap clear action failed: %s", status.message);
|
||||
};
|
||||
|
||||
m_hm_reload->on_click = [this](Node*)
|
||||
@@ -112,32 +108,16 @@ void NodePanelGrid::init_controls()
|
||||
get_samples());
|
||||
if (!plan)
|
||||
return;
|
||||
if (plan.value().shows_unsupported_message)
|
||||
{
|
||||
App::I->message_box("Rendering failed",
|
||||
"Your hardware does not support lightmap rendering.");
|
||||
return;
|
||||
}
|
||||
if (plan.value().renders_lightmap)
|
||||
{
|
||||
std::thread([this] {
|
||||
BT_SetTerminate();
|
||||
bake_uvs();
|
||||
m_hm_shading->set_index(3);
|
||||
m_shade_mode = ShadeMode::Textured;
|
||||
}).detach();
|
||||
}
|
||||
const auto status = pp::panopainter::execute_legacy_grid_ui_plan(*this, plan.value());
|
||||
if (!status.ok())
|
||||
LOG("Grid lightmap render action failed: %s", status.message);
|
||||
};
|
||||
m_commit->on_click = [this](Node*)
|
||||
{
|
||||
const auto plan = pp::app::plan_grid_heightmap_commit(Canvas::I != nullptr);
|
||||
if (!plan.commits_heightmap)
|
||||
return;
|
||||
Canvas::I->draw_objects([this](const glm::mat4& camera, const glm::mat4& proj, int i) {
|
||||
draw_heightmap(proj, camera, true);
|
||||
}, Canvas::I->layer().m_frame_index, true);
|
||||
if (plan.updates_ground_opacity)
|
||||
m_groud_opacity->set_value(0);
|
||||
const auto status = pp::panopainter::execute_legacy_grid_ui_plan(*this, plan);
|
||||
if (!status.ok())
|
||||
LOG("Grid heightmap commit action failed: %s", status.message);
|
||||
};
|
||||
m_hm_texres->on_select = [this](Node*, int index) {
|
||||
int texres = get_texres();
|
||||
@@ -227,23 +207,10 @@ bool NodePanelGrid::load_heightmap_file(const std::string& path, bool raise_grou
|
||||
if (!plan)
|
||||
return false;
|
||||
|
||||
Image img;
|
||||
if (!img.load_file(plan.value().path))
|
||||
return false;
|
||||
|
||||
m_file_path = plan.value().path;
|
||||
m_hm_image = img.resize(128, 128);
|
||||
m_hm_preview->tex = std::make_shared<Texture2D>();
|
||||
m_hm_preview->tex->create(m_hm_image);
|
||||
m_hm_preview->tex->create_mipmaps();
|
||||
auto sz = m_hm_preview->tex->size();
|
||||
m_hm_preview->SetAspectRatio(sz.x / sz.y);
|
||||
m_hm_plane.create(1, 1, m_hm_image, get_resolution(), get_height());
|
||||
m_hm_preview->SetHeight(100);
|
||||
if (plan.value().updates_ground_opacity && m_groud_opacity->get_value() == 0.f)
|
||||
m_groud_opacity->set_value(1.f);
|
||||
m_rt_dirty = true;
|
||||
return true;
|
||||
const auto status = pp::panopainter::execute_legacy_grid_ui_plan(*this, plan.value());
|
||||
if (!status.ok())
|
||||
LOG("Grid heightmap load action failed: %s", status.message);
|
||||
return status.ok();
|
||||
}
|
||||
|
||||
void NodePanelGrid::draw_heightmap(const glm::mat4& proj, const glm::mat4& camera, bool commit) const
|
||||
|
||||
@@ -1,198 +1,11 @@
|
||||
#include "pch.h"
|
||||
#include "app_core/quick_ui.h"
|
||||
#include "legacy_quick_ui_services.h"
|
||||
#include "node_panel_quick.h"
|
||||
#include "node_stroke_preview.h"
|
||||
#include "node_image.h"
|
||||
#include "app.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class LegacyQuickUiServices final : public pp::app::QuickUiServices {
|
||||
public:
|
||||
LegacyQuickUiServices(NodePanelQuick& panel, const NodePanelQuick::MiniState* restore_state = nullptr) noexcept
|
||||
: panel_(panel)
|
||||
, restore_state_(restore_state)
|
||||
{
|
||||
}
|
||||
|
||||
void select_slot(pp::app::QuickUiSlotKind slot_kind, int slot_index, bool fire_event) override
|
||||
{
|
||||
if (slot_kind == pp::app::QuickUiSlotKind::brush) {
|
||||
panel_.set_selected_brush_index(slot_index, fire_event);
|
||||
return;
|
||||
}
|
||||
|
||||
panel_.set_selected_color_index(slot_index, fire_event);
|
||||
}
|
||||
|
||||
void open_slot_popup(pp::app::QuickUiSlotKind slot_kind, int slot_index) override
|
||||
{
|
||||
if (slot_kind == pp::app::QuickUiSlotKind::brush) {
|
||||
open_brush_popup(slot_index);
|
||||
return;
|
||||
}
|
||||
|
||||
open_color_picker(slot_index);
|
||||
}
|
||||
|
||||
void restore_state(int brush_index, int color_index, bool fire_event) override
|
||||
{
|
||||
if (!restore_state_)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < static_cast<int>(panel_.m_button_brushes.size()); i++)
|
||||
{
|
||||
auto b = static_cast<NodeStrokePreview*>(panel_.m_button_brushes[i]->m_children[0].get());
|
||||
b->m_brush = restore_state_->brushes[i];
|
||||
b->draw_stroke();
|
||||
auto c = static_cast<NodeBorder*>(panel_.m_button_colors[i]->m_children[0].get());
|
||||
c->m_color = restore_state_->colors[i];
|
||||
}
|
||||
panel_.set_selected_color_index(color_index, fire_event);
|
||||
panel_.set_selected_brush_index(brush_index, fire_event);
|
||||
}
|
||||
|
||||
void reset_state(bool fire_event) override
|
||||
{
|
||||
for (int i = 0; i < static_cast<int>(panel_.m_button_brushes.size()); i++)
|
||||
{
|
||||
auto b = static_cast<NodeStrokePreview*>(panel_.m_button_brushes[i]->m_children[0].get());
|
||||
b->m_brush = std::make_shared<Brush>();
|
||||
b->m_brush->load_tip("data/brushes/Round-Hard.png", "data/brushes/thumbs/Round-Hard.png");
|
||||
b->draw_stroke();
|
||||
}
|
||||
static_cast<NodeBorder*>(panel_.m_button_colors[0]->m_children[0].get())->m_color = glm::vec4(0, 0, 0, 1);
|
||||
static_cast<NodeBorder*>(panel_.m_button_colors[1]->m_children[0].get())->m_color = glm::vec4(.5, .5, .5, 1);
|
||||
static_cast<NodeBorder*>(panel_.m_button_colors[2]->m_children[0].get())->m_color = glm::vec4(1, 1, 1, 1);
|
||||
panel_.set_selected_brush_index(0, fire_event);
|
||||
panel_.set_selected_color_index(0, fire_event);
|
||||
}
|
||||
|
||||
private:
|
||||
void open_brush_popup(int slot_index)
|
||||
{
|
||||
auto button = panel_.m_button_brushes[slot_index];
|
||||
if (!button)
|
||||
return;
|
||||
|
||||
auto popup = App::I->presets;
|
||||
auto screen = panel_.root()->m_size;
|
||||
glm::vec2 tick_sz = { 16, 32 };
|
||||
glm::vec2 tick_pos = button->m_pos + glm::vec2(button->m_size.x, 0);
|
||||
glm::vec2 popup_pos = { tick_pos.x + tick_sz.x, tick_pos.y };
|
||||
|
||||
auto tick = panel_.root()->add_child<NodeImage>();
|
||||
tick->SetPositioning(YGPositionTypeAbsolute);
|
||||
tick->SetPosition(tick_pos.x, tick_pos.y + (button->m_size.y - tick_sz.y) * 0.5f);
|
||||
tick->SetSize(tick_sz);
|
||||
tick->set_image("data/ui/popup-tick.png");
|
||||
tick->m_scale = { 1, 1 };
|
||||
|
||||
float hh = popup->m_container->m_children.size() > 10 ? (screen.y - 90.f) : 400.f;
|
||||
popup->SetWidth(350);
|
||||
popup->SetHeight(glm::max(hh, 400.f));
|
||||
popup->SetPositioning(YGPositionTypeAbsolute);
|
||||
popup->SetPosition(popup_pos);
|
||||
panel_.root()->add_child(popup);
|
||||
|
||||
panel_.root()->update();
|
||||
popup->tick(0);
|
||||
popup->update();
|
||||
|
||||
if (tick_pos.x + popup->m_size.x > screen.x)
|
||||
{
|
||||
tick_pos = button->m_pos - glm::vec2(tick_sz.x, 0);
|
||||
popup_pos = { tick_pos.x - popup->GetWidth(), tick_pos.y };
|
||||
tick->m_scale.x = -1.f;
|
||||
}
|
||||
popup_pos = glm::clamp(popup_pos, { 0, 0 }, screen - popup->m_size);
|
||||
popup->SetPosition(popup_pos);
|
||||
tick->SetPosition(tick_pos.x, tick_pos.y + (button->m_size.y - tick_sz.y) * 0.5f);
|
||||
popup->update();
|
||||
|
||||
popup->m_mouse_ignore = false;
|
||||
popup->m_flood_events = true;
|
||||
popup->m_capture_children = false;
|
||||
popup->mouse_capture();
|
||||
|
||||
popup->on_popup_close = [tick](Node*) {
|
||||
tick->destroy();
|
||||
};
|
||||
|
||||
auto* panel = &panel_;
|
||||
popup->on_brush_changed = [panel, button](Node*, std::shared_ptr<Brush>& b) {
|
||||
auto pr = static_cast<NodeStrokePreview*>(button->m_children[0].get());
|
||||
*pr->m_brush = *b;
|
||||
pr->m_brush->load();
|
||||
pr->draw_stroke();
|
||||
if (panel->on_brush_change)
|
||||
panel->on_brush_change(button, pr->m_brush);
|
||||
};
|
||||
}
|
||||
|
||||
void open_color_picker(int slot_index)
|
||||
{
|
||||
auto target = panel_.m_button_colors[slot_index];
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
auto popup = panel_.m_picker;
|
||||
auto screen = panel_.root()->m_size;
|
||||
glm::vec2 tick_sz = { 16, 32 };
|
||||
glm::vec2 tick_pos = target->m_pos + glm::vec2(target->m_size.x, 0);
|
||||
glm::vec2 popup_pos = { tick_pos.x + tick_sz.x, tick_pos.y - 140.f };
|
||||
|
||||
auto tick = panel_.root()->add_child<NodeImage>();
|
||||
tick->SetPositioning(YGPositionTypeAbsolute);
|
||||
tick->SetPosition(tick_pos.x, tick_pos.y + (target->m_size.y - tick_sz.y) * 0.5f);
|
||||
tick->SetSize(tick_sz);
|
||||
tick->set_image("data/ui/popup-tick.png");
|
||||
tick->m_scale = { 1, 1 };
|
||||
|
||||
popup->SetPositioning(YGPositionTypeAbsolute);
|
||||
popup->SetPosition(popup_pos);
|
||||
panel_.root()->add_child(popup);
|
||||
|
||||
panel_.root()->update();
|
||||
popup->tick(0);
|
||||
popup->update();
|
||||
|
||||
if (tick_pos.x + popup->m_size.x > screen.x)
|
||||
{
|
||||
tick_pos = target->m_pos - glm::vec2(tick_sz.x, 0);
|
||||
popup_pos = { tick_pos.x - popup->GetWidth(), tick_pos.y - 140.f };
|
||||
tick->m_scale.x = -1.f;
|
||||
}
|
||||
popup_pos = glm::clamp(popup_pos, { 0, 0 }, screen - popup->m_size);
|
||||
popup->SetPosition(popup_pos);
|
||||
tick->SetPosition(tick_pos.x, tick_pos.y + (target->m_size.y - tick_sz.y) * 0.5f);
|
||||
popup->update();
|
||||
|
||||
popup->m_mouse_ignore = false;
|
||||
popup->m_flood_events = true;
|
||||
popup->m_capture_children = false;
|
||||
popup->mouse_capture();
|
||||
|
||||
auto c = static_cast<NodeBorder*>(target->m_children[0].get());
|
||||
panel_.m_picker->set_color(c->m_color);
|
||||
panel_.m_picker->on_popup_close = [tick](Node*) {
|
||||
tick->destroy();
|
||||
};
|
||||
|
||||
auto* panel = &panel_;
|
||||
panel_.m_picker->on_color_change = [panel, c](Node*, glm::vec3 rgb) {
|
||||
c->m_color = glm::vec4(rgb, 1.f);
|
||||
if (panel->on_color_change)
|
||||
panel->on_color_change(panel, rgb);
|
||||
};
|
||||
}
|
||||
|
||||
NodePanelQuick& panel_;
|
||||
const NodePanelQuick::MiniState* restore_state_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
Node* NodePanelQuick::clone_instantiate() const
|
||||
{
|
||||
return new this_class;
|
||||
@@ -278,8 +91,7 @@ void NodePanelQuick::set_state(const MiniState& state, bool fire_event /*= false
|
||||
if (!plan)
|
||||
return;
|
||||
|
||||
LegacyQuickUiServices services(*this, &state);
|
||||
const auto status = pp::app::execute_quick_ui_plan(plan.value(), services);
|
||||
const auto status = pp::panopainter::execute_legacy_quick_ui_plan(*this, plan.value(), &state);
|
||||
if (!status.ok())
|
||||
LOG("Quick restore action failed: %s", status.message);
|
||||
}
|
||||
@@ -290,8 +102,7 @@ void NodePanelQuick::reset_state(bool fire_event /*= false*/)
|
||||
if (!plan)
|
||||
return;
|
||||
|
||||
LegacyQuickUiServices services(*this);
|
||||
const auto status = pp::app::execute_quick_ui_plan(plan.value(), services);
|
||||
const auto status = pp::panopainter::execute_legacy_quick_ui_plan(*this, plan.value());
|
||||
if (!status.ok())
|
||||
LOG("Quick reset action failed: %s", status.message);
|
||||
}
|
||||
@@ -400,8 +211,7 @@ void NodePanelQuick::handle_button_brush_click(Node* button)
|
||||
if (!plan)
|
||||
return;
|
||||
|
||||
LegacyQuickUiServices services(*this);
|
||||
const auto status = pp::app::execute_quick_ui_plan(plan.value(), services);
|
||||
const auto status = pp::panopainter::execute_legacy_quick_ui_plan(*this, plan.value());
|
||||
if (!status.ok())
|
||||
LOG("Quick brush action failed: %s", status.message);
|
||||
}
|
||||
@@ -418,8 +228,7 @@ void NodePanelQuick::handle_button_color_click(Node* target)
|
||||
if (!plan)
|
||||
return;
|
||||
|
||||
LegacyQuickUiServices services(*this);
|
||||
const auto status = pp::app::execute_quick_ui_plan(plan.value(), services);
|
||||
const auto status = pp::panopainter::execute_legacy_quick_ui_plan(*this, plan.value());
|
||||
if (!status.ok())
|
||||
LOG("Quick color action failed: %s", status.message);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,67 @@
|
||||
#include "app_core/grid_ui.h"
|
||||
#include "test_harness.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace {
|
||||
|
||||
class FakeGridUiServices final : public pp::app::GridUiServices {
|
||||
public:
|
||||
void request_heightmap_pick() override
|
||||
{
|
||||
picks += 1;
|
||||
call_order += "pick;";
|
||||
}
|
||||
|
||||
pp::foundation::Status load_heightmap(std::string_view path, bool raise_ground_opacity) override
|
||||
{
|
||||
loads += 1;
|
||||
last_path = std::string(path);
|
||||
last_raise_ground_opacity = raise_ground_opacity;
|
||||
call_order += "load;";
|
||||
if (fail_load) {
|
||||
return pp::foundation::Status::invalid_argument("fake load failed");
|
||||
}
|
||||
return pp::foundation::Status::success();
|
||||
}
|
||||
|
||||
void clear_heightmap(bool updates_preview) override
|
||||
{
|
||||
clears += 1;
|
||||
last_updates_preview = updates_preview;
|
||||
call_order += "clear;";
|
||||
}
|
||||
|
||||
void render_lightmap(bool shows_unsupported_message, bool renders_lightmap) override
|
||||
{
|
||||
renders += 1;
|
||||
last_shows_unsupported_message = shows_unsupported_message;
|
||||
last_renders_lightmap = renders_lightmap;
|
||||
call_order += "render;";
|
||||
}
|
||||
|
||||
void commit_heightmap(bool updates_ground_opacity) override
|
||||
{
|
||||
commits += 1;
|
||||
last_updates_ground_opacity = updates_ground_opacity;
|
||||
call_order += "commit;";
|
||||
}
|
||||
|
||||
int picks = 0;
|
||||
int loads = 0;
|
||||
int clears = 0;
|
||||
int renders = 0;
|
||||
int commits = 0;
|
||||
bool fail_load = false;
|
||||
bool last_raise_ground_opacity = false;
|
||||
bool last_updates_preview = false;
|
||||
bool last_shows_unsupported_message = false;
|
||||
bool last_renders_lightmap = false;
|
||||
bool last_updates_ground_opacity = false;
|
||||
std::string last_path;
|
||||
std::string call_order;
|
||||
};
|
||||
|
||||
void heightmap_load_reload_and_clear_plan_state(pp::tests::Harness& harness)
|
||||
{
|
||||
const auto pick = pp::app::plan_grid_heightmap_pick();
|
||||
@@ -87,6 +146,75 @@ void commit_plan_requires_canvas(pp::tests::Harness& harness)
|
||||
PP_EXPECT(harness, !headless.mutates_grid_state);
|
||||
}
|
||||
|
||||
void executor_dispatches_grid_operations(pp::tests::Harness& harness)
|
||||
{
|
||||
FakeGridUiServices services;
|
||||
|
||||
PP_EXPECT(harness, pp::app::execute_grid_ui_plan(pp::app::plan_grid_heightmap_pick(), services).ok());
|
||||
|
||||
const auto load = pp::app::plan_grid_heightmap_load("D:/Paint/height.png");
|
||||
PP_EXPECT(harness, load);
|
||||
if (load) {
|
||||
PP_EXPECT(harness, pp::app::execute_grid_ui_plan(load.value(), services).ok());
|
||||
}
|
||||
|
||||
const auto clear = pp::app::plan_grid_heightmap_clear(true);
|
||||
PP_EXPECT(harness, pp::app::execute_grid_ui_plan(clear, services).ok());
|
||||
|
||||
const auto render = pp::app::plan_grid_lightmap_render(true, true, false, 1024, 32);
|
||||
PP_EXPECT(harness, render);
|
||||
if (render) {
|
||||
PP_EXPECT(harness, pp::app::execute_grid_ui_plan(render.value(), services).ok());
|
||||
}
|
||||
|
||||
const auto commit = pp::app::plan_grid_heightmap_commit(true);
|
||||
PP_EXPECT(harness, pp::app::execute_grid_ui_plan(commit, services).ok());
|
||||
|
||||
PP_EXPECT(harness, services.picks == 1);
|
||||
PP_EXPECT(harness, services.loads == 1);
|
||||
PP_EXPECT(harness, services.clears == 1);
|
||||
PP_EXPECT(harness, services.renders == 1);
|
||||
PP_EXPECT(harness, services.commits == 1);
|
||||
PP_EXPECT(harness, services.last_path == "D:/Paint/height.png");
|
||||
PP_EXPECT(harness, services.last_raise_ground_opacity);
|
||||
PP_EXPECT(harness, services.last_updates_preview);
|
||||
PP_EXPECT(harness, services.last_renders_lightmap);
|
||||
PP_EXPECT(harness, services.last_updates_ground_opacity);
|
||||
PP_EXPECT(harness, services.call_order == "pick;load;clear;render;commit;");
|
||||
}
|
||||
|
||||
void executor_rejects_malformed_grid_plans(pp::tests::Harness& harness)
|
||||
{
|
||||
FakeGridUiServices services;
|
||||
|
||||
pp::app::GridUiPlan pick;
|
||||
pick.operation = pp::app::GridUiOperation::request_heightmap_pick;
|
||||
PP_EXPECT(harness, !pp::app::execute_grid_ui_plan(pick, services).ok());
|
||||
|
||||
pp::app::GridUiPlan load;
|
||||
load.operation = pp::app::GridUiOperation::load_heightmap;
|
||||
load.loads_heightmap = true;
|
||||
PP_EXPECT(harness, !pp::app::execute_grid_ui_plan(load, services).ok());
|
||||
|
||||
pp::app::GridUiPlan render;
|
||||
render.operation = pp::app::GridUiOperation::render_lightmap;
|
||||
render.renders_lightmap = true;
|
||||
render.texture_resolution = 0;
|
||||
render.sample_count = 32;
|
||||
PP_EXPECT(harness, !pp::app::execute_grid_ui_plan(render, services).ok());
|
||||
|
||||
const auto failing_load = pp::app::plan_grid_heightmap_load("D:/Paint/missing.png");
|
||||
PP_EXPECT(harness, failing_load);
|
||||
if (failing_load) {
|
||||
services.fail_load = true;
|
||||
PP_EXPECT(harness, !pp::app::execute_grid_ui_plan(failing_load.value(), services).ok());
|
||||
}
|
||||
|
||||
PP_EXPECT(harness, services.picks == 0);
|
||||
PP_EXPECT(harness, services.renders == 0);
|
||||
PP_EXPECT(harness, services.loads == 1);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main()
|
||||
@@ -95,5 +223,7 @@ int main()
|
||||
harness.run("heightmap load reload and clear plan state", heightmap_load_reload_and_clear_plan_state);
|
||||
harness.run("lightmap render validates capabilities and limits", lightmap_render_validates_capabilities_and_limits);
|
||||
harness.run("commit plan requires canvas", commit_plan_requires_canvas);
|
||||
harness.run("executor dispatches grid operations", executor_dispatches_grid_operations);
|
||||
harness.run("executor rejects malformed grid plans", executor_rejects_malformed_grid_plans);
|
||||
return harness.finish();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user