#include "stdafx.h"
|
#include "ApredTreeCtrl.h"
|
|
|
#define ROFFSET 7
|
#define WIDE 10
|
#define WIDE2 5
|
#define EXPANDED_WIDE 8
|
|
#define BADGE_HIDE 0
|
#define BADGE_DOT 1
|
#define BADGE_NUMBER 2
|
#define BADGE_DOT_WIDTH 12
|
#define BADGE_NUMBER_WIDTH 20
|
|
|
IMPLEMENT_DYNAMIC(CApredTreeCtrl, CTreeCtrl)
|
|
CApredTreeCtrl::CApredTreeCtrl()
|
{
|
m_hBrushItem[0] = NULL;
|
m_hBrushItem[1] = NULL;
|
m_hBrushItem[2] = NULL;
|
m_hPenItem[0] = nullptr;
|
m_hPenItem[1] = nullptr;
|
m_hPenItem[2] = nullptr;
|
m_hBrushBtn[0] = nullptr;
|
m_hBrushBtn[1] = nullptr;
|
m_hBrushBtn[2] = nullptr;
|
m_crItemBk[0] = RGB(255,255,255);
|
m_crItemBk[1] = RGB(228, 229, 234);
|
m_crItemBk[2] = RGB(236, 237, 241);
|
m_crText[0] = RGB(28, 28, 28);
|
m_crText[1] = RGB(28, 28, 28);
|
m_crText[2] = RGB(28, 28, 28);
|
m_hHoverItem = nullptr;
|
m_bTracking = FALSE;
|
}
|
|
CApredTreeCtrl::~CApredTreeCtrl()
|
{
|
if (m_hBrushItem[0] != NULL) {
|
::DeleteObject(m_hBrushItem[0]);
|
}
|
if (m_hBrushItem[1] != NULL) {
|
::DeleteObject(m_hBrushItem[1]);
|
}
|
if (m_hBrushItem[2] != NULL) {
|
::DeleteObject(m_hBrushItem[2]);
|
}
|
if (m_hBrushBtn[0] != NULL) {
|
::DeleteObject(m_hBrushBtn[0]);
|
}
|
if (m_hBrushBtn[1] != NULL) {
|
::DeleteObject(m_hBrushBtn[1]);
|
}
|
if (m_hBrushBtn[2] != NULL) {
|
::DeleteObject(m_hBrushBtn[2]);
|
}
|
if (m_hPenItem[0] != nullptr) {
|
::DeleteObject(m_hPenItem[0]);
|
}
|
if (m_hPenItem[1] != nullptr) {
|
::DeleteObject(m_hPenItem[1]);
|
}
|
if (m_hPenItem[2] != nullptr) {
|
::DeleteObject(m_hPenItem[2]);
|
}
|
}
|
|
BEGIN_MESSAGE_MAP(CApredTreeCtrl, CTreeCtrl)
|
ON_WM_PAINT()
|
ON_WM_MOUSEMOVE()
|
ON_WM_MOUSEHOVER()
|
ON_WM_MOUSELEAVE()
|
ON_WM_TIMER()
|
ON_WM_LBUTTONDOWN()
|
ON_WM_CREATE()
|
END_MESSAGE_MAP()
|
|
void CApredTreeCtrl::DrawItemButton(HTREEITEM hItem, HDC hDC, CRect* pRect)
|
{
|
// °´Å¥ÒªË¢Ò»ÏÂ
|
POINT pt[3];
|
if ((GetItemState(hItem, TVIS_EXPANDED) & TVIS_EXPANDED) == TVIS_EXPANDED) {
|
int nBottomOffset = (pRect->Height() - EXPANDED_WIDE) / 2;
|
pt[0].x = pRect->right - ROFFSET - EXPANDED_WIDE;
|
pt[0].y = pRect->bottom - nBottomOffset;
|
pt[1].x = pRect->right - ROFFSET;
|
pt[1].y = pRect->bottom - nBottomOffset;
|
pt[2].x = pRect->right - ROFFSET;
|
pt[2].y = pRect->bottom - nBottomOffset - EXPANDED_WIDE;
|
}
|
else {
|
int nBottomOffset = (pRect->Height() - WIDE) / 2;
|
pt[0].x = pRect->right - ROFFSET - WIDE2;
|
pt[0].y = pRect->bottom - nBottomOffset - WIDE;
|
pt[1].x = pRect->right - ROFFSET - WIDE2;
|
pt[1].y = pRect->bottom - nBottomOffset;
|
pt[2].x = pRect->right - ROFFSET;
|
pt[2].y = pRect->bottom - nBottomOffset - WIDE2;
|
}
|
|
::Polygon(hDC, pt, 3);
|
}
|
|
void CApredTreeCtrl::OnPaint()
|
{
|
HDC hDC, hMemDC;
|
HBITMAP hBitmap;
|
RECT rcClient;
|
CString strText;
|
HFONT hFont;
|
HBRUSH hBrushBK;
|
|
|
if (m_hBrushItem[0] == nullptr) {
|
m_hBrushItem[0] = CreateSolidBrush(m_crItemBk[0]);
|
}
|
if (m_hBrushItem[1] == nullptr) {
|
m_hBrushItem[1] = CreateSolidBrush(m_crItemBk[1]);
|
}
|
if (m_hBrushItem[2] == nullptr) {
|
m_hBrushItem[2] = CreateSolidBrush(m_crItemBk[2]);
|
}
|
if (m_hBrushBtn[0] == nullptr) {
|
m_hBrushBtn[0] = CreateSolidBrush(m_crText[0]);
|
}
|
if (m_hBrushBtn[1] == nullptr) {
|
m_hBrushBtn[1] = CreateSolidBrush(m_crText[1]);
|
}
|
if (m_hBrushBtn[2] == nullptr) {
|
m_hBrushBtn[2] = CreateSolidBrush(m_crText[2]);
|
}
|
if (m_hPenItem[0] == nullptr) {
|
m_hPenItem[0] = ::CreatePen(PS_SOLID, 1, m_crText[0]);
|
}
|
if (m_hPenItem[1] == nullptr) {
|
m_hPenItem[1] = ::CreatePen(PS_SOLID, 1, m_crText[1]);
|
}
|
if (m_hPenItem[2] == nullptr) {
|
m_hPenItem[2] = ::CreatePen(PS_SOLID, 1, m_crText[2]);
|
}
|
|
// BeginPaint
|
PAINTSTRUCT ps;
|
hDC = ::BeginPaint(m_hWnd, &ps);
|
GetClientRect(&rcClient);
|
|
hMemDC = ::CreateCompatibleDC(hDC);
|
hBitmap = ::CreateCompatibleBitmap(hDC, rcClient.right - rcClient.left,
|
rcClient.bottom - rcClient.top);
|
::SelectObject(hMemDC, hBitmap);
|
|
hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
|
::SelectObject(hMemDC, hFont);
|
::SetBkMode(hMemDC, TRANSPARENT);
|
|
|
// ±³¾°ÑÕÉ«
|
hBrushBK = CreateSolidBrush(GetBkColor());
|
::FillRect(hMemDC, &rcClient, hBrushBK);
|
DeleteObject(hBrushBK);
|
|
|
|
// »æÖÆ×ÓÏî
|
DrawItems(hMemDC);
|
|
|
// EndPaint
|
::BitBlt(hDC, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
|
hMemDC, 0, 0, SRCCOPY);
|
::EndPaint(m_hWnd, &ps);
|
::DeleteObject(hBitmap);
|
::DeleteDC(hMemDC);
|
}
|
|
void CApredTreeCtrl::DrawItems(HDC hDC)
|
{
|
HTREEITEM hCurrentItem;
|
int itemState;
|
CRect rcClient, rcItem, rcText;
|
GetClientRect(&rcClient);
|
Gdiplus::Graphics graphics(hDC);
|
graphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
|
|
|
// »æÖƿɼûµÄ×ÓÏî
|
hCurrentItem = GetFirstVisibleItem();//»ñÈ¡µÚÒ»¸ö¿Î¿É¼ûµÄÏî
|
do {
|
if (GetItemRect(hCurrentItem, &rcItem, FALSE) && GetItemRect(hCurrentItem, &rcText, TRUE)) {
|
rcText.right = rcItem.right - 8;
|
rcText.left += 8;
|
CRect fillRect(0, rcItem.top, rcClient.right, rcItem.bottom);
|
if (rcItem.top > rcClient.bottom) {
|
break;
|
}
|
|
|
// »æÖÆ×ÓÏî±³¾°
|
itemState = GetItemState(hCurrentItem, TVIF_STATE);
|
COLORREF crText = m_crText[0];
|
if (itemState & TVIS_SELECTED) {
|
crText = m_crText[1];
|
HRGN hRgn = CreateRoundRectRgn(rcItem.left, rcItem.top, rcItem.right, rcItem.bottom, 4, 4);
|
::FillRgn(hDC, hRgn, m_hBrushItem[1]);
|
::DeleteObject(hRgn);
|
::SelectObject(hDC, m_hBrushBtn[1]);
|
::SelectObject(hDC, m_hPenItem[1]);
|
}
|
else if (hCurrentItem == m_hHoverItem) {
|
crText = m_crText[2];
|
HRGN hRgn = CreateRoundRectRgn(rcItem.left, rcItem.top, rcItem.right, rcItem.bottom, 4, 4);
|
::FillRgn(hDC, hRgn, m_hBrushItem[2]);
|
::DeleteObject(hRgn);
|
::SelectObject(hDC, m_hBrushBtn[2]);
|
::SelectObject(hDC, m_hPenItem[2]);
|
}
|
else {
|
crText = m_crText[0];
|
::SelectObject(hDC, m_hBrushBtn[0]);
|
::SelectObject(hDC, m_hPenItem[0]);
|
}
|
|
|
//»æÖÆÕ¹¿ªÍ¼Æ¬
|
if (ItemHasChildren(hCurrentItem)) {
|
CRect rcBtn = rcText;
|
rcBtn.right = rcText.left - 2;
|
rcBtn.left -= rcBtn.right - 30;
|
DrawItemButton(hCurrentItem, hDC, &rcBtn);
|
}
|
|
// ͼ±ê
|
if (m_icons.find(hCurrentItem) != m_icons.end()) {
|
HICON hIcon = m_icons[hCurrentItem];
|
DrawIconEx(hDC, 8, (rcItem.bottom - rcItem.top - 32) / 2,
|
hIcon, 32, 32, 0, 0, DI_NORMAL);
|
rcText.left += 32;
|
rcText.left += 8;
|
}
|
|
|
//»æÖÆÎÄ×Ö
|
HFONT hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
|
::SelectObject(hDC, hFont);
|
LOGFONT lf;
|
::GetObject(hFont, sizeof(LOGFONT), &lf);
|
lf.lfWeight = FW_BOLD;
|
HFONT hFontBold = CreateFontIndirect(&lf);
|
|
::SetTextColor(hDC, crText);
|
CString strText = GetItemText(hCurrentItem);
|
if (FindBoldItem(hCurrentItem)) {
|
::SelectObject(hDC, hFontBold);
|
}
|
else {
|
::SelectObject(hDC, hFont);
|
}
|
DrawText(hDC, strText, strText.GetLength(), &rcText, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
|
::DeleteObject(hFontBold);
|
|
|
// ÊÇ·ñÓÐСԲµã
|
if (m_badges.find(hCurrentItem) != m_badges.end()) {
|
BADGE& badge = m_badges[hCurrentItem];
|
if (badge.type == BADGE_DOT) {
|
Gdiplus::SolidBrush brush(Gdiplus::Color(GetRValue(badge.badgeBackground), GetGValue(badge.badgeBackground), GetBValue(badge.badgeBackground)));
|
int x = rcItem.right - 18 - BADGE_DOT_WIDTH;
|
int y = rcItem.top + (rcItem.Height() - BADGE_DOT_WIDTH) / 2;
|
graphics.FillEllipse(&brush, x, y, BADGE_DOT_WIDTH, BADGE_DOT_WIDTH);
|
}
|
else if (badge.type == BADGE_NUMBER) {
|
Gdiplus::SolidBrush brush(Gdiplus::Color(GetRValue(badge.badgeBackground), GetGValue(badge.badgeBackground), GetBValue(badge.badgeBackground)));
|
int x = rcItem.right - 18 - BADGE_NUMBER_WIDTH;
|
int y = rcItem.top + (rcItem.Height() - BADGE_NUMBER_WIDTH) / 2;
|
graphics.FillEllipse(&brush, x, y, BADGE_NUMBER_WIDTH, BADGE_NUMBER_WIDTH);
|
RECT rcBadge;
|
rcBadge.left = x;
|
rcBadge.right = rcBadge.left + BADGE_NUMBER_WIDTH;
|
rcBadge.top = y;
|
rcBadge.bottom = rcBadge.top + BADGE_NUMBER_WIDTH;
|
::SetTextColor(hDC, badge.badgeForeground);
|
char szBuffer[32];
|
sprintf_s(szBuffer, 32, "%d%s", min(badge.number, 9), badge.number > 9 ? "+" : "");
|
DrawText(hDC, szBuffer, (int)strlen(szBuffer), &rcBadge, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
}
|
}
|
}
|
} while ((hCurrentItem = GetNextVisibleItem(hCurrentItem)) != NULL);
|
}
|
|
void CApredTreeCtrl::OnMouseMove(UINT nFlags, CPoint point)
|
{
|
// ¼ì²â²¢¼Ç¼ÈȵãÏî
|
HTREEITEM hHoverItem = HitTest(point);
|
if (m_hHoverItem != hHoverItem) {
|
m_hHoverItem = hHoverItem;
|
InvalidateRect(NULL);
|
}
|
|
if (!m_bTracking) {
|
TRACKMOUSEEVENT tme;
|
tme.cbSize = sizeof(tme);
|
tme.dwFlags = TME_HOVER | TME_LEAVE; // ·¢ËÍWM_MOUSEHOVERºÍWM_MOUSELEAVE
|
tme.hwndTrack = m_hWnd; // Ö¸¶¨Òª×·×ٵĴ°¿Ú
|
tme.dwHoverTime = 10; // Êó±êÔÚ°´Å¥ÉÏÍ£Áô³¬¹ý10ms£¬²ÅÈÏΪ״̬ΪHOVER
|
m_bTracking = _TrackMouseEvent(&tme); // ¿ªÆôWindowsµÄWM_MOUSELEAVEWM_MOUSEHOVERʼþÖ§³Ö
|
}
|
|
CTreeCtrl::OnMouseMove(nFlags, point);
|
}
|
|
void CApredTreeCtrl::OnTimer(UINT_PTR nIDEvent)
|
{
|
if (1 == nIDEvent) {
|
BOOL bNeedInvalidate = FALSE;
|
for (auto& item : m_badges) {
|
if (item.second.showTime > 0) {
|
item.second.showTime--;
|
if (item.second.showTime == 0) {
|
item.second.type = BADGE_HIDE;
|
bNeedInvalidate = TRUE;
|
}
|
}
|
}
|
if(bNeedInvalidate)
|
InvalidateRect(NULL, TRUE);
|
}
|
|
|
CTreeCtrl::OnTimer(nIDEvent);
|
}
|
|
void CApredTreeCtrl::OnMouseHover(UINT nFlags, CPoint point)
|
{
|
|
}
|
|
void CApredTreeCtrl::OnMouseLeave()
|
{
|
m_hHoverItem = nullptr;
|
m_bTracking = FALSE; // ÈôÒѾÀ뿪£¬ÔòÍ£Ö¹×·×Ù
|
InvalidateRect(NULL);
|
|
CTreeCtrl::OnMouseLeave();
|
}
|
|
void CApredTreeCtrl::OnLButtonDown(UINT nFlags, CPoint point)
|
{
|
HTREEITEM hItem = HitTest(point);
|
if (hItem != nullptr) {
|
SelectItem(hItem);
|
GetParent()->SendMessage(ID_MSG_TREE_CLICK_ITEM, (WPARAM)hItem, 0);
|
}
|
|
CTreeCtrl::OnLButtonDown(nFlags, point);
|
}
|
|
void CApredTreeCtrl::SetItemBadge(HTREEITEM hItem, COLORREF badgeBackground, COLORREF badgeForeground)
|
{
|
if (m_badges.find(hItem) == m_badges.end()) {
|
BADGE badge;
|
badge.badgeBackground = badgeBackground;
|
badge.badgeForeground = badgeForeground;
|
badge.type = BADGE_HIDE;
|
badge.showTime = 0;
|
m_badges[hItem] = badge;
|
}
|
else {
|
BADGE& badge = m_badges[hItem];
|
badge.badgeBackground = badgeBackground;
|
badge.badgeForeground = badgeForeground;
|
badge.type = BADGE_HIDE;
|
}
|
}
|
|
void CApredTreeCtrl::ShowItemBadgeNumber(HTREEITEM hItem, int number)
|
{
|
if (m_badges.find(hItem) == m_badges.end()) {
|
return;
|
}
|
|
BADGE& badge = m_badges[hItem];
|
badge.type = BADGE_NUMBER;
|
badge.number = number;
|
InvalidateRect(NULL, TRUE);
|
}
|
|
void CApredTreeCtrl::ShowItemBadgeDotMode(HTREEITEM hItem, int nSecond/* = 0*/)
|
{
|
if (m_badges.find(hItem) == m_badges.end()) {
|
return;
|
}
|
|
BADGE& badge = m_badges[hItem];
|
if (badge.type != BADGE_DOT) {
|
badge.type = BADGE_DOT;
|
badge.showTime = nSecond;
|
InvalidateRect(NULL, TRUE);
|
}
|
}
|
|
void CApredTreeCtrl::HideItemBadge(HTREEITEM hItem)
|
{
|
BADGE& badge = m_badges[hItem];
|
if (badge.type != BADGE_HIDE) {
|
badge.type = BADGE_HIDE;
|
badge.showTime = 0;
|
InvalidateRect(NULL, TRUE);
|
}
|
}
|
|
void CApredTreeCtrl::SetItemBold(HTREEITEM item)
|
{
|
if (!FindBoldItem(item)) {
|
m_itemBolds.push_back(item);
|
}
|
}
|
|
void CApredTreeCtrl::CancelItemBold(HTREEITEM item)
|
{
|
for (auto iter = m_itemBolds.begin(); iter != m_itemBolds.end(); iter++) {
|
if (*iter == item) {
|
m_itemBolds.erase(iter);
|
break;
|
}
|
}
|
|
InvalidateRect(NULL, TRUE);
|
}
|
|
BOOL CApredTreeCtrl::FindBoldItem(HTREEITEM item)
|
{
|
BOOL bFound = FALSE;
|
for (auto i : m_itemBolds) {
|
if (i == item) return TRUE;
|
}
|
|
return FALSE;
|
}
|
|
|
int CApredTreeCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
|
{
|
if (CTreeCtrl::OnCreate(lpCreateStruct) == -1)
|
return -1;
|
|
SetTimer(1, 1000, nullptr);
|
|
return 0;
|
}
|
|
|
void CApredTreeCtrl::PreSubclassWindow()
|
{
|
SetTimer(1, 1000, nullptr);
|
|
CTreeCtrl::PreSubclassWindow();
|
}
|
|
void CApredTreeCtrl::SetItemIcon(HTREEITEM hItem, HICON hIcon)
|
{
|
m_icons[hItem] = hIcon;
|
}
|