📄 tab.c
字号:
InflateRect(selectedRect, SELECTED_TAB_OFFSET, 0);
/* If it also a bit higher. */
if ((lStyle & TCS_BOTTOM) && (lStyle & TCS_VERTICAL))
{
selectedRect->left -= 2; /* the border is thicker on the right */
selectedRect->right += SELECTED_TAB_OFFSET;
}
else if (lStyle & TCS_VERTICAL)
{
selectedRect->left -= SELECTED_TAB_OFFSET;
selectedRect->right += 1;
}
else if (lStyle & TCS_BOTTOM)
{
selectedRect->bottom += SELECTED_TAB_OFFSET;
}
else /* not TCS_BOTTOM and not TCS_VERTICAL */
{
selectedRect->top -= SELECTED_TAB_OFFSET;
selectedRect->bottom -= 1;
}
}
/* Check for visibility */
if (lStyle & TCS_VERTICAL)
return (itemRect->top < clientRect.bottom) && (itemRect->bottom > clientRect.top);
else
return (itemRect->left < clientRect.right) && (itemRect->right > clientRect.left);
}
static inline BOOL
TAB_GetItemRect(TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
{
return TAB_InternalGetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam, (LPRECT)NULL);
}
/******************************************************************************
* TAB_KeyUp
*
* This method is called to handle keyboard input
*/
static LRESULT TAB_KeyUp(TAB_INFO* infoPtr, WPARAM keyCode)
{
int newItem = -1;
switch (keyCode)
{
case VK_LEFT:
newItem = infoPtr->uFocus - 1;
break;
case VK_RIGHT:
newItem = infoPtr->uFocus + 1;
break;
}
/*
* If we changed to a valid item, change the selection
*/
if (newItem >= 0 &&
newItem < infoPtr->uNumItem &&
infoPtr->uFocus != newItem)
{
if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING))
{
infoPtr->iSelected = newItem;
infoPtr->uFocus = newItem;
TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
TAB_EnsureSelectionVisible(infoPtr);
TAB_InvalidateTabArea(infoPtr);
}
}
return 0;
}
/******************************************************************************
* TAB_FocusChanging
*
* This method is called whenever the focus goes in or out of this control
* it is used to update the visual state of the control.
*/
static void TAB_FocusChanging(const TAB_INFO *infoPtr)
{
RECT selectedRect;
BOOL isVisible;
/*
* Get the rectangle for the item.
*/
isVisible = TAB_InternalGetItemRect(infoPtr,
infoPtr->uFocus,
NULL,
&selectedRect);
/*
* If the rectangle is not completely invisible, invalidate that
* portion of the window.
*/
if (isVisible)
{
TRACE("invalidate (%d,%d)-(%d,%d)\n",
selectedRect.left,selectedRect.top,
selectedRect.right,selectedRect.bottom);
InvalidateRect(infoPtr->hwnd, &selectedRect, TRUE);
}
}
static INT TAB_InternalHitTest (
TAB_INFO* infoPtr,
POINT pt,
UINT* flags)
{
RECT rect;
INT iCount;
for (iCount = 0; iCount < infoPtr->uNumItem; iCount++)
{
TAB_InternalGetItemRect(infoPtr, iCount, &rect, NULL);
if (PtInRect(&rect, pt))
{
*flags = TCHT_ONITEM;
return iCount;
}
}
*flags = TCHT_NOWHERE;
return -1;
}
static inline LRESULT
TAB_HitTest (TAB_INFO *infoPtr, LPTCHITTESTINFO lptest)
{
return TAB_InternalHitTest (infoPtr, lptest->pt, &lptest->flags);
}
/******************************************************************************
* TAB_NCHitTest
*
* Napster v2b5 has a tab control for its main navigation which has a client
* area that covers the whole area of the dialog pages.
* That's why it receives all msgs for that area and the underlying dialog ctrls
* are dead.
* So I decided that we should handle WM_NCHITTEST here and return
* HTTRANSPARENT if we don't hit the tab control buttons.
* FIXME: WM_NCHITTEST handling correct ? Fix it if you know that Windows
* doesn't do it that way. Maybe depends on tab control styles ?
*/
static inline LRESULT
TAB_NCHitTest (TAB_INFO *infoPtr, LPARAM lParam)
{
POINT pt;
UINT dummyflag;
pt.x = (short)LOWORD(lParam);
pt.y = (short)HIWORD(lParam);
ScreenToClient(infoPtr->hwnd, &pt);
if (TAB_InternalHitTest(infoPtr, pt, &dummyflag) == -1)
return HTTRANSPARENT;
else
return HTCLIENT;
}
static LRESULT
TAB_LButtonDown (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
{
POINT pt;
INT newItem;
UINT dummy;
if (infoPtr->hwndToolTip)
TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
WM_LBUTTONDOWN, wParam, lParam);
if (GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_FOCUSONBUTTONDOWN ) {
SetFocus (infoPtr->hwnd);
}
if (infoPtr->hwndToolTip)
TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
WM_LBUTTONDOWN, wParam, lParam);
pt.x = (short)LOWORD(lParam);
pt.y = (short)HIWORD(lParam);
newItem = TAB_InternalHitTest (infoPtr, pt, &dummy);
TRACE("On Tab, item %d\n", newItem);
if (newItem != -1 && infoPtr->iSelected != newItem)
{
if (!TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGING))
{
infoPtr->iSelected = newItem;
infoPtr->uFocus = newItem;
TAB_SendSimpleNotify(infoPtr, TCN_SELCHANGE);
TAB_EnsureSelectionVisible(infoPtr);
TAB_InvalidateTabArea(infoPtr);
}
}
return 0;
}
static inline LRESULT
TAB_LButtonUp (const TAB_INFO *infoPtr)
{
TAB_SendSimpleNotify(infoPtr, NM_CLICK);
return 0;
}
static inline LRESULT
TAB_RButtonDown (const TAB_INFO *infoPtr)
{
TAB_SendSimpleNotify(infoPtr, NM_RCLICK);
return 0;
}
/******************************************************************************
* TAB_DrawLoneItemInterior
*
* This calls TAB_DrawItemInterior. However, TAB_DrawItemInterior is normally
* called by TAB_DrawItem which is normally called by TAB_Refresh which sets
* up the device context and font. This routine does the same setup but
* only calls TAB_DrawItemInterior for the single specified item.
*/
static void
TAB_DrawLoneItemInterior(TAB_INFO* infoPtr, int iItem)
{
HDC hdc = GetDC(infoPtr->hwnd);
RECT r, rC;
/* Clip UpDown control to not draw over it */
if (infoPtr->needsScrolling)
{
GetWindowRect(infoPtr->hwnd, &rC);
GetWindowRect(infoPtr->hwndUpDown, &r);
ExcludeClipRect(hdc, r.left - rC.left, r.top - rC.top, r.right - rC.left, r.bottom - rC.top);
}
TAB_DrawItemInterior(infoPtr, hdc, iItem, NULL);
ReleaseDC(infoPtr->hwnd, hdc);
}
/* update a tab after hottracking - invalidate it or just redraw the interior,
* based on whether theming is used or not */
static inline void hottrack_refresh (TAB_INFO* infoPtr, int tabIndex)
{
if (tabIndex == -1) return;
if (GetWindowTheme (infoPtr->hwnd))
{
RECT rect;
TAB_InternalGetItemRect(infoPtr, tabIndex, &rect, NULL);
InvalidateRect (infoPtr->hwnd, &rect, FALSE);
}
else
TAB_DrawLoneItemInterior(infoPtr, tabIndex);
}
/******************************************************************************
* TAB_HotTrackTimerProc
*
* When a mouse-move event causes a tab to be highlighted (hot-tracking), a
* timer is setup so we can check if the mouse is moved out of our window.
* (We don't get an event when the mouse leaves, the mouse-move events just
* stop being delivered to our window and just start being delivered to
* another window.) This function is called when the timer triggers so
* we can check if the mouse has left our window. If so, we un-highlight
* the hot-tracked tab.
*/
static void CALLBACK
TAB_HotTrackTimerProc
(
HWND hwnd, /* handle of window for timer messages */
UINT uMsg, /* WM_TIMER message */
UINT_PTR idEvent, /* timer identifier */
DWORD dwTime /* current system time */
)
{
TAB_INFO* infoPtr = TAB_GetInfoPtr(hwnd);
if (infoPtr != NULL && infoPtr->iHotTracked >= 0)
{
POINT pt;
/*
** If we can't get the cursor position, or if the cursor is outside our
** window, we un-highlight the hot-tracked tab. Note that the cursor is
** "outside" even if it is within our bounding rect if another window
** overlaps. Note also that the case where the cursor stayed within our
** window but has moved off the hot-tracked tab will be handled by the
** WM_MOUSEMOVE event.
*/
if (!GetCursorPos(&pt) || WindowFromPoint(pt) != hwnd)
{
/* Redraw iHotTracked to look normal */
INT iRedraw = infoPtr->iHotTracked;
infoPtr->iHotTracked = -1;
hottrack_refresh (infoPtr, iRedraw);
/* Kill this timer */
KillTimer(hwnd, TAB_HOTTRACK_TIMER);
}
}
}
/******************************************************************************
* TAB_RecalcHotTrack
*
* If a tab control has the TCS_HOTTRACK style, then the tab under the mouse
* should be highlighted. This function determines which tab in a tab control,
* if any, is under the mouse and records that information. The caller may
* supply output parameters to receive the item number of the tab item which
* was highlighted but isn't any longer and of the tab item which is now
* highlighted but wasn't previously. The caller can use this information to
* selectively redraw those tab items.
*
* If the caller has a mouse position, it can supply it through the pos
* parameter. For example, TAB_MouseMove does this. Otherwise, the caller
* supplies NULL and this function determines the current mouse position
* itself.
*/
static void
TAB_RecalcHotTrack
(
TAB_INFO* infoPtr,
const LPARAM* pos,
int* out_redrawLeave,
int* out_redrawEnter
)
{
int item = -1;
if (out_redrawLeave != NULL)
*out_redrawLeave = -1;
if (out_redrawEnter != NULL)
*out_redrawEnter = -1;
if ((GetWindowLongW(infoPtr->hwnd, GWL_STYLE) & TCS_HOTTRACK)
|| GetWindowTheme (infoPtr->hwnd))
{
POINT pt;
UINT flags;
if (pos == NULL)
{
GetCursorPos(&pt);
ScreenToClient(infoPtr->hwnd, &pt);
}
else
{
pt.x = (short)LOWORD(*pos);
pt.y = (short)HIWORD(*pos);
}
item = TAB_InternalHitTest(infoPtr, pt, &flags);
}
if (item != infoPtr->iHotTracked)
{
if (infoPtr->iHotTracked >= 0)
{
/* Mark currently hot-tracked to be redrawn to look normal */
if (out_redrawLeave != NULL)
*out_redrawLeave = infoPtr->iHotTracked;
if (item < 0)
{
/* Kill timer which forces recheck of mouse pos */
KillTimer(infoPtr->hwnd, TAB_HOTTRACK_TIMER);
}
}
else
{
/* Start timer so we recheck mouse pos */
UINT timerID = SetTimer
(
infoPtr->hwnd,
TAB_HOTTRACK_TIMER,
TAB_HOTTRACK_TIMER_INTERVAL,
TAB_HotTrackTimerProc
);
if (timerID == 0)
return; /* Hot tracking not available */
}
infoPtr->iHotTracked = item;
if (item >= 0)
{
/* Mark new hot-tracked to be redrawn to look highlighted */
if (out_redrawEnter != NULL)
*out_redrawEnter = item;
}
}
}
/******************************************************************************
* TAB_MouseMove
*
* Handles the mouse-move event. Updates tooltips. Updates hot-tracking.
*/
static LRESULT
TAB_MouseMove (TAB_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
{
int redrawLeave;
int redrawEnter;
if (infoPtr->hwndToolTip)
TAB_RelayEvent (infoPtr->hwndToolTip, infoPtr->hwnd,
WM_LBUTTONDOWN, wParam, lParam);
/* Determine which tab to highlight. Redraw tabs which change highlight
** status. */
TAB_RecalcHotTrack(infoPtr, &lParam, &redrawLeave, &redrawEnter);
hottrack_refresh (infoPtr, redrawLeave);
hottrack_refresh (infoPtr, redrawEnter);
return 0;
}
/******************************************************************************
* TAB_AdjustRect
*
* Calculates the tab control's display area given the window rectangle or
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -