扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
作者:刘涛 来源:天极开发 2007年10月16日
关键字:
////////////////////////////////////////////////////// #ifndef FTAB_H #define FTAB_H // style flags #define FTS_FULLBORDER 0x1 // draw full border class CFolderTab { private: CString m_sText; // tab text CRect m_rect; // bounding rect CRgn m_rgn; // polygon region to fill (trapezoid) int ComputeRgn(CDC& dc, int x); int Draw(CDC& dc, CFont& font, BOOL bSelected); BOOL HitTest(CPoint pt) { return m_rgn.PtInRegion(pt); } CRect GetRect() const { return m_rect; } void GetTrapezoid(const CRect& rc, CPoint* pts) const; friend class CFolderTabCtrl; public: CFolderTab(LPCTSTR lpszText) : m_sText(lpszText) { } LPCTSTR GetText() const { return m_sText; } void SetText(LPCTSTR lpszText) { m_sText = lpszText; } }; enum { FTN_TABCHANGED = 1 }; // notification: tab changed struct NMFOLDERTAB : public NMHDR { // notification struct int iItem; // item index const CFolderTab* pItem; // ptr to data, if any }; //////////////////// Folder tab control, similar to tab control class CFolderTabCtrl : public CWnd { protected: CPtrList m_lsTabs; // array of CFolderTabs DWORD m_dwFtabStyle; // folder tab style flags int m_iCurItem; // current selected tab CFont m_fontNormal; // current font, normal ntab CFont m_fontSelected; // current font, selected tab int m_nDesiredWidth; // exact fit width // helpers void InvalidateTab(int iTab, BOOL bErase=TRUE); void DrawTabs(CDC& dc, const CRect& rc); CFolderTab* GetTab(int iPos); public: CFolderTabCtrl(); virtual ~CFolderTabCtrl(); BOOL CreateFromStatic(UINT nID, CWnd* pParent); virtual BOOL Create(DWORD dwWndStyle, const RECT& rc, CWnd* pParent, UINT nID, DWORD dwFtabStyle=0); virtual BOOL Load(UINT nIDRes); CFolderTab* GetItem(int iPos) { return (CFolderTab*)GetTab(iPos); } int GetSelectedItem() { return m_iCurItem; } int GetItemCount() { return m_lsTabs.GetCount(); } int GetDesiredWidth() { return m_nDesiredWidth; } int GetDesiredHeight() { return GetSystemMetrics(SM_CYHSCROLL); } BOOL AddItem(LPCTSTR lpszText); BOOL RemoveItem(int iPos); void RecomputeLayout(); int HitTest(CPoint pt); int SelectItem(int iTab); void SetFonts(CFont& fontNormal, CFont& fontSelected); protected: afx_msg void OnPaint(); afx_msg void OnLButtonDown(UINT nFlags, CPoint point); DECLARE_DYNAMIC(CFolderTabCtrl); DECLARE_MESSAGE_MAP() }; #endif // FTAB_H //////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "ftab.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif const CXOFFSET = 8; // defined pitch of trapezoid slant const CXMARGIN = 2; // left/right text margin const CYMARGIN = 1; // top/bottom text margin const CYBORDER = 1; // top border thickness // Compute the the points, rect and region for a tab,Input x is starting x pos. int CFolderTab::ComputeRgn(CDC& dc, int x) { m_rgn.DeleteObject(); CRect& rc = m_rect; rc.SetRectEmpty(); dc.DrawText(m_sText, &rc, DT_CALCRECT); // calculate desired text rectangle rc.right += 2*CXOFFSET + 2*CXMARGIN; // add margins rc.bottom = rc.top + GetSystemMetrics(SM_CYHSCROLL); // ht = scrollbar ht rc += CPoint(x,0); // shift right CPoint pts[4]; // create trapezoid region GetTrapezoid(rc, pts); m_rgn.CreatePolygonRgn(pts, 4, WINDING); return rc.Width(); } //////////////////// Given the boundint rect, compute trapezoid region. void CFolderTab::GetTrapezoid(const CRect& rc, CPoint* pts) const { pts[0] = rc.TopLeft(); pts[1] = CPoint(rc.left + CXOFFSET, rc.bottom); pts[2] = CPoint(rc.right- CXOFFSET-1, rc.bottom); pts[3] = CPoint(rc.right-1, rc.top); } //////////////////// Draw tab in normal or highlighted state int CFolderTab::Draw(CDC& dc, CFont& font, BOOL bSelected) { COLORREF bgColor = GetSysColor(bSelected ? COLOR_WINDOW: COLOR_3DFACE); COLORREF fgColor = GetSysColor(bSelected ? COLOR_WINDOWTEXT: COLOR_BTNTEXT); CBrush brush(bgColor); // background brush dc.SetBkColor(bgColor); // text background dc.SetTextColor(fgColor); // text color = fg color CPen blackPen(PS_SOLID, 1, RGB(0,0,0)); // black CPen shadowPen(PS_SOLID, 1, GetSysColor(COLOR_3DSHADOW)); CPoint pts[4]; // Fill trapezoid CRect rc = m_rect; GetTrapezoid(rc, pts); CPen* pOldPen = dc.SelectObject(&blackPen); dc.FillRgn(&m_rgn, &brush); // Draw edges. This is requires two corrections: pts[1].y--; // correction #1: true bottom edge y-coord pts[2].y--; // ...ditto pts[3].y--; // correction #2: extend final LineTo dc.MoveTo(pts[0]); // upper left dc.LineTo(pts[1]); // bottom left dc.SelectObject(&shadowPen); // bottom line is shadow color dc.MoveTo(pts[1]); // line is inside trapezoid bottom dc.LineTo(pts[2]); // ... dc.SelectObject(&blackPen); // upstroke is black dc.LineTo(pts[3]); // y-1 to include endpoint if (!bSelected) {// if not highlighted, upstroke has a 3D shadow, one pixel inside pts[2].x--; // offset left one pixel pts[3].x--; // ...ditto dc.SelectObject(&shadowPen); dc.MoveTo(pts[2]); dc.LineTo(pts[3]); } dc.SelectObject(pOldPen); rc.DeflateRect(CXOFFSET + CXMARGIN, CYMARGIN); // draw text CFont* pOldFont = dc.SelectObject(&font); dc.DrawText(m_sText, &rc, DT_CENTER|DT_VCENTER|DT_SINGLELINE); dc.SelectObject(pOldFont); return m_rect.right; } ////////////////////////////////////////////////////// CFolderTabCtrl IMPLEMENT_DYNAMIC(CFolderTabCtrl, CWnd) BEGIN_MESSAGE_MAP(CFolderTabCtrl, CWnd) ON_WM_PAINT() ON_WM_LBUTTONDOWN() END_MESSAGE_MAP() CFolderTabCtrl::CFolderTabCtrl() { m_iCurItem = 0; m_dwFtabStyle = 0; m_nDesiredWidth = 0; } CFolderTabCtrl::~CFolderTabCtrl() { while (!m_lsTabs.IsEmpty()) delete (CFolderTab*)m_lsTabs.RemoveHead(); } //////////////////// Create folder tab control from static control. // Destroys the static control. This is convenient for dialogs BOOL CFolderTabCtrl::CreateFromStatic(UINT nID, CWnd* pParent) { CStatic wndStatic; if (!wndStatic.SubclassDlgItem(nID, pParent)) return FALSE; CRect rc; wndStatic.GetWindowRect(&rc); pParent->ScreenToClient(&rc); wndStatic.DestroyWindow(); rc.bottom = rc.top + GetDesiredHeight(); return Create(WS_CHILD|WS_VISIBLE, rc, pParent, nID); } //////////////////// Create folder tab control. BOOL CFolderTabCtrl::Create(DWORD dwStyle, const RECT& rc, CWnd* pParent, UINT nID, DWORD dwFtabStyle) { ASSERT(pParent); ASSERT(dwStyle & WS_CHILD); m_dwFtabStyle = dwFtabStyle; static LPCTSTR lpClassName = _T("PDFolderTab"); static BOOL bRegistered = FALSE; // registered? if (!bRegistered) { WNDCLASS wc; memset(&wc, 0, sizeof(wc)); wc.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS; wc.lpfnWndProc = (WNDPROC)::DefWindowProc; // will get hooked by MFC wc.hInstance = AfxGetInstanceHandle(); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = CreateSolidBrush(GetSysColor(COLOR_3DFACE)); wc.lpszMenuName = NULL; wc.lpszClassName = lpClassName; if (!AfxRegisterClass(&wc)) { TRACE("*** CFolderTabCtrl::AfxRegisterClass failed!\n"); return FALSE; } bRegistered = TRUE; } if (!CWnd::CreateEx(0, lpClassName, NULL, dwStyle, rc, pParent, nID)) return FALSE; LOGFONT lf; // initialize fonts memset(&lf, 0, sizeof(lf)); lf.lfHeight = GetSystemMetrics(SM_CYHSCROLL)-CYMARGIN; lf.lfWeight = FW_NORMAL; lf.lfCharSet = DEFAULT_CHARSET; _tcscpy(lf.lfFaceName, _T("Arial")); m_fontNormal.CreateFontIndirect(&lf); lf.lfHeight -= 2; m_fontSelected.CreateFontIndirect(&lf); return TRUE; } //////////////////// copy a font static void CopyFont(CFont& dst, CFont& src) { dst.DeleteObject(); LOGFONT lf; VERIFY(src.GetLogFont(&lf)); dst.CreateFontIndirect(&lf); } //////////////////// Set normal, selected fonts void CFolderTabCtrl::SetFonts(CFont& fontNormal, CFont& fontSelected) { CopyFont(m_fontNormal, fontNormal); CopyFont(m_fontSelected, fontSelected); } //////////////////// Paint function void CFolderTabCtrl::OnPaint() { CPaintDC dc(this); // device context for painting CRect rc; GetClientRect(&rc); CFolderTab* pCurTab = NULL; int n = GetItemCount();// draw all the normal (non-selected) tabs for (int i=0; i<n; i++) { CFolderTab* pTab = GetTab(i); ASSERT(pTab); if (i==m_iCurItem) { pCurTab = pTab; } else if (pTab->Draw(dc, m_fontNormal, FALSE) > rc.right) break; } if (pCurTab) // draw selected tab last so it will be "on top" of the others pCurTab->Draw(dc, m_fontSelected, TRUE); CRect rcCurTab(0,0,0,0); // draw border: line along the top edge, excluding seleted tab if (pCurTab) rcCurTab = pCurTab->GetRect(); CPen blackPen(PS_SOLID, 1, RGB(0,0,0)); // black CPen* pOldPen = dc.SelectObject(&blackPen); dc.MoveTo(rcCurTab.right, rcCurTab.top); dc.LineTo(rc.right, rc.top); if (m_dwFtabStyle & FTS_FULLBORDER) { dc.MoveTo(rc.right-1, rc.top); dc.LineTo(rc.right-1, rc.bottom-1); dc.LineTo(rc.left, rc.bottom-1); dc.LineTo(rc.left, rc.top); } else { dc.MoveTo(rc.left, rc.top); } dc.LineTo(rcCurTab.TopLeft()); dc.SelectObject(pOldPen); } //////////////////// Handle mouse click: select new tab, if any. Notify parent, of course void CFolderTabCtrl::OnLButtonDown(UINT nFlags, CPoint pt) { int iTab = HitTest(pt); if (iTab>=0 && iTab!=m_iCurItem) { SelectItem(iTab); NMFOLDERTAB nm; nm.hwndFrom = m_hWnd; nm.idFrom = GetDlgCtrlID(); nm.code = FTN_TABCHANGED; nm.iItem = iTab; nm.pItem = GetTab(iTab); CWnd* pParent = GetParent(); pParent->SendMessage(WM_NOTIFY, nm.idFrom, (LPARAM)&nm); } } //////////////////// Find which tab is under mouse, -1 if none int CFolderTabCtrl::HitTest(CPoint pt) { CRect rc; GetClientRect(&rc); if (rc.PtInRect(pt)) { int n = GetItemCount(); for (int i=0; i<n; i++) { if (GetTab(i)->HitTest(pt)) return i; } } return -1; } //////////////////// Select ith tab. Returns index selected int CFolderTabCtrl::SelectItem(int iTab) { if (iTab<0 || iTab>=GetItemCount()) return -1; // bad if (iTab==m_iCurItem) return iTab; // already selected InvalidateTab(m_iCurItem); // invalidate old tab (repaint) m_iCurItem = iTab; // set new selected tab InvalidateTab(m_iCurItem); // repaint new tab return m_iCurItem; } ///////////////////// Invalidate a tab: invaldate its rect void CFolderTabCtrl::InvalidateTab(int iTab, BOOL bErase) { InvalidateRect(GetTab(iTab)->GetRect(), bErase); } BOOL CFolderTabCtrl::Load(UINT nIDRes) { CString s; if (!s.LoadString(nIDRes)) return FALSE; CString sTab; for (int i=0; AfxExtractSubString(sTab, s, i); i++) { AddItem(sTab); } RecomputeLayout(); return TRUE; } int CFolderTabCtrl::AddItem(LPCTSTR lpszText) { m_lsTabs.AddTail(new CFolderTab(lpszText)); return m_lsTabs.GetCount() - 1; } BOOL CFolderTabCtrl::RemoveItem(int iPos) { POSITION pos = m_lsTabs.FindIndex(iPos); if (pos) { CFolderTab* pTab = (CFolderTab*)m_lsTabs.GetAt(pos); m_lsTabs.RemoveAt(pos); delete pTab; } return pos!=NULL; } CFolderTab* CFolderTabCtrl::GetTab(int iPos) { POSITION pos = m_lsTabs.FindIndex(iPos); return pos ? static_cast<CFolderTab*>(m_lsTabs.GetAt(pos)) : NULL; } void CFolderTabCtrl::RecomputeLayout() { CClientDC dc(this); CFont* pOldFont = dc.SelectObject(&m_fontNormal); int x = 0; int n = GetItemCount(); CFolderTab* pTab; for (int i=0; i<n; i++) { pTab = GetTab(i); if (pTab) x += pTab->ComputeRgn(dc, x) - CXOFFSET; } dc.SelectObject(pOldFont); if (pTab) { CRect rc = pTab->GetRect(); m_nDesiredWidth = rc.right; } } //////////////////////////////////////////////////////////////// BOOL CFldrTabDlg::OnInitDialog() { CDialog::OnInitDialog(); m_wndStaticInfo.SubclassDlgItem(IDC_STATICINFO, this); // hook info control m_wndFolderTab.CreateFromStatic(IDC_FOLDERTAB, this); m_wndFolderTab.Load(IDR_FOLDERTABS); // Load strings SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon return TRUE; // return TRUE unless you set the focus to a control } void CFldrTabDlg::OnChangedTab(NMFOLDERTAB* nmtab, LRESULT* pRes) { CString s; s.Format(_T("选中 %d: %s"), nmtab->iItem, nmtab->pItem->GetText()); m_wndStaticInfo.SetWindowText(s); } |
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者