static void TabNotification(WindowInfo *win, UINT code, int idx1, int idx2) { if (!WindowInfoStillValid(win)) { return; } NMHDR nmhdr = { nullptr, 0, code }; if (TabsOnNotify(win, (LPARAM)&nmhdr, idx1, idx2)) { return; } TabPainter *tab = (TabPainter *)GetWindowLongPtr(win->hwndTabBar, GWLP_USERDATA); if (T_CLOSING == code) { // if we have permission to close the tab tab->Invalidate(tab->nextTab); tab->xClicked = tab->nextTab; return; } if (TCN_SELCHANGING == code) { // if we have permission to select the tab tab->Invalidate(tab->current); tab->Invalidate(tab->nextTab); tab->current = tab->nextTab; // send notification that the tab is selected nmhdr.code = TCN_SELCHANGE; TabsOnNotify(win, (LPARAM)&nmhdr); } }
void UpdateCurrentTabBgColor(WindowInfo *win) { TabPainter *tab = (TabPainter *)GetWindowLongPtr(win->hwndTabBar, GWLP_USERDATA); if (win->AsEbook()) { COLORREF txtCol; GetEbookUiColors(txtCol, tab->currBgCol); } else { // TODO: match either the toolbar (if shown) or background tab->currBgCol = DEFAULT_CURRENT_BG_COL; } tab->EvaluateColors(true); RepaintNow(win->hwndTabBar); }
static LRESULT CALLBACK WndProcTabBar(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; HDC hdc; int index; LPTCITEM tcs; TabPainter *tab = (TabPainter *)GetWindowLongPtr(hwnd, GWLP_USERDATA); switch (msg) { case WM_DESTROY: delete tab; SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)0); break; case TCM_INSERTITEM: index = (int)wParam; tcs = (LPTCITEM)lParam; CrashIf(!(TCIF_TEXT & tcs->mask)); tab->Insert(index, tcs->pszText); if ((int)index <= tab->current) tab->current++; tab->xClicked = -1; if (tab->isMouseInClientArea) PostMessage(hwnd, WM_MOUSEMOVE, 0, tab->mouseCoordinates); InvalidateRgn(hwnd, nullptr, FALSE); UpdateWindow(hwnd); break; case TCM_SETITEM: index = (int)wParam; tcs = (LPTCITEM)lParam; if (TCIF_TEXT & tcs->mask) { if (tab->Set(index, tcs->pszText)) tab->Invalidate(index); } break; case TCM_DELETEITEM: index = (int)wParam; if (tab->Delete(index)) { if ((int)index < tab->current) tab->current--; else if ((int)index == tab->current) tab->current = -1; tab->xClicked = -1; if (tab->isMouseInClientArea) PostMessage(hwnd, WM_MOUSEMOVE, 0, tab->mouseCoordinates); if (tab->Count()) { InvalidateRgn(hwnd, nullptr, FALSE); UpdateWindow(hwnd); } } break; case TCM_DELETEALLITEMS: tab->DeleteAll(); tab->current = tab->highlighted = tab->xClicked = tab->xHighlighted = -1; break; case TCM_SETITEMSIZE: if (tab->Reshape(LOWORD(lParam), HIWORD(lParam))) { tab->xClicked = -1; if (tab->isMouseInClientArea) PostMessage(hwnd, WM_MOUSEMOVE, 0, tab->mouseCoordinates); if (tab->Count()) { InvalidateRgn(hwnd, nullptr, FALSE); UpdateWindow(hwnd); } } break; case TCM_GETCURSEL: return tab->current; case TCM_SETCURSEL: { index = (int)wParam; if (index >= tab->Count()) return -1; int previous = tab->current; if ((int)index != tab->current) { tab->Invalidate(tab->current); tab->Invalidate(index); tab->current = index; UpdateWindow(hwnd); } return previous; } case WM_NCHITTEST: { if (!tab->inTitlebar || hwnd == GetCapture()) return HTCLIENT; POINT pt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; ScreenToClient(hwnd, &pt); if (-1 != tab->IndexFromPoint(pt.x, pt.y)) return HTCLIENT; } return HTTRANSPARENT; case WM_MOUSELEAVE: PostMessage(hwnd, WM_MOUSEMOVE, 0xFF, 0); return 0; case WM_MOUSEMOVE: { tab->mouseCoordinates = lParam; if (!tab->isMouseInClientArea) { // Track the mouse for leaving the client area. TRACKMOUSEEVENT tme; tme.cbSize = sizeof(TRACKMOUSEEVENT); tme.dwFlags = TME_LEAVE; tme.hwndTrack = hwnd; if (TrackMouseEvent(&tme)) tab->isMouseInClientArea = true; } if (wParam == 0xFF) // The mouse left the client area. tab->isMouseInClientArea = false; bool inX = false; int hl = wParam == 0xFF ? -1 : tab->IndexFromPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), &inX); if (tab->isDragging && hl == -1) { // preserve the highlighted tab if it's dragged outside the tabs' area hl = tab->highlighted; } if (tab->highlighted != hl) { if (tab->isDragging) { // send notification if the highlighted tab is dragged over another WindowInfo *win = FindWindowInfoByHwnd(hwnd); int tabNo = tab->highlighted; uitask::Post([=] { TabNotification(win, T_DRAG, tabNo, hl); }); } tab->Invalidate(hl); tab->Invalidate(tab->highlighted); tab->highlighted = hl; } int xHl = inX && !tab->isDragging ? hl : -1; if (tab->xHighlighted != xHl) { tab->Invalidate(xHl); tab->Invalidate(tab->xHighlighted); tab->xHighlighted = xHl; } if (!inX) tab->xClicked = -1; } return 0; case WM_LBUTTONDOWN: bool inX; tab->nextTab = tab->IndexFromPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), &inX); if (inX) { // send request to close the tab WindowInfo *win = FindWindowInfoByHwnd(hwnd); int next = tab->nextTab; uitask::Post([=] { TabNotification(win, T_CLOSING, next, -1); }); } else if (tab->nextTab != -1) { if (tab->nextTab != tab->current) { // send request to select tab WindowInfo *win = FindWindowInfoByHwnd(hwnd); uitask::Post([=] { TabNotification(win, TCN_SELCHANGING, -1, -1); }); } tab->isDragging = true; SetCapture(hwnd); } return 0; case WM_LBUTTONUP: if (tab->xClicked != -1) { // send notification that the tab is closed WindowInfo *win = FindWindowInfoByHwnd(hwnd); int clicked = tab->xClicked; uitask::Post([=] { TabNotification(win, T_CLOSE, clicked, -1); }); tab->Invalidate(clicked); tab->xClicked = -1; } if (tab->isDragging) { tab->isDragging = false; ReleaseCapture(); } return 0; case WM_MBUTTONDOWN: // middle-clicking unconditionally closes the tab { tab->nextTab = tab->IndexFromPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); // send request to close the tab WindowInfo *win = FindWindowInfoByHwnd(hwnd); int next = tab->nextTab; uitask::Post([=] { TabNotification(win, T_CLOSING, next, -1); }); } return 0; case WM_MBUTTONUP: if (tab->xClicked != -1) { // send notification that the tab is closed WindowInfo *win = FindWindowInfoByHwnd(hwnd); int clicked = tab->xClicked; uitask::Post([=] { TabNotification(win, T_CLOSE, clicked, -1); }); tab->Invalidate(clicked); tab->xClicked = -1; } return 0; case WM_ERASEBKGND: return TRUE; case WM_PAINT: { RECT rc; GetUpdateRect(hwnd, &rc, FALSE); // TODO: when is wParam != nullptr? hdc = wParam ? (HDC)wParam : BeginPaint(hwnd, &ps); DoubleBuffer buffer(hwnd, RectI::FromRECT(rc)); tab->EvaluateColors(); tab->Paint(buffer.GetDC(), rc); buffer.Flush(hdc); ValidateRect(hwnd, nullptr); if (!wParam) EndPaint(hwnd, &ps); return 0; } case WM_SIZE: { WindowInfo *win = FindWindowInfoByHwnd(hwnd); if (win) UpdateTabWidth(win); } break; } return CallWindowProc(DefWndProcTabBar, hwnd, msg, wParam, lParam); }
static LRESULT CALLBACK TabBarProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) { PAINTSTRUCT ps; HDC hdc; int index; LPTCITEM tcs; UNUSED(uIdSubclass); UNUSED(dwRefData); TabPainter* tab = (TabPainter*)GetWindowLongPtr(hwnd, GWLP_USERDATA); if (WM_NCDESTROY == msg) { RemoveWindowSubclass(GetParent(hwnd), TabBarParentProc, 0); RemoveWindowSubclass(hwnd, TabBarProc, 0); return DefSubclassProc(hwnd, msg, wp, lp); } switch (msg) { case WM_DESTROY: delete tab; SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)0); break; case TCM_INSERTITEM: index = (int)wp; tcs = (LPTCITEM)lp; CrashIf(!(TCIF_TEXT & tcs->mask)); tab->Insert(index, tcs->pszText); if (index <= tab->selectedTabIdx) tab->selectedTabIdx++; tab->xClicked = -1; InvalidateRgn(hwnd, nullptr, FALSE); UpdateWindow(hwnd); break; case TCM_SETITEM: index = (int)wp; tcs = (LPTCITEM)lp; if (TCIF_TEXT & tcs->mask) { if (tab->Set(index, tcs->pszText)) { tab->Invalidate(index); } } break; case TCM_DELETEITEM: index = (int)wp; if (tab->Delete(index)) { if (index < tab->selectedTabIdx) { tab->selectedTabIdx--; } else if (index == tab->selectedTabIdx) { tab->selectedTabIdx = -1; } tab->xClicked = -1; if (tab->Count()) { InvalidateRgn(hwnd, nullptr, FALSE); UpdateWindow(hwnd); } } break; case TCM_DELETEALLITEMS: tab->DeleteAll(); tab->selectedTabIdx = -1; tab->highlighted = -1; tab->xClicked = -1; tab->xHighlighted = -1; break; case TCM_SETITEMSIZE: if (tab->Reshape(LOWORD(lp), HIWORD(lp))) { tab->xClicked = -1; if (tab->Count()) { InvalidateRgn(hwnd, nullptr, FALSE); UpdateWindow(hwnd); } } break; case TCM_GETCURSEL: return tab->selectedTabIdx; case TCM_SETCURSEL: { index = (int)wp; if (index >= tab->Count()) { return -1; } int previous = tab->selectedTabIdx; if (index != tab->selectedTabIdx) { tab->Invalidate(tab->selectedTabIdx); tab->Invalidate(index); tab->selectedTabIdx = index; UpdateWindow(hwnd); } return previous; } case WM_NCHITTEST: { if (!tab->inTitlebar || hwnd == GetCapture()) { return HTCLIENT; } POINT pt = {GET_X_LPARAM(lp), GET_Y_LPARAM(lp)}; ScreenToClient(hwnd, &pt); if (-1 != tab->IndexFromPoint(pt.x, pt.y)) { return HTCLIENT; } } return HTTRANSPARENT; case WM_MOUSELEAVE: PostMessage(hwnd, WM_MOUSEMOVE, 0xFF, 0); return 0; case WM_MOUSEMOVE: { tab->mouseCoordinates = lp; if (0xff != wp) { TrackMouseLeave(hwnd); } bool inX = false; int hl = wp == 0xFF ? -1 : tab->IndexFromPoint(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), &inX); if (tab->isDragging && hl == -1) { // preserve the highlighted tab if it's dragged outside the tabs' area hl = tab->highlighted; } if (tab->highlighted != hl) { if (tab->isDragging) { // send notification if the highlighted tab is dragged over another WindowInfo* win = FindWindowInfoByHwnd(hwnd); int tabNo = tab->highlighted; uitask::Post([=] { TabNotification(win, T_DRAG, tabNo, hl); }); } tab->Invalidate(hl); tab->Invalidate(tab->highlighted); tab->highlighted = hl; } int xHl = inX && !tab->isDragging ? hl : -1; if (tab->xHighlighted != xHl) { tab->Invalidate(xHl); tab->Invalidate(tab->xHighlighted); tab->xHighlighted = xHl; } if (!inX) tab->xClicked = -1; } return 0; case WM_LBUTTONDOWN: bool inX; tab->nextTab = tab->IndexFromPoint(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), &inX); if (inX) { // send request to close the tab WindowInfo* win = FindWindowInfoByHwnd(hwnd); int next = tab->nextTab; uitask::Post([=] { TabNotification(win, T_CLOSING, next, -1); }); } else if (tab->nextTab != -1) { if (tab->nextTab != tab->selectedTabIdx) { // send request to select tab WindowInfo* win = FindWindowInfoByHwnd(hwnd); uitask::Post([=] { TabNotification(win, TCN_SELCHANGING, -1, -1); }); } tab->isDragging = true; SetCapture(hwnd); } return 0; case WM_LBUTTONUP: if (tab->xClicked != -1) { // send notification that the tab is closed WindowInfo* win = FindWindowInfoByHwnd(hwnd); int clicked = tab->xClicked; uitask::Post([=] { TabNotification(win, T_CLOSE, clicked, -1); }); tab->Invalidate(clicked); tab->xClicked = -1; } if (tab->isDragging) { tab->isDragging = false; ReleaseCapture(); } return 0; case WM_MBUTTONDOWN: // middle-clicking unconditionally closes the tab { tab->nextTab = tab->IndexFromPoint(GET_X_LPARAM(lp), GET_Y_LPARAM(lp)); // send request to close the tab WindowInfo* win = FindWindowInfoByHwnd(hwnd); int next = tab->nextTab; uitask::Post([=] { TabNotification(win, T_CLOSING, next, -1); }); } return 0; case WM_MBUTTONUP: if (tab->xClicked != -1) { // send notification that the tab is closed WindowInfo* win = FindWindowInfoByHwnd(hwnd); int clicked = tab->xClicked; uitask::Post([=] { TabNotification(win, T_CLOSE, clicked, -1); }); tab->Invalidate(clicked); tab->xClicked = -1; } return 0; case WM_ERASEBKGND: return TRUE; case WM_PAINT: { RECT rc; GetUpdateRect(hwnd, &rc, FALSE); // TODO: when is wp != nullptr? hdc = wp ? (HDC)wp : BeginPaint(hwnd, &ps); DoubleBuffer buffer(hwnd, RectI::FromRECT(rc)); tab->Paint(buffer.GetDC(), rc); buffer.Flush(hdc); ValidateRect(hwnd, nullptr); if (!wp) EndPaint(hwnd, &ps); return 0; } case WM_SIZE: { WindowInfo* win = FindWindowInfoByHwnd(hwnd); if (win) UpdateTabWidth(win); } break; } return DefSubclassProc(hwnd, msg, wp, lp); }
void SetCurrentTabBgCol(WindowInfo *win, COLORREF bgCol) { TabPainter *tab = (TabPainter *)GetWindowLongPtr(win->hwndTabBar, GWLP_USERDATA); tab->currBgCol = bgCol; tab->EvaluateColors(true); }