Extract main toolbar and thin startup/node helpers

This commit is contained in:
2026-06-16 12:33:36 +02:00
parent acd34540e0
commit 184f662493
7 changed files with 507 additions and 320 deletions

View File

@@ -98,6 +98,7 @@ set(PP_PANOPAINTER_APP_SOURCES
src/app_events.cpp src/app_events.cpp
src/app_layout.cpp src/app_layout.cpp
src/app_layout_sidebar.cpp src/app_layout_sidebar.cpp
src/app_layout_main_toolbar.cpp
src/app_layout_about_layer_menu.cpp src/app_layout_about_layer_menu.cpp
src/app_layout_file_menu.cpp src/app_layout_file_menu.cpp
src/app_layout_tools_menu.cpp src/app_layout_tools_menu.cpp

View File

@@ -80,10 +80,10 @@ What is still carrying too much live ownership:
Current hotspot files: Current hotspot files:
- `src/canvas.cpp`: 2645 lines - `src/canvas.cpp`: 2645 lines
- `src/app_layout.cpp`: 785 lines - `src/app_layout.cpp`: 743 lines
- `src/canvas_modes.cpp`: 1798 lines - `src/canvas_modes.cpp`: 1798 lines
- `src/node.cpp`: 1551 lines - `src/node.cpp`: 1594 lines
- `src/main.cpp`: 882 lines - `src/main.cpp`: 1098 lines
- `src/node_panel_brush.cpp`: 1197 lines - `src/node_panel_brush.cpp`: 1197 lines
- `src/node_stroke_preview.cpp`: 933 lines - `src/node_stroke_preview.cpp`: 933 lines
- `src/node_canvas.cpp`: 910 lines - `src/node_canvas.cpp`: 910 lines
@@ -140,10 +140,13 @@ Current architecture mismatches that must be treated as real blockers:
`src/app_layout_about_layer_menu.cpp` and `App::init_menu_about()` plus `src/app_layout_about_layer_menu.cpp` and `App::init_menu_about()` plus
`App::init_menu_layer()` are now thin call-throughs, while sidebar panel `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 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 `App::init_sidebar()` is now a thin call-through, while main-toolbar binding
overlay opener family now also lives in `src/app_dialogs_info_openers.cpp` now also lives in `src/app_layout_main_toolbar.cpp` and
and the corresponding `App::dialog_*` entrypoints are thinner, while the `App::init_toolbar_main()` is now a thin call-through, while the
export/video/PPBR dialog family now also lives in 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
`src/app_dialogs_export.cpp` and those `App::dialog_*` entrypoints are `src/app_dialogs_export.cpp` and those `App::dialog_*` entrypoints are
thinner too, while new/open/save/browse/resize workflow entrypoints now also thinner too, while new/open/save/browse/resize workflow entrypoints now also
live in `src/app_dialogs_workflow.cpp` and `src/app_dialogs.cpp` is now live in `src/app_dialogs_workflow.cpp` and `src/app_dialogs.cpp` is now
@@ -183,6 +186,12 @@ Current architecture mismatches that must be treated as real blockers:
sequencing, FPS title update/wakeup posting, stylus frame update, window sequencing, FPS title update/wakeup posting, stylus frame update, window
preference save, and VR lifecycle wrappers now also live in preference save, and VR lifecycle wrappers now also live in
`src/platform_windows/windows_lifecycle_shell.cpp` instead of `src/main.cpp`, `src/platform_windows/windows_lifecycle_shell.cpp` instead of `src/main.cpp`,
while the Win32 startup/window/bootstrap path in `src/main.cpp` now also
routes through named local helper functions for runtime-data discovery,
startup-state initialization, window creation, pixel-format setup, GL loader
init, runtime-info logging, and core-context upgrade sequencing even though
that state still needs to move into platform-owned modules for the file to
shrink materially,
while `App::rec_loop()` now delegates worker-iteration orchestration into while `App::rec_loop()` now delegates worker-iteration orchestration into
the retained recording bridge, `App::update_rec_frames()` now delegates the retained recording bridge, `App::update_rec_frames()` now delegates
recording label refresh through that same retained recording path, and the recording label refresh through that same retained recording path, and the
@@ -198,7 +207,11 @@ Current architecture mismatches that must be treated as real blockers:
document-IO cluster now lives in `src/legacy_canvas_document_io_services.cpp` document-IO cluster now lives in `src/legacy_canvas_document_io_services.cpp`
and `NodeStrokePreview` render-target setup plus immediate-pass sequencing and `NodeStrokePreview` render-target setup plus immediate-pass sequencing
now route through retained preview execution helpers, even though the bridge now route through retained preview execution helpers, even though the bridge
still owns worker-side readback flow and encoder-state label reads. still owns worker-side readback flow and encoder-state label reads, while
`Node` child attach/detach/reorder operations now route through named local
helpers in `src/node.cpp`, which makes the scene-graph mutation paths easier
to reason about without yet reducing the file or moving ownership into
`pp_ui_core`.
- Modern C++23 usage exists in extracted components, especially `std::span`, - Modern C++23 usage exists in extracted components, especially `std::span`,
explicit result/status objects, and a few concepts, but the live app still explicit result/status objects, and a few concepts, but the live app still
does not consistently express ownership, thread affinity, or renderer does not consistently express ownership, thread affinity, or renderer

View File

@@ -293,7 +293,7 @@ targets look like helpers under one old monolith.
Status: In Progress Status: In Progress
Why now: Why now:
`src/app_layout.cpp` is still a 1249-line mixed file that builds menus, `src/app_layout.cpp` is still a 743-line mixed file that builds menus,
attaches callbacks, computes planner inputs, and mutates UI state directly. attaches callbacks, computes planner inputs, and mutates UI state directly.
Current slice: Current slice:
@@ -313,6 +313,9 @@ Current slice:
`src/app_layout_sidebar.cpp`, and `App::init_sidebar()` is now a thin `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 call-through, but edit-menu wiring and broader layout composition are still
inline in `src/app_layout.cpp`. inline in `src/app_layout.cpp`.
- Main-toolbar binding now also lives in `src/app_layout_main_toolbar.cpp`,
and `App::init_toolbar_main()` is now a thin call-through, but edit-menu
wiring and broader layout composition are still inline in `src/app_layout.cpp`.
Write scope: Write scope:
- `src/app_layout.cpp` - `src/app_layout.cpp`
@@ -473,6 +476,12 @@ Current slice:
wakeup posting, stylus frame update, window preference save, and VR wakeup posting, stylus frame update, window preference save, and VR
lifecycle wrappers now also live in lifecycle wrappers now also live in
`src/platform_windows/windows_lifecycle_shell.cpp` instead of `src/main.cpp` `src/platform_windows/windows_lifecycle_shell.cpp` instead of `src/main.cpp`
- `main.cpp` startup/window/bootstrap flow now also routes through named local
helpers for runtime-data discovery, startup-state initialization, window
creation, pixel-format setup, GL loader init, runtime-info logging, and
core-context upgrade sequencing, but the retained Win32 shell still needs
those helpers moved into platform-owned modules before the file materially
shrinks
- prepared-file background work now runs through an `AppRuntime`-owned worker - prepared-file background work now runs through an `AppRuntime`-owned worker
queue instead of a retained static worker in `src/app_events.cpp` 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 - canvas async import/export/save/open background work now also runs through an
@@ -632,6 +641,12 @@ Why now:
mixes raw `Node*`, shared ownership, direct `add_child(...)`, and callback mixes raw `Node*`, shared ownership, direct `add_child(...)`, and callback
captures across mutation points. captures across mutation points.
Current slice:
- `src/node.cpp` child attach/detach/reorder paths now route through named
local helpers instead of repeating raw mutation logic inline. That improves
scene-graph mutation clarity, but it does not yet change the ownership model
or move those paths behind checked handles.
Write scope: Write scope:
- `src/node.*` - `src/node.*`
- `src/layout.*` - `src/layout.*`

View File

@@ -14,14 +14,12 @@
#include "app_core/document_layer.h" #include "app_core/document_layer.h"
#include "app_core/document_canvas.h" #include "app_core/document_canvas.h"
#include "app_core/app_status.h" #include "app_core/app_status.h"
#include "app_core/main_toolbar.h"
#include "app_core/tools_menu.h" #include "app_core/tools_menu.h"
#include "legacy_app_preference_services.h" #include "legacy_app_preference_services.h"
#include "legacy_app_shell_services.h" #include "legacy_app_shell_services.h"
#include "legacy_brush_ui_services.h" #include "legacy_brush_ui_services.h"
#include "legacy_canvas_tool_services.h" #include "legacy_canvas_tool_services.h"
#include "legacy_document_layer_services.h" #include "legacy_document_layer_services.h"
#include "legacy_history_services.h"
#include "legacy_preference_storage.h" #include "legacy_preference_storage.h"
#include "legacy_ui_overlay_services.h" #include "legacy_ui_overlay_services.h"
#include "serializer.h" #include "serializer.h"
@@ -32,6 +30,7 @@
#include <vector> #include <vector>
namespace pp::panopainter { namespace pp::panopainter {
void bind_legacy_main_toolbar(App& app);
void bind_legacy_file_menu(App& app); void bind_legacy_file_menu(App& app);
void bind_legacy_about_menu(App& app); void bind_legacy_about_menu(App& app);
void bind_legacy_layer_menu(App& app); void bind_legacy_layer_menu(App& app);
@@ -97,11 +96,6 @@ void apply_tools_panel_chrome(NodePanelFloating& panel, const pp::app::ToolsPane
panel.m_droppable = plan.droppable; panel.m_droppable = plan.droppable;
} }
void execute_main_toolbar_plan(App& app, const pp::app::MainToolbarPlan& plan)
{
pp::panopainter::execute_legacy_main_toolbar_plan(app, plan);
}
void execute_tools_menu_plan(App& app, const pp::app::ToolsMenuPlan& plan) void execute_tools_menu_plan(App& app, const pp::app::ToolsMenuPlan& plan)
{ {
pp::panopainter::execute_legacy_tools_menu_plan(app, plan); pp::panopainter::execute_legacy_tools_menu_plan(app, plan);
@@ -135,101 +129,7 @@ void App::title_update()
void App::init_toolbar_main() void App::init_toolbar_main()
{ {
if (auto* button = layout[main_id]->find<NodeButton>("btn-anim")) pp::panopainter::bind_legacy_main_toolbar(*this);
{
button->on_click = [this, button](Node*) {
if (canvas)
{
//canvas->m_canvas->export_anim();
}
};
}
if (auto* button = layout[main_id]->find<NodeButton>("btn-open"))
{
button->on_click = [this, button](Node*) {
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::open_document);
if (plan)
execute_main_toolbar_plan(*this, plan.value());
};
}
if (auto* button = layout[main_id]->find<NodeButton>("btn-save"))
{
button->on_click = [this, button](Node*) {
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::save_document);
if (plan)
execute_main_toolbar_plan(*this, plan.value());
};
}
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-undo"))
{
button->on_click = [this, button](Node*) {
const auto history = pp::panopainter::legacy_history_snapshot();
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::undo,
history.undo_count);
if (plan)
execute_main_toolbar_plan(*this, plan.value());
};
}
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-redo"))
{
button->on_click = [this, button](Node*) {
const auto history = pp::panopainter::legacy_history_snapshot();
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::redo,
0,
history.redo_count);
if (plan)
execute_main_toolbar_plan(*this, plan.value());
};
}
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-clean-memory"))
{
button->on_click = [this](Node*) {
const auto history = pp::panopainter::legacy_history_snapshot();
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::clear_history,
history.undo_count,
history.redo_count,
history.memory_bytes);
if (plan)
execute_main_toolbar_plan(*this, plan.value());
};
}
if (auto* button = layout[main_id]->find<NodeButton>("btn-clear"))
{
button->on_click = [this](Node*) {
//exit(0);
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::clear_canvas,
0,
0,
0,
static_cast<bool>(canvas));
if (plan)
execute_main_toolbar_plan(*this, plan.value());
};
}
if (auto* button = layout[main_id]->find<NodeButton>("btn-popup"))
{
button->on_click = [this](Node*) {
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::show_message_box);
if (plan)
execute_main_toolbar_plan(*this, plan.value());
};
}
if (auto* button = layout[main_id]->find<NodeButtonCustom>("btn-settings"))
{
button->on_click = [this](Node*) {
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::show_settings);
if (plan)
execute_main_toolbar_plan(*this, plan.value());
};
}
} }
[[nodiscard]] bool current_canvas_mode_is_draw(App& app) noexcept [[nodiscard]] bool current_canvas_mode_is_draw(App& app) noexcept

View File

@@ -0,0 +1,118 @@
#include "pch.h"
#include "app.h"
#include "app_core/main_toolbar.h"
#include "legacy_app_shell_services.h"
#include "legacy_history_services.h"
#include "node_button.h"
#include "node_button_custom.h"
namespace {
void execute_main_toolbar_plan(App& app, const pp::app::MainToolbarPlan& plan)
{
pp::panopainter::execute_legacy_main_toolbar_plan(app, plan);
}
} // namespace
namespace pp::panopainter {
void bind_legacy_main_toolbar(App& app)
{
if (auto* button = app.layout[app.main_id]->find<NodeButton>("btn-anim"))
{
button->on_click = [&app, button](Node*) {
if (app.canvas)
{
//app.canvas->m_canvas->export_anim();
}
};
}
if (auto* button = app.layout[app.main_id]->find<NodeButton>("btn-open"))
{
button->on_click = [&app, button](Node*) {
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::open_document);
if (plan)
execute_main_toolbar_plan(app, plan.value());
};
}
if (auto* button = app.layout[app.main_id]->find<NodeButton>("btn-save"))
{
button->on_click = [&app, button](Node*) {
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::save_document);
if (plan)
execute_main_toolbar_plan(app, plan.value());
};
}
if (auto* button = app.layout[app.main_id]->find<NodeButtonCustom>("btn-undo"))
{
button->on_click = [&app, button](Node*) {
const auto history = pp::panopainter::legacy_history_snapshot();
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::undo,
history.undo_count);
if (plan)
execute_main_toolbar_plan(app, plan.value());
};
}
if (auto* button = app.layout[app.main_id]->find<NodeButtonCustom>("btn-redo"))
{
button->on_click = [&app, button](Node*) {
const auto history = pp::panopainter::legacy_history_snapshot();
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::redo,
0,
history.redo_count);
if (plan)
execute_main_toolbar_plan(app, plan.value());
};
}
if (auto* button = app.layout[app.main_id]->find<NodeButtonCustom>("btn-clean-memory"))
{
button->on_click = [&app](Node*) {
const auto history = pp::panopainter::legacy_history_snapshot();
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::clear_history,
history.undo_count,
history.redo_count,
history.memory_bytes);
if (plan)
execute_main_toolbar_plan(app, plan.value());
};
}
if (auto* button = app.layout[app.main_id]->find<NodeButton>("btn-clear"))
{
button->on_click = [&app](Node*) {
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::clear_canvas,
0,
0,
0,
static_cast<bool>(app.canvas));
if (plan)
execute_main_toolbar_plan(app, plan.value());
};
}
if (auto* button = app.layout[app.main_id]->find<NodeButton>("btn-popup"))
{
button->on_click = [&app](Node*) {
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::show_message_box);
if (plan)
execute_main_toolbar_plan(app, plan.value());
};
}
if (auto* button = app.layout[app.main_id]->find<NodeButtonCustom>("btn-settings"))
{
button->on_click = [&app](Node*) {
const auto plan = pp::app::plan_main_toolbar_command(
pp::app::MainToolbarCommand::show_settings);
if (plan)
execute_main_toolbar_plan(app, plan.value());
};
}
}
} // namespace pp::panopainter

View File

@@ -112,6 +112,239 @@ void drain_main_tasks()
} }
} }
struct MainWindowStartupState
{
WNDCLASS window_class{};
PIXELFORMATDESCRIPTOR pixel_format{};
DWORD window_style = WS_OVERLAPPEDWINDOW;
RECT client_rect{ 0, 0, 0, 0 };
POINT client_pos{ CW_USEDEFAULT, CW_USEDEFAULT };
int show_command = SW_NORMAL;
};
struct OpenGlWindowContext
{
HDC device_context{};
HGLRC render_context{};
pp::renderer::gl::OpenGlRuntimeInfo runtime_info{};
};
enum class MainStartupResult
{
Ok = 0,
GladLoadFailure = 1,
MissingCoreContextSupport = 2,
};
void ensure_runtime_data_directory()
{
FILE* fp_check = fopen("data\\layout.xml", "rb");
if (!fp_check)
{
LOG("data files not found");
static char path[MAX_PATH];
GetModuleFileNameA(NULL, path, MAX_PATH);
LOG("current dir %s", path);
PathRemoveFileSpecA(path);
SetCurrentDirectoryA(path);
LOG("change dir to %s", path);
return;
}
fclose(fp_check);
LOG("data files ok");
}
MainWindowStartupState initialize_main_window_startup_state()
{
auto& state = retained_state();
auto startup = MainWindowStartupState {};
state.hInst = GetModuleHandle(NULL);
state.className = L"EngineMain";
startup.window_class.hInstance = state.hInst;
startup.window_class.lpfnWndProc = (WNDPROC)WndProc;
startup.window_class.lpszClassName = state.className;
startup.window_class.hbrBackground = (HBRUSH)COLOR_WINDOW;
startup.window_class.hCursor = LoadCursor(NULL, IDC_ARROW);
RegisterClass(&startup.window_class);
auto monitor = MonitorFromWindow(0, MONITOR_DEFAULTTOPRIMARY);
auto x = unsigned{ 96 };
auto y = unsigned{ 96 };
if (GetDpiForMonitor_fn)
GetDpiForMonitor_fn(monitor, MDT_EFFECTIVE_DPI, &x, &y);
App::I->display_density = (float)x / 96.f;
const auto window_preferences = pp::panopainter::read_legacy_window_preferences(SW_NORMAL);
if (window_preferences.has_ui_scale)
App::I->zoom = window_preferences.ui_scale;
else
App::I->zoom = (float)x / 96.f;
startup.show_command = window_preferences.show_command;
startup.client_rect = {
0,
0,
static_cast<LONG>(App::I->width * App::I->zoom),
static_cast<LONG>(App::I->height * App::I->zoom),
};
if (window_preferences.has_window_rect)
{
auto wnd_rect = window_preferences.window_rect;
App::I->width = wnd_rect.z - wnd_rect.x;
App::I->height = wnd_rect.w - wnd_rect.y;
startup.client_rect = { wnd_rect.x, wnd_rect.y, wnd_rect.z, wnd_rect.w };
startup.client_pos = { wnd_rect.x, wnd_rect.y };
}
else
{
AdjustWindowRect(&startup.client_rect, startup.window_style, false);
}
return startup;
}
void create_main_window(const MainWindowStartupState& startup, const wchar_t* window_title)
{
auto& state = retained_state();
const int window_width = startup.client_rect.right - startup.client_rect.left;
const int window_height = startup.client_rect.bottom - startup.client_rect.top;
state.hWnd = CreateWindow(
startup.window_class.lpszClassName,
window_title,
startup.window_style,
startup.client_pos.x,
startup.client_pos.y,
window_width,
window_height,
0,
0,
state.hInst,
0);
}
void initialize_pixel_format_descriptor(PIXELFORMATDESCRIPTOR& pixel_format)
{
pixel_format.nSize = sizeof(pixel_format);
pixel_format.nVersion = 1;
pixel_format.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pixel_format.iPixelType = PFD_TYPE_RGBA;
pixel_format.cColorBits = 32;
pixel_format.cDepthBits = 24;
pixel_format.iLayerType = PFD_MAIN_PLANE;
}
bool load_glad_entry_points(HDC device_context)
{
if (!gladLoadGL())
{
LOG("gladLoadGL() failed");
return false;
}
if (!gladLoadWGL(device_context))
{
LOG("gladLoadWGL() failed");
return false;
}
return true;
}
pp::renderer::gl::OpenGlRuntimeInfo log_runtime_info()
{
auto runtime_info = pp::renderer::gl::OpenGlRuntimeInfo {};
const auto runtime_info_result = pp::renderer::gl::query_opengl_runtime_info(
pp::legacy::gl_runtime::runtime_info_dispatch());
if (runtime_info_result.ok())
{
runtime_info = runtime_info_result.value();
LOG("GL version: %s", runtime_info.version);
LOG("GL vendor: %s", runtime_info.vendor);
LOG("GL renderer: %s", runtime_info.renderer);
}
else
{
LOG("OpenGL runtime info query failed: %s", runtime_info_result.status().message);
}
return runtime_info;
}
bool upgrade_to_core_gl_context(const MainWindowStartupState& startup, OpenGlWindowContext& context)
{
if (!GLAD_WGL_ARB_create_context)
{
LOG("WGL_ARB_create_context not supported");
// If not supported, go fuck yourself we are not gonna support your shitty device
return false;
}
const auto wgl_config = pp::renderer::gl::windows_wgl_core_context_3_3_config();
UINT num_format = 0;
wglMakeCurrent(NULL, NULL);
wglDeleteContext(context.render_context);
DestroyWindow(retained_state().hWnd);
create_main_window(startup, retained_state().window_title);
context.device_context = GetDC(retained_state().hWnd);
int pixel_format = 0;
wglChoosePixelFormatARB(
context.device_context,
wgl_config.pixel_format_attributes.data(),
nullptr,
1,
&pixel_format,
&num_format);
SetPixelFormat(context.device_context, pixel_format, &startup.pixel_format);
context.render_context = wglCreateContextAttribsARB(
context.device_context,
NULL,
wgl_config.context_attributes.data());
wglMakeCurrent(context.device_context, context.render_context);
pp::platform::windows::set_async_render_context(context.device_context, context.render_context);
return true;
}
MainStartupResult initialize_main_window_and_gl(MainWindowStartupState& startup, OpenGlWindowContext& context)
{
create_main_window(startup, L"PanoPainter");
initialize_pixel_format_descriptor(startup.pixel_format);
context.device_context = GetDC(retained_state().hWnd);
const int pixel_format = ChoosePixelFormat(context.device_context, &startup.pixel_format);
SetPixelFormat(context.device_context, pixel_format, &startup.pixel_format);
context.render_context = wglCreateContext(context.device_context);
wglMakeCurrent(context.device_context, context.render_context);
pp::platform::windows::set_async_render_context(context.device_context, context.render_context);
if (!load_glad_entry_points(context.device_context))
return MainStartupResult::GladLoadFailure;
context.runtime_info = log_runtime_info();
#ifdef USE_RENDERDOC
if (!win32_renderdoc_init())
LOG("Renderdoc not started");
#endif
const auto renderer_name = std::string(context.runtime_info.renderer != nullptr ? context.runtime_info.renderer : "");
swprintf_s(
retained_state().window_title,
L"PanoPainter %s (%s)",
g_version_number_w,
str2wstr(renderer_name).c_str());
if (!upgrade_to_core_gl_context(startup, context))
return MainStartupResult::MissingCoreContextSupport;
return MainStartupResult::Ok;
}
} }
HWND pp_windows_main_window_handle() HWND pp_windows_main_window_handle()
@@ -454,8 +687,6 @@ void win32_save_window_state()
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
auto& state = retained_state(); auto& state = retained_state();
WNDCLASS wc;
PIXELFORMATDESCRIPTOR pfd;
App::I = new App(); App::I = new App();
App::I->set_platform_services(&pp::platform::windows::platform_services()); App::I->set_platform_services(&pp::platform::windows::platform_services());
@@ -467,23 +698,7 @@ int main(int argc, char** argv)
if(SetProcessDpiAwareness_fn) if(SetProcessDpiAwareness_fn)
SetProcessDpiAwareness_fn(PROCESS_PER_MONITOR_DPI_AWARE); SetProcessDpiAwareness_fn(PROCESS_PER_MONITOR_DPI_AWARE);
ensure_runtime_data_directory();
FILE* fp_check = fopen("data\\layout.xml", "rb");
if (!fp_check)
{
LOG("data files not found");
static char path[MAX_PATH];
GetModuleFileNameA(NULL, path, MAX_PATH);
LOG("current dir %s", path);
PathRemoveFileSpecA(path);
SetCurrentDirectoryA(path);
LOG("change dir to %s", path);
}
else
{
fclose(fp_check);
LOG("data files ok");
}
pp::platform::windows::SplashScreen splash(GetModuleHandle(NULL)); pp::platform::windows::SplashScreen splash(GetModuleHandle(NULL));
@@ -496,135 +711,17 @@ int main(int argc, char** argv)
App::I->create(); App::I->create();
// Inizialize data structures to zero
memset(&wc, 0, sizeof(wc));
memset(&state.keys, 0, sizeof(state.keys)); memset(&state.keys, 0, sizeof(state.keys));
memset(&pfd, 0, sizeof(pfd)); auto startup = initialize_main_window_startup_state();
auto context = OpenGlWindowContext {};
// Create the main window switch (initialize_main_window_and_gl(startup, context))
state.hInst = GetModuleHandle(NULL);
state.className = L"EngineMain";
wc.hInstance = state.hInst;
wc.lpfnWndProc = (WNDPROC)WndProc;
wc.lpszClassName = state.className;
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
RegisterClass(&wc);
auto monitor = MonitorFromWindow(0, MONITOR_DEFAULTTOPRIMARY);
auto x = unsigned{ 96 };
auto y = unsigned{ 96 };
if (GetDpiForMonitor_fn)
GetDpiForMonitor_fn(monitor, MDT_EFFECTIVE_DPI, &x, &y);
App::I->display_density = (float)x / 96.f;
const auto window_preferences = pp::panopainter::read_legacy_window_preferences(SW_NORMAL);
if (window_preferences.has_ui_scale)
App::I->zoom = window_preferences.ui_scale;
else
App::I->zoom = (float)x / 96.f;
int show_cmd = window_preferences.show_command;
DWORD wnd_style = WS_OVERLAPPEDWINDOW;
//if (show_cmd == SW_MAXIMIZE)
// wnd_style != WS_MAXIMIZE;
RECT clientRect = { 0, 0, (int)App::I->width * App::I->zoom, (int)App::I->height * App::I->zoom };
POINT clientPos = { CW_USEDEFAULT, CW_USEDEFAULT };
if (window_preferences.has_window_rect)
{ {
auto wnd_rect = window_preferences.window_rect; case MainStartupResult::Ok:
App::I->width = wnd_rect.z - wnd_rect.x; break;
App::I->height = wnd_rect.w - wnd_rect.y; case MainStartupResult::GladLoadFailure:
clientRect = { wnd_rect.x, wnd_rect.y, wnd_rect.z, wnd_rect.w };
clientPos = { wnd_rect.x, wnd_rect.y };
}
else
{
AdjustWindowRect(&clientRect, wnd_style, false);
}
state.hWnd = CreateWindow(wc.lpszClassName, L"PanoPainter", wnd_style, clientPos.x, clientPos.y,
(float)(clientRect.right - clientRect.left),
(float)(clientRect.bottom - clientRect.top), 0, 0, state.hInst, 0);
// Setup GL Rendering Context
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cDepthBits = 24;
pfd.iLayerType = PFD_MAIN_PLANE;
HDC hDC = GetDC(state.hWnd);
int pxfmt = ChoosePixelFormat(hDC, &pfd);
SetPixelFormat(hDC, pxfmt, &pfd);
HGLRC hRC = wglCreateContext(hDC); // Create OpenGL 2.1 or less
wglMakeCurrent(hDC, hRC);
pp::platform::windows::set_async_render_context(hDC, hRC);
// Initialize extensions
if (!gladLoadGL())
{
LOG("gladLoadGL() failed");
return 0; return 0;
} case MainStartupResult::MissingCoreContextSupport:
if (!gladLoadWGL(hDC)) return -1;
{
LOG("gladLoadWGL() failed");
return 0;
}
auto runtime_info = pp::renderer::gl::OpenGlRuntimeInfo {};
const auto runtime_info_result = pp::renderer::gl::query_opengl_runtime_info(
pp::legacy::gl_runtime::runtime_info_dispatch());
if (runtime_info_result.ok()) {
runtime_info = runtime_info_result.value();
LOG("GL version: %s", runtime_info.version);
LOG("GL vendor: %s", runtime_info.vendor);
LOG("GL renderer: %s", runtime_info.renderer);
} else {
LOG("OpenGL runtime info query failed: %s", runtime_info_result.status().message);
}
#ifdef USE_RENDERDOC
if (!win32_renderdoc_init())
LOG("Renderdoc not started");
#endif // USE_RENDERDOC
const auto renderer_name = std::string(runtime_info.renderer != nullptr ? runtime_info.renderer : "");
swprintf_s(state.window_title, L"PanoPainter %s (%s)", g_version_number_w,
str2wstr(renderer_name).c_str());
// If supported create a 3.3 context
if (GLAD_WGL_ARB_create_context)
{
const auto wgl_config = pp::renderer::gl::windows_wgl_core_context_3_3_config();
UINT numFormat;
wglMakeCurrent(NULL, NULL);
wglDeleteContext(hRC);
DestroyWindow(state.hWnd);
state.hWnd = CreateWindow(wc.lpszClassName, state.window_title, wnd_style, clientPos.x, clientPos.y,
(float)(clientRect.right - clientRect.left),
(float)(clientRect.bottom - clientRect.top), 0, 0, state.hInst, 0);
hDC = GetDC(state.hWnd);
wglChoosePixelFormatARB(hDC, wgl_config.pixel_format_attributes.data(), nullptr, 1, &pxfmt, &numFormat);
SetPixelFormat(hDC, pxfmt, &pfd);
hRC = wglCreateContextAttribsARB(hDC, NULL, wgl_config.context_attributes.data());
wglMakeCurrent(hDC, hRC);
pp::platform::windows::set_async_render_context(hDC, hRC);
}
else
{
LOG("WGL_ARB_create_context not supported");
// If not supported, go fuck yourself we are not gonna support your shitty device
return -1; // A negative number because you are a negative one
} }
//wglSwapIntervalEXT(1); //wglSwapIntervalEXT(1);
@@ -682,7 +779,7 @@ int main(int argc, char** argv)
{ {
WINDOWPLACEMENT wp; WINDOWPLACEMENT wp;
GetWindowPlacement(state.hWnd, &wp); GetWindowPlacement(state.hWnd, &wp);
wp.showCmd = show_cmd; wp.showCmd = startup.show_command;
SetWindowPlacement(state.hWnd, &wp); SetWindowPlacement(state.hWnd, &wp);
//GetClientRect(hWnd, &clientRect); //GetClientRect(hWnd, &clientRect);
//App::I->width = clientRect.right - clientRect.left; //App::I->width = clientRect.right - clientRect.left;
@@ -721,7 +818,7 @@ int main(int argc, char** argv)
// Clean up // Clean up
WacomTablet::I.terminate(); WacomTablet::I.terminate();
UnregisterClass(state.className, state.hInst); UnregisterClass(startup.window_class.lpszClassName, state.hInst);
LogRemote::I.stop(); LogRemote::I.stop();
} }

View File

@@ -39,6 +39,86 @@
#include "node_panel_animation.h" #include "node_panel_animation.h"
#include "node_metadata.h" #include "node_metadata.h"
namespace
{
using NodeChildren = std::vector<std::shared_ptr<Node>>;
using NodeChildIterator = NodeChildren::iterator;
NodeChildIterator find_child_iterator(Node& parent, Node* child)
{
return std::find_if(parent.m_children.begin(), parent.m_children.end(),
[child](const std::shared_ptr<Node>& candidate) { return candidate.get() == child; });
}
template <typename AttachChildrenFn>
void attach_child(Node& parent, Node* child, int yoga_index, AttachChildrenFn&& attach_children)
{
if (child->m_parent)
child->m_parent->remove_child(child);
attach_children(child);
child->m_parent = &parent;
child->set_manager(parent.m_manager);
child->m_destroyed = false;
YGNodeInsertChild(parent.y_node, child->y_node, yoga_index);
child->added(&parent);
parent.on_child_added(child);
}
template <typename AttachChildrenFn>
void attach_child(Node& parent, const std::shared_ptr<Node>& child, int yoga_index, AttachChildrenFn&& attach_children)
{
if (child->m_parent)
child->m_parent->remove_child(child.get());
attach_children(child);
child->m_parent = &parent;
child->set_manager(parent.m_manager);
child->m_destroyed = false;
YGNodeInsertChild(parent.y_node, child->y_node, yoga_index);
child->added(&parent);
parent.on_child_added(child.get());
}
void detach_child(Node& parent, NodeChildIterator child_it)
{
Node* child = child_it->get();
child->removed(&parent);
child->m_parent = nullptr;
YGNodeRemoveChild(parent.y_node, child->y_node);
parent.on_child_removed(child);
parent.m_children.erase(child_it);
if (parent.child_mouse_focus.get() == child)
parent.child_mouse_focus = nullptr;
}
void detach_all_children(Node& parent)
{
for (auto& child : parent.m_children)
{
child->removed(&parent);
child->m_parent = nullptr;
YGNodeRemoveChild(parent.y_node, child->y_node);
parent.on_child_removed(child.get());
}
parent.m_children.clear();
parent.child_mouse_focus = nullptr;
}
void reorder_child(Node& parent, Node* child, int index)
{
int count = YGNodeGetChildCount(parent.y_node);
index = glm::clamp<int>(index, 0, count - 1);
YGNodeRemoveChild(parent.y_node, child->y_node);
YGNodeInsertChild(parent.y_node, child->y_node, index);
auto child_it = find_child_iterator(parent, child);
auto moved_child = *child_it;
parent.m_children.erase(child_it);
parent.m_children.insert(parent.m_children.begin() + index, moved_child);
}
}
void Node::app_redraw() void Node::app_redraw()
{ {
App::I->redraw = true; App::I->redraw = true;
@@ -404,15 +484,11 @@ void Node::add_child(Node* n)
{ {
App::I->ui_task([&] App::I->ui_task([&]
{ {
if (n->m_parent) attach_child(*this, n, YGNodeGetChildCount(y_node),
n->m_parent->remove_child(n); [this](Node* child)
m_children.emplace_back(n); {
n->m_parent = this; m_children.emplace_back(child);
n->set_manager(m_manager); });
n->m_destroyed = false;
YGNodeInsertChild(y_node, n->y_node, YGNodeGetChildCount(y_node));
n->added(this);
on_child_added(n);
}); });
} }
@@ -420,15 +496,12 @@ void Node::add_child(Node* n, int index)
{ {
App::I->ui_task([&] App::I->ui_task([&]
{ {
if (n->m_parent) attach_child(*this, n, index,
n->m_parent->remove_child(n); [this](Node* child)
m_children.emplace_back(n); {
n->m_parent = this; // Preserve the current backing-vector behavior for raw-pointer inserts.
n->set_manager(m_manager); m_children.emplace_back(child);
n->m_destroyed = false; });
YGNodeInsertChild(y_node, n->y_node, index);
n->added(this);
on_child_added(n);
}); });
} }
@@ -436,15 +509,11 @@ void Node::add_child(std::shared_ptr<Node> n)
{ {
App::I->ui_task([this,n] App::I->ui_task([this,n]
{ {
if (n->m_parent) attach_child(*this, n, YGNodeGetChildCount(y_node),
n->m_parent->remove_child(n.get()); [this](const std::shared_ptr<Node>& child)
m_children.push_back(n); {
n->m_parent = this; m_children.push_back(child);
n->set_manager(m_manager); });
n->m_destroyed = false;
YGNodeInsertChild(y_node, n->y_node, YGNodeGetChildCount(y_node));
n->added(this);
on_child_added(n.get());
}); });
} }
@@ -452,15 +521,11 @@ void Node::add_child(std::shared_ptr<Node> n, int index)
{ {
App::I->ui_task([&] App::I->ui_task([&]
{ {
if (n->m_parent) attach_child(*this, n, index,
n->m_parent->remove_child(n.get()); [this, index](const std::shared_ptr<Node>& child)
m_children.insert(m_children.begin() + index, n); {
n->m_parent = this; m_children.insert(m_children.begin() + index, child);
n->set_manager(m_manager); });
n->m_destroyed = false;
YGNodeInsertChild(y_node, n->y_node, index);
n->added(this);
on_child_added(n.get());
}); });
} }
@@ -472,18 +537,12 @@ void Node::remove_from_parent()
void Node::remove_child(Node* n) void Node::remove_child(Node* n)
{ {
auto i = std::find_if(m_children.begin(), m_children.end(), [=](auto& ptr) { return ptr.get() == n; }); auto i = find_child_iterator(*this, n);
if (i != m_children.end()) if (i != m_children.end())
{ {
App::I->ui_task([&] App::I->ui_task([&]
{ {
n->removed(this); detach_child(*this, i);
n->m_parent = nullptr;
YGNodeRemoveChild(y_node, n->y_node);
on_child_removed(n);
m_children.erase(i);
if (child_mouse_focus.get() == n)
child_mouse_focus = nullptr;
}); });
} }
} }
@@ -492,15 +551,7 @@ void Node::remove_all_children()
{ {
App::I->ui_task([&] App::I->ui_task([&]
{ {
for (auto& n : m_children) detach_all_children(*this);
{
n->removed(this);
n->m_parent = nullptr;
YGNodeRemoveChild(y_node, n->y_node);
on_child_removed(n.get());
}
m_children.clear();
child_mouse_focus = nullptr;
}); });
} }
@@ -508,15 +559,7 @@ void Node::move_child(Node* n, int index)
{ {
App::I->ui_task([&] App::I->ui_task([&]
{ {
int count = YGNodeGetChildCount(y_node); reorder_child(*this, n, index);
index = glm::clamp<int>(index, 0, count - 1);
YGNodeRemoveChild(y_node, n->y_node);
YGNodeInsertChild(y_node, n->y_node, index);
auto it = std::find_if(m_children.begin(), m_children.end(),
[n](const std::shared_ptr<Node>& o) { return o.get() == n; });
auto tmp = *it; // copy the ptr before removing it
m_children.erase(it);
m_children.insert(m_children.begin() + index, tmp);
}); });
} }