📄 cooltabctrls.h
字号:
LRESULT OnMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
MSG msg = { m_hWnd, uMsg, wParam, lParam };
if( m_Tip.IsWindow() ) m_Tip.RelayEvent(&msg);
bHandled = FALSE;
return 1;
}
LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
{
switch( wParam ) {
case VK_HOME:
if( m_iCurSel != 0 ) SetCurSel(0);
return 0;
case VK_END:
if( m_Items.GetSize() > 0 ) SetCurSel(m_Items.GetSize() - 1);
return 0;
case VK_LEFT:
if( m_iCurSel > 0 ) SetCurSel(m_iCurSel - 1);
return 0;
case VK_RIGHT:
if( m_iCurSel < m_Items.GetSize() - 1 ) SetCurSel(m_iCurSel + 1);
return 0;
}
bHandled = FALSE;
return 0;
}
// Overridables
void UpdateLayout()
{
int nCount = m_Items.GetSize();
if( nCount == 0 ) return;
CClientDC dc(m_hWnd);
HFONT hOldFont = dc.SelectStockFont(DEFAULT_GUI_FONT);
RECT rcClient;
GetClientRect(&rcClient);
SIZE szIcon = { 0 };
if( !m_ImageList.IsNull() ) m_ImageList.GetIconSize(szIcon);
HFONT hFont = GetFont();
HFONT hSelFont = GetSelFont();
DWORD dwStyle = GetStyle();
// Reposition buttons
int xpos = -m_iScrollPos + m_metrics.cxIndent;
for( int i = 0; i < nCount; i++ ) {
COOLTCITEM* pItem = m_Items[i];
// Hidden button?
if( (pItem->dwState & TCIS_HIDDEN) != 0 ) continue;
// Determine width...
int cx = 0;
// Expand width according to decorations
if( pItem->iImage >= 0 && (pItem->mask & TCIF_IMAGE) != 0 && !m_ImageList.IsNull() ) {
cx += szIcon.cx + (m_metrics.cxImagePadding * 2);
}
if( (pItem->mask & TCIF_TEXT) != 0 ) {
RECT rcText = { 0 };
dc.SelectFont(i == m_iCurSel ? hSelFont : hFont);
dc.DrawText(pItem->pszText, ::lstrlen(pItem->pszText), &rcText, DT_SINGLELINE|DT_CALCRECT);
cx += (rcText.right - rcText.left) + (m_metrics.cxPadding * 2);
}
// Add margins
cx += m_metrics.cxMargin * 2;
// Selected button is allowed to grow further
if( m_iCurSel == i ) cx += m_metrics.cxSelMargin * 2;
// Need separators?
if( (m_dwExtStyle & TCS_EX_FLATSEPARATORS) != 0 ) cx += 2;
// Fixed width?
if( (pItem->mask & TCIF_WIDTH) != 0 ) cx = pItem->cx;
// Minimum width?
if( cx < m_nMinWidth ) cx = m_nMinWidth;
// Finally...
RECT& rc = pItem->rcItem;
rc.top = 0;
rc.bottom = rcClient.bottom - rcClient.top;
rc.left = xpos;
rc.right = xpos + cx;
xpos += cx + m_metrics.cxButtonSpacing;
}
// Allow buttons to fill entire row
int cx = (rcClient.right - rcClient.left) - xpos;
if( (dwStyle & TCS_RAGGEDRIGHT) != 0 ) {
if( cx > 0 ) {
int iDiff = cx / m_Items.GetSize();
for( int i = 0; i < nCount; i++ ) {
m_Items[i]->rcItem.right += iDiff;
if( i > 0 ) m_Items[i]->rcItem.left += iDiff;
iDiff *= 2;
}
}
}
// Compress buttons on same line
if( cx < 0 && (m_dwExtStyle & TCS_EX_COMPRESSLINE) != 0 ) {
int xpos = m_metrics.cxIndent;
int iWidth = (rcClient.right - rcClient.left) / nCount;
for( int i = 0; i < nCount; i++ ) {
COOLTCITEM* pItem = m_Items[i];
int cx = min( iWidth, pItem->rcItem.right - pItem->rcItem.left );
pItem->rcItem.right = xpos + cx;
pItem->rcItem.left = xpos;
xpos += cx;
}
}
// Expand currently selected button to overlap other buttons.
// NOTE: To make sense, take the cxIndent/cxMargin/cxSelMargin into
// account when choosing a value.
if( m_iCurSel != - 1 ) ::InflateRect(&m_Items[m_iCurSel]->rcItem, m_metrics.cxOverlap, 0);
dc.SelectFont(hOldFont);
// Remove tooltips
if( m_Tip.IsWindow() ) {
TOOLINFO ti = { 0 };
ti.cbSize = sizeof(ti);
while( m_Tip.EnumTools(0, &ti) ) m_Tip.DelTool(&ti);
}
// Recreate tooltip rects
for( int j = 0; j < nCount; j++ ) {
if( (m_Items[j]->mask & TCIF_TOOLTIP) != 0 ) {
if( !m_Tip.IsWindow() ) m_Tip.Create(m_hWnd);
m_Tip.AddTool(m_hWnd, m_Items[j]->pszTipText, &m_Items[j]->rcItem, j + 1);
}
else if( (dwStyle & TCS_TOOLTIPS) != 0 ) {
if( !m_Tip.IsWindow() ) m_Tip.Create(m_hWnd);
m_Tip.AddTool(m_hWnd, m_Items[j]->pszText, &m_Items[j]->rcItem, j + 1);
}
}
// Reactivate tooltips
if( m_Tip.IsWindow() ) m_Tip.Activate(m_Tip.GetToolCount() > 0);
// Show scrollbuttons
if( (m_dwExtStyle & TCS_EX_SCROLLBUTTONS) == 0 )
{
m_ctrlLeft.ShowWindow(SW_HIDE);
m_ctrlRight.ShowWindow(SW_HIDE);
}
else
{
BOOL bLeftNeeded = _IsScrollBarNeeded(HTLEFT);
RECT rcLeft = { rcClient.right - 38, rcClient.top + 2, rcClient.right - 23, rcClient.bottom - 2 };
m_ctrlLeft.SetWindowPos(NULL, &rcLeft, SWP_NOZORDER | SWP_NOACTIVATE | (bLeftNeeded ? SWP_SHOWWINDOW : SWP_HIDEWINDOW));
BOOL bRightNeeded = _IsScrollBarNeeded(HTRIGHT);
RECT rcRight = { rcClient.right - 20, rcClient.top + 2, rcClient.right - 5, rcClient.bottom - 2 };
m_ctrlRight.SetWindowPos(NULL, &rcRight, SWP_NOZORDER | SWP_NOACTIVATE | (bRightNeeded ? SWP_SHOWWINDOW : SWP_HIDEWINDOW));
if( m_iScrollPos > 0 && !_IsScrollBarNeeded(HTHSCROLL) ) SetScrollPos(0);
}
}
void DoPaint(CDCHandle dc, RECT &rcClip)
{
// NOTE: The handling of NM_CUSTOMDRAW is probably not entirely correct
// in the code below. But at least it makes a brave attempt to
// implement all the states described in MSDN docs.
// Save current DC selections
int save = dc.SaveDC();
ATLASSERT(save!=0);
// Make sure we don't paint outside client area (possible with paint dc)
RECT rcClient;
GetClientRect(&rcClient);
::IntersectClipRect(dc, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
dc.FillRect(&rcClient, ::GetSysColorBrush(COLOR_3DFACE));
// Prepare DC
dc.SelectFont(GetFont());
T* pT = static_cast<T*>(this);
LRESULT lResStage;
NMCUSTOMDRAW nmc = { 0 };
nmc.hdr.hwndFrom = m_hWnd;
nmc.hdr.idFrom = m_idDlgCtrl;
nmc.hdr.code = NM_CUSTOMDRAW;
nmc.hdc = dc;
nmc.dwDrawStage = CDDS_PREPAINT;
lResStage = m_wndNotify.SendMessage(WM_NOTIFY, nmc.hdr.idFrom, (LPARAM) &nmc);
if( lResStage == CDRF_NOTIFYITEMDRAW || lResStage == CDRF_DODEFAULT ) {
RECT rc;
int nCount = m_Items.GetSize();
// Draw the list items, except the selected one. It is drawn last
// so it can cover the tabs below it.
RECT rcIntersect;
for( int i = 0; i < nCount; i++ ) {
rc = m_Items[i]->rcItem;
if( rc.bottom - rc.top == 0 ) pT->UpdateLayout();
if( i != m_iCurSel ) {
if( ::IntersectRect(&rcIntersect, &rc, &rcClip) ) {
nmc.dwItemSpec = i;
nmc.uItemState = 0;
if( (m_Items[i]->dwState & TCIS_DISABLED) != 0 ) nmc.uItemState |= CDIS_DISABLED;
nmc.rc = rc;
pT->ProcessItem(lResStage, nmc);
}
}
}
if( m_iCurSel != -1 ) {
rc = m_Items[m_iCurSel]->rcItem;
if( ::IntersectRect(&rcIntersect, &rc, &rcClip) ) {
nmc.dwItemSpec = m_iCurSel;
nmc.uItemState = CDIS_SELECTED;
nmc.rc = rc;
pT->ProcessItem(lResStage, nmc);
}
}
}
if( lResStage == CDRF_NOTIFYPOSTPAINT ) {
nmc.dwItemSpec = 0;
nmc.uItemState = 0;
nmc.dwDrawStage = CDDS_POSTPAINT;
m_wndNotify.SendMessage(WM_NOTIFY, nmc.hdr.idFrom, (LPARAM) &nmc);
}
dc.RestoreDC(save);
}
void ProcessItem(LRESULT lResStage, NMCUSTOMDRAW &nmc)
{
LRESULT lResItem = CDRF_DODEFAULT;
if( lResStage == CDRF_NOTIFYITEMDRAW ) {
nmc.dwDrawStage = CDDS_ITEMPREPAINT;
lResItem = m_wndNotify.SendMessage(WM_NOTIFY, nmc.hdr.idFrom, (LPARAM) &nmc);
}
if( lResItem != CDRF_SKIPDEFAULT ) {
// Do default item-drawing
T* pT = static_cast<T*>(this);
pT->DoItemPaint(nmc);
}
if( lResStage == CDRF_NOTIFYITEMDRAW && lResItem == CDRF_NOTIFYPOSTPAINT ) {
nmc.dwDrawStage = CDDS_ITEMPOSTPAINT;
m_wndNotify.SendMessage(WM_NOTIFY, nmc.hdr.idFrom, (LPARAM) &nmc);
}
}
void DoItemPaint(NMCUSTOMDRAW &/*nmc*/)
{
}
};
/////////////////////////////////////////////////////////////////////////////
//
// The sample tab controls
//
// The follwing samples derive directly from CCustomTabCtrl.
// This means that they can actually use the internal members
// of this class. But they will not! To keep the code clean, I'm only
// using public member methods to access all variables.
//
// You need to add the...
// REFLECT_NOTIFICATIONS()
// macro to the parent's message map.
//
class CButtonTabCtrl :
public CCustomTabCtrl<CButtonTabCtrl>,
public CCustomDraw<CButtonTabCtrl>
{
public:
DECLARE_WND_CLASS(_T("WTL_CoolButtonTabCtrl"))
BEGIN_MSG_MAP(CButtonTabCtrl)
REFLECTED_NOTIFY_CODE_HANDLER(TCN_INITIALIZE, OnInitialize)
CHAIN_MSG_MAP(CCustomTabCtrl<CButtonTabCtrl>)
CHAIN_MSG_MAP_ALT(CCustomDraw<CButtonTabCtrl>, 1)
DEFAULT_REFLECTION_HANDLER()
END_MSG_MAP()
LRESULT OnInitialize(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
{
TCMETRICS metrics;
GetMetrics(&metrics);
metrics.cxButtonSpacing = 3;
metrics.cxMargin = 10;
SetMetrics(&metrics);
return 0;
}
DWORD OnPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/)
{
return CDRF_NOTIFYITEMDRAW; // We need per-item notifications
}
DWORD OnItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW lpNMCustomDraw)
{
::SetBkMode(lpNMCustomDraw->hdc, TRANSPARENT);
::SetTextColor(lpNMCustomDraw->hdc, ::GetSysColor(COLOR_BTNTEXT));
UINT state = 0;
if( (lpNMCustomDraw->uItemState & CDIS_SELECTED) != 0 ) state |= DFCS_PUSHED;
if( (lpNMCustomDraw->uItemState & CDIS_DISABLED) != 0 ) state |= DFCS_INACTIVE;
::DrawFrameControl(lpNMCustomDraw->hdc, &lpNMCustomDraw->rc, DFC_BUTTON, DFCS_BUTTONPUSH | state );
TCITEM item = { 0 };
TCHAR szText[128] = { 0 };
item.mask = TCIF_TEXT;
item.pszText = szText;
item.cchTextMax = (sizeof(szText)/sizeof(TCHAR)) - 1;
GetItem(lpNMCustomDraw->dwItemSpec, &item);
if( (lpNMCustomDraw->uItemState & CDIS_SELECTED) != 0 ) {
lpNMCustomDraw->rc.left += 2;
lpNMCustomDraw->rc.top += 2;
}
::DrawText(lpNMCustomDraw->hdc, item.pszText, ::lstrlen(item.pszText), &lpNMCustomDraw->rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
return CDRF_SKIPDEFAULT;
}
};
class CFolderTabCtrl :
public CCustomTabCtrl<CFolderTabCtrl>,
public CCustomDraw<CFolderTabCtrl>
{
public:
DECLARE_WND_CLASS(_T("WTL_CoolFolderTabCtrl"))
CFont m_font;
enum { CXOFFSET = 8 }; // defined pitch of trapezoid slant
enum { CXMARGIN = 2 }; // left/right text margin
enum { CYMARGIN = 1 }; // top/bottom text margin
enum { CYBORDER = 1 }; // top border thickness
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -