123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 |
- namespace hiro {
- static auto CALLBACK ToolTip_windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> LRESULT {
- if(auto toolTip = (pToolTip*)GetWindowLongPtr(hwnd, GWLP_USERDATA)) {
- if(auto result = toolTip->windowProc(hwnd, msg, wparam, lparam)) {
- return result();
- }
- }
- return DefWindowProc(hwnd, msg, wparam, lparam);
- }
- pToolTip::pToolTip(const string& toolTipText) {
- text = toolTipText;
- htheme = OpenThemeData(hwnd, L"TOOLTIP");
- hwnd = CreateWindowEx(
- WS_EX_TOOLWINDOW | WS_EX_TOPMOST | (htheme ? WS_EX_LAYERED : 0), L"hiroToolTip", L"",
- WS_POPUP, 0, 0, 0, 0,
- 0, 0, GetModuleHandle(0), 0
- );
- SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)this);
- tracking.x = -1;
- tracking.y = -1;
- timeout.setInterval(Timeout);
- timeout.onActivate([&] { hide(); });
- }
- pToolTip::~pToolTip() {
- hide();
- if(htheme) { CloseThemeData(htheme); htheme = nullptr; }
- if(hwnd) { DestroyWindow(hwnd); hwnd = nullptr; }
- }
- auto pToolTip::drawLayered() -> void {
- auto hdcOutput = GetDC(nullptr);
- uint32_t* below = nullptr;
- auto hdcBelow = CreateCompatibleDC(hdcOutput);
- auto hbmBelow = CreateBitmap(hdcBelow, size.cx, size.cy, below);
- SelectObject(hdcBelow, hbmBelow);
- RECT rc{};
- rc.left = 0, rc.top = 0, rc.right = size.cx, rc.bottom = size.cy;
- DrawThemeBackground(htheme, hdcBelow, TTP_STANDARD, TTSS_NORMAL, &rc, nullptr);
- uint32_t* above = nullptr;
- auto hdcAbove = CreateCompatibleDC(hdcOutput);
- auto hbmAbove = CreateBitmap(hdcAbove, size.cx, size.cy, above);
- SelectObject(hdcAbove, hbmAbove);
- memory::copy<uint32_t>(above, below, size.cx * size.cy);
- auto hfont = pFont::create(Font());
- SelectObject(hdcAbove, hfont);
- SetBkMode(hdcAbove, TRANSPARENT);
- SetTextColor(hdcAbove, RGB(0, 0, 0));
- utf16_t drawText(text);
- rc.left += 6, rc.top += 6, rc.right -= 6, rc.bottom -= 6;
- DrawText(hdcAbove, drawText, -1, &rc, DT_LEFT | DT_TOP);
- DeleteObject(hfont);
- for(uint n : range(size.cx * size.cy)) {
- below[n] = (below[n] & 0xff000000) | (above[n] & 0x00ffffff);
- }
- BLENDFUNCTION blend{};
- blend.BlendOp = AC_SRC_OVER;
- blend.SourceConstantAlpha = 255;
- blend.AlphaFormat = AC_SRC_ALPHA;
- POINT zero{};
- zero.x = 0, zero.y = 0;
- UpdateLayeredWindow(hwnd, hdcOutput, &position, &size, hdcBelow, &zero, RGB(0, 0, 0), &blend, ULW_ALPHA);
- DeleteObject(hbmAbove);
- DeleteObject(hbmBelow);
- DeleteDC(hdcAbove);
- DeleteDC(hdcBelow);
- ReleaseDC(nullptr, hdcOutput);
- }
- auto pToolTip::drawOpaque() -> void {
- PAINTSTRUCT ps;
- BeginPaint(hwnd, &ps);
- RECT rc{};
- GetClientRect(hwnd, &rc);
- auto brush = CreateSolidBrush(RGB(0, 0, 0));
- FillRect(ps.hdc, &rc, brush);
- DeleteObject(brush);
- rc.left += 1, rc.top += 1, rc.right -= 1, rc.bottom -= 1;
- brush = CreateSolidBrush(RGB(255, 255, 225));
- FillRect(ps.hdc, &rc, brush);
- DeleteObject(brush);
- rc.left += 5, rc.top += 5, rc.right -= 5, rc.bottom -= 5;
- SetBkMode(ps.hdc, TRANSPARENT);
- auto font = pFont::create(Font());
- SelectObject(ps.hdc, font);
- SetTextColor(ps.hdc, RGB(0, 0, 0));
- DrawText(ps.hdc, utf16_t(text), -1, &rc, DT_LEFT | DT_TOP);
- DeleteObject(font);
- EndPaint(hwnd, &ps);
- }
- auto pToolTip::show() -> void {
- if(auto toolTip = pApplication::state().toolTip) {
- if(toolTip != this) toolTip->hide();
- }
- pApplication::state().toolTip = this;
- GetCursorPos(&position);
- if(position.x == tracking.x && position.y == tracking.y) return;
- tracking.x = position.x, tracking.y = position.y;
- position.y += 18;
- auto textSize = pFont::size(Font(), text ? text : " ");
- size.cx = 12 + textSize.width();
- size.cy = 12 + textSize.height();
- //try to keep the tool-tip onscreen
- auto desktop = pDesktop::size();
- if(position.x + size.cx >= desktop.width ()) position.x = desktop.width () - size.cx;
- if(position.y + size.cy >= desktop.height()) position.y = desktop.height() - size.cy;
- if(position.x < 0) position.x = 0;
- if(position.y < 0) position.y = 0;
- SetWindowPos(hwnd, HWND_TOP, position.x, position.y, size.cx, size.cy, SWP_NOACTIVATE | SWP_SHOWWINDOW);
- if(htheme) drawLayered();
- timeout.setEnabled(true);
- }
- auto pToolTip::hide() -> void {
- pApplication::state().toolTip = nullptr;
- timeout.setEnabled(false);
- ShowWindow(hwnd, SW_HIDE);
- GetCursorPos(&tracking);
- }
- auto pToolTip::windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> maybe<LRESULT> {
- switch(msg) {
- case WM_ERASEBKGND:
- case WM_PAINT:
- if(htheme) break;
- drawOpaque();
- return msg == WM_ERASEBKGND;
- case WM_MOUSEMOVE:
- case WM_MOUSELEAVE: {
- POINT point{};
- GetCursorPos(&point);
- if(point.x != tracking.x || point.y != tracking.y) hide();
- } break;
- case WM_LBUTTONDOWN: case WM_LBUTTONUP:
- case WM_MBUTTONDOWN: case WM_MBUTTONUP:
- case WM_RBUTTONDOWN: case WM_RBUTTONUP:
- hide();
- break;
- }
- return {};
- }
- }
|