📄 tabctrl.h
字号:
#pragma once
////////////////////////////////////////////////////////////////////////////
// MTL Version 0.10
// Copyright (C) 2001 MB<mb2@geocities.co.jp>
// All rights unreserved.
//
// This file is a part of Mb Template Library.
// The code and information is *NOT* provided "as-is" without
// warranty of any kind, either expressed or implied.
//
// TabCtrl.h: Last updated: February 12, 2001
/////////////////////////////////////////////////////////////////////////////
#include <vector>
namespace MTL
{
// for debug
#ifdef _DEBUG
const bool _Mtl_TabCtrl2_traceOn = false;
#define TCTRACE if (_Mtl_TabCtrl2_traceOn) ATLTRACE
#else
#define TCTRACE
#endif
#define TOOLTIP_RELAY_EVENT(tip) \
if(uMsg == WM_LBUTTONDOWN || uMsg == WM_MOUSEMOVE || uMsg == WM_LBUTTONUP || uMsg == WM_RBUTTONDOWN || \
uMsg == WM_MBUTTONDOWN || uMsg == WM_RBUTTONUP || uMsg == WM_MBUTTONUP) \
{ \
bHandled = FALSE; \
MSG msg = { m_hWnd, uMsg, wParam, lParam }; \
if (tip.IsWindow()) \
tip.RelayEvent(&msg); \
}
/////////////////////////////////////////////////////////////////////////////
// Tab Bars
// Window Styles:
#define TAB2WS_TOP CCS_TOP
#define TAB2WS_BOTTOM CCS_BOTTOM
#define TAB2WS_NORESIZE CCS_NORESIZE
#define TAB2WS_NOPARENTALIGN CCS_NOPARENTALIGN
#define TAB2WS_NODIVIDER CCS_NODIVIDER
// Extended styles
#define TAB2_EX_TRANSPARENT 0x00000001L // not supported yet
#define TAB2_EX_SHAREIMGLIST 0x00000002L
#define TAB2_EX_MULTILINE 0x00000004L
#define TAB2_EX_FIXEDSIZE 0x00000008L
#define TAB2_EX_SUPPORTREBAR 0x00000010L
#define TAB2_EX_SCROLLOPPOSITE 0x00000020L
#define TAB2_EX_ANCHORCOLOR 0x00000040L
// standard command bar styles
#define MTL_SIMPLE_TAB2_PANE_STYLE \
(WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | TAB2WS_NODIVIDER | TAB2WS_NORESIZE | TAB2WS_NOPARENTALIGN)
/////////////////////////////////////////////////////////////////////////////
// Forward declarations
template <class T, class TBase = CTabCtrl2Base, class TWinTraits = CControlWinTraits> class CCommandBarCtrlImpl;
class CTabCtrl2;
/////////////////////////////////////////////////////////////////////////////
// CTabButton
// CTabButton state flags
#define TCISTATE_HIDDEN 0x01
// standard text state
#define TCISTATE_ENABLED 0x02
#define TCISTATE_INACTIVE 0x04
// select or not
#define TCISTATE_SELECTED 0x08 // ordianry selected
#define TCISTATE_MSELECTED 0x10 // multi-selected
// event state
#define TCISTATE_PRESSED 0x20 // mouse pressed
#define TCISTATE_HOT 0x40 // mouse above item
#define TCISTATE_MOUSEAWAYCAPTURED 0x80// mouse away but captured
class CTabCtrlItem
{
public:
// Data members
BYTE m_fsState;
CString m_strItem; // string on button
CRect m_rcItem;
int m_nImgIndex; // image list index
DWORD m_dwUser; // user's data
// Constants
enum { s_kcxIconGap = 2 };
// Constructor/destructor
CTabCtrlItem(const CString& strBtn = CString(), int nImgIndex = -1,
DWORD dwUser = 0, BYTE fsState = TCISTATE_ENABLED)
: m_fsState(fsState), m_strItem(strBtn), m_nImgIndex(nImgIndex), m_dwUser(dwUser)
{
m_rcItem.SetRectEmpty();
}
// Attributes
bool ModifyState(BYTE fsRemove, BYTE fsAdd)
{
BYTE fsStateOld = m_fsState;
m_fsState = (m_fsState & ~fsRemove) | fsAdd;
if ((fsStateOld & TCISTATE_SELECTED || fsStateOld & TCISTATE_MSELECTED) &&
(fsRemove == TCISTATE_HOT || fsAdd == TCISTATE_HOT))// selected, no need to update
return false;
else
return m_fsState != fsStateOld;
}
// Methods
void Update(CDCHandle dc, HIMAGELIST hImgList, bool bAnchorColor)
{
// TCTRACE(_T("CTabCtrlItem::Update\n"));
if (m_fsState & TCISTATE_HIDDEN)
return;
int cxIcon = 0, cyIcon = 0;
int cxIconOffset = 0;
if (m_nImgIndex != -1) {
::ImageList_GetIconSize(hImgList, &cxIcon, &cyIcon);
cxIconOffset += cxIcon + s_kcxIconGap*2;
}
bool bHot = (m_fsState & TCISTATE_HOT) != 0;
bool bPressed = (m_fsState & TCISTATE_PRESSED) != 0;
CPoint ptOffset(0, 0);
if (m_fsState & TCISTATE_SELECTED) { // now selected
if (bHot && bPressed) {
COLORREF crTxt = dc.SetTextColor(::GetSysColor(COLOR_BTNFACE));
COLORREF crBk = dc.SetBkColor(::GetSysColor(COLOR_BTNHILIGHT));
CBrush hbr(CDCHandle::GetHalftoneBrush());
dc.SetBrushOrg(m_rcItem.left, m_rcItem.top);
dc.FillRect(m_rcItem, hbr);
dc.SetTextColor(crTxt);
dc.SetBkColor(crBk);
dc.DrawEdge(m_rcItem, BDR_RAISEDINNER, BF_RECT);
}
else {// ignored hot
TCTRACE(_T(" draw selected(checked) button\n"));
COLORREF crTxt = dc.SetTextColor(::GetSysColor(COLOR_BTNFACE));
COLORREF crBk = dc.SetBkColor(::GetSysColor(COLOR_BTNHILIGHT));
CBrush hbr(CDCHandle::GetHalftoneBrush());
dc.SetBrushOrg(m_rcItem.left, m_rcItem.top);
dc.FillRect(m_rcItem, hbr);
dc.SetTextColor(crTxt);
dc.SetBkColor(crBk);
dc.DrawEdge(m_rcItem, EDGE_SUNKEN, BF_RECT);
}
ptOffset += CPoint(2, 2);
}
else if (m_fsState & TCISTATE_MSELECTED) { // multi-selected
if (bHot && bPressed) {
dc.DrawEdge(m_rcItem, BDR_RAISEDINNER, BF_RECT);
}
else {
dc.DrawEdge(m_rcItem, EDGE_SUNKEN, BF_RECT);
}
ptOffset += CPoint(2, 2);
}
else { // not selected
if (bHot && bPressed) {
dc.DrawEdge(m_rcItem, BDR_RAISEDINNER, BF_RECT);// hot
ptOffset += CPoint(2, 2); // sunk a little
}
else if (bHot && !bPressed) {
dc.DrawEdge(m_rcItem, BDR_RAISEDINNER, BF_RECT);// hot
}
}
_DrawText(dc, ptOffset + CPoint(cxIconOffset, 0), bAnchorColor);
if (m_nImgIndex != -1) {
::ImageList_Draw(hImgList, m_nImgIndex, dc, m_rcItem.left + ptOffset.x + s_kcxIconGap,
m_rcItem.top + (m_rcItem.Height() - cyIcon) / 2 + ptOffset.y / 2, ILD_TRANSPARENT);
}
}
// Implementation
void _DrawText(CDCHandle dc, CPoint ptOffset, bool bAnchorColor)
{
COLORREF clr;
if (!(m_fsState & TCISTATE_ENABLED))
clr = ::GetSysColor(COLOR_3DSHADOW);
else if (m_fsState & TCISTATE_INACTIVE)
clr = bAnchorColor ? RGB(0, 0, 255) : ::GetSysColor(COLOR_BTNTEXT);
else
clr = bAnchorColor ? RGB(128, 0, 128) : ::GetSysColor(COLOR_BTNTEXT);
COLORREF clrOld = dc.SetTextColor(clr);
CRect rcBtn(m_rcItem.left + ptOffset.x, m_rcItem.top + ptOffset.y, m_rcItem.right, m_rcItem.bottom);
rcBtn.DeflateRect(2, 0);
UINT uFortmat;
int nWidth = MtlComputeWidthOfText(m_strItem, dc.GetCurrentFont());
if (nWidth > rcBtn.Width())
uFortmat = DT_LEFT | DT_END_ELLIPSIS;
else
uFortmat = DT_CENTER | DT_NOCLIP;
if (!(m_fsState & TCISTATE_ENABLED)) {
// disabled - draw shadow text shifted down and right 1 pixel (unles selected)
CRect rcDisabled = rcBtn + CPoint(1, 1);
COLORREF clrOld2 = dc.SetTextColor(::GetSysColor(COLOR_3DHILIGHT));
dc.DrawText(m_strItem, -1, rcDisabled, DT_SINGLELINE | uFortmat | DT_VCENTER | DT_NOPREFIX);
dc.SetTextColor(clrOld2);
}
dc.DrawText(m_strItem, -1, rcBtn, DT_SINGLELINE | uFortmat | DT_VCENTER | DT_NOPREFIX);
dc.SetTextColor(clrOld);
}
void DrawHot(CDCHandle dc)
{
dc.DrawEdge(m_rcItem, BDR_RAISEDINNER, BF_RECT);
}
void DrawPressed(CDCHandle dc)
{
dc.DrawEdge(m_rcItem, EDGE_SUNKEN, BF_MIDDLE);
}
void DrawSelected(CDCHandle dc)
{
dc.DrawEdge(m_rcItem, EDGE_SUNKEN, BF_RECT);
}
};
/////////////////////////////////////////////////////////////////////////////
// CTabCtrl2Base - base class for the Tab Ctrl2 implementation
class CTabCtrl2Base : public CWindow
{
public:
CTabCtrl2Base()
{
}
};
/////////////////////////////////////////////////////////////////////////////
// CTabCtrl2 - MTL implementation of Tab Ctrl2
template <class T, class TBase = CTabCtrl2Base, class TWinTraits = CControlWinTraits>
class ATL_NO_VTABLE CTabCtrl2Impl :
public CWindowImpl< T, TBase, TWinTraits >,
public CTrackMouseLeave<CTabCtrl2Impl>
{
public:
DECLARE_WND_CLASS_EX(NULL, CS_DBLCLKS, -1)
typedef CTabCtrl2Impl< T, TBase, TWinTraits > thisClass;
// Constants
enum _TabCtrl2DrawConstants
{
s_kcxTextMargin = 7,
s_kcyTextMargin = 3,
s_kcxGap = 2,
s_kcyGap = 2,
s_kcxSeparator = 2,
s_kcxUpDown = 28,
s_kcyUpDown = 14
};
enum
{
_nMaxMenuItemTextLength = 100,
};
enum _CurrentState
{
none = 0,
pressed,
captured,
hot_by_mouse
};
// Data members
std::vector<CTabCtrlItem> m_items;
CImageList m_imgs;
_CurrentState m_nCurrentState;
CFont m_font;
DWORD m_dwTabCtrl2ExtendedStyle; // Tab Ctrl2 specific extended styles
int m_nHotIndex; // Current hot index
int m_nPressedIndex; // Current capturing index
CSimpleArray<CPoint> m_arrSeparators; // Point of separator
int m_nFirstIndexOnSingleLine; // for single line
CUpDownCtrl m_wndUpDown; //
CSize m_sizeItem; // for fixed size
bool m_bLockRefreshBandInfo;
CToolTipCtrl m_tip;
// Constructor/destructor
CTabCtrl2Impl() :
m_dwTabCtrl2ExtendedStyle(TAB2_EX_TRANSPARENT|TAB2_EX_SUPPORTREBAR|TAB2_EX_SCROLLOPPOSITE),
m_nCurrentState(none),
m_nFirstIndexOnSingleLine(0),
m_sizeItem(200, 50),
m_nHotIndex(-1),
m_nPressedIndex(-1),
m_bLockRefreshBandInfo(false)
{
}
~CTabCtrl2Impl()
{
if (m_imgs.m_hImageList != NULL && (m_dwTabCtrl2ExtendedStyle & TAB2_EX_SHAREIMGLIST) == 0)
m_imgs.Destroy();
}
// Overridables
void OnSetCurSel(int nIndex)
{
}
CString OnGetToolTipText(int nIndex)
{
CString strItem;
GetItemText(nIndex, strItem);
return strItem;
}
// Attributes
DWORD GetTabCtrl2ExtendedStyle() const
{
return m_dwTabCtrl2ExtendedStyle;
}
void SetItemSize(const CSize& size)
{
CSize sizePrev = m_sizeItem;
m_sizeItem = size;
if (sizePrev != m_sizeItem &&
m_dwTabCtrl2ExtendedStyle & TAB2_EX_FIXEDSIZE)
{
_UpdateLayout();
}
}
CSize GetItemSize()
{
return m_sizeItem;
}
void ModifyTabCtrl2ExtendedStyle(DWORD dwRemove, DWORD dwAdd)
{
DWORD dwOldStyle = m_dwTabCtrl2ExtendedStyle;
m_dwTabCtrl2ExtendedStyle = (m_dwTabCtrl2ExtendedStyle & ~dwRemove) | dwAdd;
if (dwOldStyle != m_dwTabCtrl2ExtendedStyle) {
m_nFirstIndexOnSingleLine = 0;
_UpdateLayout();
}
if ( (dwOldStyle & TAB2_EX_ANCHORCOLOR) != (m_dwTabCtrl2ExtendedStyle & TAB2_EX_ANCHORCOLOR) )
Invalidate();
}
int GetItemCount()
{
return m_items.size();
}
int GetCurSel()
{
for (int i = 0; i < m_items.size(); ++i) {
if (m_items[i].m_fsState & TCISTATE_SELECTED)
return i;
}
return -1;
}
int HitTest(CPoint point)
{
CRect rc;
GetClientRect(&rc);
if (!rc.PtInRect(point))
return -1;
int i = (m_dwTabCtrl2ExtendedStyle & TAB2_EX_MULTILINE) ? 0 : m_nFirstIndexOnSingleLine;
for (; i < m_items.size(); ++i) {
if (m_items[i].m_rcItem.PtInRect(point)/* &&
m_items[i].m_fsState & TCISTATE_ENABLED*/) {
return i;
}
}
return -1;
}
bool InsertItem(int nIndex, const CTabCtrlItem& item)
{// if nDestIndex is invalid, added to tail
_HotItem();
if (m_items.size() == 0) {
m_items.push_back(item);
}
else {
if (nIndex < 0 || nIndex > m_items.size()) {
nIndex = m_items.size();
}
m_items.insert(m_items.begin() + nIndex, item);
}
_UpdateLayout();
return true;
}
void DeleteItems(CSimpleArray<int>& arrSrcs)
{
for (int i = arrSrcs.GetSize() - 1; i >= 0; --i) {
int nIndex = arrSrcs[i];
if (!_IsValidIndex(nIndex))
continue;
DeleteItem(nIndex);
}
}
bool MoveItems(int nDestIndex, CSimpleArray<int>& arrSrcs)
{// arrSrcs has to be sorted! if nDestIndex is invalid, added to tail
TCTRACE(_T("CTabCtrlImpl::MoveItems\n"));
if (arrSrcs.GetSize() <= 0)
return false;
int i = 0;
if (arrSrcs.GetSize() == 1) {
if (nDestIndex == arrSrcs[0] || nDestIndex == arrSrcs[0] + 1)
return true;// no need
if (!_IsValidIndex(nDestIndex) && arrSrcs[0] == m_items.size() - 1)
return true;// no need
}
CLockRedraw lock(m_hWnd);
m_bLockRefreshBandInfo = true;
std::vector<CTabCtrlItem> temp;
TCTRACE(_T(" save src indexs - "));
for (i = 0; i < arrSrcs.GetSize(); ++i) {
int nIndex = arrSrcs[i];
if (!_IsValidIndex(nIndex))
continue;
TCTRACE(_T(" %d"), nIndex);
temp.push_back(m_items[nIndex]);
}
TCTRACE(_T("\n"));
TCTRACE(_T(" delete item indexs - "));
for (i = arrSrcs.GetSize() - 1; i >= 0; --i) {
int nIndex = arrSrcs[i];
if (!_IsValidIndex(nIndex))
continue;
if (nDestIndex > nIndex)
--nDestIndex;
TCTRACE(_T(" %d"), nIndex);
DeleteItem(nIndex);
}
TCTRACE(_T("\n"));
// add all
if (!_IsValidIndex(nDestIndex)) {
nDestIndex = GetItemCount();
}
for (i = 0; i < temp.size(); ++i) {
InsertItem(nDestIndex, temp[i]);
++nDestIndex;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -