#pragma comment(linker, "\"/manifestdependency:type='win32' \ name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") #include #include #include "../res/resource.h" #include "layout.h" #include #include #include // namespace wnin { // namespace _ { // using CallbackFn = std::function; // std::map> handlers; // std::map lIds; // NOTIFYICONDATA niData = { 0 }; // void // ShowNotificationIcon() // { // Shell_NotifyIconA(NIM_ADD, &_::niData); // Shell_NotifyIconA(NIM_SETVERSION, &_::niData); // } // void // HideNotificationIcon() // { // Shell_NotifyIconA(NIM_DELETE, &_::niData); // } // lay_context ctx; // lay_id root; // LRESULT CALLBACK // WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) // { // switch (msg) { // case WM_CLOSE: // DestroyWindow(hwnd); // break; // case WM_DESTROY: // Shell_NotifyIconA(NIM_DELETE, &niData); // lay_destroy_context(&ctx); // PostQuitMessage(0); // break; // case WM_SIZE: // if (wParam == SIZE_MINIMIZED) { // ShowNotificationIcon(); // ShowWindow(hwnd, false); // } // else { // lay_set_size_xy(&_::ctx, _::root, LOWORD(lParam), HIWORD(lParam)); // lay_run_context(&_::ctx); // for (auto &lId : lIds) { // lay_vec4 rect = lay_get_rect(&_::ctx, lId.second); // SetWindowPos(lId.first, HWND_TOP, // rect[0], // rect[1], // rect[2], // rect[3], // SWP_NOZORDER // ); // } // RedrawWindow(hwnd, 0, 0, RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN); // } // break; // case WM_COMMAND: // if (handlers.find((HWND)lParam) != handlers.end()) { // auto handler = handlers[(HWND)lParam]; // if (handler.find(HIWORD(wParam)) != handler.end()) { // auto cb = handler[HIWORD(wParam)]; // cb(); // } // } // break; // case WM_NOTIFY: // break; // case WM_APP + 1: // if (LOWORD(lParam) == NIN_SELECT) { // HideNotificationIcon(); // ShowWindow(hwnd, true); // SetForegroundWindow(hwnd); // SetActiveWindow(hwnd); // } // break; // case WM_CTLCOLORSTATIC: // return (LONG)GetStockObject(WHITE_BRUSH); // case WM_GETMINMAXINFO: { // MINMAXINFO *mmInfo = (MINMAXINFO*)lParam; // mmInfo->ptMinTrackSize.x = 400; // mmInfo->ptMinTrackSize.y = 200; // break; // } // default: // return DefWindowProc(hwnd, msg, wParam, lParam); // } // return 0; // } // } // void // Callback(HWND hwnd, WORD ev, std::function cb) // { // _::handlers[hwnd][ev] = cb; // } // HWND // Window(string title, string className, HINSTANCE hInstance) // { // WNDCLASSEX wc; // wc.cbSize = sizeof(WNDCLASSEX); // wc.style = 0; // wc.lpfnWndProc = _::WndProc; // wc.cbClsExtra = 0; // wc.cbWndExtra = 0; // wc.hInstance = hInstance; // wc.hIcon = LoadIcon(nullptr, IDI_APPLICATION); // wc.hCursor = LoadCursor(nullptr, IDC_ARROW); // wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // wc.lpszMenuName = nullptr; // wc.lpszClassName = className.c_str(); // wc.hIconSm = LoadIcon(nullptr, IDI_APPLICATION); // RegisterClassEx(&wc); // lay_init_context(&_::ctx); // _::root = lay_item(&_::ctx); // lay_set_contain(&_::ctx, _::root, LAY_COLUMN); // HWND result = CreateWindowA(className.c_str(), // title.c_str(), // WS_OVERLAPPEDWINDOW, // CW_USEDEFAULT, // CW_USEDEFAULT, // CW_USEDEFAULT, // CW_USEDEFAULT, // nullptr, // nullptr, // hInstance, // nullptr); // _::niData.cbSize = sizeof(_::niData); // _::niData.uID = 12345; // _::niData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; // _::niData.hIcon = LoadIconA(hInstance, MAKEINTRESOURCEA(IDI_MY_ICON)); // _::niData.hWnd = result; // _::niData.uCallbackMessage = WM_APP+1; // _::niData.uVersion = NOTIFYICON_VERSION_4; // return result; // } // bool // UpdateWindow(HWND hwnd) // { // MSG msg; // if (GetMessage(&msg, nullptr, 0, 0) > 0) { // TranslateMessage(&msg); // DispatchMessage(&msg); // return true; // } // return false; // } // void // ShowWindow(HWND hwnd) // { // ShowWindow(hwnd, true); // EnumChildWindows( // hwnd, // [](HWND hwnd, LPARAM lParam) -> BOOL { // HFONT guiFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT); // SendMessage(hwnd, WM_SETFONT, (WPARAM)guiFont, MAKELPARAM(TRUE, 0)); // return TRUE; // }, // 0); // } // HWND // Button(HWND hwnd, string title, lay_id parent, lay_scalar w, lay_scalar h, uint32_t contain, uint32_t behave) // { // lay_id lId = lay_item(&_::ctx); // lay_insert(&_::ctx, parent, lId); // lay_set_size_xy(&_::ctx, lId, w, h); // lay_set_contain(&_::ctx, lId, contain); // lay_set_behave(&_::ctx, lId, behave); // HWND result = CreateWindowExA(0, // WC_BUTTONA, // title.c_str(), // WS_VISIBLE | WS_CHILD, // 0, 0, 0, 0, // hwnd, // nullptr, // nullptr, // nullptr); // _::lIds[result] = lId; // return result; // } // HWND // ListBox(HWND hwnd, lay_id parent, lay_scalar w, lay_scalar h, uint32_t contain, uint32_t behave) // { // lay_id lId = lay_item(&_::ctx); // lay_insert(&_::ctx, parent, lId); // lay_set_size_xy(&_::ctx, lId, w, h); // lay_set_contain(&_::ctx, lId, contain); // lay_set_behave(&_::ctx, lId, behave); // HWND result = CreateWindowExA(0, // WC_LISTBOXA, // "", // WS_VISIBLE | WS_CHILD | WS_BORDER | WS_VSCROLL, // 0, 0, 0, 0, // hwnd, // nullptr, // nullptr, // nullptr); // _::lIds[result] = lId; // return result; // } // void // ListAddString(HWND hwnd, string str) // { // SendMessage(hwnd, LB_ADDSTRING, 0, (LPARAM)str.c_str()); // } // int // ListGetSelectedIndex(HWND hwnd) // { // int sel = SendMessage(hwnd, LB_GETCURSEL, 0, 0); // return sel; // } // int ListFindString(HWND hwnd, string str) // { // return SendMessageA(hwnd, LB_FINDSTRINGEXACT, -1, (LPARAM)str.c_str()); // } // string // ListGetText(HWND hwnd, int index) // { // char buffer[1024]; // SendMessage(hwnd, LB_GETTEXT, index, (LPARAM)buffer); // return string(buffer); // } // void // ListClear(HWND hwnd) // { // SendMessageA(hwnd, LB_RESETCONTENT, 0, 0); // } // void ListRemove(HWND hwnd, int index) // { // SendMessageA(hwnd, LB_DELETESTRING, index, 0); // } // HWND // ListView(HWND hwnd, lay_id parent, lay_scalar w, lay_scalar h, uint32_t contain, uint32_t behave) // { // lay_id lId = lay_item(&_::ctx); // lay_insert(&_::ctx, parent, lId); // lay_set_size_xy(&_::ctx, lId, w, h); // lay_set_contain(&_::ctx, lId, contain); // lay_set_behave(&_::ctx, lId, behave); // HWND result = CreateWindowExA(0, // WC_LISTVIEWA, // "", // WS_VISIBLE | WS_CHILD | WS_BORDER | WS_VSCROLL, // 0, 0, 0, 0, // hwnd, // nullptr, // nullptr, // nullptr); // _::lIds[result] = lId; // return result; // } // HWND // CheckBox(HWND hwnd, string title, lay_id parent, lay_scalar w, lay_scalar h, uint32_t contain, uint32_t behave) // { // lay_id lId = lay_item(&_::ctx); // lay_insert(&_::ctx, parent, lId); // lay_set_size_xy(&_::ctx, lId, w, h); // lay_set_contain(&_::ctx, lId, contain); // lay_set_behave(&_::ctx, lId, behave); // HWND result = CreateWindowExA(0, // WC_BUTTONA, // title.c_str(), // WS_VISIBLE | WS_CHILD | BS_CHECKBOX, // 0, 0, 0, 0, // hwnd, // nullptr, // nullptr, // nullptr); // _::lIds[result] = lId; // return result; // } // void SetStyle(HWND hwnd, DWORD style) // { // SetWindowLongPtrA(hwnd, GWL_STYLE, style); // } // DWORD GetStyle(HWND hwnd) // { // return GetWindowLongPtrA(hwnd, GWL_STYLE); // } // void AddStyle(HWND hwnd, DWORD style) // { // SetWindowLongPtrA(hwnd, GWL_STYLE, GetStyle(hwnd) | style); // } // void RemoveStyle(HWND hwnd, DWORD style) // { // SetWindowLongPtrA(hwnd, GWL_STYLE, GetStyle(hwnd) & (~style)); // } // } namespace win { struct Window; struct Hwnd { HWND hwnd; lay_id lId; Window *window; Hwnd() {} Hwnd(Window *window, Hwnd *parent, LPCSTR className, LPCSTR windowName, lay_scalar w, lay_scalar h, uint32_t contain, uint32_t behave); void setStyle(DWORD style) { SetWindowLongPtrA(hwnd, GWL_STYLE, style); } DWORD getStyle() { return GetWindowLongPtrA(hwnd, GWL_STYLE); } void addStyle(DWORD style) { SetWindowLongPtrA(hwnd, GWL_STYLE, getStyle() | style); } void removeStyle(DWORD style) { SetWindowLongPtrA(hwnd, GWL_STYLE, getStyle() & (~style)); } }; struct Window : Hwnd { private: NOTIFYICONDATAA niData = { 0 }; static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { Window *window = (Window*)GetWindowLongPtrA(hwnd, 0); if (window == nullptr) return DefWindowProc(hwnd, msg, wParam, lParam); bool defaultHandler = false; switch (msg) { case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: Shell_NotifyIconA(NIM_DELETE, &window->niData); lay_destroy_context(&window->ctx); PostQuitMessage(0); break; case WM_SIZE: if (wParam == SIZE_MINIMIZED) { //TODO: auslagen //ShowNotificationIcon(); ShowWindow(hwnd, false); } else { lay_set_size_xy(&window->ctx, window->lId, LOWORD(lParam), HIWORD(lParam)); lay_run_context(&window->ctx); //RedrawWindow(hwnd, 0, 0, RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN); } break; case WM_NOTIFY: break; case WM_APP + 1: if (LOWORD(lParam) == NIN_SELECT) { //TODO: auslagern //HideNotificationIcon(); ShowWindow(hwnd, true); SetForegroundWindow(hwnd); SetActiveWindow(hwnd); } break; case WM_CTLCOLORSTATIC: return (LONG)GetStockObject(WHITE_BRUSH); case WM_GETMINMAXINFO: { MINMAXINFO *mmInfo = (MINMAXINFO*)lParam; mmInfo->ptMinTrackSize.x = 400; mmInfo->ptMinTrackSize.y = 200; break; } default: defaultHandler = true; break; } for (auto handler : window->handlers[msg]) handler(hwnd, msg, wParam, lParam); if (defaultHandler) return DefWindowProc(hwnd, msg, wParam, lParam); else return 0; } public: lay_context ctx; std::unordered_map>> handlers; Window(std::string title, std::string className, HINSTANCE hInstance) { WNDCLASSEXA wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = 0; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = sizeof(Window*); wc.hInstance = hInstance; wc.hIcon = LoadIcon(nullptr, IDI_APPLICATION); wc.hCursor = LoadCursor(nullptr, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszMenuName = nullptr; wc.lpszClassName = className.c_str(); wc.hIconSm = LoadIcon(nullptr, IDI_APPLICATION); RegisterClassExA(&wc); lay_init_context(&ctx); lId = lay_item(&ctx); lay_set_contain(&ctx, lId, LAY_COLUMN); hwnd = CreateWindowA(className.c_str(), title.c_str(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, hInstance, nullptr); SetWindowLongPtrA(hwnd, 0, (LONG_PTR)this); niData.cbSize = sizeof(niData); niData.uID = 12345; niData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; niData.hIcon = LoadIconA(hInstance, MAKEINTRESOURCEA(IDI_MY_ICON)); niData.hWnd = hwnd; niData.uCallbackMessage = WM_APP+1; niData.uVersion = NOTIFYICON_VERSION_4; } bool update() { MSG msg; if (GetMessage(&msg, nullptr, 0, 0) > 0) { TranslateMessage(&msg); DispatchMessage(&msg); return true; } return false; } void show() { ShowWindow(hwnd, true); EnumChildWindows( hwnd, [](HWND hwnd, LPARAM lParam) -> BOOL { HFONT guiFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT); SendMessage(hwnd, WM_SETFONT, (WPARAM)guiFont, MAKELPARAM(TRUE, 0)); return TRUE; }, 0); } }; struct Button : Hwnd { Button(Window *window, Hwnd *parent, std::string title, lay_scalar w, lay_scalar h, uint32_t contain, uint32_t behave) : Hwnd(window, parent, WC_BUTTONA, title.c_str(), w, h, contain, behave) { window->handlers[WM_COMMAND].push_back([&](HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { if ((HWND)lParam == this->hwnd && HIWORD(wParam) == BN_CLICKED) for (auto handler : this->onClickHandlers) handler(); }); } void onClick(std::function cb) { onClickHandlers.push_back(cb); } private: std::vector> onClickHandlers; }; struct CheckBox : Hwnd { CheckBox(Window *window, Hwnd *parent, std::string title, lay_scalar w, lay_scalar h, uint32_t contain, uint32_t behave) : Hwnd(window, parent, WC_BUTTONA, title.c_str(), w, h, contain, behave) { addStyle(BS_CHECKBOX); window->handlers[WM_COMMAND].push_back([&](HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { if ((HWND)lParam == this->hwnd && HIWORD(wParam) == BN_CLICKED) SendMessageA(this->hwnd, BM_SETCHECK, SendMessageA(this->hwnd, BM_GETCHECK, 0, 0) ? BST_UNCHECKED : BST_CHECKED, 0); }); } }; struct ListBox : Hwnd { ListBox(Window *window, Hwnd *parent, lay_scalar w, lay_scalar h, uint32_t contain, uint32_t behave) : Hwnd(window, parent, WC_LISTBOXA, "", w, h, contain, behave) { addStyle(WS_BORDER); addStyle(WS_VSCROLL); } void addString(std::string str) { SendMessage(hwnd, LB_ADDSTRING, 0, (LPARAM)str.c_str()); } int getSelectedIndex() { return SendMessage(hwnd, LB_GETCURSEL, 0, 0); } int findString(std::string str) { return SendMessageA(hwnd, LB_FINDSTRINGEXACT, -1, (LPARAM)str.c_str()); } std::string getText(int index) { int len = SendMessageA(hwnd, LB_GETTEXTLEN, index, 0); std::string result; result.reserve(len); SendMessage(hwnd, LB_GETTEXT, index, (LPARAM)result.data()); return result; } void clear() { SendMessageA(hwnd, LB_RESETCONTENT, 0, 0); } void remove(int index) { SendMessageA(hwnd, LB_DELETESTRING, index, 0); } }; } win::Hwnd::Hwnd(Window *window, Hwnd *parent, LPCSTR className, LPCSTR windowName, lay_scalar w, lay_scalar h, uint32_t contain, uint32_t behave) { this->window = window; lId = lay_item(&window->ctx); lay_insert(&window->ctx, parent->lId, lId); lay_set_size_xy(&window->ctx, lId, w, h); lay_set_contain(&window->ctx, lId, contain); lay_set_behave(&window->ctx, lId, behave); hwnd = CreateWindowExA(0, className, windowName, WS_VISIBLE | WS_CHILD, 0, 0, 0, 0, window->hwnd, nullptr, nullptr, nullptr); window->handlers[WM_SIZE].push_back([this](HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { lay_vec4 rect = lay_get_rect(&this->window->ctx, this->lId); SetWindowPos(this->hwnd, HWND_TOP, rect[0], rect[1], rect[2], rect[3], SWP_NOZORDER ); }); }