mirror of https://github.com/bsnes-emu/bsnes.git
162 lines
4.7 KiB
C++
162 lines
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);
|
|
|
|
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 {};
|
|
}
|
|
|
|
}
|