]> gitweb.ps.run Git - autorec/commitdiff
Refactor into classes
authorPatrick Schönberger <patrick.schoenberger@posteo.de>
Mon, 10 Jan 2022 12:29:19 +0000 (13:29 +0100)
committerPatrick Schönberger <patrick.schoenberger@posteo.de>
Mon, 10 Jan 2022 12:29:19 +0000 (13:29 +0100)
build.cmd
src/main.cpp
src/win.h

index d9e6d4ffccb70585ad5ddf5dc166cd034eabe628..c55948099100cc5b8a4e2c7df36a4ca396eee549 100644 (file)
--- a/build.cmd
+++ b/build.cmd
@@ -1,6 +1,7 @@
+@echo off\r
 \r
 REM cl src/mongoose.c -c\r
 REM rc res/res.rc\r
 \r
-cl /EHsc src/main.cpp mongoose.obj /link user32.lib gdi32.lib shell32.lib Shlwapi.lib ws2_32.lib res/res.res\r
+cl /EHsc /Zi src/main.cpp mongoose.obj /link user32.lib gdi32.lib shell32.lib Shlwapi.lib ws2_32.lib res/res.res\r
 mt -manifest .\main.exe.manifest -outputresource:main.exe;1
\ No newline at end of file
index a59041103694d92ca1d4623e5f220ff14ec7fdd5..805932d9ff32054122de192e9a904850b0d3b7ed 100644 (file)
@@ -87,60 +87,48 @@ checkForegroundProcess(std::string exeName)
 bool recording = false;\r
 HANDLE process = NULL;\r
 \r
-int WINAPI\r
-WinMain(HINSTANCE hInstance,\r
-        HINSTANCE hPrevInstance,\r
-        LPSTR lpCmdLine,\r
-        int nCmdShow)\r
+// int WINAPI\r
+// WinMain(HINSTANCE hInstance,\r
+//         HINSTANCE hPrevInstance,\r
+//         LPSTR lpCmdLine,\r
+//         int nCmdShow)\r
+int main(int argc, char **argv)\r
 {\r
 \r
-  HWND window = win::Window("Title", "MyWindowClass", hInstance);\r
-\r
-  lay_id row1 = lay_item(&win::_::ctx);\r
-  lay_insert(&win::_::ctx, win::_::root, row1);\r
-  lay_set_size_xy(&win::_::ctx, row1, 0, 25);\r
-  lay_set_behave(&win::_::ctx, row1, LAY_LEFT);\r
-  lay_set_contain(&win::_::ctx, row1, LAY_ROW);\r
-  lay_set_margins_ltrb(&win::_::ctx, row1, 5, 5, 5, 5);\r
-  lay_id row2 = lay_item(&win::_::ctx);\r
-  lay_insert(&win::_::ctx, win::_::root, row2);\r
-  lay_set_size_xy(&win::_::ctx, row2, 0, 0);\r
-  lay_set_behave(&win::_::ctx, row2, LAY_FILL);\r
-  lay_set_contain(&win::_::ctx, row2, LAY_ROW);\r
-  lay_set_margins_ltrb(&win::_::ctx, row2, 5, 5, 5, 5);\r
-  lay_id col1 = lay_item(&win::_::ctx);\r
-  lay_set_size_xy(&win::_::ctx, col1, 80, 0);\r
-  lay_set_behave(&win::_::ctx, col1, LAY_VCENTER);\r
-  lay_set_contain(&win::_::ctx, col1, LAY_COLUMN);\r
-  lay_set_margins_ltrb(&win::_::ctx, col1, 5, 0, 5, 0);\r
-\r
-  HWND cbWindowTitle = win::CheckBox(window, "Window Title", row1, 100, 25, 0, 0);\r
-  HWND cbFullscreenWindow = win::CheckBox(window, "Any Fullscreen Application", row1, 200, 25, 0, 0);\r
-\r
-  HWND btnConnect = win::Button(window, "Connect", row1, 100, 25, 0, 0);\r
-  win::Callback(btnConnect, BN_CLICKED, [&]() {\r
-    ws::connect("ws://127.0.0.1:4444");\r
-  });\r
+  //win::Window window("Title", "MyWindowClass", hInstance);\r
+  win::Window window("Title", "MyWindowClass", GetModuleHandle(0));\r
 \r
-  win::Callback(cbWindowTitle, BN_CLICKED, [&]() {\r
-    SendMessageA(cbWindowTitle, BM_SETCHECK, SendMessageA(cbWindowTitle, BM_GETCHECK, 0, 0) ? BST_UNCHECKED : BST_CHECKED, 0);\r
-  });\r
-  win::Callback(cbFullscreenWindow, BN_CLICKED, [&]() {\r
-    SendMessageA(cbFullscreenWindow, BM_SETCHECK, SendMessageA(cbFullscreenWindow, BM_GETCHECK, 0, 0) ? BST_UNCHECKED : BST_CHECKED, 0);\r
+  lay_context *ctx = &window.ctx;\r
+  lay_id root = window.lId;\r
+\r
+  win::Hwnd row1(&window, &window, 0, 0, 0, 25, LAY_ROW, LAY_LEFT);\r
+  lay_set_margins_ltrb(ctx, row1.lId, 5, 5, 5, 5);\r
+  win::Hwnd row2(&window, &window, 0, 0, 0, 0, LAY_ROW, LAY_FILL);\r
+  lay_set_margins_ltrb(ctx, row2.lId, 5, 5, 5, 5);\r
+\r
+  win::CheckBox cbWindowTitle(&window, &row1, "Window Title", 100, 25, 0, 0);\r
+  win::CheckBox cbFullscreenWindow(&window, &row1, "Any Fullscreen Application", 200, 25, 0, 0);\r
+\r
+  win::Button btnConnect(&window, &row1, "Connect", 100, 25, 0, 0);\r
+  btnConnect.onClick([&]() {\r
+    ws::connect("ws://127.0.0.1:4444");\r
   });\r
 \r
-  HWND lstActiveProcesses = win::ListView(window, row2, 0, 0, 0, LAY_FILL);\r
-  lay_insert(&win::_::ctx, row2, col1);\r
-  HWND lstMonitoredProcesses = win::ListBox(window, row2, 0, 0, 0, LAY_FILL);\r
-  win::AddStyle(lstActiveProcesses, WS_VSCROLL);\r
-  win::AddStyle(lstMonitoredProcesses, WS_VSCROLL);\r
-\r
-  HWND btnUpdateWindows = win::Button(window, "Update", col1, 85, 25, 0, 0);\r
-  HWND btnStartMonitoringName = win::Button(window, "Exe name >>", col1, 85, 25, 0, 0);\r
-  HWND btnStartMonitoringPath = win::Button(window, "Full path >>", col1, 85, 25, 0, 0);\r
-  HWND btnStopMonitoring = win::Button(window, "Remove", col1, 85, 25, 0, 0);\r
-  win::Callback(btnUpdateWindows, BN_CLICKED, [&]() {\r
-    win::ListClear(lstActiveProcesses);\r
+  win::ListBox lstActiveProcesses(&window, &row2, 0, 0, 0, LAY_FILL);\r
+  \r
+  win::Hwnd col1(&window, &row2, 0, 0, 80, 0, LAY_COLUMN, LAY_VCENTER);\r
+  lay_set_margins_ltrb(ctx, col1.lId, 5, 0, 5, 0);\r
+\r
+  win::ListBox lstMonitoredProcesses(&window, &row2, 0, 0, 0, LAY_FILL);\r
+  lstActiveProcesses.addStyle(WS_VSCROLL);\r
+  lstMonitoredProcesses.addStyle(WS_VSCROLL);\r
+\r
+  win::Button btnUpdateWindows(&window, &col1, "Update", 85, 25, 0, 0);\r
+  win::Button btnStartMonitoringName(&window, &col1, "Exe name >>", 85, 25, 0, 0);\r
+  win::Button btnStartMonitoringPath(&window, &col1, "Full path >>", 85, 25, 0, 0);\r
+  win::Button btnStopMonitoring(&window, &col1, "Remove", 85, 25, 0, 0);\r
+  btnUpdateWindows.onClick([&]() {\r
+    lstActiveProcesses.clear();\r
     for (HWND hwnd = GetTopWindow(NULL); hwnd != nullptr;\r
          hwnd = GetNextWindow(hwnd, GW_HWNDNEXT)) {\r
       if (!IsWindowVisible(hwnd))\r
@@ -151,44 +139,44 @@ WinMain(HINSTANCE hInstance,
 \r
       char str[1024];\r
       if (GetModuleFileNameExA(getHwndProcess(hwnd), 0, str, 1024) != 0 &&\r
-          win::ListFindString(lstActiveProcesses, str) == LB_ERR) {\r
-        win::ListAddString(lstActiveProcesses, str);\r
+          lstActiveProcesses.findString(str) == LB_ERR) {\r
+        lstActiveProcesses.addString(str);\r
       }\r
     }\r
   });\r
-  win::Callback(btnStartMonitoringName, BN_CLICKED, [&]() {\r
-    int sel = win::ListGetSelectedIndex(lstActiveProcesses);\r
+  btnStartMonitoringName.onClick([&]() {\r
+    int sel = lstActiveProcesses.getSelectedIndex();\r
     if (sel < 0) return;\r
 \r
-    std::string selStr = win::ListGetText(lstActiveProcesses, sel);\r
+    std::string selStr = lstActiveProcesses.getText(sel);\r
     \r
     char *filename = new char[selStr.size()];\r
     std::memcpy(filename, selStr.c_str(), selStr.size());\r
     PathStripPathA(filename);\r
 \r
-    if (win::ListFindString(lstMonitoredProcesses, std::string(filename)) == LB_ERR)\r
-      win::ListAddString(lstMonitoredProcesses, std::string(filename));\r
+    if (lstMonitoredProcesses.findString(std::string(filename)) == LB_ERR)\r
+      lstMonitoredProcesses.addString(std::string(filename));\r
 \r
     delete[] filename;\r
   });\r
-  win::Callback(btnStartMonitoringPath, BN_CLICKED, [&]() {\r
-    int sel = win::ListGetSelectedIndex(lstActiveProcesses);\r
+  btnStartMonitoringPath.onClick([&]() {\r
+    int sel = lstActiveProcesses.getSelectedIndex();\r
     if (sel < 0) return;\r
-    std::string selStr = win::ListGetText(lstActiveProcesses, sel);\r
-    if (win::ListFindString(lstMonitoredProcesses, selStr) == LB_ERR)\r
-    win::ListAddString(lstMonitoredProcesses, selStr);\r
+    std::string selStr = lstActiveProcesses.getText(sel);\r
+    if (lstMonitoredProcesses.findString(selStr) == LB_ERR)\r
+    lstMonitoredProcesses.addString(selStr);\r
   });\r
-  win::Callback(btnStopMonitoring, BN_CLICKED, [&]() {\r
-    int sel = win::ListGetSelectedIndex(lstMonitoredProcesses);\r
+  btnStopMonitoring.onClick([&]() {\r
+    int sel = lstMonitoredProcesses.getSelectedIndex();\r
     if (sel < 0) return;\r
-    win::ListRemove(lstMonitoredProcesses, sel);\r
+    lstMonitoredProcesses.remove(sel);\r
   });\r
 \r
-  win::ShowWindow(window);\r
+  window.show();\r
 \r
   ws::init();\r
 \r
-  SetTimer(window, 10123, 100, [](HWND, UINT, UINT_PTR, DWORD) {\r
+  SetTimer(window.hwnd, 10123, 100, [](HWND, UINT, UINT_PTR, DWORD) {\r
     if (!recording) {\r
       if (checkForegroundProcess("League of Legends.exe")) {\r
         recording = true;\r
@@ -204,7 +192,7 @@ WinMain(HINSTANCE hInstance,
     }\r
   });\r
 \r
-  while (win::UpdateWindow(window)) {\r
+  while (window.update()) {\r
     ws::update();\r
   }\r
 }\r
index 167e162d9b6858c7979eaaafa077f05210d0d54d..fe58ebe8e6acbf730458c55ba3728716f338ab4d 100644 (file)
--- a/src/win.h
+++ b/src/win.h
@@ -10,320 +10,606 @@ processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
 #include "layout.h"\r
 \r
 #include <functional>\r
-#include <map>\r
+#include <unordered_map>\r
 #include <string>\r
 \r
-using std::string;\r
-\r
-namespace win {\r
-namespace _ {\r
-using CallbackFn = std::function<void()>;\r
-std::map<HWND, std::map<WORD, CallbackFn>> handlers;\r
-std::map<HWND, lay_id> lIds;\r
-\r
-NOTIFYICONDATA niData = { 0 };\r
-void\r
-ShowNotificationIcon()\r
-{\r
-  Shell_NotifyIconA(NIM_ADD, &_::niData);\r
-  Shell_NotifyIconA(NIM_SETVERSION, &_::niData);\r
-}\r
-\r
-void\r
-HideNotificationIcon()\r
-{\r
-  Shell_NotifyIconA(NIM_DELETE, &_::niData);\r
-}\r
-\r
-lay_context ctx;\r
-lay_id root;\r
-\r
-LRESULT CALLBACK\r
-WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)\r
+// namespace wnin {\r
+// namespace _ {\r
+// using CallbackFn = std::function<void()>;\r
+// std::map<HWND, std::map<WORD, CallbackFn>> handlers;\r
+// std::map<HWND, lay_id> lIds;\r
+\r
+// NOTIFYICONDATA niData = { 0 };\r
+// void\r
+// ShowNotificationIcon()\r
+// {\r
+//   Shell_NotifyIconA(NIM_ADD, &_::niData);\r
+//   Shell_NotifyIconA(NIM_SETVERSION, &_::niData);\r
+// }\r
+\r
+// void\r
+// HideNotificationIcon()\r
+// {\r
+//   Shell_NotifyIconA(NIM_DELETE, &_::niData);\r
+// }\r
+\r
+// lay_context ctx;\r
+// lay_id root;\r
+\r
+// LRESULT CALLBACK\r
+// WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)\r
+// {\r
+//   switch (msg) {\r
+//     case WM_CLOSE:\r
+//       DestroyWindow(hwnd);\r
+//       break;\r
+//     case WM_DESTROY:\r
+//       Shell_NotifyIconA(NIM_DELETE, &niData);\r
+//       lay_destroy_context(&ctx);\r
+//       PostQuitMessage(0);\r
+//       break;\r
+//     case WM_SIZE:\r
+//       if (wParam == SIZE_MINIMIZED) {\r
+//         ShowNotificationIcon();\r
+//         ShowWindow(hwnd, false);\r
+//       }\r
+//       else {\r
+//         lay_set_size_xy(&_::ctx, _::root, LOWORD(lParam), HIWORD(lParam));\r
+//         lay_run_context(&_::ctx);\r
+\r
+//         for (auto &lId : lIds) {\r
+//           lay_vec4 rect = lay_get_rect(&_::ctx, lId.second);\r
+//           SetWindowPos(lId.first, HWND_TOP,\r
+//             rect[0],\r
+//             rect[1],\r
+//             rect[2],\r
+//             rect[3],\r
+//             SWP_NOZORDER\r
+//           );\r
+//         }\r
+//         RedrawWindow(hwnd, 0, 0, RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN);\r
+//       }\r
+//       break;\r
+//     case WM_COMMAND:\r
+//       if (handlers.find((HWND)lParam) != handlers.end()) {\r
+//         auto handler = handlers[(HWND)lParam];\r
+//         if (handler.find(HIWORD(wParam)) != handler.end()) {\r
+//           auto cb = handler[HIWORD(wParam)];\r
+//           cb();\r
+//         }\r
+//       }\r
+//       break;\r
+//     case WM_NOTIFY:\r
+//       break;\r
+//     case WM_APP + 1:\r
+//       if (LOWORD(lParam) == NIN_SELECT) {\r
+//         HideNotificationIcon();\r
+//         ShowWindow(hwnd, true);\r
+//         SetForegroundWindow(hwnd);\r
+//         SetActiveWindow(hwnd);\r
+//       }\r
+//       break;\r
+//     case WM_CTLCOLORSTATIC:\r
+//       return (LONG)GetStockObject(WHITE_BRUSH);\r
+//     case WM_GETMINMAXINFO: {\r
+//       MINMAXINFO *mmInfo = (MINMAXINFO*)lParam;\r
+//       mmInfo->ptMinTrackSize.x = 400;\r
+//       mmInfo->ptMinTrackSize.y = 200;\r
+//       break;\r
+//     }\r
+//     default:\r
+//       return DefWindowProc(hwnd, msg, wParam, lParam);\r
+//   }\r
+//   return 0;\r
+// }\r
+// }\r
+\r
+\r
+// void\r
+// Callback(HWND hwnd, WORD ev, std::function<void()> cb)\r
+// {\r
+//   _::handlers[hwnd][ev] = cb;\r
+// }\r
+\r
+// HWND\r
+// Window(string title, string className, HINSTANCE hInstance)\r
+// {\r
+//   WNDCLASSEX wc;\r
+//   wc.cbSize = sizeof(WNDCLASSEX);\r
+//   wc.style = 0;\r
+//   wc.lpfnWndProc = _::WndProc;\r
+//   wc.cbClsExtra = 0;\r
+//   wc.cbWndExtra = 0;\r
+//   wc.hInstance = hInstance;\r
+//   wc.hIcon = LoadIcon(nullptr, IDI_APPLICATION);\r
+//   wc.hCursor = LoadCursor(nullptr, IDC_ARROW);\r
+//   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);\r
+//   wc.lpszMenuName = nullptr;\r
+//   wc.lpszClassName = className.c_str();\r
+//   wc.hIconSm = LoadIcon(nullptr, IDI_APPLICATION);\r
+//   RegisterClassEx(&wc);\r
+\r
+//   lay_init_context(&_::ctx);\r
+//   _::root = lay_item(&_::ctx);\r
+//   lay_set_contain(&_::ctx, _::root, LAY_COLUMN);\r
+\r
+//   HWND result = CreateWindowA(className.c_str(),\r
+//                        title.c_str(),\r
+//                        WS_OVERLAPPEDWINDOW,\r
+//                        CW_USEDEFAULT,\r
+//                        CW_USEDEFAULT,\r
+//                        CW_USEDEFAULT,\r
+//                        CW_USEDEFAULT,\r
+//                        nullptr,\r
+//                        nullptr,\r
+//                        hInstance,\r
+//                        nullptr);\r
+                       \r
+//   _::niData.cbSize = sizeof(_::niData);\r
+//   _::niData.uID = 12345;\r
+//   _::niData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;\r
+//   _::niData.hIcon = LoadIconA(hInstance, MAKEINTRESOURCEA(IDI_MY_ICON));\r
+//   _::niData.hWnd = result;\r
+//   _::niData.uCallbackMessage = WM_APP+1;\r
+//   _::niData.uVersion = NOTIFYICON_VERSION_4;\r
+\r
+//   return result;\r
+// }\r
+\r
+// bool\r
+// UpdateWindow(HWND hwnd)\r
+// {\r
+//   MSG msg;\r
+//   if (GetMessage(&msg, nullptr, 0, 0) > 0) {\r
+//     TranslateMessage(&msg);\r
+//     DispatchMessage(&msg);\r
+//     return true;\r
+//   }\r
+//   return false;\r
+// }\r
+\r
+// void\r
+// ShowWindow(HWND hwnd)\r
+// {\r
+//   ShowWindow(hwnd, true);\r
+\r
+//   EnumChildWindows(\r
+//     hwnd,\r
+//     [](HWND hwnd, LPARAM lParam) -> BOOL {\r
+//       HFONT guiFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);\r
+//       SendMessage(hwnd, WM_SETFONT, (WPARAM)guiFont, MAKELPARAM(TRUE, 0));\r
+//       return TRUE;\r
+//     },\r
+//     0);\r
+// }\r
+\r
+// HWND\r
+// Button(HWND hwnd, string title, lay_id parent, lay_scalar w, lay_scalar h, uint32_t contain, uint32_t behave)\r
+// {\r
+//   lay_id lId = lay_item(&_::ctx);\r
+//   lay_insert(&_::ctx, parent, lId);\r
+//   lay_set_size_xy(&_::ctx, lId, w, h);\r
+//   lay_set_contain(&_::ctx, lId, contain);\r
+//   lay_set_behave(&_::ctx, lId, behave);\r
+\r
+//   HWND result = CreateWindowExA(0,\r
+//                                 WC_BUTTONA,\r
+//                                 title.c_str(),\r
+//                                 WS_VISIBLE | WS_CHILD,\r
+//                                 0, 0, 0, 0,\r
+//                                 hwnd,\r
+//                                 nullptr,\r
+//                                 nullptr,\r
+//                                 nullptr);\r
+//   _::lIds[result] = lId;\r
+//   return result;\r
+// }\r
+\r
+// HWND\r
+// ListBox(HWND hwnd, lay_id parent, lay_scalar w, lay_scalar h, uint32_t contain, uint32_t behave)\r
+// {\r
+//   lay_id lId = lay_item(&_::ctx);\r
+//   lay_insert(&_::ctx, parent, lId);\r
+//   lay_set_size_xy(&_::ctx, lId, w, h);\r
+//   lay_set_contain(&_::ctx, lId, contain);\r
+//   lay_set_behave(&_::ctx, lId, behave);\r
+\r
+//   HWND result = CreateWindowExA(0,\r
+//                          WC_LISTBOXA,\r
+//                          "",\r
+//                          WS_VISIBLE | WS_CHILD | WS_BORDER | WS_VSCROLL,\r
+//                          0, 0, 0, 0,\r
+//                          hwnd,\r
+//                          nullptr,\r
+//                          nullptr,\r
+//                          nullptr);\r
+//   _::lIds[result] = lId;\r
+//   return result;\r
+// }\r
+\r
+// void\r
+// ListAddString(HWND hwnd, string str)\r
+// {\r
+//   SendMessage(hwnd, LB_ADDSTRING, 0, (LPARAM)str.c_str());\r
+// }\r
+\r
+// int\r
+// ListGetSelectedIndex(HWND hwnd)\r
+// {\r
+//   int sel = SendMessage(hwnd, LB_GETCURSEL, 0, 0);\r
+//   return sel;\r
+// }\r
+\r
+// int ListFindString(HWND hwnd, string str)\r
+// {\r
+//   return SendMessageA(hwnd, LB_FINDSTRINGEXACT, -1, (LPARAM)str.c_str());\r
+// }\r
+\r
+// string\r
+// ListGetText(HWND hwnd, int index)\r
+// {\r
+//   char buffer[1024];\r
+//   SendMessage(hwnd, LB_GETTEXT, index, (LPARAM)buffer);\r
+//   return string(buffer);\r
+// }\r
+\r
+// void\r
+// ListClear(HWND hwnd)\r
+// {\r
+//   SendMessageA(hwnd, LB_RESETCONTENT, 0, 0);\r
+// }\r
+\r
+// void ListRemove(HWND hwnd, int index)\r
+// {\r
+//   SendMessageA(hwnd, LB_DELETESTRING, index, 0);\r
+// }\r
+\r
+// HWND\r
+// ListView(HWND hwnd, lay_id parent, lay_scalar w, lay_scalar h, uint32_t contain, uint32_t behave)\r
+// {\r
+//   lay_id lId = lay_item(&_::ctx);\r
+//   lay_insert(&_::ctx, parent, lId);\r
+//   lay_set_size_xy(&_::ctx, lId, w, h);\r
+//   lay_set_contain(&_::ctx, lId, contain);\r
+//   lay_set_behave(&_::ctx, lId, behave);\r
+\r
+//   HWND result = CreateWindowExA(0,\r
+//                          WC_LISTVIEWA,\r
+//                          "",\r
+//                          WS_VISIBLE | WS_CHILD | WS_BORDER | WS_VSCROLL,\r
+//                          0, 0, 0, 0,\r
+//                          hwnd,\r
+//                          nullptr,\r
+//                          nullptr,\r
+//                          nullptr);\r
+//   _::lIds[result] = lId;\r
+//   return result;\r
+// }\r
+\r
+// HWND\r
+// CheckBox(HWND hwnd, string title, lay_id parent, lay_scalar w, lay_scalar h, uint32_t contain, uint32_t behave)\r
+// {\r
+//   lay_id lId = lay_item(&_::ctx);\r
+//   lay_insert(&_::ctx, parent, lId);\r
+//   lay_set_size_xy(&_::ctx, lId, w, h);\r
+//   lay_set_contain(&_::ctx, lId, contain);\r
+//   lay_set_behave(&_::ctx, lId, behave);\r
+\r
+//   HWND result = CreateWindowExA(0,\r
+//                          WC_BUTTONA,\r
+//                          title.c_str(),\r
+//                          WS_VISIBLE | WS_CHILD | BS_CHECKBOX,\r
+//                          0, 0, 0, 0,\r
+//                          hwnd,\r
+//                          nullptr,\r
+//                          nullptr,\r
+//                          nullptr);\r
+//   _::lIds[result] = lId;\r
+//   return result;\r
+// }\r
+\r
+// void SetStyle(HWND hwnd, DWORD style)\r
+// {\r
+//   SetWindowLongPtrA(hwnd, GWL_STYLE, style);\r
+// }\r
+// DWORD GetStyle(HWND hwnd)\r
+// {\r
+//   return GetWindowLongPtrA(hwnd, GWL_STYLE);\r
+// }\r
+// void AddStyle(HWND hwnd, DWORD style)\r
+// {\r
+//   SetWindowLongPtrA(hwnd, GWL_STYLE, GetStyle(hwnd) | style);\r
+// }\r
+// void RemoveStyle(HWND hwnd, DWORD style)\r
+// {\r
+//   SetWindowLongPtrA(hwnd, GWL_STYLE, GetStyle(hwnd) & (~style));\r
+// }\r
+// }\r
+\r
+\r
+\r
+\r
+namespace win\r
 {\r
-  switch (msg) {\r
-    case WM_CLOSE:\r
-      DestroyWindow(hwnd);\r
-      break;\r
-    case WM_DESTROY:\r
-      Shell_NotifyIconA(NIM_DELETE, &niData);\r
-      lay_destroy_context(&ctx);\r
-      PostQuitMessage(0);\r
-      break;\r
-    case WM_SIZE:\r
-      if (wParam == SIZE_MINIMIZED) {\r
-        ShowNotificationIcon();\r
-        ShowWindow(hwnd, false);\r
-      }\r
-      else {\r
-        lay_set_size_xy(&_::ctx, _::root, LOWORD(lParam), HIWORD(lParam));\r
-        lay_run_context(&_::ctx);\r
-\r
-        for (auto &lId : lIds) {\r
-          lay_vec4 rect = lay_get_rect(&_::ctx, lId.second);\r
-          SetWindowPos(lId.first, HWND_TOP,\r
-            rect[0],\r
-            rect[1],\r
-            rect[2],\r
-            rect[3],\r
-            SWP_NOZORDER\r
-          );\r
-        }\r
-        RedrawWindow(hwnd, 0, 0, RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN);\r
-      }\r
-      break;\r
-    case WM_COMMAND:\r
-      if (handlers.find((HWND)lParam) != handlers.end()) {\r
-        auto handler = handlers[(HWND)lParam];\r
-        if (handler.find(HIWORD(wParam)) != handler.end()) {\r
-          auto cb = handler[HIWORD(wParam)];\r
-          cb();\r
+  struct Window;\r
+  struct Hwnd\r
+  {\r
+    HWND hwnd;\r
+    lay_id lId;\r
+    Window *window;\r
+\r
+    Hwnd() {}\r
+    Hwnd(Window *window, Hwnd *parent, LPCSTR className, LPCSTR windowName, lay_scalar w, lay_scalar h, uint32_t contain, uint32_t behave);\r
+\r
+    void setStyle(DWORD style)\r
+    {\r
+      SetWindowLongPtrA(hwnd, GWL_STYLE, style);\r
+    }\r
+    DWORD getStyle()\r
+    {\r
+      return GetWindowLongPtrA(hwnd, GWL_STYLE);\r
+    }\r
+    void addStyle(DWORD style)\r
+    {\r
+      SetWindowLongPtrA(hwnd, GWL_STYLE, getStyle() | style);\r
+    }\r
+    void removeStyle(DWORD style)\r
+    {\r
+      SetWindowLongPtrA(hwnd, GWL_STYLE, getStyle() & (~style));\r
+    }\r
+  };\r
+  struct Window : Hwnd\r
+  {\r
+  private:\r
+    NOTIFYICONDATAA niData = { 0 };\r
+\r
+    static LRESULT CALLBACK\r
+    WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)\r
+    {\r
+      Window *window = (Window*)GetWindowLongPtrA(hwnd, 0);\r
+      if (window == nullptr)\r
+        return DefWindowProc(hwnd, msg, wParam, lParam);\r
+\r
+      bool defaultHandler = false;\r
+\r
+      switch (msg) {\r
+        case WM_CLOSE:\r
+          DestroyWindow(hwnd);\r
+          break;\r
+        case WM_DESTROY:\r
+          Shell_NotifyIconA(NIM_DELETE, &window->niData);\r
+          lay_destroy_context(&window->ctx);\r
+          PostQuitMessage(0);\r
+          break;\r
+        case WM_SIZE:\r
+          if (wParam == SIZE_MINIMIZED) {\r
+            //TODO: auslagen\r
+            //ShowNotificationIcon();\r
+            ShowWindow(hwnd, false);\r
+          }\r
+          else {\r
+            lay_set_size_xy(&window->ctx, window->lId, LOWORD(lParam), HIWORD(lParam));\r
+            lay_run_context(&window->ctx);\r
+\r
+            //RedrawWindow(hwnd, 0, 0, RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN);\r
+          }\r
+          break;\r
+        case WM_NOTIFY:\r
+          break;\r
+        case WM_APP + 1:\r
+          if (LOWORD(lParam) == NIN_SELECT) {\r
+            //TODO: auslagern\r
+            //HideNotificationIcon();\r
+            ShowWindow(hwnd, true);\r
+            SetForegroundWindow(hwnd);\r
+            SetActiveWindow(hwnd);\r
+          }\r
+          break;\r
+        case WM_CTLCOLORSTATIC:\r
+          return (LONG)GetStockObject(WHITE_BRUSH);\r
+        case WM_GETMINMAXINFO: {\r
+          MINMAXINFO *mmInfo = (MINMAXINFO*)lParam;\r
+          mmInfo->ptMinTrackSize.x = 400;\r
+          mmInfo->ptMinTrackSize.y = 200;\r
+          break;\r
         }\r
+        default:\r
+          defaultHandler = true;\r
+          break;\r
       }\r
-      break;\r
-    case WM_NOTIFY:\r
-      break;\r
-    case WM_APP + 1:\r
-      if (LOWORD(lParam) == NIN_SELECT) {\r
-        HideNotificationIcon();\r
-        ShowWindow(hwnd, true);\r
-        SetForegroundWindow(hwnd);\r
-        SetActiveWindow(hwnd);\r
+      \r
+      for (auto handler : window->handlers[msg])\r
+        handler(hwnd, msg, wParam, lParam);\r
+\r
+      if (defaultHandler)\r
+        return DefWindowProc(hwnd, msg, wParam, lParam);\r
+      else\r
+        return 0;\r
+    }\r
+  public:\r
+    lay_context ctx;\r
+    std::unordered_map<UINT,\r
+      std::vector<\r
+        std::function<void(HWND, UINT, WPARAM, LPARAM)>>> handlers;\r
+\r
+    Window(std::string title, std::string className, HINSTANCE hInstance)\r
+    {\r
+      WNDCLASSEXA wc;\r
+      wc.cbSize = sizeof(WNDCLASSEX);\r
+      wc.style = 0;\r
+      wc.lpfnWndProc = WndProc;\r
+      wc.cbClsExtra = 0;\r
+      wc.cbWndExtra = sizeof(Window*);\r
+      wc.hInstance = hInstance;\r
+      wc.hIcon = LoadIcon(nullptr, IDI_APPLICATION);\r
+      wc.hCursor = LoadCursor(nullptr, IDC_ARROW);\r
+      wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);\r
+      wc.lpszMenuName = nullptr;\r
+      wc.lpszClassName = className.c_str();\r
+      wc.hIconSm = LoadIcon(nullptr, IDI_APPLICATION);\r
+      RegisterClassExA(&wc);\r
+\r
+      lay_init_context(&ctx);\r
+      lId = lay_item(&ctx);\r
+      lay_set_contain(&ctx, lId, LAY_COLUMN);\r
+\r
+      hwnd = CreateWindowA(className.c_str(),\r
+                          title.c_str(),\r
+                          WS_OVERLAPPEDWINDOW,\r
+                          CW_USEDEFAULT,\r
+                          CW_USEDEFAULT,\r
+                          CW_USEDEFAULT,\r
+                          CW_USEDEFAULT,\r
+                          nullptr,\r
+                          nullptr,\r
+                          hInstance,\r
+                          nullptr);\r
+\r
+      SetWindowLongPtrA(hwnd, 0, (LONG_PTR)this);\r
+      \r
+                          \r
+      niData.cbSize = sizeof(niData);\r
+      niData.uID = 12345;\r
+      niData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;\r
+      niData.hIcon = LoadIconA(hInstance, MAKEINTRESOURCEA(IDI_MY_ICON));\r
+      niData.hWnd = hwnd;\r
+      niData.uCallbackMessage = WM_APP+1;\r
+      niData.uVersion = NOTIFYICON_VERSION_4;\r
+    }\r
+    bool update()\r
+    {\r
+      MSG msg;\r
+      if (GetMessage(&msg, nullptr, 0, 0) > 0) {\r
+        TranslateMessage(&msg);\r
+        DispatchMessage(&msg);\r
+        return true;\r
       }\r
-      break;\r
-    case WM_CTLCOLORSTATIC:\r
-      return (LONG)GetStockObject(WHITE_BRUSH);\r
-    case WM_GETMINMAXINFO: {\r
-      MINMAXINFO *mmInfo = (MINMAXINFO*)lParam;\r
-      mmInfo->ptMinTrackSize.x = 400;\r
-      mmInfo->ptMinTrackSize.y = 200;\r
-      break;\r
+      return false;\r
+    }\r
+    void show()\r
+    {\r
+      ShowWindow(hwnd, true);\r
+\r
+      EnumChildWindows(\r
+        hwnd,\r
+        [](HWND hwnd, LPARAM lParam) -> BOOL {\r
+          HFONT guiFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);\r
+          SendMessage(hwnd, WM_SETFONT, (WPARAM)guiFont, MAKELPARAM(TRUE, 0));\r
+          return TRUE;\r
+        },\r
+        0);\r
+    }\r
+  };\r
+\r
+  struct Button : Hwnd\r
+  {\r
+    Button(Window *window, Hwnd *parent, std::string title, lay_scalar w, lay_scalar h, uint32_t contain, uint32_t behave)\r
+      : Hwnd(window, parent, WC_BUTTONA, title.c_str(), w, h, contain, behave)\r
+    {\r
+      window->handlers[WM_COMMAND].push_back([&](HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {\r
+        if ((HWND)lParam == this->hwnd && HIWORD(wParam) == BN_CLICKED)\r
+          for (auto handler : this->onClickHandlers)\r
+            handler();\r
+      });\r
     }\r
-    default:\r
-      return DefWindowProc(hwnd, msg, wParam, lParam);\r
-  }\r
-  return 0;\r
-}\r
-}\r
-\r
-\r
-void\r
-Callback(HWND hwnd, WORD ev, std::function<void()> cb)\r
-{\r
-  _::handlers[hwnd][ev] = cb;\r
-}\r
-\r
-HWND\r
-Window(string title, string className, HINSTANCE hInstance)\r
-{\r
-  WNDCLASSEX wc;\r
-  wc.cbSize = sizeof(WNDCLASSEX);\r
-  wc.style = 0;\r
-  wc.lpfnWndProc = _::WndProc;\r
-  wc.cbClsExtra = 0;\r
-  wc.cbWndExtra = 0;\r
-  wc.hInstance = hInstance;\r
-  wc.hIcon = LoadIcon(nullptr, IDI_APPLICATION);\r
-  wc.hCursor = LoadCursor(nullptr, IDC_ARROW);\r
-  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);\r
-  wc.lpszMenuName = nullptr;\r
-  wc.lpszClassName = className.c_str();\r
-  wc.hIconSm = LoadIcon(nullptr, IDI_APPLICATION);\r
-  RegisterClassEx(&wc);\r
-\r
-  lay_init_context(&_::ctx);\r
-  _::root = lay_item(&_::ctx);\r
-  lay_set_contain(&_::ctx, _::root, LAY_COLUMN);\r
-\r
-  HWND result = CreateWindowA(className.c_str(),\r
-                       title.c_str(),\r
-                       WS_OVERLAPPEDWINDOW,\r
-                       CW_USEDEFAULT,\r
-                       CW_USEDEFAULT,\r
-                       CW_USEDEFAULT,\r
-                       CW_USEDEFAULT,\r
-                       nullptr,\r
-                       nullptr,\r
-                       hInstance,\r
-                       nullptr);\r
-                       \r
-  _::niData.cbSize = sizeof(_::niData);\r
-  _::niData.uID = 12345;\r
-  _::niData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;\r
-  _::niData.hIcon = LoadIconA(hInstance, MAKEINTRESOURCEA(IDI_MY_ICON));\r
-  _::niData.hWnd = result;\r
-  _::niData.uCallbackMessage = WM_APP+1;\r
-  _::niData.uVersion = NOTIFYICON_VERSION_4;\r
-\r
-  return result;\r
-}\r
-\r
-bool\r
-UpdateWindow(HWND hwnd)\r
-{\r
-  MSG msg;\r
-  if (GetMessage(&msg, nullptr, 0, 0) > 0) {\r
-    TranslateMessage(&msg);\r
-    DispatchMessage(&msg);\r
-    return true;\r
-  }\r
-  return false;\r
-}\r
-\r
-void\r
-ShowWindow(HWND hwnd)\r
-{\r
-  ShowWindow(hwnd, true);\r
-\r
-  EnumChildWindows(\r
-    hwnd,\r
-    [](HWND hwnd, LPARAM lParam) -> BOOL {\r
-      HFONT guiFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);\r
-      SendMessage(hwnd, WM_SETFONT, (WPARAM)guiFont, MAKELPARAM(TRUE, 0));\r
-      return TRUE;\r
-    },\r
-    0);\r
-}\r
-\r
-HWND\r
-Button(HWND hwnd, string title, lay_id parent, lay_scalar w, lay_scalar h, uint32_t contain, uint32_t behave)\r
-{\r
-  lay_id lId = lay_item(&_::ctx);\r
-  lay_insert(&_::ctx, parent, lId);\r
-  lay_set_size_xy(&_::ctx, lId, w, h);\r
-  lay_set_contain(&_::ctx, lId, contain);\r
-  lay_set_behave(&_::ctx, lId, behave);\r
-\r
-  HWND result = CreateWindowExA(0,\r
-                                WC_BUTTONA,\r
-                                title.c_str(),\r
-                                WS_VISIBLE | WS_CHILD,\r
-                                0, 0, 0, 0,\r
-                                hwnd,\r
-                                nullptr,\r
-                                nullptr,\r
-                                nullptr);\r
-  _::lIds[result] = lId;\r
-  return result;\r
-}\r
-\r
-HWND\r
-ListBox(HWND hwnd, lay_id parent, lay_scalar w, lay_scalar h, uint32_t contain, uint32_t behave)\r
-{\r
-  lay_id lId = lay_item(&_::ctx);\r
-  lay_insert(&_::ctx, parent, lId);\r
-  lay_set_size_xy(&_::ctx, lId, w, h);\r
-  lay_set_contain(&_::ctx, lId, contain);\r
-  lay_set_behave(&_::ctx, lId, behave);\r
-\r
-  HWND result = CreateWindowExA(0,\r
-                         WC_LISTBOXA,\r
-                         "",\r
-                         WS_VISIBLE | WS_CHILD | WS_BORDER | WS_VSCROLL,\r
-                         0, 0, 0, 0,\r
-                         hwnd,\r
-                         nullptr,\r
-                         nullptr,\r
-                         nullptr);\r
-  _::lIds[result] = lId;\r
-  return result;\r
-}\r
-\r
-void\r
-ListAddString(HWND hwnd, string str)\r
-{\r
-  SendMessage(hwnd, LB_ADDSTRING, 0, (LPARAM)str.c_str());\r
-}\r
 \r
-int\r
-ListGetSelectedIndex(HWND hwnd)\r
-{\r
-  int sel = SendMessage(hwnd, LB_GETCURSEL, 0, 0);\r
-  return sel;\r
-}\r
+    void onClick(std::function<void()> cb)\r
+    {\r
+      onClickHandlers.push_back(cb);\r
+    }\r
+  private:\r
+    std::vector<std::function<void()>> onClickHandlers;\r
+  };\r
+  \r
+  struct CheckBox : Hwnd\r
+  {\r
+    CheckBox(Window *window, Hwnd *parent, std::string title, lay_scalar w, lay_scalar h, uint32_t contain, uint32_t behave)\r
+      : Hwnd(window, parent, WC_BUTTONA, title.c_str(), w, h, contain, behave)\r
+    {\r
+      addStyle(BS_CHECKBOX);\r
+      window->handlers[WM_COMMAND].push_back([&](HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {\r
+        if ((HWND)lParam == this->hwnd && HIWORD(wParam) == BN_CLICKED)\r
+          SendMessageA(this->hwnd, BM_SETCHECK, SendMessageA(this->hwnd, BM_GETCHECK, 0, 0) ? BST_UNCHECKED : BST_CHECKED, 0);\r
+      });\r
+    }\r
+  };\r
+\r
+  struct ListBox : Hwnd\r
+  {\r
+    ListBox(Window *window, Hwnd *parent, lay_scalar w, lay_scalar h, uint32_t contain, uint32_t behave)\r
+      : Hwnd(window, parent, WC_LISTBOXA, "", w, h, contain, behave)\r
+    {\r
+      addStyle(WS_BORDER);\r
+      addStyle(WS_VSCROLL);\r
+    }\r
 \r
-int ListFindString(HWND hwnd, string str)\r
-{\r
-  return SendMessageA(hwnd, LB_FINDSTRINGEXACT, -1, (LPARAM)str.c_str());\r
-}\r
+    void addString(std::string str)\r
+    {\r
+      SendMessage(hwnd, LB_ADDSTRING, 0, (LPARAM)str.c_str());\r
+    }\r
 \r
-string\r
-ListGetText(HWND hwnd, int index)\r
-{\r
-  char buffer[1024];\r
-  SendMessage(hwnd, LB_GETTEXT, index, (LPARAM)buffer);\r
-  return string(buffer);\r
-}\r
+    int getSelectedIndex()\r
+    {\r
+      return SendMessage(hwnd, LB_GETCURSEL, 0, 0);\r
+    }\r
 \r
-void\r
-ListClear(HWND hwnd)\r
-{\r
-  SendMessageA(hwnd, LB_RESETCONTENT, 0, 0);\r
-}\r
+    int findString(std::string str)\r
+    {\r
+      return SendMessageA(hwnd, LB_FINDSTRINGEXACT, -1, (LPARAM)str.c_str());\r
+    }\r
 \r
-void ListRemove(HWND hwnd, int index)\r
-{\r
-  SendMessageA(hwnd, LB_DELETESTRING, index, 0);\r
-}\r
+    std::string getText(int index)\r
+    {\r
+      int len = SendMessageA(hwnd, LB_GETTEXTLEN, index, 0);\r
+      std::string result;\r
+      result.reserve(len);\r
+      SendMessage(hwnd, LB_GETTEXT, index, (LPARAM)result.data());\r
+      return result;\r
+    }\r
 \r
-HWND\r
-ListView(HWND hwnd, lay_id parent, lay_scalar w, lay_scalar h, uint32_t contain, uint32_t behave)\r
-{\r
-  lay_id lId = lay_item(&_::ctx);\r
-  lay_insert(&_::ctx, parent, lId);\r
-  lay_set_size_xy(&_::ctx, lId, w, h);\r
-  lay_set_contain(&_::ctx, lId, contain);\r
-  lay_set_behave(&_::ctx, lId, behave);\r
-\r
-  HWND result = CreateWindowExA(0,\r
-                         WC_LISTVIEWA,\r
-                         "",\r
-                         WS_VISIBLE | WS_CHILD | WS_BORDER | WS_VSCROLL,\r
-                         0, 0, 0, 0,\r
-                         hwnd,\r
-                         nullptr,\r
-                         nullptr,\r
-                         nullptr);\r
-  _::lIds[result] = lId;\r
-  return result;\r
-}\r
+    void clear()\r
+    {\r
+      SendMessageA(hwnd, LB_RESETCONTENT, 0, 0);\r
+    }\r
 \r
-HWND\r
-CheckBox(HWND hwnd, string title, lay_id parent, lay_scalar w, lay_scalar h, uint32_t contain, uint32_t behave)\r
-{\r
-  lay_id lId = lay_item(&_::ctx);\r
-  lay_insert(&_::ctx, parent, lId);\r
-  lay_set_size_xy(&_::ctx, lId, w, h);\r
-  lay_set_contain(&_::ctx, lId, contain);\r
-  lay_set_behave(&_::ctx, lId, behave);\r
-\r
-  HWND result = CreateWindowExA(0,\r
-                         WC_BUTTONA,\r
-                         title.c_str(),\r
-                         WS_VISIBLE | WS_CHILD | BS_CHECKBOX,\r
-                         0, 0, 0, 0,\r
-                         hwnd,\r
-                         nullptr,\r
-                         nullptr,\r
-                         nullptr);\r
-  _::lIds[result] = lId;\r
-  return result;\r
+    void remove(int index)\r
+    {\r
+      SendMessageA(hwnd, LB_DELETESTRING, index, 0);\r
+    }\r
+  };\r
 }\r
 \r
-void SetStyle(HWND hwnd, DWORD style)\r
+win::Hwnd::Hwnd(Window *window, Hwnd *parent, LPCSTR className, LPCSTR windowName, lay_scalar w, lay_scalar h, uint32_t contain, uint32_t behave)\r
 {\r
-  SetWindowLongPtrA(hwnd, GWL_STYLE, style);\r
-}\r
-DWORD GetStyle(HWND hwnd)\r
-{\r
-  return GetWindowLongPtrA(hwnd, GWL_STYLE);\r
-}\r
-void AddStyle(HWND hwnd, DWORD style)\r
-{\r
-  SetWindowLongPtrA(hwnd, GWL_STYLE, GetStyle(hwnd) | style);\r
-}\r
-void RemoveStyle(HWND hwnd, DWORD style)\r
-{\r
-  SetWindowLongPtrA(hwnd, GWL_STYLE, GetStyle(hwnd) & (~style));\r
-}\r
+  this->window = window;\r
+  \r
+  lId = lay_item(&window->ctx);\r
+  lay_insert(&window->ctx, parent->lId, lId);\r
+  lay_set_size_xy(&window->ctx, lId, w, h);\r
+  lay_set_contain(&window->ctx, lId, contain);\r
+  lay_set_behave(&window->ctx, lId, behave);\r
+  \r
+  hwnd = CreateWindowExA(0,\r
+                          className,\r
+                          windowName,\r
+                          WS_VISIBLE | WS_CHILD,\r
+                          0,\r
+                          0,\r
+                          0,\r
+                          0,\r
+                          window->hwnd,\r
+                          nullptr,\r
+                          nullptr,\r
+                          nullptr);\r
+\r
+  window->handlers[WM_SIZE].push_back([this](HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {\r
+    lay_vec4 rect = lay_get_rect(&this->window->ctx, this->lId);\r
+    SetWindowPos(this->hwnd, HWND_TOP,\r
+      rect[0],\r
+      rect[1],\r
+      rect[2],\r
+      rect[3],\r
+      SWP_NOZORDER\r
+    );\r
+  });\r
 }
\ No newline at end of file