Extract sidebar, lifecycle shell, and canvas unmerged draw

This commit is contained in:
2026-06-16 12:23:27 +02:00
parent 3407daff08
commit acd34540e0
10 changed files with 720 additions and 530 deletions

View File

@@ -97,6 +97,7 @@ set(PP_PANOPAINTER_APP_SOURCES
src/app_dialogs_info_openers.cpp
src/app_events.cpp
src/app_layout.cpp
src/app_layout_sidebar.cpp
src/app_layout_about_layer_menu.cpp
src/app_layout_file_menu.cpp
src/app_layout_tools_menu.cpp
@@ -166,6 +167,8 @@ set(PP_PANOPAINTER_UI_SOURCES
set(PP_WINDOWS_PLATFORM_SOURCES
src/main.cpp
src/platform_windows/windows_bootstrap_helpers.cpp
src/platform_windows/windows_lifecycle_shell.cpp
src/platform_windows/windows_lifecycle_shell.h
src/platform_windows/windows_platform_services.cpp
src/platform_windows/windows_platform_services.h
src/platform_windows/windows_splash.cpp

View File

@@ -80,13 +80,13 @@ What is still carrying too much live ownership:
Current hotspot files:
- `src/canvas.cpp`: 2645 lines
- `src/app_layout.cpp`: 1249 lines
- `src/app_layout.cpp`: 785 lines
- `src/canvas_modes.cpp`: 1798 lines
- `src/node.cpp`: 1551 lines
- `src/main.cpp`: 909 lines
- `src/main.cpp`: 882 lines
- `src/node_panel_brush.cpp`: 1197 lines
- `src/node_stroke_preview.cpp`: 933 lines
- `src/node_canvas.cpp`: 905 lines
- `src/node_canvas.cpp`: 910 lines
- `src/app.cpp`: 502 lines
- `src/app_dialogs.cpp`: 142 lines
@@ -138,7 +138,9 @@ Current architecture mismatches that must be treated as real blockers:
live in `src/app_layout_file_menu.cpp` and `App::init_menu_file()` is now a
thin call-through, while about-menu and layer-menu wiring now also live in
`src/app_layout_about_layer_menu.cpp` and `App::init_menu_about()` plus
`App::init_menu_layer()` are now thin call-throughs, while the informational
`App::init_menu_layer()` are now thin call-throughs, while sidebar panel
binding and popup wiring now also live in `src/app_layout_sidebar.cpp` and
`App::init_sidebar()` is now a thin call-through, while the informational
overlay opener family now also lives in `src/app_dialogs_info_openers.cpp`
and the corresponding `App::dialog_*` entrypoints are thinner, while the
export/video/PPBR dialog family now also lives in
@@ -177,7 +179,10 @@ Current architecture mismatches that must be treated as real blockers:
`src/main.cpp`, while RenderDoc startup/frame capture, SHCore DPI bootstrap,
Win32 error-string conversion, and the GL debug pre/post callbacks now also
live in `src/platform_windows/windows_bootstrap_helpers.cpp` instead of
`src/main.cpp`,
`src/main.cpp`, while Win32 lifecycle running-state, close/shutdown
sequencing, FPS title update/wakeup posting, stylus frame update, window
preference save, and VR lifecycle wrappers now also live in
`src/platform_windows/windows_lifecycle_shell.cpp` instead of `src/main.cpp`,
while `App::rec_loop()` now delegates worker-iteration orchestration into
the retained recording bridge, `App::update_rec_frames()` now delegates
recording label refresh through that same retained recording path, and the

View File

@@ -210,6 +210,11 @@ Current slice:
`execute_node_canvas_draw_merge_tail(...)`, which trims another live tail
block from `NodeCanvas::draw()` even though the broader outer draw shell is
still inline.
- `NodeCanvas` non-`draw_merged` cache/background/layer-traversal/cache-
composite shell now also routes through
`execute_legacy_canvas_draw_unmerged_shell(...)`, which removes another
coherent orchestration block from `NodeCanvas::draw()` even though the
broader node draw loop still lives in `src/node_canvas.cpp`.
Write scope:
- `src/node_stroke_preview.cpp`
@@ -304,6 +309,10 @@ Current slice:
`src/app_layout_about_layer_menu.cpp`, and `App::init_menu_about()` plus
`App::init_menu_layer()` are now thin call-throughs, but edit/sidebar and
broader layout composition are still inline in `src/app_layout.cpp`.
- Sidebar panel binding plus popup wiring now also live in
`src/app_layout_sidebar.cpp`, and `App::init_sidebar()` is now a thin
call-through, but edit-menu wiring and broader layout composition are still
inline in `src/app_layout.cpp`.
Write scope:
- `src/app_layout.cpp`
@@ -460,6 +469,10 @@ Current slice:
conversion, `UnadjustWindowRectEx`, and GL debug pre/post callbacks now also
live in `src/platform_windows/windows_bootstrap_helpers.cpp` instead of
`src/main.cpp`
- Win32 lifecycle running-state, close/shutdown handling, FPS title update and
wakeup posting, stylus frame update, window preference save, and VR
lifecycle wrappers now also live in
`src/platform_windows/windows_lifecycle_shell.cpp` instead of `src/main.cpp`
- prepared-file background work now runs through an `AppRuntime`-owned worker
queue instead of a retained static worker in `src/app_events.cpp`
- canvas async import/export/save/open background work now also runs through an

View File

@@ -74,27 +74,6 @@ std::shared_ptr<NodePopupMenu> add_menu_popup(
return popup.value();
}
void close_legacy_overlay_handle_ignoring_status(
Node& anchor,
pp::ui::NodeHandle overlay) noexcept
{
(void)pp::panopainter::close_legacy_overlay_node(anchor, overlay);
}
template <typename PopupOverlay, typename TickOverlay>
void close_legacy_overlay_handles_if_open(
Node& anchor,
const PopupOverlay& popup_overlay,
const TickOverlay& tick_overlay) noexcept
{
if (popup_overlay) {
close_legacy_overlay_handle_ignoring_status(anchor, popup_overlay.value());
}
if (tick_overlay) {
close_legacy_overlay_handle_ignoring_status(anchor, tick_overlay.value());
}
}
[[nodiscard]] bool should_open_tools_panel(const pp::app::ToolsPanelPlan& plan) noexcept
{
return plan.action == pp::app::ToolsPanelAction::open_floating_panel;
@@ -135,16 +114,6 @@ void execute_document_layer_merge_plan(App& app, const pp::app::DocumentLayerMer
LOG("Layer merge failed: %s", status.message);
}
void execute_document_layer_operation_plan(
App& app,
const pp::app::DocumentLayerOperationPlan& plan,
const std::shared_ptr<class Layer>& pending_layer = nullptr)
{
const auto status = pp::panopainter::execute_legacy_document_layer_operation_plan(app, plan, pending_layer);
if (!status.ok())
LOG("Layer operation failed: %s", status.message);
}
} // namespace
void App::title_update()
@@ -263,381 +232,6 @@ void App::init_toolbar_main()
}
}
template <class T> std::shared_ptr<T> create_panel(LayoutManager& manager)
{
std::shared_ptr<T> ret;
ret = std::make_shared<T>();
ret->set_manager(&manager);
ret->init();
ret->create();
ret->loaded();
return ret;
}
void App::init_sidebar()
{
sidebar = layout[main_id]->find<NodeBorder>("sidebar");
canvas = layout[main_id]->find<NodeCanvas>("paint-canvas");
quick = layout[main_id]->find<NodePanelQuick>("panel-quick");
floatings_container = layout[main_id]->find<Node>("floatings");
//brushes = layout[main_id]->find<NodePanelBrush>("panel-brush");
//layers = layout[main_id]->find<NodePanelLayer>("panel-layer");
//color = layout[main_id]->find<NodePanelColor>("panel-color");
//stroke = layout[main_id]->find<NodePanelStroke>("panel-stroke");
//brushes = find_or_create_panel<NodePanelBrush>(panels);
layers = create_panel<NodePanelLayer>(layout);
color = create_panel<NodePanelColor>(layout);
stroke = create_panel<NodePanelStroke>(layout);
grid = create_panel<NodePanelGrid>(layout);
presets = create_panel<NodePanelBrushPreset>(layout);
animation = create_panel<NodePanelAnimation>(layout);
//presets = find_or_create_panel<NodePanelBrushPreset>(panels);
canvas->m_canvas->on_mode_changed = [this](kCanvasMode prev, kCanvasMode mode) {
quick_mode_state[prev] = quick->get_state();
if (quick_mode_state.find(mode) != quick_mode_state.end())
quick->set_state(quick_mode_state[mode], true);
else
quick->reset_state(true);
brush_update(true, true);
};
color->on_color_changed = [this](Node* target, glm::vec4 color) {
apply_brush_color_plan(*this, color, true, false);
};
stroke->on_brush_changed = [this](Node* target, const std::string& path, const std::string& thumb) {
apply_brush_texture_plan(*this, pp::app::BrushUiTextureSlot::tip, path, thumb);
};
stroke->on_pattern_changed = [this](Node*target, const std::string& path, const std::string& thumb) {
apply_brush_texture_plan(*this, pp::app::BrushUiTextureSlot::pattern, path, thumb);
};
stroke->on_dual_changed = [this](Node*target, const std::string& path, const std::string& thumb) {
apply_brush_texture_plan(*this, pp::app::BrushUiTextureSlot::dual, path, thumb);
};
stroke->on_stroke_change = [this](Node*) {
const auto status = pp::panopainter::execute_legacy_brush_stroke_changed_plan(*this);
if (!status.ok())
LOG("Brush stroke settings action failed: %s", status.message);
};
quick->on_color_change = [this](Node*, glm::vec3 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);
};
quick->on_size_change = [this](Node*, float value) {
stroke->set_size(value, true, true);
};
quick->on_brush_change = [this](Node*, std::shared_ptr<Brush> b) {
apply_brush_preset_plan(*this, b);
};
layers->on_layer_add = [this](Node*, std::shared_ptr<class Layer> layer, int index) {
const auto plan = pp::app::plan_document_layer_add(
static_cast<int>(Canvas::I->m_layers.size()),
index,
layers->m_layers.back()->m_label_text);
if (!plan)
return;
execute_document_layer_operation_plan(*this, plan.value(), layer);
};
layers->on_layer_duplicate = [this](Node*, int source_index) {
const auto plan = pp::app::plan_document_layer_duplicate(
static_cast<int>(Canvas::I->m_layers.size()),
source_index);
if (!plan)
return;
execute_document_layer_operation_plan(*this, plan.value());
};
layers->on_layer_change = [this](Node*, int, int new_idx) {
const auto plan = pp::app::plan_document_layer_select(
static_cast<int>(canvas->m_canvas->m_layers.size()),
new_idx);
if (!plan)
return;
execute_document_layer_operation_plan(*this, plan.value());
};
layers->on_layer_order = [this](Node*, int old_idx, int new_idx) {
const auto plan = pp::app::plan_document_layer_reorder(
static_cast<int>(canvas->m_canvas->m_layers.size()),
old_idx,
new_idx);
if (!plan)
return;
execute_document_layer_operation_plan(*this, plan.value());
};
layers->on_layer_delete = [this](Node*, int idx) {
const auto plan = pp::app::plan_document_layer_remove(
static_cast<int>(canvas->m_canvas->m_layers.size()),
idx);
if (!plan)
return;
execute_document_layer_operation_plan(*this, plan.value());
};
layers->on_layer_opacity_changed = [this](Node*, int idx, float value) {
const auto plan = pp::app::plan_document_layer_opacity(
static_cast<int>(canvas->m_canvas->m_layers.size()),
idx,
value);
if (!plan)
return;
execute_document_layer_operation_plan(*this, plan.value());
};
layers->on_layer_visibility_changed = [this](Node*, int idx, bool visible) {
const auto plan = pp::app::plan_document_layer_visibility(
static_cast<int>(canvas->m_canvas->m_layers.size()),
idx,
visible);
if (!plan)
return;
execute_document_layer_operation_plan(*this, plan.value());
};
layers->on_layer_alpha_lock_changed = [this](Node*, int idx, bool locked) {
const auto plan = pp::app::plan_document_layer_alpha_lock(
static_cast<int>(canvas->m_canvas->m_layers.size()),
idx,
locked);
if (!plan)
return;
execute_document_layer_operation_plan(*this, plan.value());
};
layers->on_layer_blend_mode_changed = [this](Node*, int idx, int mode) {
const auto plan = pp::app::plan_document_layer_blend_mode(
static_cast<int>(canvas->m_canvas->m_layers.size()),
idx,
mode);
if (!plan)
return;
execute_document_layer_operation_plan(*this, plan.value());
};
layers->on_layer_highlight_changed = [this](Node*, int idx, bool highlight) {
const auto plan = pp::app::plan_document_layer_highlight(
static_cast<int>(canvas->m_canvas->m_layers.size()),
idx,
highlight);
if (!plan)
return;
execute_document_layer_operation_plan(*this, plan.value());
};
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-stroke"))
{
button->on_click = [this, button](Node*) {
auto* popup_root = layout[main_id];
if (!popup_root) {
return;
}
auto screen = popup_root->m_size;
glm::vec2 pos = button->m_pos + glm::vec2(button->m_size.x * 0.5f, button->m_size.y);
if (stroke->m_parent)
{
if (auto fp = dynamic_cast<NodePanelFloating*>(stroke->m_parent->m_parent))
{
pp::panopainter::detach_legacy_node_from_parent(*stroke);
pp::panopainter::close_legacy_dialog_node(*fp);
}
}
const auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, stroke);
if (!popup_overlay) {
return;
}
stroke->SetSize(350, glm::max(100.f, screen.y - pos.y - 50.f));
stroke->find("title")->SetVisibility(true);
auto tick = pp::panopainter::make_legacy_overlay_node_for_anchor<NodeImage>(*popup_root);
tick->SetPositioning(YGPositionTypeAbsolute);
tick->SetSize(32, 16);
tick->SetPosition(pos.x - 16, pos.y);
tick->set_image("data/ui/popup-tick-up.png");
auto tick_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, tick);
if (!popup_overlay || !tick_overlay)
{
close_legacy_overlay_handles_if_open(*popup_root, popup_overlay, tick_overlay);
return;
}
const auto popup_handle = popup_overlay.value();
const auto tick_handle = tick_overlay.value();
popup_root->update();
stroke->SetPosition(pos.x - stroke->m_size.x / 2.f, pos.y + 16);
stroke->SetPositioning(YGPositionTypeAbsolute);
pp::panopainter::activate_legacy_popup_overlay(*stroke);
auto scroll = stroke->find<NodeScroll>("scroller");
//scroll->SetHeight(glm::max(100.f, screen.y - pos.y - 200.f));
stroke->m_popup_overlay_handle = popup_handle;
stroke->m_tick_overlay_handle = tick_handle;
};
}
//if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-brush"))
//{
// button->on_click = [this, button](Node*) {
// panels->get_child_index(brushes.get()) == -1 ? panels->add_child(brushes) : panels->remove_child(brushes.get());
// panels->fix_scroll();
// button->set_color(panels->get_child_index(brushes.get()) == -1 ? color_button_normal : color_button_hlight);
// };
//}
//if (auto* button = layout[main_id]->find<NodeButton>("btn-brush-preset"))
//{
// button->on_click = [this, button](Node*) {
// panels->get_child_index(presets.get()) == -1 ? panels->add_child(presets) : panels->remove_child(presets.get());
// panels->fix_scroll();
// button->set_color(panels->get_child_index(presets.get()) == -1 ? color_button_normal : color_button_hlight);
// };
//}
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-color"))
{
button->on_click = [this, button](Node*) {
auto* popup_root = layout[main_id];
if (!popup_root) {
return;
}
glm::vec2 pos = button->m_pos + glm::vec2(button->m_size.x * 0.5f, button->m_size.y);
const auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, color);
if (!popup_overlay)
{
LOG("Color popup overlay failed: %s", popup_overlay.status().message);
return;
}
color->find("title")->SetVisibility(true);
color->SetSize(350, 350);
auto tick = pp::panopainter::make_legacy_overlay_node_for_anchor<NodeImage>(*layout[main_id]);
tick->SetPositioning(YGPositionTypeAbsolute);
tick->SetSize(32, 16);
tick->SetPosition(pos.x - 16, pos.y);
tick->set_image("data/ui/popup-tick-up.png");
auto tick_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, tick);
if (!popup_overlay || !tick_overlay)
{
close_legacy_overlay_handles_if_open(*popup_root, popup_overlay, tick_overlay);
return;
}
const auto popup_handle = popup_overlay.value();
const auto tick_handle = tick_overlay.value();
popup_root->update();
color->SetPosition(pos.x - color->m_size.x / 2.f, pos.y + 16);
color->SetPositioning(YGPositionTypeAbsolute);
pp::panopainter::activate_legacy_popup_overlay(*color);
color->on_popup_close = [popup_root, popup_handle, tick_handle](Node*) {
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
close_legacy_overlay_handle_ignoring_status(*popup_root, tick_handle);
};
};
}
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-layer"))
{
button->on_click = [this, button](Node*) {
auto* popup_root = layout[main_id];
if (!popup_root) {
return;
}
auto screen = popup_root->m_size;
glm::vec2 pos = button->m_pos + glm::vec2(button->m_size.x * 0.5f, button->m_size.y);
layers->find("title")->SetVisibility(true);
layers->SetSize(350, YGUndefined);
if (layers->m_parent)
{
if (auto fp = dynamic_cast<NodePanelFloating*>(layers->m_parent->m_parent))
{
pp::panopainter::detach_legacy_node_from_parent(*layers);
pp::panopainter::close_legacy_dialog_node(*fp);
}
}
const auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, layers);
if (!popup_overlay) {
return;
}
auto tick = pp::panopainter::make_legacy_overlay_node_for_anchor<NodeImage>(*popup_root);
tick->SetPositioning(YGPositionTypeAbsolute);
tick->SetSize(32, 16);
tick->SetPosition(pos.x - 16, pos.y);
tick->set_image("data/ui/popup-tick-up.png");
auto tick_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, tick);
if (!popup_overlay || !tick_overlay)
{
close_legacy_overlay_handles_if_open(*popup_root, popup_overlay, tick_overlay);
return;
}
const auto popup_handle = popup_overlay.value();
const auto tick_handle = tick_overlay.value();
popup_root->update();
layers->SetPosition(pos.x - layers->m_size.x / 2.f, pos.y + 16);
layers->SetPositioning(YGPositionTypeAbsolute);
pp::panopainter::activate_legacy_popup_overlay(*layers);
auto scroll = layers->find<NodeScroll>("layers-container");
scroll->SetMaxHeight(glm::max(100.f, screen.y - pos.y - 200.f));
layers->on_popup_close = [popup_root, popup_handle, tick_handle](Node*) {
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
close_legacy_overlay_handle_ignoring_status(*popup_root, tick_handle);
};
};
}
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-grids-panel"))
{
button->on_click = [this, button](Node*) {
auto* popup_root = layout[main_id];
if (!popup_root) {
return;
}
auto screen = popup_root->m_size;
glm::vec2 pos = button->m_pos + glm::vec2(button->m_size.x * 0.5f, button->m_size.y);
grid->find("title")->SetVisibility(true);
grid->SetSize(350, YGUndefined);
if (grid->m_parent)
{
if (auto fp = dynamic_cast<NodePanelFloating*>(grid->m_parent->m_parent))
{
pp::panopainter::detach_legacy_node_from_parent(*grid);
pp::panopainter::close_legacy_dialog_node(*fp);
}
}
const auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, grid);
if (!popup_overlay)
{
LOG("Grid popup overlay failed: %s", popup_overlay.status().message);
return;
}
auto tick = pp::panopainter::make_legacy_overlay_node_for_anchor<NodeImage>(*popup_root);
tick->SetPositioning(YGPositionTypeAbsolute);
tick->SetSize(32, 16);
tick->SetPosition(pos.x - 16, pos.y);
tick->set_image("data/ui/popup-tick-up.png");
auto tick_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, tick);
if (!popup_overlay || !tick_overlay)
{
close_legacy_overlay_handles_if_open(*popup_root, popup_overlay, tick_overlay);
return;
}
const auto popup_handle = popup_overlay.value();
const auto tick_handle = tick_overlay.value();
popup_root->update();
grid->SetPosition(pos.x - grid->m_size.x / 2.f, pos.y + 16);
grid->SetPositioning(YGPositionTypeAbsolute);
pp::panopainter::activate_legacy_popup_overlay(*grid);
auto scroll = grid->find<NodeScroll>("scroller");
scroll->SetMaxHeight(glm::max(100.f, screen.y - pos.y - 250.f));
grid->on_popup_close = [popup_root, popup_handle, tick_handle](Node*) {
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
close_legacy_overlay_handle_ignoring_status(*popup_root, tick_handle);
};
};
}
}
[[nodiscard]] bool current_canvas_mode_is_draw(App& app) noexcept
{
return app.canvas && app.canvas->m_canvas && app.canvas->m_canvas->m_current_mode == kCanvasMode::Draw;

435
src/app_layout_sidebar.cpp Normal file
View File

@@ -0,0 +1,435 @@
#include "pch.h"
#include "app.h"
#include "node_panel_grid.h"
#include "node_panel_floating.h"
#include "app_core/brush_ui.h"
#include "app_core/document_layer.h"
#include "legacy_brush_ui_services.h"
#include "legacy_document_layer_services.h"
#include "legacy_ui_overlay_services.h"
namespace {
bool apply_brush_color_plan(App& app, glm::vec4 color, bool update_quick, bool update_color_panel)
{
return pp::panopainter::apply_legacy_brush_color_plan(app, color, update_quick, update_color_panel);
}
bool apply_brush_texture_plan(App& app, pp::app::BrushUiTextureSlot slot, const std::string& path, const std::string& thumb)
{
return pp::panopainter::apply_legacy_brush_texture_plan(app, slot, path, thumb);
}
bool apply_brush_preset_plan(App& app, const std::shared_ptr<Brush>& brush)
{
return pp::panopainter::apply_legacy_brush_preset_plan(app, brush);
}
void execute_document_layer_operation_plan(
App& app,
const pp::app::DocumentLayerOperationPlan& plan,
const std::shared_ptr<class Layer>& pending_layer = nullptr)
{
const auto status = pp::panopainter::execute_legacy_document_layer_operation_plan(app, plan, pending_layer);
if (!status.ok())
LOG("Layer operation failed: %s", status.message);
}
void close_legacy_overlay_handle_ignoring_status(
Node& anchor,
pp::ui::NodeHandle overlay) noexcept
{
(void)pp::panopainter::close_legacy_overlay_node(anchor, overlay);
}
template <typename PopupOverlay, typename TickOverlay>
void close_legacy_overlay_handles_if_open(
Node& anchor,
const PopupOverlay& popup_overlay,
const TickOverlay& tick_overlay) noexcept
{
if (popup_overlay) {
close_legacy_overlay_handle_ignoring_status(anchor, popup_overlay.value());
}
if (tick_overlay) {
close_legacy_overlay_handle_ignoring_status(anchor, tick_overlay.value());
}
}
template <class T>
std::shared_ptr<T> create_panel(LayoutManager& manager)
{
auto ret = std::make_shared<T>();
ret->set_manager(&manager);
ret->init();
ret->create();
ret->loaded();
return ret;
}
} // namespace
void App::init_sidebar()
{
sidebar = layout[main_id]->find<NodeBorder>("sidebar");
canvas = layout[main_id]->find<NodeCanvas>("paint-canvas");
quick = layout[main_id]->find<NodePanelQuick>("panel-quick");
floatings_container = layout[main_id]->find<Node>("floatings");
//brushes = layout[main_id]->find<NodePanelBrush>("panel-brush");
//layers = layout[main_id]->find<NodePanelLayer>("panel-layer");
//color = layout[main_id]->find<NodePanelColor>("panel-color");
//stroke = layout[main_id]->find<NodePanelStroke>("panel-stroke");
//brushes = find_or_create_panel<NodePanelBrush>(panels);
layers = create_panel<NodePanelLayer>(layout);
color = create_panel<NodePanelColor>(layout);
stroke = create_panel<NodePanelStroke>(layout);
grid = create_panel<NodePanelGrid>(layout);
presets = create_panel<NodePanelBrushPreset>(layout);
animation = create_panel<NodePanelAnimation>(layout);
//presets = find_or_create_panel<NodePanelBrushPreset>(panels);
canvas->m_canvas->on_mode_changed = [this](kCanvasMode prev, kCanvasMode mode) {
quick_mode_state[prev] = quick->get_state();
if (quick_mode_state.find(mode) != quick_mode_state.end())
quick->set_state(quick_mode_state[mode], true);
else
quick->reset_state(true);
brush_update(true, true);
};
color->on_color_changed = [this](Node* target, glm::vec4 color) {
apply_brush_color_plan(*this, color, true, false);
};
stroke->on_brush_changed = [this](Node* target, const std::string& path, const std::string& thumb) {
apply_brush_texture_plan(*this, pp::app::BrushUiTextureSlot::tip, path, thumb);
};
stroke->on_pattern_changed = [this](Node* target, const std::string& path, const std::string& thumb) {
apply_brush_texture_plan(*this, pp::app::BrushUiTextureSlot::pattern, path, thumb);
};
stroke->on_dual_changed = [this](Node* target, const std::string& path, const std::string& thumb) {
apply_brush_texture_plan(*this, pp::app::BrushUiTextureSlot::dual, path, thumb);
};
stroke->on_stroke_change = [this](Node*) {
const auto status = pp::panopainter::execute_legacy_brush_stroke_changed_plan(*this);
if (!status.ok())
LOG("Brush stroke settings action failed: %s", status.message);
};
quick->on_color_change = [this](Node*, glm::vec3 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);
};
quick->on_size_change = [this](Node*, float value) {
stroke->set_size(value, true, true);
};
quick->on_brush_change = [this](Node*, std::shared_ptr<Brush> b) {
apply_brush_preset_plan(*this, b);
};
layers->on_layer_add = [this](Node*, std::shared_ptr<class Layer> layer, int index) {
const auto plan = pp::app::plan_document_layer_add(
static_cast<int>(Canvas::I->m_layers.size()),
index,
layers->m_layers.back()->m_label_text);
if (!plan)
return;
execute_document_layer_operation_plan(*this, plan.value(), layer);
};
layers->on_layer_duplicate = [this](Node*, int source_index) {
const auto plan = pp::app::plan_document_layer_duplicate(
static_cast<int>(Canvas::I->m_layers.size()),
source_index);
if (!plan)
return;
execute_document_layer_operation_plan(*this, plan.value());
};
layers->on_layer_change = [this](Node*, int, int new_idx) {
const auto plan = pp::app::plan_document_layer_select(
static_cast<int>(canvas->m_canvas->m_layers.size()),
new_idx);
if (!plan)
return;
execute_document_layer_operation_plan(*this, plan.value());
};
layers->on_layer_order = [this](Node*, int old_idx, int new_idx) {
const auto plan = pp::app::plan_document_layer_reorder(
static_cast<int>(canvas->m_canvas->m_layers.size()),
old_idx,
new_idx);
if (!plan)
return;
execute_document_layer_operation_plan(*this, plan.value());
};
layers->on_layer_delete = [this](Node*, int idx) {
const auto plan = pp::app::plan_document_layer_remove(
static_cast<int>(canvas->m_canvas->m_layers.size()),
idx);
if (!plan)
return;
execute_document_layer_operation_plan(*this, plan.value());
};
layers->on_layer_opacity_changed = [this](Node*, int idx, float value) {
const auto plan = pp::app::plan_document_layer_opacity(
static_cast<int>(canvas->m_canvas->m_layers.size()),
idx,
value);
if (!plan)
return;
execute_document_layer_operation_plan(*this, plan.value());
};
layers->on_layer_visibility_changed = [this](Node*, int idx, bool visible) {
const auto plan = pp::app::plan_document_layer_visibility(
static_cast<int>(canvas->m_canvas->m_layers.size()),
idx,
visible);
if (!plan)
return;
execute_document_layer_operation_plan(*this, plan.value());
};
layers->on_layer_alpha_lock_changed = [this](Node*, int idx, bool locked) {
const auto plan = pp::app::plan_document_layer_alpha_lock(
static_cast<int>(canvas->m_canvas->m_layers.size()),
idx,
locked);
if (!plan)
return;
execute_document_layer_operation_plan(*this, plan.value());
};
layers->on_layer_blend_mode_changed = [this](Node*, int idx, int mode) {
const auto plan = pp::app::plan_document_layer_blend_mode(
static_cast<int>(canvas->m_canvas->m_layers.size()),
idx,
mode);
if (!plan)
return;
execute_document_layer_operation_plan(*this, plan.value());
};
layers->on_layer_highlight_changed = [this](Node*, int idx, bool highlight) {
const auto plan = pp::app::plan_document_layer_highlight(
static_cast<int>(canvas->m_canvas->m_layers.size()),
idx,
highlight);
if (!plan)
return;
execute_document_layer_operation_plan(*this, plan.value());
};
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-stroke"))
{
button->on_click = [this, button](Node*) {
auto* popup_root = layout[main_id];
if (!popup_root) {
return;
}
auto screen = popup_root->m_size;
glm::vec2 pos = button->m_pos + glm::vec2(button->m_size.x * 0.5f, button->m_size.y);
if (stroke->m_parent)
{
if (auto fp = dynamic_cast<NodePanelFloating*>(stroke->m_parent->m_parent))
{
pp::panopainter::detach_legacy_node_from_parent(*stroke);
pp::panopainter::close_legacy_dialog_node(*fp);
}
}
const auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, stroke);
if (!popup_overlay) {
return;
}
stroke->SetSize(350, glm::max(100.f, screen.y - pos.y - 50.f));
stroke->find("title")->SetVisibility(true);
auto tick = pp::panopainter::make_legacy_overlay_node_for_anchor<NodeImage>(*popup_root);
tick->SetPositioning(YGPositionTypeAbsolute);
tick->SetSize(32, 16);
tick->SetPosition(pos.x - 16, pos.y);
tick->set_image("data/ui/popup-tick-up.png");
auto tick_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, tick);
if (!popup_overlay || !tick_overlay)
{
close_legacy_overlay_handles_if_open(*popup_root, popup_overlay, tick_overlay);
return;
}
const auto popup_handle = popup_overlay.value();
const auto tick_handle = tick_overlay.value();
popup_root->update();
stroke->SetPosition(pos.x - stroke->m_size.x / 2.f, pos.y + 16);
stroke->SetPositioning(YGPositionTypeAbsolute);
pp::panopainter::activate_legacy_popup_overlay(*stroke);
auto scroll = stroke->find<NodeScroll>("scroller");
(void)scroll;
stroke->m_popup_overlay_handle = popup_handle;
stroke->m_tick_overlay_handle = tick_handle;
};
}
//if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-brush"))
//{
// button->on_click = [this, button](Node*) {
// panels->get_child_index(brushes.get()) == -1 ? panels->add_child(brushes) : panels->remove_child(brushes.get());
// panels->fix_scroll();
// button->set_color(panels->get_child_index(brushes.get()) == -1 ? color_button_normal : color_button_hlight);
// };
//}
//if (auto* button = layout[main_id]->find<NodeButton>("btn-brush-preset"))
//{
// button->on_click = [this, button](Node*) {
// panels->get_child_index(presets.get()) == -1 ? panels->add_child(presets) : panels->remove_child(presets.get());
// panels->fix_scroll();
// button->set_color(panels->get_child_index(presets.get()) == -1 ? color_button_normal : color_button_hlight);
// };
//}
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-color"))
{
button->on_click = [this, button](Node*) {
auto* popup_root = layout[main_id];
if (!popup_root) {
return;
}
glm::vec2 pos = button->m_pos + glm::vec2(button->m_size.x * 0.5f, button->m_size.y);
const auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, color);
if (!popup_overlay)
{
LOG("Color popup overlay failed: %s", popup_overlay.status().message);
return;
}
color->find("title")->SetVisibility(true);
color->SetSize(350, 350);
auto tick = pp::panopainter::make_legacy_overlay_node_for_anchor<NodeImage>(*layout[main_id]);
tick->SetPositioning(YGPositionTypeAbsolute);
tick->SetSize(32, 16);
tick->SetPosition(pos.x - 16, pos.y);
tick->set_image("data/ui/popup-tick-up.png");
auto tick_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, tick);
if (!popup_overlay || !tick_overlay)
{
close_legacy_overlay_handles_if_open(*popup_root, popup_overlay, tick_overlay);
return;
}
const auto popup_handle = popup_overlay.value();
const auto tick_handle = tick_overlay.value();
popup_root->update();
color->SetPosition(pos.x - color->m_size.x / 2.f, pos.y + 16);
color->SetPositioning(YGPositionTypeAbsolute);
pp::panopainter::activate_legacy_popup_overlay(*color);
color->on_popup_close = [popup_root, popup_handle, tick_handle](Node*) {
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
close_legacy_overlay_handle_ignoring_status(*popup_root, tick_handle);
};
};
}
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-layer"))
{
button->on_click = [this, button](Node*) {
auto* popup_root = layout[main_id];
if (!popup_root) {
return;
}
auto screen = popup_root->m_size;
glm::vec2 pos = button->m_pos + glm::vec2(button->m_size.x * 0.5f, button->m_size.y);
layers->find("title")->SetVisibility(true);
layers->SetSize(350, YGUndefined);
if (layers->m_parent)
{
if (auto fp = dynamic_cast<NodePanelFloating*>(layers->m_parent->m_parent))
{
pp::panopainter::detach_legacy_node_from_parent(*layers);
pp::panopainter::close_legacy_dialog_node(*fp);
}
}
const auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, layers);
if (!popup_overlay) {
return;
}
auto tick = pp::panopainter::make_legacy_overlay_node_for_anchor<NodeImage>(*popup_root);
tick->SetPositioning(YGPositionTypeAbsolute);
tick->SetSize(32, 16);
tick->SetPosition(pos.x - 16, pos.y);
tick->set_image("data/ui/popup-tick-up.png");
auto tick_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, tick);
if (!popup_overlay || !tick_overlay)
{
close_legacy_overlay_handles_if_open(*popup_root, popup_overlay, tick_overlay);
return;
}
const auto popup_handle = popup_overlay.value();
const auto tick_handle = tick_overlay.value();
popup_root->update();
layers->SetPosition(pos.x - layers->m_size.x / 2.f, pos.y + 16);
layers->SetPositioning(YGPositionTypeAbsolute);
pp::panopainter::activate_legacy_popup_overlay(*layers);
auto scroll = layers->find<NodeScroll>("layers-container");
scroll->SetMaxHeight(glm::max(100.f, screen.y - pos.y - 200.f));
layers->on_popup_close = [popup_root, popup_handle, tick_handle](Node*) {
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
close_legacy_overlay_handle_ignoring_status(*popup_root, tick_handle);
};
};
}
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-grids-panel"))
{
button->on_click = [this, button](Node*) {
auto* popup_root = layout[main_id];
if (!popup_root) {
return;
}
auto screen = popup_root->m_size;
glm::vec2 pos = button->m_pos + glm::vec2(button->m_size.x * 0.5f, button->m_size.y);
grid->find("title")->SetVisibility(true);
grid->SetSize(350, YGUndefined);
if (grid->m_parent)
{
if (auto fp = dynamic_cast<NodePanelFloating*>(grid->m_parent->m_parent))
{
pp::panopainter::detach_legacy_node_from_parent(*grid);
pp::panopainter::close_legacy_dialog_node(*fp);
}
}
const auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, grid);
if (!popup_overlay)
{
LOG("Grid popup overlay failed: %s", popup_overlay.status().message);
return;
}
auto tick = pp::panopainter::make_legacy_overlay_node_for_anchor<NodeImage>(*popup_root);
tick->SetPositioning(YGPositionTypeAbsolute);
tick->SetSize(32, 16);
tick->SetPosition(pos.x - 16, pos.y);
tick->set_image("data/ui/popup-tick-up.png");
auto tick_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, tick);
if (!popup_overlay || !tick_overlay)
{
close_legacy_overlay_handles_if_open(*popup_root, popup_overlay, tick_overlay);
return;
}
const auto popup_handle = popup_overlay.value();
const auto tick_handle = tick_overlay.value();
popup_root->update();
grid->SetPosition(pos.x - grid->m_size.x / 2.f, pos.y + 16);
grid->SetPositioning(YGPositionTypeAbsolute);
pp::panopainter::activate_legacy_popup_overlay(*grid);
auto scroll = grid->find<NodeScroll>("scroller");
scroll->SetMaxHeight(glm::max(100.f, screen.y - pos.y - 250.f));
grid->on_popup_close = [popup_root, popup_handle, tick_handle](Node*) {
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
close_legacy_overlay_handle_ignoring_status(*popup_root, tick_handle);
};
};
}
}

View File

@@ -945,6 +945,49 @@ inline void execute_legacy_canvas_draw_merge_background_setup(
}
}
template <
typename PrepareBlendCache,
typename DrawBackground,
typename ConfigureBlendState,
typename DisableDepthTest,
typename PlanOnionRange,
typename ShouldDrawPlane,
typename VisitLayerPlane,
typename LogOnionRangeFailure,
typename CompositeBlendCache>
inline void execute_legacy_canvas_draw_unmerged_shell(
bool use_blend,
size_t layer_count,
PrepareBlendCache&& prepare_blend_cache,
DrawBackground&& draw_background,
ConfigureBlendState&& configure_blend_state,
DisableDepthTest&& disable_depth_test,
PlanOnionRange&& plan_onion_range,
ShouldDrawPlane&& should_draw_plane,
VisitLayerPlane&& visit_layer_plane,
LogOnionRangeFailure&& log_onion_range_failure,
CompositeBlendCache&& composite_blend_cache)
{
if (use_blend) {
prepare_blend_cache();
}
draw_background();
configure_blend_state(use_blend);
disable_depth_test();
execute_legacy_canvas_draw_layer_traversal(
layer_count,
std::forward<PlanOnionRange>(plan_onion_range),
std::forward<ShouldDrawPlane>(should_draw_plane),
std::forward<VisitLayerPlane>(visit_layer_plane),
std::forward<LogOnionRangeFailure>(log_onion_range_failure));
if (use_blend) {
composite_blend_cache();
}
}
inline void execute_legacy_canvas_draw_merge_plane_setup(
const LegacyCanvasDrawMergePlaneSetupUniforms& uniforms,
const LegacyCanvasDrawMergePlaneSetupExecution& execution)

View File

@@ -11,6 +11,7 @@
#include "legacy_preference_storage.h"
#include "renderer_gl/opengl_capabilities.h"
#include "platform_windows/windows_platform_services.h"
#include "platform_windows/windows_lifecycle_shell.h"
#include "platform_windows/windows_splash.h"
#include "platform_windows/windows_stylus_input.h"
#include "platform_windows/windows_vr_shell.h"
@@ -30,10 +31,6 @@
#include <iomanip>
#include <ctime>
#include <sstream>
#include <atomic>
#define WM_USER_CLOSE (WM_USER + 1)
#define WM_USER_WAKEUP (WM_USER + 2)
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp);
namespace pp::platform::windows {
@@ -127,13 +124,10 @@ void pp_windows_enqueue_main_task(std::packaged_task<void()> task)
enqueue_main_task_bridge(std::move(task));
}
std::atomic<int> running{-1};
void destroy_window()
{
auto& state = retained_state();
enqueue_main_task([hWnd = state.hWnd] {
PostMessage(hWnd, WM_USER_CLOSE, 0, 0);
enqueue_main_task([hWnd = retained_state().hWnd] {
pp::platform::windows::request_window_close(hWnd);
});
}
@@ -159,25 +153,15 @@ void async_unlock()
void win32_update_stylus(float dt)
{
pp::platform::windows::update_stylus_state(dt);
pp::platform::windows::update_stylus_frame(dt);
}
void win32_update_fps(int frames)
{
static wchar_t title_fps[512];
auto& state = retained_state();
const int vr_fps = pp::platform::windows::current_vr_fps(state.vr);
if (App::I->vr_active)
swprintf_s(title_fps, L"%s - %d fps - %d vr fps", state.window_title, frames, vr_fps);
else
swprintf_s(title_fps, L"%s - %d fps", state.window_title, frames);
{
enqueue_main_task([hWnd = state.hWnd] {
SetWindowText(hWnd, title_fps);
});
}
PostMessage(state.hWnd, WM_USER_WAKEUP, 0, 0);
enqueue_main_task([hWnd = state.hWnd, window_title = state.window_title, &vr = state.vr, frames] {
pp::platform::windows::update_window_fps(hWnd, window_title, vr, frames);
});
}
int read_WMI_info()
@@ -455,25 +439,17 @@ void init_vk_map()
bool win32_vr_start()
{
auto& state = retained_state();
return pp::platform::windows::start_vr_shell(state.vr, state.sandboxed, running);
return pp::platform::windows::start_window_vr(state.vr, state.sandboxed);
}
void win32_vr_stop()
{
auto& state = retained_state();
pp::platform::windows::stop_vr_shell(state.vr);
pp::platform::windows::stop_window_vr(retained_state().vr);
}
void win32_save_window_state()
{
auto& state = retained_state();
WINDOWPLACEMENT p;
GetWindowPlacement(state.hWnd, &p);
pp::panopainter::set_legacy_window_preferences(p.showCmd, {
p.rcNormalPosition.left,
p.rcNormalPosition.top,
p.rcNormalPosition.right,
p.rcNormalPosition.bottom });
pp::platform::windows::save_window_preferences(retained_state().hWnd);
}
int main(int argc, char** argv)
{
@@ -678,7 +654,7 @@ int main(int argc, char** argv)
wglMakeCurrent(NULL, NULL);
running.store(1, std::memory_order_relaxed);
pp::platform::windows::mark_lifecycle_running();
App::I->runtime().render_thread_start(*App::I);
App::I->runtime().ui_thread_start(*App::I);
@@ -724,14 +700,14 @@ int main(int argc, char** argv)
MSG msg;
LOG("start main loop");
while (running.load(std::memory_order_relaxed) == 1)
while (pp::platform::windows::lifecycle_is_running())
{
// If there any message in the queue process it
auto present = App::I->animate || App::I->redraw ?
PeekMessage(&msg, 0, 0, 0, PM_REMOVE) : GetMessage(&msg, 0, 0, 0);
if (msg.message == WM_QUIT)
running.store(0, std::memory_order_relaxed);
pp::platform::windows::mark_lifecycle_stopped();
if (present)
{
@@ -762,14 +738,8 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
switch (msg)
{
case WM_USER_CLOSE:
running.store(0, std::memory_order_relaxed);
pp::platform::windows::request_stop_and_join_vr_thread(state.vr);
App::I->runtime().ui_thread_stop();
App::I->runtime().render_thread_stop();
App::I->terminate();
delete App::I;
PostQuitMessage(0);
case pp::platform::windows::kUserCloseMessage:
pp::platform::windows::handle_window_close_message(state.vr);
return 0;
case WM_PAINT:
App::I->redraw = true;
@@ -792,7 +762,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
auto w = (float)LOWORD(lp);
auto h = (float)HIWORD(lp);
if (h != 0 && running.load(std::memory_order_relaxed) == 1)
if (h != 0 && pp::platform::windows::lifecycle_is_running())
{
App::I->ui_task_async([=]
{

View File

@@ -668,38 +668,41 @@ void NodeCanvas::draw()
const bool use_blend = blend_gate.shader_blend;
const bool copy_blend_destination = use_blend && !blend_gate.reads_destination_color;
if (use_blend)
{
apply_node_canvas_viewport(0, 0, m_cache_rtt.getWidth(), m_cache_rtt.getHeight());
m_cache_rtt.bindFramebuffer();
m_cache_rtt.clear({ 1, 1, 1, 0 });
}
pp::panopainter::execute_legacy_canvas_draw_merge_background_setup(
{
.use_blend = use_blend,
},
{
.disable_blend = [&] {
apply_node_canvas_capability(pp::renderer::gl::blend_state(), false);
},
.draw_checkerboard_plane = pp::panopainter::make_legacy_canvas_draw_merge_background_checkerboard_plane(
proj,
camera,
m_canvas->m_layers.size() + 500.f,
m_canvas->m_plane_transform,
[&] {
m_face_plane.draw_fill();
}),
});
// if not using shader blend, use gl rasterizer blend
use_blend ? apply_node_canvas_capability(pp::renderer::gl::blend_state(), false) : apply_node_canvas_capability(pp::renderer::gl::blend_state(), true);
apply_node_canvas_capability(pp::renderer::gl::depth_test_state(), false);
const auto& b = m_canvas->m_current_stroke->m_brush;
pp::panopainter::execute_legacy_canvas_draw_layer_traversal(
pp::panopainter::execute_legacy_canvas_draw_unmerged_shell(
use_blend,
m_canvas->m_layers.size(),
[&] {
apply_node_canvas_viewport(0, 0, m_cache_rtt.getWidth(), m_cache_rtt.getHeight());
m_cache_rtt.bindFramebuffer();
m_cache_rtt.clear({ 1, 1, 1, 0 });
},
[&] {
pp::panopainter::execute_legacy_canvas_draw_merge_background_setup(
{
.use_blend = use_blend,
},
{
.disable_blend = [&] {
apply_node_canvas_capability(pp::renderer::gl::blend_state(), false);
},
.draw_checkerboard_plane = pp::panopainter::make_legacy_canvas_draw_merge_background_checkerboard_plane(
proj,
camera,
m_canvas->m_layers.size() + 500.f,
m_canvas->m_plane_transform,
[&] {
m_face_plane.draw_fill();
}),
});
},
[&](bool enable_shader_blend) {
// if not using shader blend, use gl rasterizer blend
apply_node_canvas_capability(pp::renderer::gl::blend_state(), !enable_shader_blend);
},
[&] {
apply_node_canvas_capability(pp::renderer::gl::depth_test_state(), false);
},
[&](size_t layer_index) {
return pp::app::plan_animation_onion_frame_range(
m_canvas->m_layers[layer_index]->frames_count(),
@@ -748,51 +751,50 @@ void NodeCanvas::draw()
},
[&](const char* message) {
LOG("NodeCanvas onion frame range failed: %s", message);
});
if (use_blend)
{
m_cache_rtt.unbindFramebuffer();
if (m_density != 1.f)
apply_node_canvas_viewport(0, 0, m_rtt.getWidth(), m_rtt.getHeight());
else
apply_node_canvas_viewport(c.x + App::I->off_x, c.y + App::I->off_y, c.z, c.w);
pp::panopainter::execute_legacy_canvas_draw_merge_cache_to_screen_composite(
pp::panopainter::LegacyCanvasDrawMergeCacheToScreenCompositeUniforms {
.checkerboard = {
.colorize = false,
},
[&] {
m_cache_rtt.unbindFramebuffer();
if (m_density != 1.f)
apply_node_canvas_viewport(0, 0, m_rtt.getWidth(), m_rtt.getHeight());
else
apply_node_canvas_viewport(c.x + App::I->off_x, c.y + App::I->off_y, c.z, c.w);
pp::panopainter::execute_legacy_canvas_draw_merge_cache_to_screen_composite(
pp::panopainter::LegacyCanvasDrawMergeCacheToScreenCompositeUniforms {
.checkerboard = {
.colorize = false,
},
.texture = {
.mvp = glm::ortho<float>(-1, 1, -1, 1),
.texture_slot = 0,
},
},
.texture = {
.mvp = glm::ortho<float>(-1, 1, -1, 1),
.texture_slot = 0,
},
},
{
.enable_blend = [&] {
apply_node_canvas_capability(pp::renderer::gl::blend_state(), true);
},
.draw_checkerboard_plane = pp::panopainter::make_legacy_canvas_draw_merge_cache_to_screen_checkerboard_plane(
proj,
camera,
m_canvas->m_layers.size() + 500.f,
m_canvas->m_plane_transform,
[&] {
{
.enable_blend = [&] {
apply_node_canvas_capability(pp::renderer::gl::blend_state(), true);
},
.draw_checkerboard_plane = pp::panopainter::make_legacy_canvas_draw_merge_cache_to_screen_checkerboard_plane(
proj,
camera,
m_canvas->m_layers.size() + 500.f,
m_canvas->m_plane_transform,
[&] {
m_face_plane.draw_fill();
}),
.bind_sampler = [&] {
m_sampler.bind(0);
set_active_texture_unit(0);
},
.bind_cache_texture = [&] {
m_cache_rtt.bindTexture();
},
.draw_cache_texture = [&] {
m_face_plane.draw_fill();
}),
.bind_sampler = [&] {
m_sampler.bind(0);
set_active_texture_unit(0);
},
.bind_cache_texture = [&] {
m_cache_rtt.bindTexture();
},
.draw_cache_texture = [&] {
m_face_plane.draw_fill();
},
.unbind_cache_texture = [&] {
m_cache_rtt.unbindTexture();
},
});
}
},
.unbind_cache_texture = [&] {
m_cache_rtt.unbindTexture();
},
});
});
}
execute_node_canvas_draw_merge_tail(*this, ortho_proj, proj, camera, c);

View File

@@ -0,0 +1,96 @@
#include "pch.h"
#include "platform_windows/windows_lifecycle_shell.h"
#include "app.h"
#include "legacy_preference_storage.h"
#include "platform_windows/windows_stylus_input.h"
namespace pp::platform::windows {
namespace {
[[nodiscard]] std::atomic<int>& retained_running_state() noexcept
{
static std::atomic<int> running{-1};
return running;
}
}
std::atomic<int>& lifecycle_running_state() noexcept
{
return retained_running_state();
}
void mark_lifecycle_running() noexcept
{
retained_running_state().store(1, std::memory_order_relaxed);
}
void mark_lifecycle_stopped() noexcept
{
retained_running_state().store(0, std::memory_order_relaxed);
}
bool lifecycle_is_running() noexcept
{
return retained_running_state().load(std::memory_order_relaxed) == 1;
}
void request_window_close(HWND hWnd)
{
PostMessage(hWnd, kUserCloseMessage, 0, 0);
}
void handle_window_close_message(VrShellState& vr)
{
mark_lifecycle_stopped();
request_stop_and_join_vr_thread(vr);
App::I->runtime().ui_thread_stop();
App::I->runtime().render_thread_stop();
App::I->terminate();
delete App::I;
PostQuitMessage(0);
}
void update_stylus_frame(float dt)
{
update_stylus_state(dt);
}
void update_window_fps(HWND hWnd, const wchar_t* window_title, VrShellState& vr, int frames)
{
static wchar_t title_fps[512];
const int vr_fps = current_vr_fps(vr);
if (App::I->vr_active)
swprintf_s(title_fps, L"%s - %d fps - %d vr fps", window_title, frames, vr_fps);
else
swprintf_s(title_fps, L"%s - %d fps", window_title, frames);
SetWindowText(hWnd, title_fps);
PostMessage(hWnd, kUserWakeupMessage, 0, 0);
}
void save_window_preferences(HWND hWnd)
{
WINDOWPLACEMENT placement{};
GetWindowPlacement(hWnd, &placement);
pp::panopainter::set_legacy_window_preferences(placement.showCmd, {
placement.rcNormalPosition.left,
placement.rcNormalPosition.top,
placement.rcNormalPosition.right,
placement.rcNormalPosition.bottom });
}
bool start_window_vr(VrShellState& vr, bool sandboxed)
{
return start_vr_shell(vr, sandboxed, retained_running_state());
}
void stop_window_vr(VrShellState& vr)
{
stop_vr_shell(vr);
}
}

View File

@@ -0,0 +1,29 @@
#pragma once
#include <Windows.h>
#include <atomic>
#include "platform_windows/windows_vr_shell.h"
namespace pp::platform::windows {
inline constexpr UINT kUserCloseMessage = WM_USER + 1;
inline constexpr UINT kUserWakeupMessage = WM_USER + 2;
[[nodiscard]] std::atomic<int>& lifecycle_running_state() noexcept;
void mark_lifecycle_running() noexcept;
void mark_lifecycle_stopped() noexcept;
[[nodiscard]] bool lifecycle_is_running() noexcept;
void request_window_close(HWND hWnd);
void handle_window_close_message(VrShellState& vr);
void update_stylus_frame(float dt);
void update_window_fps(HWND hWnd, const wchar_t* window_title, VrShellState& vr, int frames);
void save_window_preferences(HWND hWnd);
[[nodiscard]] bool start_window_vr(VrShellState& vr, bool sandboxed);
void stop_window_vr(VrShellState& vr);
}