// Draws the 'x' close button in regular state or onhover state // Tries to mimic visual style of Chrome tab close button static void DrawCloseButton(HDC hdc, LabelWithCloseWnd *w) { Graphics g(hdc); g.SetCompositingQuality(CompositingQualityHighQuality); g.SetSmoothingMode(SmoothingModeAntiAlias); g.SetPageUnit(UnitPixel); // GDI+ doesn't pick up the window's orientation through the device context, // so we have to explicitly mirror all rendering horizontally if (IsRtl(w->hwnd)) { g.ScaleTransform(-1, 1); g.TranslateTransform((REAL)ClientRect(w->hwnd).dx, 0, MatrixOrderAppend); } Color c; RectI &r = w->closeBtnPos; // in onhover state, background is a red-ish circle bool onHover = IsMouseOverClose(w); if (onHover) { c.SetFromCOLORREF(COL_CLOSE_HOVER_BG); SolidBrush b(c); g.FillEllipse(&b, r.x, r.y, r.dx - 2, r.dy - 2); } // draw 'x' c.SetFromCOLORREF(onHover ? COL_CLOSE_X_HOVER : COL_CLOSE_X); g.TranslateTransform((float)r.x, (float)r.y); Pen p(c, 2); if (onHover) { g.DrawLine(&p, Point(4, 4), Point(r.dx - 6, r.dy - 6)); g.DrawLine(&p, Point(r.dx - 6, 4), Point(4, r.dy - 6)); } else { g.DrawLine(&p, Point(4, 5), Point(r.dx - 6, r.dy - 5)); g.DrawLine(&p, Point(r.dx - 6, 5), Point(4, r.dy - 5)); } }
static void PaintHDC(LabelWithCloseWnd *w, HDC hdc, const PAINTSTRUCT& ps) { HBRUSH br = CreateSolidBrush(w->bgCol); FillRect(hdc, &ps.rcPaint, br); ClientRect cr(w->hwnd); int x = win::DpiAdjust(w->hwnd, w->padL); int y = win::DpiAdjust(w->hwnd, w->padT); UINT opts = ETO_OPAQUE; if (IsRtl(w->hwnd)) { opts = opts | ETO_RTLREADING; } HGDIOBJ prevFont = NULL; if (w->font) { prevFont = SelectObject(hdc, w->font); } SetTextColor(hdc, w->txtCol); SetBkColor(hdc, w->bgCol); WCHAR *s = win::GetText(w->hwnd); ExtTextOut(hdc, x, y, opts, NULL, s, (UINT)str::Len(s), NULL); free(s); // Text might be too long and invade close button area. We just re-paint // the background, which is not the pretties but works. // A better way would be to intelligently truncate text or shrink the font // size (within reason) x = w->closeBtnPos.x - win::DpiAdjust(w->hwnd, LABEL_BUTTON_SPACE_DX); RectI ri(x, 0, cr.dx - x, cr.dy); RECT r = ri.ToRECT(); FillRect(hdc, &r, br); DrawCloseButton(hdc, w->closeBtnPos, IsMouseOverClose(w)); DeleteObject(br); if (w->font) { SelectObject(hdc, prevFont); } }
static LRESULT CALLBACK WndProcLabelWithClose(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { if (WM_ERASEBKGND == msg) { return TRUE; // tells Windows we handle background erasing so it doesn't do it } LabelWithCloseWnd *w = nullptr; if (WM_NCCREATE == msg) { LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lp); w = reinterpret_cast<LabelWithCloseWnd *>(lpcs->lpCreateParams); w->hwnd = hwnd; SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LPARAM>(w)); goto DoDefault; } else { w = reinterpret_cast<LabelWithCloseWnd *>(GetWindowLongPtr(hwnd, GWLP_USERDATA)); } if (!w) { goto DoDefault; } // to match other controls, preferred way is explict SetFont() call if (WM_SETFONT == msg) { SetFont(w, (HFONT)wp); return 0; } if (WM_GETFONT == msg) { return (LRESULT)w->font; } if (WM_SIZE == msg) { int dx = LOWORD(lp); int dy = HIWORD(lp); CalcCloseButtonPos(w, dx, dy); ScheduleRepaint(hwnd); return 0; } if (WM_MOUSEMOVE == msg) { ScheduleRepaint(w->hwnd); if (IsMouseOverClose(w)) { // ask for WM_MOUSELEAVE notifications TRACKMOUSEEVENT tme = { 0 }; tme.cbSize = sizeof(tme); tme.dwFlags = TME_LEAVE; tme.hwndTrack = hwnd; TrackMouseEvent(&tme); } goto DoDefault; } if (WM_MOUSELEAVE == msg) { ScheduleRepaint(w->hwnd); return 0; } if (WM_LBUTTONUP == msg) { if (IsMouseOverClose(w)) { HWND parent = GetParent(w->hwnd); SendMessage(parent, WM_COMMAND, w->cmd, 0); } return 0; } if (WM_PAINT == msg) { OnPaint(w); return 0; } DoDefault: return DefWindowProc(hwnd, msg, wp, lp); }