Extract UI state and Win32 window shell

This commit is contained in:
2026-06-16 13:16:19 +02:00
parent cb9d06c6dc
commit 8ea56cbd30
12 changed files with 619 additions and 670 deletions

View File

@@ -21,8 +21,6 @@
#include "legacy_canvas_tool_services.h"
#include "legacy_document_layer_services.h"
#include "legacy_preference_storage.h"
#include "legacy_ui_overlay_services.h"
#include "serializer.h"
#include "font.h"
#include "node_remote_page.h"
#include "node_shorcuts.h"
@@ -398,320 +396,3 @@ void App::set_ui_scale(float scale)
pp::panopainter::save_legacy_ui_scale_preference(plan.scale);
App::I->title_update();
}
void App::set_ui_rtl(bool rtl)
{
const auto plan = pp::app::plan_interface_direction(rtl);
ui_rtl = plan.direction == pp::app::InterfaceDirection::right_to_left;
layout[main_id]->find("central-row")->SetRTL(
ui_rtl ? YGDirectionRTL : YGDirectionLTR);
}
bool App::get_ui_rtl() const
{
return ui_rtl;
}
void App::ui_save()
{
Serializer::Descriptor d;
d.class_id = "ui-state";
Serializer::List list_floatings;
for (auto const& c : layout[main_id]->find("floatings")->m_children)
{
if (auto const& f = std::dynamic_pointer_cast<NodePanelFloating>(c))
{
auto fd = list_floatings.add<Serializer::Descriptor>();
fd->class_id = "ui-flt";
fd->set("pos", Serializer::Vec2(f->GetPosition()));
fd->set("size", Serializer::Vec2(f->m_size));
fd->set("class", Serializer::Integer((int)f->m_class));
fd->set("title", Serializer::CString(f->m_title->m_text));
}
}
d.set("floatings", list_floatings);
Serializer::List list_drop_left;
for (auto const& c : layout[main_id]->find("drop-left")->m_children)
{
if (auto const& f = std::dynamic_pointer_cast<NodePanelFloating>(c))
{
auto fd = list_drop_left.add<Serializer::Descriptor>();
fd->class_id = "ui-dpl";
fd->set("size", Serializer::Vec2(f->m_size));
fd->set("class", Serializer::Integer((int)f->m_class));
fd->set("title", Serializer::CString(f->m_title->m_text));
}
}
d.set("drop-left", list_drop_left);
Serializer::List list_drop_right;
for (auto const& c : layout[main_id]->find("drop-right")->m_children)
{
if (auto const& f = std::dynamic_pointer_cast<NodePanelFloating>(c))
{
auto fd = list_drop_right.add<Serializer::Descriptor>();
fd->class_id = "ui-dpr";
fd->set("size", Serializer::Vec2(f->m_size));
fd->set("class", Serializer::Integer((int)f->m_class));
fd->set("title", Serializer::CString(f->m_title->m_text));
}
}
d.set("drop-right", list_drop_right);
pp::panopainter::set_legacy_ui_state_preferences(d, ui_rtl);
save_platform_ui_state();
pp::panopainter::save_legacy_preferences();
}
void App::ui_restore()
{
const auto preferences = pp::panopainter::read_legacy_ui_preferences();
if (preferences.has_rtl)
set_ui_rtl(preferences.rtl);
if (!preferences.state)
return;
auto floatings = layout[main_id]->find_ref("floatings");
auto drop_left = layout[main_id]->find_ref("drop-left");
auto drop_right = layout[main_id]->find_ref("drop-right");
auto d = preferences.state;
for (auto const& l : d->get<Serializer::List>("floatings")->items)
{
auto ld = std::static_pointer_cast<Serializer::Descriptor>(l);
auto pos = ld->value<Serializer::Vec2>("pos");
auto size = ld->value<Serializer::Vec2>("size");
auto cls = static_cast<NodePanelFloating::kClass>(ld->value<Serializer::Integer>("class"));
auto f = floatings->add_child<NodePanelFloating>();
std::string title = "Floating Panel";
ld->value<Serializer::CString>("title", title);
f->m_title->set_text(title.c_str());
switch (cls)
{
case NodePanelFloating::kClass::Presets:
{
floating_presets = f->m_container->add_child_ref<NodePanelBrushPreset>();
floating_presets->SetHeightP(100);
//floating_presets->find("toolbar")->destroy();
floating_presets->on_brush_changed = [this](Node* target, std::shared_ptr<Brush>& b) {
apply_brush_preset_plan(*this, b);
};
break;
}
case NodePanelFloating::kClass::Color:
{
floating_color = f->m_container->add_child_ref<NodePanelColor>();
floating_color->SetHeightP(100);
floating_color->find("title")->SetVisibility(false);
floating_color->on_color_changed = [this](Node* target, glm::vec4 color) {
apply_brush_color_plan(*this, color, false, false);
};
break;
}
case NodePanelFloating::kClass::ColorAdv:
{
floating_picker = f->m_container->add_child_ref<NodeColorPicker>();
floating_picker->m_autohide = false;
floating_picker->on_color_change = [this](Node* target, glm::vec3 color) {
apply_brush_color_plan(*this, glm::vec4(color, 1.f), false, false);
};
break;
}
case NodePanelFloating::kClass::Layers:
f->m_container->add_child(layers);
f->SetMinHeight(100);
f->SetHeight(300);
layers->find("title")->SetVisibility(false);
layers->SetPositioning(YGPositionTypeRelative);
layers->SetPosition(0, 0);
layers->SetWidthP(100);
layers->SetHeightP(100);
layers->SetFlexShrink(0);
break;
case NodePanelFloating::kClass::Brush:
f->m_container->add_child(stroke);
stroke->find("title")->SetVisibility(false);
stroke->SetPositioning(YGPositionTypeRelative);
stroke->SetPosition(0, 0);
stroke->SetWidthP(100);
stroke->SetHeightP(100);
break;
case NodePanelFloating::kClass::Grids:
f->m_container->add_child(grid);
grid->find("title")->SetVisibility(false);
grid->SetPositioning(YGPositionTypeRelative);
grid->SetPosition(0, 0);
grid->SetWidthP(100);
grid->SetHeightP(100);
break;
case NodePanelFloating::kClass::Animation:
f->m_container->add_child(animation);
f->m_droppable = false;
//grid->find("title")->SetVisibility(false);
animation->SetPositioning(YGPositionTypeRelative);
animation->SetPosition(0, 0);
animation->SetWidthP(100);
animation->SetHeightP(100);
break;
case NodePanelFloating::kClass::Generic:
default:
f->m_container->add_child<Node>();
break;
}
f->m_class = cls;
f->SetSize(size);
f->SetPosition(pos);
f->SetPositioning(YGPositionTypeAbsolute);
}
for (auto const& l : d->get<Serializer::List>("drop-left")->items)
{
auto ld = std::static_pointer_cast<Serializer::Descriptor>(l);
auto size = ld->value<Serializer::Vec2>("size");
auto cls = static_cast<NodePanelFloating::kClass>(ld->value<Serializer::Integer>("class"));
auto f = drop_left->add_child<NodePanelFloating>();
std::string title = "Floating Panel";
ld->value<Serializer::CString>("title", title);
f->m_title->set_text(title.c_str());
switch (cls)
{
case NodePanelFloating::kClass::Presets:
{
auto floating_presets = f->m_container->add_child<NodePanelBrushPreset>();
floating_presets->SetHeightP(100);
//floating_presets->find("toolbar")->destroy();
floating_presets->on_brush_changed = [this](Node* target, std::shared_ptr<Brush>& b) {
apply_brush_preset_plan(*this, b);
};
break;
}
case NodePanelFloating::kClass::Color:
{
auto floating_color = f->m_container->add_child<NodePanelColor>();
floating_color->SetHeightP(100);
pp::panopainter::destroy_legacy_node(*floating_color->find("title"));
floating_color->on_color_changed = [this](Node* target, glm::vec4 color) {
apply_brush_color_plan(*this, color, false, false);
};
break;
}
case NodePanelFloating::kClass::ColorAdv:
{
floating_picker = f->m_container->add_child_ref<NodeColorPicker>();
floating_picker->m_autohide = false;
floating_picker->on_color_change = [this](Node* target, glm::vec3 color) {
apply_brush_color_plan(*this, glm::vec4(color, 1.f), false, false);
};
break;
}
case NodePanelFloating::kClass::Layers:
f->m_container->add_child(layers);
layers->SetPositioning(YGPositionTypeRelative);
layers->SetPosition(0, 0);
layers->SetWidthP(100);
layers->SetHeightP(100);
layers->SetFlexShrink(0);
break;
case NodePanelFloating::kClass::Brush:
f->m_container->add_child(stroke);
stroke->SetPositioning(YGPositionTypeRelative);
stroke->SetPosition(0, 0);
stroke->SetWidthP(100);
stroke->SetHeightP(100);
break;
case NodePanelFloating::kClass::Grids:
f->m_container->add_child(grid);
grid->SetPositioning(YGPositionTypeRelative);
grid->SetPosition(0, 0);
grid->SetWidthP(100);
grid->SetHeightP(100);
break;
case NodePanelFloating::kClass::Generic:
default:
f->m_container->add_child<Node>();
break;
}
f->m_class = cls;
f->m_dock = drop_left;
f->SetPositioning(YGPositionTypeRelative);
f->SetPosition(0, 0);
f->SetSize(size);
}
for (auto const& l : d->get<Serializer::List>("drop-right")->items)
{
auto ld = std::static_pointer_cast<Serializer::Descriptor>(l);
auto size = ld->value<Serializer::Vec2>("size");
auto cls = static_cast<NodePanelFloating::kClass>(ld->value<Serializer::Integer>("class"));
auto f = drop_right->add_child<NodePanelFloating>();
std::string title = "Floating Panel";
ld->value<Serializer::CString>("title", title);
f->m_title->set_text(title.c_str());
switch (cls)
{
case NodePanelFloating::kClass::Presets:
{
auto floating_presets = f->m_container->add_child<NodePanelBrushPreset>();
floating_presets->SetHeightP(100);
//floating_presets->find("toolbar")->destroy();
floating_presets->on_brush_changed = [this](Node* target, std::shared_ptr<Brush>& b) {
apply_brush_preset_plan(*this, b);
};
break;
}
case NodePanelFloating::kClass::Color:
{
auto floating_color = f->m_container->add_child<NodePanelColor>();
floating_color->SetHeightP(100);
pp::panopainter::destroy_legacy_node(*floating_color->find("title"));
floating_color->on_color_changed = [this](Node* target, glm::vec4 color) {
apply_brush_color_plan(*this, color, false, false);
};
break;
}
case NodePanelFloating::kClass::ColorAdv:
{
floating_picker = f->m_container->add_child_ref<NodeColorPicker>();
floating_picker->m_autohide = false;
floating_picker->on_color_change = [this](Node* target, glm::vec3 color) {
apply_brush_color_plan(*this, glm::vec4(color, 1.f), false, false);
};
break;
}
case NodePanelFloating::kClass::Layers:
f->m_container->add_child(layers);
layers->SetPositioning(YGPositionTypeRelative);
layers->SetPosition(0, 0);
layers->SetWidthP(100);
layers->SetHeightP(100);
layers->SetFlexShrink(0);
break;
case NodePanelFloating::kClass::Brush:
f->m_container->add_child(stroke);
stroke->SetPositioning(YGPositionTypeRelative);
stroke->SetPosition(0, 0);
stroke->SetWidthP(100);
stroke->SetHeightP(100);
break;
case NodePanelFloating::kClass::Grids:
f->m_container->add_child(grid);
grid->SetPositioning(YGPositionTypeRelative);
grid->SetPosition(0, 0);
grid->SetWidthP(100);
grid->SetHeightP(100);
break;
case NodePanelFloating::kClass::Generic:
default:
f->m_container->add_child<Node>();
break;
}
f->m_class = cls;
f->m_dock = drop_right;
f->SetPositioning(YGPositionTypeRelative);
f->SetPosition(0, 0);
f->SetSize(size);
}
}

216
src/app_layout_ui_state.cpp Normal file
View File

@@ -0,0 +1,216 @@
#include "pch.h"
#include "app.h"
#include "app_core/app_preferences.h"
#include "legacy_brush_ui_services.h"
#include "legacy_ui_overlay_services.h"
#include "legacy_preference_storage.h"
#include "node_dialog_picker.h"
#include "node_panel_brush.h"
#include "node_panel_color.h"
#include "node_panel_grid.h"
#include "node_panel_floating.h"
#include "serializer.h"
namespace {
bool apply_brush_color_plan(App& app, glm::vec4 color, bool update_quick, bool update_color_panel)
{
return pp::panopainter::apply_legacy_brush_color_plan(app, color, update_quick, update_color_panel);
}
bool apply_brush_preset_plan(App& app, const std::shared_ptr<Brush>& brush)
{
return pp::panopainter::apply_legacy_brush_preset_plan(app, brush);
}
void save_floating_panel_state(Serializer::List& list, const std::shared_ptr<Node>& child)
{
if (auto const& f = std::dynamic_pointer_cast<NodePanelFloating>(child))
{
auto fd = list.add<Serializer::Descriptor>();
fd->class_id = "ui-flt";
fd->set("pos", Serializer::Vec2(f->GetPosition()));
fd->set("size", Serializer::Vec2(f->m_size));
fd->set("class", Serializer::Integer((int)f->m_class));
fd->set("title", Serializer::CString(f->m_title->m_text));
}
}
void save_docked_panel_state(Serializer::List& list, const std::shared_ptr<Node>& child, const char* class_id)
{
if (auto const& f = std::dynamic_pointer_cast<NodePanelFloating>(child))
{
auto fd = list.add<Serializer::Descriptor>();
fd->class_id = class_id;
fd->set("size", Serializer::Vec2(f->m_size));
fd->set("class", Serializer::Integer((int)f->m_class));
fd->set("title", Serializer::CString(f->m_title->m_text));
}
}
template <class TList>
void restore_panel_children(
App& app,
std::shared_ptr<Node> container,
const TList& items,
bool docked)
{
for (auto const& l : items)
{
auto ld = std::static_pointer_cast<Serializer::Descriptor>(l);
auto size = ld->value<Serializer::Vec2>("size");
auto cls = static_cast<NodePanelFloating::kClass>(ld->value<Serializer::Integer>("class"));
auto f = container->add_child<NodePanelFloating>();
std::string title = "Floating Panel";
ld->value<Serializer::CString>("title", title);
f->m_title->set_text(title.c_str());
switch (cls)
{
case NodePanelFloating::kClass::Presets:
{
auto floating_presets = f->m_container->add_child_ref<NodePanelBrushPreset>();
if (!docked)
app.floating_presets = floating_presets;
floating_presets->SetHeightP(100);
floating_presets->on_brush_changed = [&app](Node*, std::shared_ptr<Brush>& b) {
apply_brush_preset_plan(app, b);
};
break;
}
case NodePanelFloating::kClass::Color:
{
auto floating_color = f->m_container->add_child_ref<NodePanelColor>();
if (!docked)
app.floating_color = floating_color;
floating_color->SetHeightP(100);
if (docked)
pp::panopainter::destroy_legacy_node(*floating_color->find("title"));
else
floating_color->find("title")->SetVisibility(false);
floating_color->on_color_changed = [&app](Node*, glm::vec4 color) {
apply_brush_color_plan(app, color, false, false);
};
break;
}
case NodePanelFloating::kClass::ColorAdv:
{
app.floating_picker = f->m_container->add_child_ref<NodeColorPicker>();
app.floating_picker->m_autohide = false;
app.floating_picker->on_color_change = [&app](Node*, glm::vec3 color) {
apply_brush_color_plan(app, glm::vec4(color, 1.f), false, false);
};
break;
}
case NodePanelFloating::kClass::Layers:
f->m_container->add_child(app.layers);
if (!docked)
f->SetMinHeight(100);
if (!docked)
f->SetHeight(300);
if (!docked)
app.layers->find("title")->SetVisibility(false);
app.layers->SetPositioning(YGPositionTypeRelative);
app.layers->SetPosition(0, 0);
app.layers->SetWidthP(100);
app.layers->SetHeightP(100);
app.layers->SetFlexShrink(0);
break;
case NodePanelFloating::kClass::Brush:
f->m_container->add_child(app.stroke);
if (!docked)
app.stroke->find("title")->SetVisibility(false);
app.stroke->SetPositioning(YGPositionTypeRelative);
app.stroke->SetPosition(0, 0);
app.stroke->SetWidthP(100);
app.stroke->SetHeightP(100);
break;
case NodePanelFloating::kClass::Grids:
f->m_container->add_child(app.grid);
if (!docked)
app.grid->find("title")->SetVisibility(false);
app.grid->SetPositioning(YGPositionTypeRelative);
app.grid->SetPosition(0, 0);
app.grid->SetWidthP(100);
app.grid->SetHeightP(100);
break;
case NodePanelFloating::kClass::Animation:
f->m_container->add_child(app.animation);
f->m_droppable = false;
app.animation->SetPositioning(YGPositionTypeRelative);
app.animation->SetPosition(0, 0);
app.animation->SetWidthP(100);
app.animation->SetHeightP(100);
break;
case NodePanelFloating::kClass::Generic:
default:
f->m_container->add_child<Node>();
break;
}
f->m_class = cls;
if (docked)
f->m_dock = container;
f->SetPositioning(docked ? YGPositionTypeRelative : YGPositionTypeAbsolute);
f->SetPosition(0, 0);
f->SetSize(size);
}
}
} // namespace
void App::set_ui_rtl(bool rtl)
{
const auto plan = pp::app::plan_interface_direction(rtl);
ui_rtl = plan.direction == pp::app::InterfaceDirection::right_to_left;
layout[main_id]->find("central-row")->SetRTL(
ui_rtl ? YGDirectionRTL : YGDirectionLTR);
}
bool App::get_ui_rtl() const
{
return ui_rtl;
}
void App::ui_save()
{
Serializer::Descriptor d;
d.class_id = "ui-state";
Serializer::List list_floatings;
for (auto const& c : layout[main_id]->find("floatings")->m_children)
save_floating_panel_state(list_floatings, c);
d.set("floatings", list_floatings);
Serializer::List list_drop_left;
for (auto const& c : layout[main_id]->find("drop-left")->m_children)
save_docked_panel_state(list_drop_left, c, "ui-dpl");
d.set("drop-left", list_drop_left);
Serializer::List list_drop_right;
for (auto const& c : layout[main_id]->find("drop-right")->m_children)
save_docked_panel_state(list_drop_right, c, "ui-dpr");
d.set("drop-right", list_drop_right);
pp::panopainter::set_legacy_ui_state_preferences(d, ui_rtl);
save_platform_ui_state();
pp::panopainter::save_legacy_preferences();
}
void App::ui_restore()
{
const auto preferences = pp::panopainter::read_legacy_ui_preferences();
if (preferences.has_rtl)
set_ui_rtl(preferences.rtl);
if (!preferences.state)
return;
auto floatings = layout[main_id]->find_ref("floatings");
auto drop_left = layout[main_id]->find_ref("drop-left");
auto drop_right = layout[main_id]->find_ref("drop-right");
auto d = preferences.state;
restore_panel_children(*this, floatings, d->get<Serializer::List>("floatings")->items, false);
restore_panel_children(*this, drop_left, d->get<Serializer::List>("drop-left")->items, true);
restore_panel_children(*this, drop_right, d->get<Serializer::List>("drop-right")->items, true);
}

View File

@@ -119,7 +119,7 @@ enum {
kVK_UpArrow = 0x7E
};
kKey convert_key(int key)
inline kKey convert_key(int key)
{
switch(key)
{
@@ -240,7 +240,7 @@ kKey convert_key(int key)
}
}
#elif __WIN__
kKey convert_key(int key)
inline kKey convert_key(int key)
{
static auto KL = GetKeyboardLayout(0);
if (key == (VkKeyScanA('[') & 0xFF))
@@ -450,7 +450,7 @@ kKey convert_key(int key)
}
}
#elif __ANDROID__
kKey convert_key(int key)
inline kKey convert_key(int key)
{
switch (key)
{
@@ -682,7 +682,7 @@ kKey convert_key(int key)
}
}
#elif __WEB__
kKey convert_key(int key)
inline kKey convert_key(int key)
{
switch (key)
{

View File

@@ -12,6 +12,7 @@
#include "platform_windows/windows_lifecycle_shell.h"
#include "platform_windows/windows_splash.h"
#include "platform_windows/windows_stylus_input.h"
#include "platform_windows/windows_window_shell.h"
#include "platform_windows/windows_vr_shell.h"
#include "../resource.h"
@@ -30,24 +31,9 @@ namespace pp::platform::windows {
bool try_lock_async_render_context();
void unlock_async_render_context();
void swap_async_render_context();
RetainedState& retained_state();
}
struct RetainedState
{
HINSTANCE hInst{};
HWND hWnd{};
bool keys[256]{};
std::map<kKey, int> vkey_map;
wchar_t window_title[512]{};
bool sandboxed = false;
pp::platform::windows::VrShellState vr;
};
RetainedState& retained_state()
{
static RetainedState state;
return state;
}
using pp::platform::windows::retained_state;
namespace {
struct RetainedMainTaskQueue final
@@ -571,262 +557,6 @@ int main(int argc, char** argv)
LogRemote::I.stop();
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
auto& state = retained_state();
static bool leftDown = false;
static DWORD lastTime;
static glm::vec2 lastPoint;
auto extra = GetMessageExtraInfo();
// if ((extra & 0xFFFFFF00) == 0xFF515700)
// LOG("source %s", extra & 0x80 ? "touch" : "pen");
switch (msg)
{
case pp::platform::windows::kUserCloseMessage:
pp::platform::windows::handle_window_close_message(state.vr);
return 0;
case WM_PAINT:
App::I->redraw = true;
break;
case WM_CREATE:
BT_SetTerminate();
break;
case WM_CLOSE:
{
App::I->ui_task_async([] {
if (App::I->request_close())
{
destroy_window();
}
});
return 1;
break;
}
case WM_SIZE:
{
auto w = (float)LOWORD(lp);
auto h = (float)HIWORD(lp);
if (h != 0 && pp::platform::windows::lifecycle_is_running())
{
App::I->ui_task_async([=]
{
App::I->resize(w, h);
App::I->redraw = true;
}, true);
}
break;
}
case WM_ACTIVATE:
{
pp::platform::windows::platform_services().set_cursor_visible(true);
App::I->ui_task_async([&state, wp, lp] {
int active = GET_WM_ACTIVATE_STATE(wp, lp);
WacomTablet::I.set_focus(active);
static BYTE keys[256];
if (GetKeyboardState(keys))
{
bool alt = keys[VK_MENU] & 0x80;
for (auto k : state.vkey_map)
{
// ignore alt + tab
if (alt && k.first == kKey::KeyTab)
continue;
bool down = keys[k.second] & 0x80;
if (App::I->keys[(int)k.first] && !down)
App::I->key_up(k.first);
else if(!App::I->keys[(int)k.first] && down)
App::I->key_down(k.first);
}
}
});
break;
}
// case WM_TOUCH:
// {
// App::I->ui_task_async([=] {
// //LOG("touch");
// });
// break;
// }
case WT_PACKET:
{
pp::platform::windows::note_wintab_packet();
App::I->ui_task_async([=] {
WacomTablet::I.handle_message(hWnd, msg, wp, lp);
});
break;
}
case WM_SYSKEYDOWN:
case WM_KEYDOWN:
if ((lp >> 30 & 1) == 0 && // ignore repeated
!(wp == VK_TAB && App::I->keys[(int)kKey::KeyAlt])) // ignore alt+tab
{
App::I->ui_task_async([wp] {
App::I->key_down(convert_key((int)wp));
});
}
break;
case WM_SYSKEYUP:
case WM_KEYUP:
if (!(wp == VK_TAB && App::I->keys[(int)kKey::KeyAlt])) // ignore alt+tab
{
App::I->ui_task_async([wp] {
App::I->key_up(convert_key((int)wp));
});
}
break;
case WM_CHAR:
{
App::I->ui_task_async([wp]{
App::I->key_char((int)wp);
});
}
break;
case WM_MOUSEMOVE:
{
/*
RECT r;
POINT curpos;
GetWindowRect(hWnd, &r);
glm::vec2 center = { r.left + (r.right - r.left) / 2, r.top + (r.bottom - r.top) / 2 };
GetCursorPos(&curpos);
SetCursorPos(center.x, center.y);
//glm::vec2 cur = { GET_X_LPARAM(lp), GET_Y_LPARAM(lp) };
glm::vec2 sz = { App::I->width, App::I->height };
glm::vec2 diff = { curpos.x - center.x, curpos.y - center.y };
lastPoint = glm::clamp(lastPoint + diff, { 0, 0 }, sz);
*/
lastPoint = { GET_X_LPARAM(lp), GET_Y_LPARAM(lp) };
auto pt = lastPoint;
App::I->ui_task_async([pt, extra, p = WacomTablet::I.get_pressure()]{
kEventSource pointer_source;
if (WacomTablet::I.m_ink_pen || WacomTablet::I.m_ink_touch)
{
pointer_source = WacomTablet::I.m_ink_pen ? kEventSource::Stylus : kEventSource::Touch;
}
else
{
pointer_source = WacomTablet::I.m_stylus ? kEventSource::Stylus : kEventSource::Mouse;
if ((extra & 0xFFFFFF00) == 0xFF515700)
pointer_source = kEventSource::Touch;
}
App::I->mouse_move((float)pt.x, (float)pt.y, p, pointer_source, WacomTablet::I.m_eraser);
});
}
break;
case WM_LBUTTONDOWN:
{
SetCapture(hWnd);
auto pt = lastPoint;
App::I->ui_task_async([pt, extra, hWnd, p = WacomTablet::I.get_pressure()]{
kEventSource pointer_source;
if (WacomTablet::I.m_ink_pen || WacomTablet::I.m_ink_touch)
{
pointer_source = WacomTablet::I.m_ink_pen ? kEventSource::Stylus : kEventSource::Touch;
}
else
{
pointer_source = WacomTablet::I.m_stylus ? kEventSource::Stylus : kEventSource::Mouse;
if ((extra & 0xFFFFFF00) == 0xFF515700)
pointer_source = kEventSource::Touch;
}
App::I->mouse_down(0, (float)pt.x, (float)pt.y, p, pointer_source, WacomTablet::I.m_eraser);
});
}
break;
case WM_LBUTTONUP:
{
ReleaseCapture();
auto pt = lastPoint;
App::I->ui_task_async([pt, extra] {
WacomTablet::I.reset_pressure();
kEventSource pointer_source;
if (WacomTablet::I.m_ink_pen || WacomTablet::I.m_ink_touch)
{
pointer_source = WacomTablet::I.m_ink_pen ? kEventSource::Stylus : kEventSource::Touch;
}
else
{
pointer_source = WacomTablet::I.m_stylus ? kEventSource::Stylus : kEventSource::Mouse;
if ((extra & 0xFFFFFF00) == 0xFF515700)
pointer_source = kEventSource::Touch;
}
App::I->mouse_up(0, (float)pt.x, (float)pt.y, pointer_source, WacomTablet::I.m_eraser);
});
}
break;
case WM_RBUTTONDOWN:
{
SetCapture(hWnd);
auto pt = lastPoint;
App::I->ui_task_async([pt, extra, hWnd] {
kEventSource pointer_source;
if (WacomTablet::I.m_ink_pen || WacomTablet::I.m_ink_touch)
{
pointer_source = WacomTablet::I.m_ink_pen ? kEventSource::Stylus : kEventSource::Touch;
}
else
{
pointer_source = WacomTablet::I.m_stylus ? kEventSource::Stylus : kEventSource::Mouse;
if ((extra & 0xFFFFFF00) == 0xFF515700)
pointer_source = kEventSource::Touch;
}
App::I->mouse_down(1, (float)pt.x, (float)pt.y, 1.f, pointer_source, 0);
});
}
break;
case WM_RBUTTONUP:
{
ReleaseCapture();
auto pt = lastPoint;
App::I->ui_task_async([pt, extra] {
kEventSource pointer_source;
if (WacomTablet::I.m_ink_pen || WacomTablet::I.m_ink_touch)
{
pointer_source = WacomTablet::I.m_ink_pen ? kEventSource::Stylus : kEventSource::Touch;
}
else
{
pointer_source = WacomTablet::I.m_stylus ? kEventSource::Stylus : kEventSource::Mouse;
if ((extra & 0xFFFFFF00) == 0xFF515700)
pointer_source = kEventSource::Touch;
}
App::I->mouse_up(1, (float)pt.x, (float)pt.y, pointer_source, 0);
});
}
break;
case WM_MOUSEWHEEL:
{
POINT pt;
pt.x = GET_X_LPARAM(lp);
pt.y = GET_Y_LPARAM(lp);
ScreenToClient(hWnd, &pt);
{
App::I->ui_task_async([pt, wp] {
App::I->mouse_scroll((float)pt.x, (float)pt.y,
(float)GET_WHEEL_DELTA_WPARAM(wp) / (float)WHEEL_DELTA);
});
}
break;
}
case WM_POINTERUPDATE:
{
pp::platform::windows::handle_pointer_update_message(wp);
break;
}
}
// avoid annoying alt system menu
if (wp == SC_KEYMENU && (lp >> 16) <= 0) return 0;
return DefWindowProc(hWnd, msg, wp, lp);
}
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
int argc = 0;

View File

@@ -656,68 +656,16 @@ void NodeStrokePreview::draw_stroke_immediate()
.mvp = ortho_proj,
});
const bool copy_stroke_destination = pass_orchestration.copy_stroke_destination;
const auto& material = pass_orchestration.material;
pp::panopainter::setup_legacy_stroke_shader(pass_orchestration.stroke_shader);
const bool sequence_ok = pp::panopainter::execute_legacy_node_stroke_preview_immediate_pass_sequence(
pp::panopainter::LegacyNodeStrokePreviewImmediatePassSequenceRequest {
.execute_dual_pass = [&] {
if (!pass_orchestration.material.dual_pass.enabled) {
return;
}
pp::panopainter::setup_legacy_stroke_dual_shader(
pass_orchestration.material.dual_pass.uses_pattern);
bind_stroke_preview_dual_pass_textures(*dual_brush);
execute_stroke_draw_immediate_dual_pass(
m_dual_stroke,
*b,
pass_orchestration,
std::move(dual_brush),
copy_stroke_destination,
zoom,
size);
},
.capture_background = [&] {
execute_stroke_preview_background_capture_pass(
size,
pass_orchestration.background_colorize,
m_tex_background,
[&] {
m_plane.draw_fill();
});
},
.execute_main_live_pass = [&]() -> bool {
return pp::panopainter::execute_legacy_node_stroke_preview_main_live_pass(
make_stroke_draw_immediate_main_live_pass_request(
m_stroke,
*b,
pass_orchestration,
copy_stroke_destination,
zoom,
size));
},
.execute_final_composite = [&]() -> bool {
return pp::panopainter::execute_legacy_node_stroke_preview_final_composite(
size,
glm::vec2(b->m_pattern_scale),
*b,
material.composite_pass,
m_tex_background,
m_tex,
m_tex_dual,
m_tex_preview,
m_sampler_linear,
m_sampler_linear_repeat,
[&] {
b->m_pattern_texture ? b->m_pattern_texture->bind() : unbind_texture_2d();
},
[&] {
m_plane.draw_fill();
});
},
});
assert(sequence_ok);
execute_stroke_draw_immediate_pass_sequence(
m_stroke,
m_dual_stroke,
*b,
std::move(dual_brush),
pass_orchestration,
copy_stroke_destination,
zoom,
size);
m_rtt.unbindFramebuffer();
@@ -811,6 +759,78 @@ void NodeStrokePreview::execute_stroke_draw_immediate_dual_pass(
});
}
void NodeStrokePreview::execute_stroke_draw_immediate_pass_sequence(
Stroke& stroke,
Stroke& dual_stroke,
const Brush& brush,
std::shared_ptr<Brush> dual_brush,
const pp::panopainter::LegacyNodeStrokePreviewPassOrchestrationPlan& pass_orchestration,
bool copy_stroke_destination,
float zoom,
const glm::vec2& size)
{
const auto& material = pass_orchestration.material;
const bool sequence_ok = pp::panopainter::execute_legacy_node_stroke_preview_immediate_pass_sequence(
pp::panopainter::LegacyNodeStrokePreviewImmediatePassSequenceRequest {
.execute_dual_pass = [&] {
if (!material.dual_pass.enabled) {
return;
}
pp::panopainter::setup_legacy_stroke_dual_shader(
material.dual_pass.uses_pattern);
bind_stroke_preview_dual_pass_textures(*dual_brush);
execute_stroke_draw_immediate_dual_pass(
dual_stroke,
brush,
pass_orchestration,
std::move(dual_brush),
copy_stroke_destination,
zoom,
size);
},
.capture_background = [&] {
execute_stroke_preview_background_capture_pass(
size,
pass_orchestration.background_colorize,
m_tex_background,
[&] {
m_plane.draw_fill();
});
},
.execute_main_live_pass = [&]() -> bool {
return pp::panopainter::execute_legacy_node_stroke_preview_main_live_pass(
make_stroke_draw_immediate_main_live_pass_request(
stroke,
brush,
pass_orchestration,
copy_stroke_destination,
zoom,
size));
},
.execute_final_composite = [&]() -> bool {
return pp::panopainter::execute_legacy_node_stroke_preview_final_composite(
size,
glm::vec2(brush.m_pattern_scale),
brush,
material.composite_pass,
m_tex_background,
m_tex,
m_tex_dual,
m_tex_preview,
m_sampler_linear,
m_sampler_linear_repeat,
[&] {
brush.m_pattern_texture ? brush.m_pattern_texture->bind() : unbind_texture_2d();
},
[&] {
m_plane.draw_fill();
});
},
});
assert(sequence_ok);
}
void NodeStrokePreview::execute_stroke_draw_immediate_main_live_sample_pass(
const Brush& brush,
bool copy_stroke_destination,

View File

@@ -30,6 +30,15 @@ class NodeStrokePreview : public NodeBorder
static Sampler m_sampler_mipmap;
static DynamicShape m_brush_shape;
Texture2D m_tex_preview;
void execute_stroke_draw_immediate_pass_sequence(
Stroke& stroke,
Stroke& dual_stroke,
const Brush& brush,
std::shared_ptr<Brush> dual_brush,
const pp::panopainter::LegacyNodeStrokePreviewPassOrchestrationPlan& pass_orchestration,
bool copy_stroke_destination,
float zoom,
const glm::vec2& size);
public:
using parent = NodeBorder;
static std::atomic_int s_instances;

View File

@@ -1,6 +1,7 @@
#include "pch.h"
#include "platform_windows/windows_bootstrap_helpers.h"
#include "platform_windows/windows_window_shell.h"
#include "app.h"
#include "legacy_gl_runtime_dispatch.h"
@@ -15,8 +16,6 @@
#define USE_RENDERDOC
#endif
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp);
namespace pp::platform::windows {
void set_async_render_context(HDC hdc, HGLRC hrc);
}
@@ -113,7 +112,7 @@ MainWindowStartupState initialize_main_window_startup_state()
const auto className = L"EngineMain";
startup.window_class.hInstance = hInst;
startup.window_class.lpfnWndProc = (WNDPROC)WndProc;
startup.window_class.lpfnWndProc = (WNDPROC)main_window_proc;
startup.window_class.lpszClassName = className;
startup.window_class.hbrBackground = (HBRUSH)COLOR_WINDOW;
startup.window_class.hCursor = LoadCursor(NULL, IDC_ARROW);

View File

@@ -0,0 +1,247 @@
#include "pch.h"
#include "platform_windows/windows_window_shell.h"
#include "app.h"
#include "platform_windows/windows_lifecycle_shell.h"
#include "platform_windows/windows_platform_services.h"
#include "platform_windows/windows_stylus_input.h"
#include "keymap.h"
#include "wacom.h"
void destroy_window();
namespace pp::platform::windows {
RetainedState& retained_state()
{
static RetainedState state;
return state;
}
LRESULT CALLBACK main_window_proc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
auto& state = retained_state();
static glm::vec2 lastPoint;
auto extra = GetMessageExtraInfo();
switch (msg)
{
case kUserCloseMessage:
handle_window_close_message(state.vr);
return 0;
case WM_PAINT:
App::I->redraw = true;
break;
case WM_CREATE:
BT_SetTerminate();
break;
case WM_CLOSE:
{
App::I->ui_task_async([] {
if (App::I->request_close())
{
destroy_window();
}
});
return 1;
break;
}
case WM_SIZE:
{
auto w = (float)LOWORD(lp);
auto h = (float)HIWORD(lp);
if (h != 0 && lifecycle_is_running())
{
App::I->ui_task_async([=]
{
App::I->resize(w, h);
App::I->redraw = true;
}, true);
}
break;
}
case WM_ACTIVATE:
{
platform_services().set_cursor_visible(true);
App::I->ui_task_async([&state, wp, lp] {
int active = GET_WM_ACTIVATE_STATE(wp, lp);
WacomTablet::I.set_focus(active);
static BYTE keys[256];
if (GetKeyboardState(keys))
{
bool alt = keys[VK_MENU] & 0x80;
for (auto k : state.vkey_map)
{
if (alt && k.first == kKey::KeyTab)
continue;
bool down = keys[k.second] & 0x80;
if (App::I->keys[(int)k.first] && !down)
App::I->key_up(k.first);
else if (!App::I->keys[(int)k.first] && down)
App::I->key_down(k.first);
}
}
});
break;
}
case WT_PACKET:
{
note_wintab_packet();
App::I->ui_task_async([=] {
WacomTablet::I.handle_message(hWnd, msg, wp, lp);
});
break;
}
case WM_SYSKEYDOWN:
case WM_KEYDOWN:
if ((lp >> 30 & 1) == 0 &&
!(wp == VK_TAB && App::I->keys[(int)kKey::KeyAlt]))
{
App::I->ui_task_async([wp] {
App::I->key_down(convert_key((int)wp));
});
}
break;
case WM_SYSKEYUP:
case WM_KEYUP:
if (!(wp == VK_TAB && App::I->keys[(int)kKey::KeyAlt]))
{
App::I->ui_task_async([wp] {
App::I->key_up(convert_key((int)wp));
});
}
break;
case WM_CHAR:
App::I->ui_task_async([wp] {
App::I->key_char((int)wp);
});
break;
case WM_MOUSEMOVE:
lastPoint = { GET_X_LPARAM(lp), GET_Y_LPARAM(lp) };
{
auto pt = lastPoint;
App::I->ui_task_async([pt, extra, p = WacomTablet::I.get_pressure()] {
kEventSource pointer_source;
if (WacomTablet::I.m_ink_pen || WacomTablet::I.m_ink_touch)
{
pointer_source = WacomTablet::I.m_ink_pen ? kEventSource::Stylus : kEventSource::Touch;
}
else
{
pointer_source = WacomTablet::I.m_stylus ? kEventSource::Stylus : kEventSource::Mouse;
if ((extra & 0xFFFFFF00) == 0xFF515700)
pointer_source = kEventSource::Touch;
}
App::I->mouse_move((float)pt.x, (float)pt.y, p, pointer_source, WacomTablet::I.m_eraser);
});
}
break;
case WM_LBUTTONDOWN:
{
SetCapture(hWnd);
auto pt = lastPoint;
App::I->ui_task_async([pt, extra, hWnd, p = WacomTablet::I.get_pressure()] {
kEventSource pointer_source;
if (WacomTablet::I.m_ink_pen || WacomTablet::I.m_ink_touch)
{
pointer_source = WacomTablet::I.m_ink_pen ? kEventSource::Stylus : kEventSource::Touch;
}
else
{
pointer_source = WacomTablet::I.m_stylus ? kEventSource::Stylus : kEventSource::Mouse;
if ((extra & 0xFFFFFF00) == 0xFF515700)
pointer_source = kEventSource::Touch;
}
App::I->mouse_down(0, (float)pt.x, (float)pt.y, p, pointer_source, WacomTablet::I.m_eraser);
});
}
break;
case WM_LBUTTONUP:
{
ReleaseCapture();
auto pt = lastPoint;
App::I->ui_task_async([pt, extra] {
WacomTablet::I.reset_pressure();
kEventSource pointer_source;
if (WacomTablet::I.m_ink_pen || WacomTablet::I.m_ink_touch)
{
pointer_source = WacomTablet::I.m_ink_pen ? kEventSource::Stylus : kEventSource::Touch;
}
else
{
pointer_source = WacomTablet::I.m_stylus ? kEventSource::Stylus : kEventSource::Mouse;
if ((extra & 0xFFFFFF00) == 0xFF515700)
pointer_source = kEventSource::Touch;
}
App::I->mouse_up(0, (float)pt.x, (float)pt.y, pointer_source, WacomTablet::I.m_eraser);
});
}
break;
case WM_RBUTTONDOWN:
{
SetCapture(hWnd);
auto pt = lastPoint;
App::I->ui_task_async([pt, extra, hWnd] {
kEventSource pointer_source;
if (WacomTablet::I.m_ink_pen || WacomTablet::I.m_ink_touch)
{
pointer_source = WacomTablet::I.m_ink_pen ? kEventSource::Stylus : kEventSource::Touch;
}
else
{
pointer_source = WacomTablet::I.m_stylus ? kEventSource::Stylus : kEventSource::Mouse;
if ((extra & 0xFFFFFF00) == 0xFF515700)
pointer_source = kEventSource::Touch;
}
App::I->mouse_down(1, (float)pt.x, (float)pt.y, 1.f, pointer_source, 0);
});
}
break;
case WM_RBUTTONUP:
{
ReleaseCapture();
auto pt = lastPoint;
App::I->ui_task_async([pt, extra] {
kEventSource pointer_source;
if (WacomTablet::I.m_ink_pen || WacomTablet::I.m_ink_touch)
{
pointer_source = WacomTablet::I.m_ink_pen ? kEventSource::Stylus : kEventSource::Touch;
}
else
{
pointer_source = WacomTablet::I.m_stylus ? kEventSource::Stylus : kEventSource::Mouse;
if ((extra & 0xFFFFFF00) == 0xFF515700)
pointer_source = kEventSource::Touch;
}
App::I->mouse_up(1, (float)pt.x, (float)pt.y, pointer_source, 0);
});
}
break;
case WM_MOUSEWHEEL:
{
POINT pt;
pt.x = GET_X_LPARAM(lp);
pt.y = GET_Y_LPARAM(lp);
ScreenToClient(hWnd, &pt);
App::I->ui_task_async([pt, wp] {
App::I->mouse_scroll((float)pt.x, (float)pt.y,
(float)GET_WHEEL_DELTA_WPARAM(wp) / (float)WHEEL_DELTA);
});
break;
}
case WM_POINTERUPDATE:
{
handle_pointer_update_message(wp);
break;
}
}
if (wp == SC_KEYMENU && (lp >> 16) <= 0)
return 0;
return DefWindowProc(hWnd, msg, wp, lp);
}
}

View File

@@ -0,0 +1,25 @@
#pragma once
#include <Windows.h>
#include <map>
#include "event.h"
#include "platform_windows/windows_vr_shell.h"
struct RetainedState
{
HINSTANCE hInst{};
HWND hWnd{};
bool keys[256]{};
std::map<kKey, int> vkey_map;
wchar_t window_title[512]{};
bool sandboxed = false;
pp::platform::windows::VrShellState vr;
};
namespace pp::platform::windows {
RetainedState& retained_state();
LRESULT CALLBACK main_window_proc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp);
}