123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478 |
- namespace hiro {
- static const uint Windows2000 = 0x0500;
- static const uint WindowsXP = 0x0501;
- static const uint WindowsVista = 0x0600;
- static const uint Windows7 = 0x0601;
- static auto Button_CustomDraw(HWND, PAINTSTRUCT&, bool, bool, bool, unsigned, const Font&, const image&, Orientation, const string&) -> void;
- static auto OsVersion() -> uint {
- OSVERSIONINFO versionInfo{0};
- versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
- GetVersionEx(&versionInfo);
- return (versionInfo.dwMajorVersion << 8) + (versionInfo.dwMajorVersion << 0);
- }
- static auto CreateBitmap(HDC hdc, uint width, uint height, uint32_t*& data) -> HBITMAP {
- BITMAPINFO info{};
- info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
- info.bmiHeader.biWidth = width;
- info.bmiHeader.biHeight = -(int)height; //bitmaps are stored upside down unless we negate height
- info.bmiHeader.biPlanes = 1;
- info.bmiHeader.biBitCount = 32;
- info.bmiHeader.biCompression = BI_RGB;
- info.bmiHeader.biSizeImage = width * height * sizeof(uint32_t);
- void* bits = nullptr;
- auto bitmap = CreateDIBSection(hdc, &info, DIB_RGB_COLORS, &bits, nullptr, 0);
- data = (uint32_t*)bits;
- return bitmap;
- }
- static auto CreateBitmap(image icon) -> HBITMAP {
- icon.alphaMultiply(); //Windows AlphaBlend() requires premultiplied image data
- icon.transform();
- uint32_t* data = nullptr;
- auto hdc = GetDC(nullptr);
- auto bitmap = CreateBitmap(hdc, icon.width(), icon.height(), data);
- memory::copy(data, icon.data(), icon.size());
- ReleaseDC(nullptr, hdc);
- return bitmap;
- }
- static auto CreateRGB(const Color& color) -> COLORREF {
- return RGB(color.red(), color.green(), color.blue());
- }
- static auto DropPaths(WPARAM wparam) -> vector<string> {
- auto dropList = HDROP(wparam);
- auto fileCount = DragQueryFile(dropList, ~0u, nullptr, 0);
- vector<string> paths;
- for(auto n : range(fileCount)) {
- auto length = DragQueryFile(dropList, n, nullptr, 0);
- auto buffer = new wchar_t[length + 1];
- if(DragQueryFile(dropList, n, buffer, length + 1)) {
- string path = (const char*)utf8_t(buffer);
- path.transform("\\", "/");
- if(directory::exists(path) && !path.endsWith("/")) path.append("/");
- paths.append(path);
- }
- delete[] buffer;
- }
- return paths;
- }
- static auto WINAPI EnumVisibleChildWindowsProc(HWND hwnd, LPARAM lparam) -> BOOL {
- auto children = (vector<HWND>*)lparam;
- if(IsWindowVisible(hwnd)) children->append(hwnd);
- return true;
- }
- static auto EnumVisibleChildWindows(HWND hwnd) -> vector<HWND> {
- vector<HWND> children;
- EnumChildWindows(hwnd, EnumVisibleChildWindowsProc, (LPARAM)&children);
- return children;
- }
- static auto GetWindowZOrder(HWND hwnd) -> unsigned {
- uint z = 0;
- for(HWND next = hwnd; next != nullptr; next = GetWindow(next, GW_HWNDPREV)) z++;
- return z;
- }
- static auto ImageList_Append(HIMAGELIST imageList, image icon, unsigned scale) -> void {
- if(icon) {
- icon.scale(scale, scale);
- } else {
- icon.allocate(scale, scale);
- icon.fill(GetSysColor(COLOR_WINDOW));
- }
- HBITMAP bitmap = CreateBitmap(icon);
- ImageList_Add(imageList, bitmap, nullptr);
- DeleteObject(bitmap);
- }
- //post message only if said message is not already pending in the queue
- static auto PostMessageOnce(HWND hwnd, UINT id, WPARAM wparam, LPARAM lparam) -> void {
- MSG msg;
- if(!PeekMessage(&msg, hwnd, id, id, PM_NOREMOVE)) {
- PostMessage(hwnd, id, wparam, lparam);
- }
- }
- static auto ScrollEvent(HWND hwnd, WPARAM wparam) -> unsigned {
- SCROLLINFO info;
- memset(&info, 0, sizeof(SCROLLINFO));
- info.cbSize = sizeof(SCROLLINFO);
- info.fMask = SIF_ALL;
- GetScrollInfo(hwnd, SB_CTL, &info);
- switch(LOWORD(wparam)) {
- case SB_LEFT: info.nPos = info.nMin; break;
- case SB_RIGHT: info.nPos = info.nMax; break;
- case SB_LINELEFT: info.nPos--; break;
- case SB_LINERIGHT: info.nPos++; break;
- case SB_PAGELEFT: info.nPos -= info.nMax >> 3; break;
- case SB_PAGERIGHT: info.nPos += info.nMax >> 3; break;
- case SB_THUMBTRACK: info.nPos = info.nTrackPos; break;
- }
- info.fMask = SIF_POS;
- SetScrollInfo(hwnd, SB_CTL, &info, TRUE);
- //Windows may clamp position to scrollbar range
- GetScrollInfo(hwnd, SB_CTL, &info);
- return info.nPos;
- }
- static auto CALLBACK Default_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
- return DefWindowProc(hwnd, msg, wparam, lparam);
- }
- //separate because PopupMenu HWND does not contain GWLP_USERDATA pointing at Window needed for Shared_windowProc
- static auto CALLBACK Menu_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
- switch(msg) {
- case WM_MENUCOMMAND: {
- MENUITEMINFO mii{sizeof(MENUITEMINFO)};
- mii.fMask = MIIM_DATA;
- GetMenuItemInfo((HMENU)lparam, wparam, true, &mii);
- auto object = (mObject*)mii.dwItemData;
- if(!object) break;
- #if defined(Hiro_MenuItem)
- if(auto menuItem = dynamic_cast<mMenuItem*>(object)) {
- return menuItem->self()->onActivate(), false;
- }
- #endif
- #if defined(Hiro_MenuCheckItem)
- if(auto menuCheckItem = dynamic_cast<mMenuCheckItem*>(object)) {
- return menuCheckItem->self()->onToggle(), false;
- }
- #endif
- #if defined(Hiro_MenuRadioItem)
- if(auto menuRadioItem = dynamic_cast<mMenuRadioItem*>(object)) {
- return menuRadioItem->self()->onActivate(), false;
- }
- #endif
- break;
- }
- }
- return DefWindowProc(hwnd, msg, wparam, lparam);
- }
- static auto CALLBACK Shared_windowProc(WindowProc windowProc, HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
- if(Application::state().quit) return DefWindowProc(hwnd, msg, wparam, lparam);
- auto object = (mObject*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
- if(!object) return DefWindowProc(hwnd, msg, wparam, lparam);
- auto window = dynamic_cast<mWindow*>(object);
- if(!window) window = object->parentWindow(true);
- if(!window) return DefWindowProc(hwnd, msg, wparam, lparam);
- auto pWindow = window->self();
- if(!pWindow) return DefWindowProc(hwnd, msg, wparam, lparam);
- switch(msg) {
- case WM_CTLCOLORBTN:
- case WM_CTLCOLOREDIT:
- case WM_CTLCOLORSTATIC: {
- auto object = (mObject*)GetWindowLongPtr((HWND)lparam, GWLP_USERDATA);
- if(!object) break;
- //allow custom colors for various widgets
- //note that this happens always: default colors are black text on a white background, unless overridden
- //this intentionally overrides the default behavior of Windows to paint disabled controls with the window background color
- #if defined(Hiro_Window) && defined(Hiro_TabFrame)
- if(!object->parentTabFrame(true) && window->self()->hbrush) {
- SetBkColor((HDC)wparam, window->self()->hbrushColor);
- return (LRESULT)window->self()->hbrush;
- }
- #endif
- #if defined(Hiro_HexEdit)
- if(auto hexEdit = dynamic_cast<mHexEdit*>(object)) {
- if(auto background = hexEdit->backgroundColor()) SetBkColor((HDC)wparam, CreateRGB(background));
- if(auto foreground = hexEdit->foregroundColor()) SetTextColor((HDC)wparam, CreateRGB(foreground));
- return (LRESULT)hexEdit->self()->backgroundBrush;
- }
- #endif
- #if defined(Hiro_LineEdit)
- if(auto lineEdit = dynamic_cast<mLineEdit*>(object)) {
- if(auto background = lineEdit->backgroundColor()) SetBkColor((HDC)wparam, CreateRGB(background));
- if(auto foreground = lineEdit->foregroundColor()) SetTextColor((HDC)wparam, CreateRGB(foreground));
- return (LRESULT)lineEdit->self()->backgroundBrush;
- }
- #endif
- #if defined(Hiro_TextEdit)
- if(auto textEdit = dynamic_cast<mTextEdit*>(object)) {
- if(auto background = textEdit->backgroundColor()) SetBkColor((HDC)wparam, CreateRGB(background));
- if(auto foreground = textEdit->foregroundColor()) SetTextColor((HDC)wparam, CreateRGB(foreground));
- return (LRESULT)textEdit->self()->backgroundBrush;
- }
- #endif
- break;
- }
- case WM_GETMINMAXINFO: {
- auto info = (LPMINMAXINFO)lparam;
- auto frameMargin = pWindow->frameMargin();
- if(auto minimumSize = window->state.minimumSize) {
- info->ptMinTrackSize.x = minimumSize.width() + frameMargin.width();
- info->ptMinTrackSize.y = minimumSize.height() + frameMargin.height();
- }
- if(auto maximumSize = window->state.maximumSize) {
- info->ptMaxTrackSize.x = maximumSize.width() + frameMargin.width();
- info->ptMaxTrackSize.y = maximumSize.height() + frameMargin.height();
- }
- break;
- }
- case WM_MENUCOMMAND: {
- return Menu_windowProc(hwnd, msg, wparam, lparam);
- }
- case WM_COMMAND: {
- if(!lparam) break;
- auto object = (mObject*)GetWindowLongPtr((HWND)lparam, GWLP_USERDATA);
- if(!object) break;
- #if defined(Hiro_Button)
- if(auto button = dynamic_cast<mButton*>(object)) {
- return button->self()->onActivate(), false;
- }
- #endif
- #if defined(Hiro_CheckButton)
- if(auto checkButton = dynamic_cast<mCheckButton*>(object)) {
- return checkButton->self()->onToggle(), false;
- }
- #endif
- #if defined(Hiro_CheckLabel)
- if(auto checkLabel = dynamic_cast<mCheckLabel*>(object)) {
- return checkLabel->self()->onToggle(), false;
- }
- #endif
- #if defined(Hiro_ComboButton)
- if(auto comboButton = dynamic_cast<mComboButton*>(object)) {
- if(HIWORD(wparam) == CBN_SELCHANGE) {
- return comboButton->self()->onChange(), false;
- }
- }
- #endif
- #if defined(Hiro_LineEdit)
- if(auto lineEdit = dynamic_cast<mLineEdit*>(object)) {
- if(HIWORD(wparam) == EN_CHANGE) {
- return lineEdit->self()->onChange(), false;
- }
- }
- #endif
- #if defined(Hiro_RadioButton)
- if(auto radioButton = dynamic_cast<mRadioButton*>(object)) {
- return radioButton->self()->onActivate(), false;
- }
- #endif
- #if defined(Hiro_RadioLabel)
- if(auto radioLabel = dynamic_cast<mRadioLabel*>(object)) {
- return radioLabel->self()->onActivate(), false;
- }
- #endif
- #if defined(Hiro_TextEdit)
- if(auto textEdit = dynamic_cast<mTextEdit*>(object)) {
- if(HIWORD(wparam) == EN_CHANGE) {
- return textEdit->self()->onChange(), false;
- }
- }
- #endif
- break;
- }
- case WM_NOTIFY: {
- //Widgets inside a TabFrame must be parented to it rather than the Window.
- //This is critical for proper inheritance of styles and message passing.
- //However, by doing this, some WM_NOTIFY messages end up being sent to both
- //the TabFrame and the Window; while others are only sent to the TabFrame.
- //To save code, hiro uses a shared callback for both of these cases.
- //So when a message is sent to both, we ignore the TabFrame message.
- bool isWindowCallback = (object == window);
- auto header = (LPNMHDR)lparam;
- auto object = (mObject*)GetWindowLongPtr((HWND)header->hwndFrom, GWLP_USERDATA);
- if(!object) break;
- #if defined(Hiro_TableView)
- if(auto tableView = dynamic_cast<mTableView*>(object)) {
- if(header->code == LVN_ITEMACTIVATE) {
- tableView->self()->onActivate(lparam);
- break;
- }
- if(header->code == LVN_ITEMCHANGED) {
- tableView->self()->onChange(lparam);
- break;
- }
- if(header->code == LVN_COLUMNCLICK) {
- if(isWindowCallback) tableView->self()->onSort(lparam);
- break;
- }
- if(header->code == NM_CLICK || header->code == NM_DBLCLK) {
- //onToggle performs the test to ensure the TableViewItem clicked was checkable
- if(isWindowCallback) tableView->self()->onToggle(lparam);
- break;
- }
- if(header->code == NM_RCLICK) {
- if(isWindowCallback) tableView->self()->onContext(lparam);
- break;
- }
- if(header->code == NM_CUSTOMDRAW) {
- return tableView->self()->onCustomDraw(lparam);
- }
- }
- #endif
- #if defined(Hiro_TabFrame)
- if(auto tabFrame = dynamic_cast<mTabFrame*>(object)) {
- if(header->code == TCN_SELCHANGE) {
- tabFrame->self()->onChange();
- break;
- }
- }
- #endif
- break;
- }
- case WM_SIZE: {
- bool maximized = IsZoomed(pWindow->hwnd);
- bool minimized = IsIconic(pWindow->hwnd);
- window->state.maximized = maximized;
- window->state.minimized = minimized;
- //todo: call Window::doSize() ?
- break;
- }
- case WM_HSCROLL:
- case WM_VSCROLL: {
- if(!lparam) break;
- auto object = (mObject*)GetWindowLongPtr((HWND)lparam, GWLP_USERDATA);
- if(!object) break;
- #if defined(Hiro_HorizontalScrollBar)
- if(auto horizontalScrollBar = dynamic_cast<mHorizontalScrollBar*>(object)) {
- return horizontalScrollBar->self()->onChange(wparam), true;
- }
- #endif
- #if defined(Hiro_HorizontalSlider)
- if(auto horizontalSlider = dynamic_cast<mHorizontalSlider*>(object)) {
- return horizontalSlider->self()->onChange(), true;
- }
- #endif
- #if defined(Hiro_VerticalScrollBar)
- if(auto verticalScrollBar = dynamic_cast<mVerticalScrollBar*>(object)) {
- return verticalScrollBar->self()->onChange(wparam), true;
- }
- #endif
- #if defined(Hiro_VerticalSlider)
- if(auto verticalSlider = dynamic_cast<mVerticalSlider*>(object)) {
- return verticalSlider->self()->onChange(), true;
- }
- #endif
- break;
- }
- //catch mouse events over disabled windows
- case WM_MOUSEMOVE:
- case WM_MOUSELEAVE:
- case WM_MOUSEHOVER: {
- POINT p{};
- GetCursorPos(&p);
- ScreenToClient(hwnd, &p);
- for(auto window : EnumVisibleChildWindows(hwnd)) {
- if(auto widget = (mWidget*)GetWindowLongPtr(window, GWLP_USERDATA)) {
- auto geometry = widget->geometry();
- if(p.x < geometry.x()) continue;
- if(p.y < geometry.y()) continue;
- if(p.x >= geometry.x() + geometry.width ()) continue;
- if(p.y >= geometry.y() + geometry.height()) continue;
- if(msg == WM_MOUSEMOVE) {
- TRACKMOUSEEVENT event{sizeof(TRACKMOUSEEVENT)};
- event.hwndTrack = hwnd;
- event.dwFlags = TME_LEAVE | TME_HOVER;
- event.dwHoverTime = pToolTip::Delay;
- TrackMouseEvent(&event);
- POINT p{};
- GetCursorPos(&p);
- widget->self()->doMouseMove(p.x, p.y);
- if(auto toolTip = pApplication::state().toolTip) {
- toolTip->windowProc(hwnd, msg, wparam, lparam);
- }
- }
- if(msg == WM_MOUSELEAVE) {
- widget->self()->doMouseLeave();
- }
- if(msg == WM_MOUSEHOVER) {
- widget->self()->doMouseHover();
- }
- }
- }
- break;
- }
- #if defined(Hiro_TableView)
- case AppMessage::TableView_doPaint: {
- if(auto tableView = (mTableView*)lparam) {
- if(auto self = tableView->self()) {
- InvalidateRect(self->hwnd, nullptr, true);
- }
- }
- break;
- }
- case AppMessage::TableView_onActivate: {
- if(auto tableView = (mTableView*)lparam) {
- if(auto self = tableView->self()) {
- tableView->doActivate(self->activateCell);
- }
- }
- break;
- }
- case AppMessage::TableView_onChange: {
- if(auto tableView = (mTableView*)lparam) {
- tableView->doChange();
- }
- }
- #endif
- }
- return CallWindowProc(windowProc, hwnd, msg, wparam, lparam);
- }
- }
|