ares-openbsd/hiro/windows/tool-tip.cpp

162 行
4.7 KiB
C++

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);
u32* 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);
u32* above = nullptr;
auto hdcAbove = CreateCompatibleDC(hdcOutput);
auto hbmAbove = CreateBitmap(hdcAbove, size.cx, size.cy, above);
SelectObject(hdcAbove, hbmAbove);
memory::copy<u32>(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(u32 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 : " "_s);
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 {};
}
}