Extract file menu binding and Win32 splash helper

This commit is contained in:
2026-06-16 11:10:31 +02:00
parent 8afeb087b8
commit d135835787
8 changed files with 369 additions and 232 deletions

View File

@@ -94,6 +94,7 @@ set(PP_PANOPAINTER_APP_SOURCES
src/app_dialogs.cpp src/app_dialogs.cpp
src/app_events.cpp src/app_events.cpp
src/app_layout.cpp src/app_layout.cpp
src/app_layout_file_menu.cpp
src/app_layout_tools_menu.cpp src/app_layout_tools_menu.cpp
src/app_shaders.cpp src/app_shaders.cpp
src/app_vr.cpp src/app_vr.cpp
@@ -161,6 +162,8 @@ set(PP_WINDOWS_PLATFORM_SOURCES
src/main.cpp src/main.cpp
src/platform_windows/windows_platform_services.cpp src/platform_windows/windows_platform_services.cpp
src/platform_windows/windows_platform_services.h src/platform_windows/windows_platform_services.h
src/platform_windows/windows_splash.cpp
src/platform_windows/windows_splash.h
) )
set(PP_WINDOWS_APP_SOURCES set(PP_WINDOWS_APP_SOURCES

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`: 1498 lines - `src/app_layout.cpp`: 1360 lines
- `src/canvas_modes.cpp`: 1798 lines - `src/canvas_modes.cpp`: 1798 lines
- `src/node.cpp`: 1551 lines - `src/node.cpp`: 1551 lines
- `src/main.cpp`: 1374 lines - `src/main.cpp`: 1181 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`: 888 lines - `src/node_canvas.cpp`: 888 lines
@@ -127,7 +127,9 @@ Current architecture mismatches that must be treated as real blockers:
rather than thin composition/binding surfaces, even though tools-menu binding rather than thin composition/binding surfaces, even though tools-menu binding
plus nested panels/options submenu wiring now live in plus nested panels/options submenu wiring now live in
`src/app_layout_tools_menu.cpp` and `App::init_menu_tools()` is now a thin `src/app_layout_tools_menu.cpp` and `App::init_menu_tools()` is now a thin
call-through. call-through, while file-menu binding plus the export submenu wiring now also
live in `src/app_layout_file_menu.cpp` and `App::init_menu_file()` is now a
thin call-through.
- `App`, `Canvas`, `Node`, retained workers, and platform entrypoints still use - `App`, `Canvas`, `Node`, retained workers, and platform entrypoints still use
global singleton reach, raw observer pointers, retained static worker global singleton reach, raw observer pointers, retained static worker
ownership in several app families, and ad hoc mutex/condition-variable ownership in several app families, and ad hoc mutex/condition-variable
@@ -149,7 +151,10 @@ Current architecture mismatches that must be treated as real blockers:
local worker-state helper instead of a bare static accessor, the local worker-state helper instead of a bare static accessor, the
prepared-file worker and the canvas async import/export/save/open worker now prepared-file worker and the canvas async import/export/save/open worker now
live under `AppRuntime` instead of retained static app-events/canvas live under `AppRuntime` instead of retained static app-events/canvas
workers, `App::rec_loop()` now delegates worker-iteration orchestration into workers, and the splash-screen dialog loop, HWND ownership, and bitmap setup
now live in `src/platform_windows/windows_splash.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 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
canvas state-management cluster for picking, clear/clear-all, layer canvas state-management cluster for picking, clear/clear-all, layer

View File

@@ -271,7 +271,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 1498-line mixed file that builds menus, `src/app_layout.cpp` is still a 1360-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:
@@ -279,6 +279,10 @@ Current slice:
now lives in `src/app_layout_tools_menu.cpp`, and `App::init_menu_tools()` now lives in `src/app_layout_tools_menu.cpp`, and `App::init_menu_tools()`
is now a thin call-through, but file/about/layer/sidebar and broader layout is now a thin call-through, but file/about/layer/sidebar and broader layout
composition are still inline in `src/app_layout.cpp`. composition are still inline in `src/app_layout.cpp`.
- File-menu UI binding, including the export submenu wiring, now also lives in
`src/app_layout_file_menu.cpp`, and `App::init_menu_file()` is now a thin
call-through, but about/layer/sidebar and broader layout composition are
still inline in `src/app_layout.cpp`.
Write scope: Write scope:
- `src/app_layout.cpp` - `src/app_layout.cpp`
@@ -392,6 +396,9 @@ Current slice:
- `main.cpp` Win32 window handles, GL task/mutex state, splash-dialog state, - `main.cpp` Win32 window handles, GL task/mutex state, splash-dialog state,
stylus timers, and VR worker state now sit behind one retained local state stylus timers, and VR worker state now sit behind one retained local state
object instead of separate file-scope globals object instead of separate file-scope globals
- the splash-screen dialog loop, HWND ownership, and bitmap setup now also live
in `src/platform_windows/windows_splash.cpp` instead of `src/main.cpp`, and
`main.cpp` now just owns the helper lifecycle
- Win32 async GL/context lock state now lives in - Win32 async GL/context lock state now lives in
`src/platform_windows/windows_platform_services.cpp` instead of `main.cpp` `src/platform_windows/windows_platform_services.cpp` instead of `main.cpp`
retained state, and `main.cpp` only seeds that platform-owned context handle retained state, and `main.cpp` only seeds that platform-owned context handle

View File

@@ -13,8 +13,6 @@
#include "app_core/canvas_tool_ui.h" #include "app_core/canvas_tool_ui.h"
#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/document_export.h"
#include "app_core/file_menu.h"
#include "app_core/app_status.h" #include "app_core/app_status.h"
#include "app_core/main_toolbar.h" #include "app_core/main_toolbar.h"
#include "app_core/tools_menu.h" #include "app_core/tools_menu.h"
@@ -34,6 +32,7 @@
#include <vector> #include <vector>
namespace pp::panopainter { namespace pp::panopainter {
void bind_legacy_file_menu(App& app);
void bind_legacy_tools_menu(App& app); void bind_legacy_tools_menu(App& app);
} }
@@ -54,16 +53,6 @@ bool apply_brush_preset_plan(App& app, const std::shared_ptr<Brush>& brush)
return pp::panopainter::apply_legacy_brush_preset_plan(app, brush); return pp::panopainter::apply_legacy_brush_preset_plan(app, brush);
} }
bool apply_document_export_menu_plan(App& app, pp::app::DocumentExportMenuKind kind)
{
return pp::panopainter::apply_legacy_document_export_menu_plan(app, kind);
}
void apply_file_menu_plan(App& app, pp::app::FileMenuCommand command)
{
pp::panopainter::apply_legacy_file_menu_command(app, command);
}
std::shared_ptr<NodePopupMenu> add_menu_popup( std::shared_ptr<NodePopupMenu> add_menu_popup(
App& app, App& app,
const char* template_id, const char* template_id,
@@ -752,138 +741,7 @@ void App::init_toolbar_draw()
void App::init_menu_file() void App::init_menu_file()
{ {
if (auto* menu_file = layout[main_id]->find<NodeButtonCustom>("menu-file")) pp::panopainter::bind_legacy_file_menu(*this);
{
menu_file->on_click = [this, menu_file](Node*) {
auto* popup_root = layout[main_id];
if (!popup_root) {
return;
}
const auto open_checked_menu_popup = [this, popup_root](const char* id, glm::vec2 pos, float width)
-> std::pair<std::shared_ptr<NodePopupMenu>, pp::ui::NodeHandle>
{
auto popup = add_menu_popup(*this, id, pos, width);
if (!popup) {
return {};
}
pp::panopainter::detach_legacy_node_from_parent(*popup);
auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, popup);
if (!popup_overlay) {
pp::panopainter::destroy_legacy_node(*popup);
return {};
}
return { popup, popup_overlay.value() };
};
glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y);
const auto [popup, popup_handle] = open_checked_menu_popup("file-menu", pos, menu_file->m_size.x);
if (!popup)
return;
if (auto b = popup->find<NodeButtonCustom>("file-newdoc"))
b->on_click = [this, popup_root, popup_handle](Node*) {
apply_file_menu_plan(*this, pp::app::FileMenuCommand::new_document);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-import"))
b->on_click = [this, popup_root, popup_handle](Node*) {
apply_file_menu_plan(*this, pp::app::FileMenuCommand::import_image);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-open"))
b->on_click = [this, popup_root, popup_handle](Node*) {
apply_file_menu_plan(*this, pp::app::FileMenuCommand::open_project);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-browse"))
b->on_click = [this, popup_root, popup_handle](Node*) {
apply_file_menu_plan(*this, pp::app::FileMenuCommand::browse_cloud);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-save"))
b->on_click = [this, popup_root, popup_handle](Node*) {
apply_file_menu_plan(*this, pp::app::FileMenuCommand::save);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-save-as"))
b->on_click = [this, popup_root, popup_handle](Node*) {
apply_file_menu_plan(*this, pp::app::FileMenuCommand::save_as);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-save-ver"))
b->on_click = [this, popup_root, popup_handle](Node*) {
apply_file_menu_plan(*this, pp::app::FileMenuCommand::save_version);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-export"))
b->on_click = [this, popup_root, popup_handle](Node*) {
apply_file_menu_plan(*this, pp::app::FileMenuCommand::export_jpeg);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-export-tick"))
b->on_click = [this, b, popup_root, popup_handle, open_checked_menu_popup](Node*) {
glm::vec2 pos = b->m_pos + glm::vec2(b->m_size.x, 0);
const auto [subpopup, subpopup_handle] = open_checked_menu_popup("file-submenu-export", pos, b->m_size.x);
if (!subpopup)
return;
subpopup->find<NodeButtonCustom>("file-submenu-export-png")->on_click = [this, popup_root, popup_handle, subpopup_handle](Node*) {
apply_document_export_menu_plan(*this, pp::app::DocumentExportMenuKind::png);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle);
};
subpopup->find<NodeButtonCustom>("file-submenu-export-layers")->on_click = [this, popup_root, popup_handle, subpopup_handle](Node*) {
apply_document_export_menu_plan(*this, pp::app::DocumentExportMenuKind::layers);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle);
};
subpopup->find<NodeButtonCustom>("file-submenu-export-cube")->on_click = [this, popup_root, popup_handle, subpopup_handle](Node*) {
apply_document_export_menu_plan(*this, pp::app::DocumentExportMenuKind::cube_faces);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle);
};
subpopup->find<NodeButtonCustom>("file-submenu-export-depth")->on_click = [this, popup_root, popup_handle, subpopup_handle](Node*) {
apply_document_export_menu_plan(*this, pp::app::DocumentExportMenuKind::depth);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle);
};
subpopup->find<NodeButtonCustom>("file-submenu-export-anim")->on_click = [this, popup_root, popup_handle, subpopup_handle](Node*) {
apply_document_export_menu_plan(*this, pp::app::DocumentExportMenuKind::animation_frames);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle);
};
subpopup->find<NodeButtonCustom>("file-submenu-export-anim-mp4")->on_click = [this, popup_root, popup_handle, subpopup_handle](Node*) {
apply_document_export_menu_plan(*this, pp::app::DocumentExportMenuKind::animation_mp4);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle);
};
subpopup->find<NodeButtonCustom>("file-submenu-export-timelapse")->on_click = [this, popup_root, popup_handle, subpopup_handle](Node*) {
apply_document_export_menu_plan(*this, pp::app::DocumentExportMenuKind::timelapse);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle);
};
};
if (auto b = popup->find<NodeButtonCustom>("file-share"))
b->on_click = [this, popup_root, popup_handle](Node*) {
apply_file_menu_plan(*this, pp::app::FileMenuCommand::share);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-resize"))
b->on_click = [this, popup_root, popup_handle](Node*) {
apply_file_menu_plan(*this, pp::app::FileMenuCommand::resize);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-cloud-upload"))
b->on_click = [this, popup_root, popup_handle](Node*) {
apply_file_menu_plan(*this, pp::app::FileMenuCommand::cloud_upload);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-cloud-browse"))
b->on_click = [this, popup_root, popup_handle](Node*) {
apply_file_menu_plan(*this, pp::app::FileMenuCommand::cloud_browse);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
};
}
} }
void App::init_menu_edit() void App::init_menu_edit()

View File

@@ -0,0 +1,190 @@
#include "pch.h"
#include "app.h"
#include "app_core/document_export.h"
#include "app_core/file_menu.h"
#include "legacy_app_shell_services.h"
#include "legacy_ui_overlay_services.h"
#include "node_button_custom.h"
#include "node_popup_menu.h"
namespace {
void apply_document_export_menu_plan(App& app, pp::app::DocumentExportMenuKind kind)
{
(void)pp::panopainter::apply_legacy_document_export_menu_plan(app, kind);
}
void apply_file_menu_plan(App& app, pp::app::FileMenuCommand command)
{
pp::panopainter::apply_legacy_file_menu_command(app, command);
}
std::shared_ptr<NodePopupMenu> add_menu_popup(
App& app,
const char* template_id,
glm::vec2 position,
float rtl_anchor_width)
{
const auto popup = pp::panopainter::add_legacy_popup_menu(
app,
template_id,
position.x,
position.y,
rtl_anchor_width);
if (!popup) {
LOG("Popup menu '%s' failed: %s", template_id ? template_id : "<null>", popup.status().message);
return nullptr;
}
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);
}
} // namespace
namespace pp::panopainter {
void bind_legacy_file_menu(App& app)
{
auto main = app.layout[app.main_id];
if (auto* menu_file = main->find<NodeButtonCustom>("menu-file"))
{
menu_file->on_click = [&app, menu_file](Node*) {
auto* popup_root = app.layout[app.main_id];
if (!popup_root) {
return;
}
const auto open_checked_menu_popup = [popup_root](App& app_ref, const char* id, glm::vec2 pos, float width)
-> std::pair<std::shared_ptr<NodePopupMenu>, pp::ui::NodeHandle>
{
auto popup = add_menu_popup(app_ref, id, pos, width);
if (!popup) {
return {};
}
pp::panopainter::detach_legacy_node_from_parent(*popup);
auto popup_overlay = pp::panopainter::open_legacy_overlay_node_with_handle(*popup_root, popup);
if (!popup_overlay) {
pp::panopainter::destroy_legacy_node(*popup);
return {};
}
return { popup, popup_overlay.value() };
};
glm::vec2 pos = menu_file->m_pos + glm::vec2(0, menu_file->m_size.y);
const auto [popup, popup_handle] = open_checked_menu_popup(app, "file-menu", pos, menu_file->m_size.x);
if (!popup)
return;
if (auto b = popup->find<NodeButtonCustom>("file-newdoc"))
b->on_click = [&app, popup_root, popup_handle](Node*) {
apply_file_menu_plan(app, pp::app::FileMenuCommand::new_document);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-import"))
b->on_click = [&app, popup_root, popup_handle](Node*) {
apply_file_menu_plan(app, pp::app::FileMenuCommand::import_image);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-open"))
b->on_click = [&app, popup_root, popup_handle](Node*) {
apply_file_menu_plan(app, pp::app::FileMenuCommand::open_project);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-browse"))
b->on_click = [&app, popup_root, popup_handle](Node*) {
apply_file_menu_plan(app, pp::app::FileMenuCommand::browse_cloud);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-save"))
b->on_click = [&app, popup_root, popup_handle](Node*) {
apply_file_menu_plan(app, pp::app::FileMenuCommand::save);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-save-as"))
b->on_click = [&app, popup_root, popup_handle](Node*) {
apply_file_menu_plan(app, pp::app::FileMenuCommand::save_as);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-save-ver"))
b->on_click = [&app, popup_root, popup_handle](Node*) {
apply_file_menu_plan(app, pp::app::FileMenuCommand::save_version);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-export"))
b->on_click = [&app, popup_root, popup_handle](Node*) {
apply_file_menu_plan(app, pp::app::FileMenuCommand::export_jpeg);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-export-tick"))
b->on_click = [&app, b, popup_root, popup_handle, open_checked_menu_popup](Node*) {
glm::vec2 pos = b->m_pos + glm::vec2(b->m_size.x, 0);
const auto [subpopup, subpopup_handle] = open_checked_menu_popup(app, "file-submenu-export", pos, b->m_size.x);
if (!subpopup)
return;
subpopup->find<NodeButtonCustom>("file-submenu-export-png")->on_click = [&app, popup_root, popup_handle, subpopup_handle](Node*) {
apply_document_export_menu_plan(app, pp::app::DocumentExportMenuKind::png);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle);
};
subpopup->find<NodeButtonCustom>("file-submenu-export-layers")->on_click = [&app, popup_root, popup_handle, subpopup_handle](Node*) {
apply_document_export_menu_plan(app, pp::app::DocumentExportMenuKind::layers);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle);
};
subpopup->find<NodeButtonCustom>("file-submenu-export-cube")->on_click = [&app, popup_root, popup_handle, subpopup_handle](Node*) {
apply_document_export_menu_plan(app, pp::app::DocumentExportMenuKind::cube_faces);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle);
};
subpopup->find<NodeButtonCustom>("file-submenu-export-depth")->on_click = [&app, popup_root, popup_handle, subpopup_handle](Node*) {
apply_document_export_menu_plan(app, pp::app::DocumentExportMenuKind::depth);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle);
};
subpopup->find<NodeButtonCustom>("file-submenu-export-anim")->on_click = [&app, popup_root, popup_handle, subpopup_handle](Node*) {
apply_document_export_menu_plan(app, pp::app::DocumentExportMenuKind::animation_frames);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle);
};
subpopup->find<NodeButtonCustom>("file-submenu-export-anim-mp4")->on_click = [&app, popup_root, popup_handle, subpopup_handle](Node*) {
apply_document_export_menu_plan(app, pp::app::DocumentExportMenuKind::animation_mp4);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle);
};
subpopup->find<NodeButtonCustom>("file-submenu-export-timelapse")->on_click = [&app, popup_root, popup_handle, subpopup_handle](Node*) {
apply_document_export_menu_plan(app, pp::app::DocumentExportMenuKind::timelapse);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
close_legacy_overlay_handle_ignoring_status(*popup_root, subpopup_handle);
};
};
if (auto b = popup->find<NodeButtonCustom>("file-share"))
b->on_click = [&app, popup_root, popup_handle](Node*) {
apply_file_menu_plan(app, pp::app::FileMenuCommand::share);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-resize"))
b->on_click = [&app, popup_root, popup_handle](Node*) {
apply_file_menu_plan(app, pp::app::FileMenuCommand::resize);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-cloud-upload"))
b->on_click = [&app, popup_root, popup_handle](Node*) {
apply_file_menu_plan(app, pp::app::FileMenuCommand::cloud_upload);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
if (auto b = popup->find<NodeButtonCustom>("file-cloud-browse"))
b->on_click = [&app, popup_root, popup_handle](Node*) {
apply_file_menu_plan(app, pp::app::FileMenuCommand::cloud_browse);
close_legacy_overlay_handle_ignoring_status(*popup_root, popup_handle);
};
};
}
}
} // namespace pp::panopainter

View File

@@ -12,6 +12,7 @@
#include "legacy_preference_storage.h" #include "legacy_preference_storage.h"
#include "renderer_gl/opengl_capabilities.h" #include "renderer_gl/opengl_capabilities.h"
#include "platform_windows/windows_platform_services.h" #include "platform_windows/windows_platform_services.h"
#include "platform_windows/windows_splash.h"
#include "../resource.h" #include "../resource.h"
#include <shellscalingapi.h> #include <shellscalingapi.h>
@@ -61,7 +62,6 @@ struct RetainedState
std::mutex hmd_render_mutex; std::mutex hmd_render_mutex;
std::condition_variable hmd_render_cv; std::condition_variable hmd_render_cv;
std::unique_ptr<Vive> vive; std::unique_ptr<Vive> vive;
HWND splash_dialog{};
}; };
RetainedState& retained_state() RetainedState& retained_state()
@@ -669,84 +669,6 @@ void win32_save_window_state()
p.rcNormalPosition.right, p.rcNormalPosition.right,
p.rcNormalPosition.bottom }); p.rcNormalPosition.bottom });
} }
HBITMAP image_to_hbitmap(const Image& img)
{
BITMAPINFOHEADER bmih;
memset(&bmih, 0, sizeof(BITMAPINFOHEADER));
bmih.biWidth = img.width;
bmih.biHeight = img.height;
bmih.biBitCount = 32;
bmih.biCompression = BI_RGB;
bmih.biSize = sizeof(BITMAPINFOHEADER);
bmih.biPlanes = 1;
BITMAPINFO* bmi = (BITMAPINFO*)&bmih;
return CreateDIBitmap(GetDC(NULL), &bmih, CBM_INIT, img.data(), bmi, DIB_RGB_COLORS);
}
LRESULT CALLBACK splash_proc(HWND hWndDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch (Msg)
{
case WM_INITDIALOG:
{
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);
float z = (float)x / 96.f;
static char base_path[MAX_PATH];
GetCurrentDirectoryA(MAX_PATH, base_path);
std::string path = std::string(base_path) + "\\data\\splash.jpg";
//auto hbitmap = (HBITMAP)LoadImageA(NULL, path.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
Image img;
img.load_file(path);
img.flip();
auto hbitmap = image_to_hbitmap(img.resize(512 * z, 200 * z));
SendMessage(GetDlgItem(hWndDlg, IDC_STATIC_IMAGE), STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbitmap);
SetDlgItemText(hWndDlg, IDC_STATIC_VERSION, g_version_number_w);
RECT r;
GetClientRect(hWndDlg, &r);
MoveWindow(GetDlgItem(hWndDlg, IDC_STATIC_IMAGE), 0, 0, 512 * z, 200 * z, TRUE);
SetWindowPos(hWndDlg, HWND_TOP, 0, 0, 512 * z, 200 * z, SWP_NOMOVE);
HWND hWndVersion = GetDlgItem(hWndDlg, IDC_STATIC_VERSION);
RECT rv;
GetClientRect(hWndVersion, &rv);
MoveWindow(hWndVersion, 0, 200 * z - (rv.bottom - rv.top), r.right - r.left, rv.bottom - rv.top, TRUE);
return TRUE;
}
case WM_USER_CLOSE:
PostQuitMessage(0);
return TRUE;
}
return DefWindowProc(hWndDlg, Msg, wParam, lParam);;
}
void splash_thread_loop()
{
auto& state = retained_state();
BT_SetTerminate();
state.splash_dialog = CreateDialog(state.hInst, MAKEINTRESOURCE(IDD_SPLASH), NULL, reinterpret_cast<DLGPROC>(splash_proc));
MSG msg;
while (GetMessage(&msg, 0, 0, 0) > 0)
{
if (IsDialogMessage(state.splash_dialog, &msg))
{
DispatchMessage(&msg);
TranslateMessage(&msg);
}
}
DestroyWindow(state.splash_dialog);
}
BOOL UnadjustWindowRectEx(LPRECT prc, DWORD dwStyle, BOOL fMenu, DWORD dwExStyle) BOOL UnadjustWindowRectEx(LPRECT prc, DWORD dwStyle, BOOL fMenu, DWORD dwExStyle)
{ {
RECT rc; RECT rc;
@@ -811,7 +733,7 @@ int main(int argc, char** argv)
LOG("data files ok"); LOG("data files ok");
} }
std::jthread dialog_thread(splash_thread_loop); pp::platform::windows::SplashScreen splash(GetModuleHandle(NULL));
init_vk_map(); init_vk_map();
@@ -1022,9 +944,7 @@ int main(int argc, char** argv)
SetForegroundWindow(state.hWnd); SetForegroundWindow(state.hWnd);
//ShowWindow(hWnd, show_cmd); //ShowWindow(hWnd, show_cmd);
SendMessage(state.splash_dialog, WM_USER_CLOSE, 0, 0); splash.dismiss();
if (dialog_thread.joinable())
dialog_thread.join();
MSG msg; MSG msg;
LOG("start main loop"); LOG("start main loop");

View File

@@ -0,0 +1,124 @@
#include "pch.h"
#include "platform_windows/windows_splash.h"
#include "app.h"
#include "image.h"
#include "log.h"
#include "version.h"
#include "../resource.h"
#include <chrono>
#include <shellscalingapi.h>
#include <string>
#include <stop_token>
extern HRESULT(*GetDpiForMonitor_fn)(HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT* dpiX, UINT* dpiY);
namespace {
constexpr UINT kSplashCloseMessage = WM_USER + 1;
HBITMAP image_to_hbitmap(const Image& img)
{
BITMAPINFOHEADER bmih;
memset(&bmih, 0, sizeof(BITMAPINFOHEADER));
bmih.biWidth = img.width;
bmih.biHeight = img.height;
bmih.biBitCount = 32;
bmih.biCompression = BI_RGB;
bmih.biSize = sizeof(BITMAPINFOHEADER);
bmih.biPlanes = 1;
BITMAPINFO* bmi = (BITMAPINFO*)&bmih;
return CreateDIBitmap(GetDC(NULL), &bmih, CBM_INIT, img.data(), bmi, DIB_RGB_COLORS);
}
LRESULT CALLBACK splash_proc(HWND hWndDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch (Msg)
{
case WM_INITDIALOG:
{
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);
float z = (float)x / 96.f;
static char base_path[MAX_PATH];
GetCurrentDirectoryA(MAX_PATH, base_path);
std::string path = std::string(base_path) + "\\data\\splash.jpg";
Image img;
img.load_file(path);
img.flip();
auto hbitmap = image_to_hbitmap(img.resize(512 * z, 200 * z));
SendMessage(GetDlgItem(hWndDlg, IDC_STATIC_IMAGE), STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbitmap);
SetDlgItemText(hWndDlg, IDC_STATIC_VERSION, g_version_number_w);
RECT r;
GetClientRect(hWndDlg, &r);
MoveWindow(GetDlgItem(hWndDlg, IDC_STATIC_IMAGE), 0, 0, 512 * z, 200 * z, TRUE);
SetWindowPos(hWndDlg, HWND_TOP, 0, 0, 512 * z, 200 * z, SWP_NOMOVE);
HWND hWndVersion = GetDlgItem(hWndDlg, IDC_STATIC_VERSION);
RECT rv;
GetClientRect(hWndVersion, &rv);
MoveWindow(hWndVersion, 0, 200 * z - (rv.bottom - rv.top), r.right - r.left, rv.bottom - rv.top, TRUE);
return TRUE;
}
case kSplashCloseMessage:
PostQuitMessage(0);
return TRUE;
}
return DefWindowProc(hWndDlg, Msg, wParam, lParam);
}
}
namespace pp::platform::windows {
SplashScreen::SplashScreen(HINSTANCE hInst)
: hInst_(hInst)
, thread_([this](std::stop_token) { thread_main(); })
{
}
SplashScreen::~SplashScreen()
{
dismiss();
}
void SplashScreen::dismiss()
{
if (HWND dialog = dialog_.load(std::memory_order_acquire))
SendMessage(dialog, kSplashCloseMessage, 0, 0);
if (thread_.joinable())
thread_.join();
}
void SplashScreen::thread_main()
{
BT_SetTerminate();
HWND dialog = CreateDialog(hInst_, MAKEINTRESOURCE(IDD_SPLASH), NULL, reinterpret_cast<DLGPROC>(splash_proc));
dialog_.store(dialog, std::memory_order_release);
if (!dialog)
return;
MSG msg;
while (GetMessage(&msg, 0, 0, 0) > 0)
{
if (IsDialogMessage(dialog, &msg))
{
DispatchMessage(&msg);
TranslateMessage(&msg);
}
}
if (HWND dialog = dialog_.exchange(nullptr))
DestroyWindow(dialog);
}
}

View File

@@ -0,0 +1,30 @@
#pragma once
#include <windows.h>
#include <atomic>
#include <thread>
namespace pp::platform::windows {
class SplashScreen final
{
public:
explicit SplashScreen(HINSTANCE hInst);
~SplashScreen();
SplashScreen(const SplashScreen&) = delete;
SplashScreen& operator=(const SplashScreen&) = delete;
SplashScreen(SplashScreen&&) = delete;
SplashScreen& operator=(SplashScreen&&) = delete;
void dismiss();
private:
void thread_main();
HINSTANCE hInst_{};
std::atomic<HWND> dialog_{};
std::jthread thread_;
};
}