diff --git a/cmake/PanoPainterSources.cmake b/cmake/PanoPainterSources.cmake index 32efe9e1..d9110f3c 100644 --- a/cmake/PanoPainterSources.cmake +++ b/cmake/PanoPainterSources.cmake @@ -98,6 +98,7 @@ set(PP_PANOPAINTER_APP_SOURCES src/app_events.cpp src/app_layout.cpp src/app_layout_sidebar.cpp + src/app_layout_main_toolbar.cpp src/app_layout_about_layer_menu.cpp src/app_layout_file_menu.cpp src/app_layout_tools_menu.cpp diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index d2e5fc9d..8fbdfefd 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -80,10 +80,10 @@ What is still carrying too much live ownership: Current hotspot files: - `src/canvas.cpp`: 2645 lines -- `src/app_layout.cpp`: 785 lines +- `src/app_layout.cpp`: 743 lines - `src/canvas_modes.cpp`: 1798 lines -- `src/node.cpp`: 1551 lines -- `src/main.cpp`: 882 lines +- `src/node.cpp`: 1594 lines +- `src/main.cpp`: 1098 lines - `src/node_panel_brush.cpp`: 1197 lines - `src/node_stroke_preview.cpp`: 933 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 `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 + `App::init_sidebar()` is now a thin call-through, while main-toolbar binding + now also lives in `src/app_layout_main_toolbar.cpp` and + `App::init_toolbar_main()` 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 `src/app_dialogs_export.cpp` and those `App::dialog_*` entrypoints are 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 @@ -183,6 +186,12 @@ Current architecture mismatches that must be treated as real blockers: 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 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 the retained recording bridge, `App::update_rec_frames()` now delegates 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` and `NodeStrokePreview` render-target setup plus immediate-pass sequencing 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`, explicit result/status objects, and a few concepts, but the live app still does not consistently express ownership, thread affinity, or renderer diff --git a/docs/modernization/tasks.md b/docs/modernization/tasks.md index d4999e1e..e86426ee 100644 --- a/docs/modernization/tasks.md +++ b/docs/modernization/tasks.md @@ -293,7 +293,7 @@ targets look like helpers under one old monolith. Status: In Progress 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. Current slice: @@ -313,6 +313,9 @@ Current slice: `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`. +- 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: - `src/app_layout.cpp` @@ -473,6 +476,12 @@ Current slice: 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` +- `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 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 @@ -632,6 +641,12 @@ Why now: mixes raw `Node*`, shared ownership, direct `add_child(...)`, and callback 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: - `src/node.*` - `src/layout.*` diff --git a/src/app_layout.cpp b/src/app_layout.cpp index 882c5a56..db293310 100644 --- a/src/app_layout.cpp +++ b/src/app_layout.cpp @@ -14,14 +14,12 @@ #include "app_core/document_layer.h" #include "app_core/document_canvas.h" #include "app_core/app_status.h" -#include "app_core/main_toolbar.h" #include "app_core/tools_menu.h" #include "legacy_app_preference_services.h" #include "legacy_app_shell_services.h" #include "legacy_brush_ui_services.h" #include "legacy_canvas_tool_services.h" #include "legacy_document_layer_services.h" -#include "legacy_history_services.h" #include "legacy_preference_storage.h" #include "legacy_ui_overlay_services.h" #include "serializer.h" @@ -32,6 +30,7 @@ #include namespace pp::panopainter { +void bind_legacy_main_toolbar(App& app); void bind_legacy_file_menu(App& app); void bind_legacy_about_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; } -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) { pp::panopainter::execute_legacy_tools_menu_plan(app, plan); @@ -135,101 +129,7 @@ void App::title_update() void App::init_toolbar_main() { - if (auto* button = layout[main_id]->find("btn-anim")) - { - button->on_click = [this, button](Node*) { - if (canvas) - { - //canvas->m_canvas->export_anim(); - } - }; - } - if (auto* button = layout[main_id]->find("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("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("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("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("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("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(canvas)); - if (plan) - execute_main_toolbar_plan(*this, plan.value()); - }; - } - if (auto* button = layout[main_id]->find("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("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()); - }; - } + pp::panopainter::bind_legacy_main_toolbar(*this); } [[nodiscard]] bool current_canvas_mode_is_draw(App& app) noexcept diff --git a/src/app_layout_main_toolbar.cpp b/src/app_layout_main_toolbar.cpp new file mode 100644 index 00000000..5de148b9 --- /dev/null +++ b/src/app_layout_main_toolbar.cpp @@ -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("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("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("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("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("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("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("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(app.canvas)); + if (plan) + execute_main_toolbar_plan(app, plan.value()); + }; + } + if (auto* button = app.layout[app.main_id]->find("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("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 diff --git a/src/main.cpp b/src/main.cpp index 7962edd3..c383c4e0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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(App::I->width * App::I->zoom), + static_cast(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() @@ -454,8 +687,6 @@ void win32_save_window_state() int main(int argc, char** argv) { auto& state = retained_state(); - WNDCLASS wc; - PIXELFORMATDESCRIPTOR pfd; App::I = new App(); App::I->set_platform_services(&pp::platform::windows::platform_services()); @@ -467,23 +698,7 @@ int main(int argc, char** argv) if(SetProcessDpiAwareness_fn) SetProcessDpiAwareness_fn(PROCESS_PER_MONITOR_DPI_AWARE); - - 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"); - } + ensure_runtime_data_directory(); pp::platform::windows::SplashScreen splash(GetModuleHandle(NULL)); @@ -496,135 +711,17 @@ int main(int argc, char** argv) App::I->create(); - // Inizialize data structures to zero - memset(&wc, 0, sizeof(wc)); memset(&state.keys, 0, sizeof(state.keys)); - memset(&pfd, 0, sizeof(pfd)); - - // Create the main window - - 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 startup = initialize_main_window_startup_state(); + auto context = OpenGlWindowContext {}; + switch (initialize_main_window_and_gl(startup, context)) { - 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; - 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"); + case MainStartupResult::Ok: + break; + case MainStartupResult::GladLoadFailure: return 0; - } - if (!gladLoadWGL(hDC)) - { - 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 + case MainStartupResult::MissingCoreContextSupport: + return -1; } //wglSwapIntervalEXT(1); @@ -682,7 +779,7 @@ int main(int argc, char** argv) { WINDOWPLACEMENT wp; GetWindowPlacement(state.hWnd, &wp); - wp.showCmd = show_cmd; + wp.showCmd = startup.show_command; SetWindowPlacement(state.hWnd, &wp); //GetClientRect(hWnd, &clientRect); //App::I->width = clientRect.right - clientRect.left; @@ -721,7 +818,7 @@ int main(int argc, char** argv) // Clean up WacomTablet::I.terminate(); - UnregisterClass(state.className, state.hInst); + UnregisterClass(startup.window_class.lpszClassName, state.hInst); LogRemote::I.stop(); } diff --git a/src/node.cpp b/src/node.cpp index 10090042..ff805f08 100644 --- a/src/node.cpp +++ b/src/node.cpp @@ -39,6 +39,86 @@ #include "node_panel_animation.h" #include "node_metadata.h" +namespace +{ +using NodeChildren = std::vector>; +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& candidate) { return candidate.get() == child; }); +} + +template +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 +void attach_child(Node& parent, const std::shared_ptr& 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(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() { App::I->redraw = true; @@ -404,15 +484,11 @@ void Node::add_child(Node* n) { App::I->ui_task([&] { - if (n->m_parent) - n->m_parent->remove_child(n); - m_children.emplace_back(n); - n->m_parent = this; - 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); + attach_child(*this, n, YGNodeGetChildCount(y_node), + [this](Node* child) + { + m_children.emplace_back(child); + }); }); } @@ -420,15 +496,12 @@ void Node::add_child(Node* n, int index) { App::I->ui_task([&] { - if (n->m_parent) - n->m_parent->remove_child(n); - m_children.emplace_back(n); - n->m_parent = this; - n->set_manager(m_manager); - n->m_destroyed = false; - YGNodeInsertChild(y_node, n->y_node, index); - n->added(this); - on_child_added(n); + attach_child(*this, n, index, + [this](Node* child) + { + // Preserve the current backing-vector behavior for raw-pointer inserts. + m_children.emplace_back(child); + }); }); } @@ -436,15 +509,11 @@ void Node::add_child(std::shared_ptr n) { App::I->ui_task([this,n] { - if (n->m_parent) - n->m_parent->remove_child(n.get()); - m_children.push_back(n); - n->m_parent = this; - 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()); + attach_child(*this, n, YGNodeGetChildCount(y_node), + [this](const std::shared_ptr& child) + { + m_children.push_back(child); + }); }); } @@ -452,15 +521,11 @@ void Node::add_child(std::shared_ptr n, int index) { App::I->ui_task([&] { - if (n->m_parent) - n->m_parent->remove_child(n.get()); - m_children.insert(m_children.begin() + index, n); - n->m_parent = this; - n->set_manager(m_manager); - n->m_destroyed = false; - YGNodeInsertChild(y_node, n->y_node, index); - n->added(this); - on_child_added(n.get()); + attach_child(*this, n, index, + [this, index](const std::shared_ptr& child) + { + m_children.insert(m_children.begin() + index, child); + }); }); } @@ -472,18 +537,12 @@ void Node::remove_from_parent() 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()) { App::I->ui_task([&] { - n->removed(this); - 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; + detach_child(*this, i); }); } } @@ -492,15 +551,7 @@ void Node::remove_all_children() { App::I->ui_task([&] { - for (auto& n : m_children) - { - 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; + detach_all_children(*this); }); } @@ -508,15 +559,7 @@ void Node::move_child(Node* n, int index) { App::I->ui_task([&] { - int count = YGNodeGetChildCount(y_node); - index = glm::clamp(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& 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); + reorder_child(*this, n, index); }); }