From d64036c0510cf06009a7252e318d828fbc2658f0 Mon Sep 17 00:00:00 2001
From: mrDarker <mr.darker@163.com>
Date: 星期四, 11 九月 2025 09:22:02 +0800
Subject: [PATCH] Merge branch 'clh' into liuyang
---
SourceCode/Bond/Servo/CExpandableListCtrl.cpp | 397 +++++++++++++++++++++++++++++++++++++-------------------
1 files changed, 264 insertions(+), 133 deletions(-)
diff --git a/SourceCode/Bond/Servo/CExpandableListCtrl.cpp b/SourceCode/Bond/Servo/CExpandableListCtrl.cpp
index 11470ce..9022dce 100644
--- a/SourceCode/Bond/Servo/CExpandableListCtrl.cpp
+++ b/SourceCode/Bond/Servo/CExpandableListCtrl.cpp
@@ -17,7 +17,6 @@
if (CListCtrl::OnCreate(lpCreateStruct) == -1)
return -1;
- // 鎶ヨ〃椋庢牸鍒椾妇渚�
SetExtendedStyle(GetExtendedStyle()
| LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP | LVS_EX_GRIDLINES | LVS_EX_DOUBLEBUFFER);
@@ -26,13 +25,12 @@
void CExpandableListCtrl::PreSubclassWindow()
{
- // 鎶ヨ〃椋庢牸鍒椾妇渚�
SetExtendedStyle(GetExtendedStyle()
| LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP | LVS_EX_GRIDLINES | LVS_EX_DOUBLEBUFFER);
-
CListCtrl::PreSubclassWindow();
}
+// ===== 鏍� API =====
CExpandableListCtrl::Node* CExpandableListCtrl::InsertRoot(const std::vector<CString>& cols)
{
auto n = std::make_unique<Node>((int)max(1, (int)cols.size()));
@@ -67,15 +65,12 @@
void CExpandableListCtrl::RebuildVisible()
{
- // 1) 閲嶅缓鍙搴忓垪
m_visible.clear();
for (auto& r : m_roots) appendVisible(r.get());
- // 2) 閲嶇粯/閲嶅~鏁版嵁
SetRedraw(FALSE);
DeleteAllItems();
- // 鎻掑叆鍙琛�
for (int i = 0; i < (int)m_visible.size(); ++i) {
Node* n = m_visible[i];
LVITEM lvi{};
@@ -85,11 +80,16 @@
lvi.pszText = const_cast<LPTSTR>((LPCTSTR)(n->cols.empty() ? _T("") : n->cols[0]));
InsertItem(&lvi);
- for (int col = 1; col < GetHeaderCtrl()->GetItemCount(); ++col) {
+ const int colCount = GetHeaderCtrl() ? GetHeaderCtrl()->GetItemCount() : 1;
+ for (int col = 1; col < colCount; ++col) {
CString txt = (col < (int)n->cols.size()) ? n->cols[col] : _T("");
SetItemText(i, col, txt);
}
}
+
+ // 閲嶅缓鍚庯紝鎸夎鍙烽鑹叉暟缁勫榻�
+ m_rowColors.resize(GetItemCount());
+
SetRedraw(TRUE);
Invalidate();
}
@@ -119,6 +119,69 @@
return m_visible[i];
}
+// ===== 棰滆壊 API =====
+void CExpandableListCtrl::SetNodeColor(Node* n, COLORREF text, COLORREF bk)
+{
+ if (!n) return;
+ RowColor rc{};
+ rc.text = text; rc.bk = bk;
+ rc.hasText = (text != CLR_DEFAULT);
+ rc.hasBk = (bk != CLR_DEFAULT);
+ m_colorByNode[n] = rc;
+
+ for (int i = 0; i < (int)m_visible.size(); ++i) {
+ if (m_visible[i] == n) {
+ RedrawItems(i, i);
+ UpdateWindow();
+ return;
+ }
+ }
+}
+
+void CExpandableListCtrl::ClearNodeColor(Node* n)
+{
+ if (!n) return;
+ auto it = m_colorByNode.find(n);
+ if (it != m_colorByNode.end()) {
+ m_colorByNode.erase(it);
+ for (int i = 0; i < (int)m_visible.size(); ++i) {
+ if (m_visible[i] == n) {
+ RedrawItems(i, i);
+ UpdateWindow();
+ return;
+ }
+ }
+ }
+}
+
+void CExpandableListCtrl::ClearAllColors()
+{
+ m_colorByNode.clear();
+ m_rowColors.clear();
+ Invalidate(FALSE);
+}
+
+// 鍏煎鏃ф帴鍙o細鎸夆�滃彲瑙佽鍙封�濈潃鑹�
+void CExpandableListCtrl::SetItemColor(DWORD_PTR iItem, COLORREF TextColor, COLORREF TextBkColor)
+{
+ SetItemColorByVisibleIndex((int)iItem, TextColor, TextBkColor);
+}
+void CExpandableListCtrl::SetItemColorByVisibleIndex(int row, COLORREF text, COLORREF bk)
+{
+ if (row < 0) return;
+ if (row >= (int)m_rowColors.size())
+ m_rowColors.resize(row + 1);
+
+ RowColor rc{};
+ rc.text = text; rc.bk = bk;
+ rc.hasText = (text != CLR_DEFAULT);
+ rc.hasBk = (bk != CLR_DEFAULT);
+ m_rowColors[row] = rc;
+
+ RedrawItems(row, row);
+ UpdateWindow();
+}
+
CRect CExpandableListCtrl::expanderRectForRow(int row) const
{
CRect rcLabel;
@@ -127,7 +190,7 @@
Node* n = const_cast<CExpandableListCtrl*>(this)->GetNodeByVisibleIndex(row);
if (!n || n->children.empty())
- return CRect(0, 0, 0, 0); // 鍙跺瓙涓嶅崰浣嶏紝鏂囨湰灏变笉浼氳澶氭帹涓�鏍�
+ return CRect(0, 0, 0, 0);
const int indent = n->level;
const int left = rcLabel.left + m_expanderPadding + indent * 16;
@@ -140,14 +203,44 @@
);
}
+// 棰滆壊璁$畻锛氫紭鍏� Node*锛屽叾娆¤鍙凤紱鑻ラ渶瑕佸垯璁╃郴缁熼珮浜鐩�
+void CExpandableListCtrl::computeColorsForRow(int row, COLORREF& outText, COLORREF& outBk) const
+{
+ outText = ListView_GetTextColor(const_cast<CExpandableListCtrl*>(this)->m_hWnd);
+ outBk = ListView_GetBkColor(const_cast<CExpandableListCtrl*>(this)->m_hWnd);
+
+ const bool selected = (const_cast<CExpandableListCtrl*>(this)->GetItemState(row, LVIS_SELECTED) & LVIS_SELECTED) != 0;
+ const bool focusOnCtrl = (const_cast<CExpandableListCtrl*>(this)->GetSafeHwnd() == ::GetFocus());
+
+ if (m_preserveSelHighlight && selected) {
+ outBk = GetSysColor(focusOnCtrl ? COLOR_HIGHLIGHT : COLOR_3DFACE);
+ outText = GetSysColor(focusOnCtrl ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT);
+ return;
+ }
+
+ // Node* 棰滆壊
+ if (Node* n = const_cast<CExpandableListCtrl*>(this)->GetNodeByVisibleIndex(row)) {
+ auto it = m_colorByNode.find(n);
+ if (it != m_colorByNode.end()) {
+ if (it->second.hasText) outText = it->second.text;
+ if (it->second.hasBk) outBk = it->second.bk;
+ return;
+ }
+ }
+
+ // 琛屽彿棰滆壊
+ if (row >= 0 && row < (int)m_rowColors.size()) {
+ const RowColor& rc = m_rowColors[row];
+ if (rc.hasText) outText = rc.text;
+ if (rc.hasBk) outBk = rc.bk;
+ }
+}
void CExpandableListCtrl::OnClick(NMHDR* pNMHDR, LRESULT* pResult)
{
LPNMITEMACTIVATE pia = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
if (pia->iItem >= 0) {
CPoint pt = pia->ptAction;
-
- // 鍛戒腑灞曞紑鎸夐挳锛�
CRect expRc = expanderRectForRow(pia->iItem);
if (expRc.PtInRect(pt)) {
Node* n = GetNodeByVisibleIndex(pia->iItem);
@@ -170,8 +263,15 @@
return;
case CDDS_ITEMPREPAINT:
+ {
+ const int row = (int)pCD->nmcd.dwItemSpec;
+ COLORREF txt, bk;
+ computeColorsForRow(row, txt, bk);
+ pCD->clrText = txt;
+ pCD->clrTextBk = bk;
*pResult = CDRF_NOTIFYSUBITEMDRAW;
return;
+ }
case CDDS_ITEMPREPAINT | CDDS_SUBITEM:
{
@@ -179,135 +279,166 @@
const int col = pCD->iSubItem;
CDC* pDC = CDC::FromHandle(pCD->nmcd.hdc);
- if (col == 0)
- {
- CRect rc; GetSubItemRect(row, 0, LVIR_LABEL, rc);
- Node* n = GetNodeByVisibleIndex(row);
- if (!n) { *pResult = CDRF_DODEFAULT; return; }
-
- // 1) 鑳屾櫙/鍓嶆櫙棰滆壊锛氭寜鏄惁閫変腑
- const bool selected = (GetItemState(row, LVIS_SELECTED) & LVIS_SELECTED) != 0;
- const bool focusOnCtrl = (GetSafeHwnd() == ::GetFocus());
- COLORREF bk = selected ? GetSysColor(focusOnCtrl ? COLOR_HIGHLIGHT : COLOR_3DFACE)
- : ListView_GetBkColor(m_hWnd);
- COLORREF txt = selected ? GetSysColor(focusOnCtrl ? COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT)
- : ListView_GetTextColor(m_hWnd);
-
- // 浠呭湪闇�瑕佹椂濉厖鑳屾櫙锛堥伩鍏嶁�滈粦涓�鐗団�濓級
- CBrush bkBrush(bk);
- pDC->FillRect(rc, &bkBrush);
-
- // 2) 灞曞紑/鎶樺彔鎸囩ず锛堝弬鑰冩棫椤圭洰鐨勫彸瀵归綈鍧愭爣娉曪紝鍋氬儚绱犲榻愶紝绾疓DI锛�
- if (!n->children.empty())
- {
- CRect box = expanderRectForRow(row);
-
- // ---- 鍙皟鍙傛暟锛氫笌鏃т唬鐮佸懡鍚嶄竴鑷� ----
- // 鍙充晶鐣欑櫧锛堜笌鏂囨湰闂撮殭/缃戞牸绾夸繚鎸佽窛绂伙級
- const int ROFFSET = 2;
- // 闂悎/灞曞紑鐨勨�滃搴︹�濊缃細濂囨暟鏇撮『鐪硷紙9/11 閮借锛�
- const int WIDE = max(9, min(min(box.Width(), box.Height()), 13)); // 鈻� 鐨勮竟闀�
- const int WIDE2 = WIDE / 2; // 涓�鍗�
- const int EXPANDED_WIDE = WIDE; // 鈻� 鐨勮竟闀�
-
- // 杞诲井鍐呯缉锛岄伩鍏嶈创杈癸紙涓庝綘鏃т唬鐮佲�滄寜閽鍒蜂竴涓嬧�濆悓鏁堬級
- box.DeflateRect(1, 1);
-
- // 缁熶竴鍋氬伓鏁板榻愶紝鍑忓皯鍗婂儚绱犻敮榻�
- auto even = [](int v) { return (v & 1) ? (v - 1) : v; };
-
- // 璁$畻鈥滆嚜涓嬪悜涓娾�濈殑鍩哄噯鍋忕Щ锛屼笌鏃� TreeCtrl 涓�鑷�
- // 杩欓噷鐢� box 浣滀负 pRect
- POINT pt[3];
- if (n->expanded) {
- // 鈻�
- int nBottomOffset = (box.Height() - EXPANDED_WIDE) / 2;
- pt[0].x = box.right - ROFFSET - EXPANDED_WIDE;
- pt[0].y = box.bottom - nBottomOffset;
- pt[1].x = box.right - ROFFSET;
- pt[1].y = box.bottom - nBottomOffset;
- pt[2].x = box.right - ROFFSET;
- pt[2].y = box.bottom - nBottomOffset - EXPANDED_WIDE;
- }
- else {
- // 鈻�
- int nBottomOffset = (box.Height() - WIDE) / 2;
-
- pt[0].x = box.right - ROFFSET - WIDE2;
- pt[0].y = box.bottom - nBottomOffset - WIDE;
- pt[1].x = box.right - ROFFSET - WIDE2;
- pt[1].y = box.bottom - nBottomOffset;
- pt[2].x = box.right - ROFFSET;
- pt[2].y = box.bottom - nBottomOffset - WIDE2;
- }
-
- // 浠呭~鍏咃紝涓嶆弿杈癸紙鎻忚竟浼氬姞閲嶅彴闃舵劅锛夛紱棰滆壊鐢� txt 涓庝富棰樹竴鑷�
- HGDIOBJ oldPen = pDC->SelectObject(GetStockObject(NULL_PEN));
- HBRUSH hBrush = CreateSolidBrush(txt);
- HGDIOBJ oldBrush = pDC->SelectObject(hBrush);
-
- pDC->Polygon(pt, 3);
-
- pDC->SelectObject(oldPen);
- pDC->SelectObject(oldBrush);
- DeleteObject(hBrush);
- }
-
-
-
- // 3) 鏂囨湰锛氬熀浜庨鍒楀尯鍩熷彸绉伙紙鍖哄垎鏄惁鏈夊瓙鑺傜偣锛�
- const int indentPx = n->level * 14;
- const int baseLeft = rc.left + m_expanderPadding + indentPx;
-
- CRect textRc = rc;
- if (!n->children.empty()) {
- // 鏈夊瓙椤癸細棰勭暀鎸夐挳浣� + 鏂囨湰闂撮殭
- textRc.left = baseLeft + m_expanderSize + m_textGap;
- }
- else {
- // 鍙跺瓙琛岋細涓嶉鐣欐寜閽綅锛屽彧缁欎竴鐐圭偣鍙跺瓙闂撮殭锛堣灞傜骇缂╄繘浠嶇劧鐢熸晥锛�
- constexpr int kLeafGap = 2; // 浣犲彲璋� 0~4
- textRc.left = baseLeft + kLeafGap;
- }
-
- pDC->SetBkMode(TRANSPARENT);
- pDC->SetTextColor(txt);
- CString txt0 = n->cols.empty() ? _T("") : n->cols[0];
- pDC->DrawText(txt0, textRc, DT_SINGLELINE | DT_VCENTER | DT_NOPREFIX | DT_END_ELLIPSIS);
-
-
- // 鈥斺�� 鐢诲畬涓夎涓庢枃鏈箣鍚庯紝琛ヤ竴鏉¤琛岀殑搴曢儴妯悜缃戞牸绾� 鈥斺��
- // 浠呭綋寮�鍚簡 LVS_EX_GRIDLINES 鎵嶇粯鍒�
- if (GetExtendedStyle() & LVS_EX_GRIDLINES)
- {
- // 鐢ㄦ暣琛� bounds锛屼繚璇佹í绾胯疮绌挎墍鏈夊垪鐨勫彲瑙佸搴�
- CRect rcRow;
- GetSubItemRect(row, 0, LVIR_BOUNDS, rcRow);
-
- // 搴曡竟 y 鍧愭爣锛堜笌绯荤粺缃戞牸绾垮榻愶級
- const int y = rcRow.bottom - 1;
-
- // 棰滆壊涓庣郴缁熼鏍兼帴杩戯紱鑻ヨ寰楀亸娴咃紝鍙崲 COLOR_3DSHADOW
- CPen pen(PS_SOLID, 1, GetSysColor(COLOR_3DLIGHT));
- CPen* oldPen = pDC->SelectObject(&pen);
-
- // 妯嚎浠庤宸﹀埌琛屽彸锛堝綋鍓嶅彲瑙佸尯鍩燂級
- pDC->MoveTo(rcRow.left, y);
- pDC->LineTo(rcRow.right, y);
-
- pDC->SelectObject(oldPen);
- }
-
- *pResult = CDRF_SKIPDEFAULT;
+ // 濡傛灉娌℃湁鏍戣妭鐐癸紙绾钩鍒楄〃锛夛紝棣栧垪涔熻蛋榛樿缁樺埗
+ Node* n = GetNodeByVisibleIndex(row);
+ if (col != 0 || !n) {
+ *pResult = CDRF_DODEFAULT;
return;
}
- // 鍏朵粬鍒楅粯璁ょ粯鍒�
- *pResult = CDRF_DODEFAULT;
+ // 棣栧垪鑷粯锛堟爲妯″紡锛�
+ CRect rc; GetSubItemRect(row, 0, LVIR_LABEL, rc);
+
+ COLORREF bk, txt;
+ computeColorsForRow(row, txt, bk);
+
+ CBrush bkBrush(bk);
+ pDC->FillRect(rc, &bkBrush);
+
+ if (!n->children.empty())
+ {
+ CRect box = expanderRectForRow(row);
+ const int ROFFSET = 2;
+ const int WIDE = max(9, min(min(box.Width(), box.Height()), 13));
+ const int WIDE2 = WIDE / 2;
+ const int EXPANDED_WIDE = WIDE;
+
+ box.DeflateRect(1, 1);
+
+ POINT pt[3];
+ if (n->expanded) {
+ int nBottomOffset = (box.Height() - EXPANDED_WIDE) / 2;
+ pt[0].x = box.right - ROFFSET - EXPANDED_WIDE;
+ pt[0].y = box.bottom - nBottomOffset;
+ pt[1].x = box.right - ROFFSET;
+ pt[1].y = box.bottom - nBottomOffset;
+ pt[2].x = box.right - ROFFSET;
+ pt[2].y = box.bottom - nBottomOffset - EXPANDED_WIDE;
+ }
+ else {
+ int nBottomOffset = (box.Height() - WIDE) / 2;
+ pt[0].x = box.right - ROFFSET - WIDE2;
+ pt[0].y = box.bottom - nBottomOffset - WIDE;
+ pt[1].x = box.right - ROFFSET - WIDE2;
+ pt[1].y = box.bottom - nBottomOffset;
+ pt[2].x = box.right - ROFFSET;
+ pt[2].y = box.bottom - nBottomOffset - WIDE2;
+ }
+
+ HGDIOBJ oldPen = pDC->SelectObject(GetStockObject(NULL_PEN));
+ HBRUSH hBrush = CreateSolidBrush(txt);
+ HGDIOBJ oldBrush = pDC->SelectObject(hBrush);
+ pDC->Polygon(pt, 3);
+ pDC->SelectObject(oldPen);
+ pDC->SelectObject(oldBrush);
+ DeleteObject(hBrush);
+ }
+
+ const int indentPx = n->level * 14;
+ const int baseLeft = rc.left + m_expanderPadding + indentPx;
+
+ CRect textRc = rc;
+ if (!n->children.empty()) {
+ textRc.left = baseLeft + m_expanderSize + m_textGap;
+ }
+ else {
+ constexpr int kLeafGap = 2;
+ textRc.left = baseLeft + kLeafGap;
+ }
+
+ pDC->SetBkMode(TRANSPARENT);
+ pDC->SetTextColor(txt);
+ CString txt0 = n->cols.empty() ? _T("") : n->cols[0];
+ pDC->DrawText(txt0, textRc, DT_SINGLELINE | DT_VCENTER | DT_NOPREFIX | DT_END_ELLIPSIS);
+
+ if (GetExtendedStyle() & LVS_EX_GRIDLINES)
+ {
+ CRect rcRow; GetSubItemRect(row, 0, LVIR_BOUNDS, rcRow);
+ const int y = rcRow.bottom - 1;
+ CPen pen(PS_SOLID, 1, GetSysColor(COLOR_3DLIGHT));
+ CPen* oldPen = pDC->SelectObject(&pen);
+ pDC->MoveTo(rcRow.left, y);
+ pDC->LineTo(rcRow.right, y);
+ pDC->SelectObject(oldPen);
+ }
+
+ *pResult = CDRF_SKIPDEFAULT;
return;
}
-
}
*pResult = CDRF_DODEFAULT;
}
+
+// 鍏煎琛屼负锛氬悓姝� SetItemText 鍒� Node->cols锛涚淮鎶よ鍙烽鑹叉暟缁勭殑鎻掑叆/鍒犻櫎
+LRESULT CExpandableListCtrl::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
+{
+ // 鍚屾 SetItemText 鍒� Node锛圓/W 鍏煎锛�
+ if (message == LVM_SETITEMTEXT
+#ifdef LVM_SETITEMTEXTA
+ || message == LVM_SETITEMTEXTA
+#endif
+#ifdef LVM_SETITEMTEXTW
+ || message == LVM_SETITEMTEXTW
+#endif
+ )
+ {
+ int row = static_cast<int>(wParam);
+ LVITEM* p = reinterpret_cast<LVITEM*>(lParam);
+ if (p) {
+ Node* n = GetNodeByVisibleIndex(row);
+ if (n) {
+ int sub = p->iSubItem;
+ if (sub >= (int)n->cols.size())
+ n->cols.resize(sub + 1);
+ CString newText = p->pszText ? p->pszText : _T("");
+ n->cols[sub] = newText;
+ }
+ }
+ // 缁х画浜ょ粰鍩虹被澶勭悊
+ }
+
+ LRESULT nRet = CListCtrl::WindowProc(message, wParam, lParam);
+
+ // 缁存姢琛屽彿棰滆壊鏁扮粍锛堝吋瀹规棫 SetItemColor锛�
+ if (message == LVM_INSERTITEM) {
+ if (nRet != -1) {
+ LVITEM* p = (LVITEM*)lParam;
+ int pos = p ? p->iItem : (int)nRet;
+ if (pos < 0) pos = (int)nRet;
+ if (pos > (int)m_rowColors.size()) pos = (int)m_rowColors.size();
+ m_rowColors.insert(m_rowColors.begin() + pos, RowColor{}); // 榛樿鑹�
+ }
+ }
+ else if (message == LVM_DELETEITEM) {
+ if (nRet != 0) {
+ int pos = (int)wParam;
+ if (pos >= 0 && pos < (int)m_rowColors.size())
+ m_rowColors.erase(m_rowColors.begin() + pos);
+ }
+ }
+ else if (message == LVM_DELETEALLITEMS) {
+ if (nRet != 0) {
+ m_rowColors.clear();
+ }
+ }
+
+ return nRet;
+}
+
+// CExpandableListCtrl.cpp 閲�
+void CExpandableListCtrl::ClearTree()
+{
+ // 娓呮暟鎹�
+ m_roots.clear();
+ m_visible.clear();
+
+ // 娓呭彲瑙侀」锛堝姟蹇咃紒鍚﹀垯鏃ч〉鐨勮浼氭畫鐣欙級
+ SetRedraw(FALSE);
+ DeleteAllItems();
+ SetRedraw(TRUE);
+
+ Invalidate();
+}
+
+
--
Gitblit v1.9.3