| | |
| | | m_rowColors.resize(GetItemCount()); |
| | | |
| | | SetRedraw(TRUE); |
| | | Invalidate(); |
| | | Invalidate(FALSE); |
| | | } |
| | | |
| | | // —— 优化后的展开/收起:局部插入/删除,不全量 RebuildVisible —— // |
| | | void CExpandableListCtrl::Expand(Node* n) |
| | | { |
| | | if (!n || n->children.empty()) return; |
| | | if (!n->expanded) { n->expanded = true; RebuildVisible(); } |
| | | if (n->expanded) return; |
| | | |
| | | // 本地工具:找节点在 m_visible 中的行号 |
| | | auto VisibleIndexOf = [&](Node* x)->int { |
| | | for (int i = 0; i < (int)m_visible.size(); ++i) |
| | | if (m_visible[i] == x) return i; |
| | | return -1; |
| | | }; |
| | | // 递归收集“应当可见”的子树(受 expanded 影响) |
| | | std::vector<Node*> toInsert; |
| | | std::function<void(Node*)> CollectExpandedSubtree = [&](Node* x) { |
| | | if (!x) return; |
| | | for (auto& up : x->children) { |
| | | Node* ch = up.get(); |
| | | toInsert.push_back(ch); |
| | | if (ch->expanded && !ch->children.empty()) |
| | | CollectExpandedSubtree(ch); |
| | | } |
| | | }; |
| | | // 从 pos 起插入 nodes,对齐 m_visible / ListCtrl / m_rowColors |
| | | auto InsertRowsAt = [&](int pos, const std::vector<Node*>& nodes) { |
| | | if (nodes.empty()) return; |
| | | const int colCount = GetHeaderCtrl() ? GetHeaderCtrl()->GetItemCount() : 1; |
| | | |
| | | SetRedraw(FALSE); |
| | | |
| | | // 1) 先插 m_visible |
| | | m_visible.insert(m_visible.begin() + pos, nodes.begin(), nodes.end()); |
| | | |
| | | // 2) 再插 ListCtrl |
| | | for (int i = 0; i < (int)nodes.size(); ++i) { |
| | | Node* cur = nodes[i]; |
| | | LVITEM lvi{}; lvi.mask = LVIF_TEXT; |
| | | lvi.iItem = pos + i; |
| | | lvi.iSubItem = 0; |
| | | lvi.pszText = const_cast<LPTSTR>((LPCTSTR)(cur->cols.empty() ? _T("") : cur->cols[0])); |
| | | InsertItem(&lvi); |
| | | |
| | | for (int col = 1; col < colCount; ++col) { |
| | | CString txt = (col < (int)cur->cols.size()) ? cur->cols[col] : _T(""); |
| | | SetItemText(pos + i, col, txt); |
| | | } |
| | | } |
| | | |
| | | // 3) 行号颜色数组同步插入默认色 |
| | | m_rowColors.insert(m_rowColors.begin() + pos, nodes.size(), RowColor{}); |
| | | |
| | | SetRedraw(TRUE); |
| | | Invalidate(FALSE); |
| | | }; |
| | | |
| | | // —— 标记展开 |
| | | n->expanded = true; |
| | | |
| | | // —— 在 UI 里插入其“应当可见”的子树 |
| | | const int pos = VisibleIndexOf(n); |
| | | if (pos < 0) { RebuildVisible(); return; } |
| | | |
| | | CollectExpandedSubtree(n); |
| | | InsertRowsAt(pos + 1, toInsert); |
| | | } |
| | | |
| | | void CExpandableListCtrl::Collapse(Node* n) |
| | | { |
| | | if (!n || n->children.empty()) return; |
| | | if (n->expanded) { n->expanded = false; RebuildVisible(); } |
| | | if (!n->expanded) return; |
| | | |
| | | // 本地工具:找节点行号 |
| | | auto VisibleIndexOf = [&](Node* x)->int { |
| | | for (int i = 0; i < (int)m_visible.size(); ++i) |
| | | if (m_visible[i] == x) return i; |
| | | return -1; |
| | | }; |
| | | // 计算“当前可见的所有后代数量”(基于 level 递减判断) |
| | | auto CountDescendantsInVisible = [&](Node* x)->int { |
| | | if (!x) return 0; |
| | | const int start = VisibleIndexOf(x); |
| | | if (start < 0) return 0; |
| | | const int baseLevel = x->level; |
| | | int cnt = 0; |
| | | for (int i = start + 1; i < (int)m_visible.size(); ++i) { |
| | | if (!m_visible[i]) break; |
| | | if (m_visible[i]->level <= baseLevel) break; |
| | | ++cnt; |
| | | } |
| | | return cnt; |
| | | }; |
| | | // 从 UI 删除 pos 开始的 count 行,并同步 m_visible/m_rowColors |
| | | auto DeleteRowsAt = [&](int pos, int count) { |
| | | if (count <= 0) return; |
| | | |
| | | SetRedraw(FALSE); |
| | | |
| | | // 删 ListCtrl:一直删 pos,因为删一行后后续上移 |
| | | for (int i = 0; i < count; ++i) { |
| | | DeleteItem(pos); |
| | | } |
| | | // 删 m_visible |
| | | m_visible.erase(m_visible.begin() + pos, m_visible.begin() + pos + count); |
| | | // 删颜色 |
| | | if (pos >= 0 && pos <= (int)m_rowColors.size()) { |
| | | int end = min((int)m_rowColors.size(), pos + count); |
| | | m_rowColors.erase(m_rowColors.begin() + pos, m_rowColors.begin() + end); |
| | | } |
| | | |
| | | SetRedraw(TRUE); |
| | | Invalidate(FALSE); |
| | | }; |
| | | |
| | | // —— 标记收起 |
| | | n->expanded = false; |
| | | |
| | | // —— 只删除其“当前可见”的所有后代 |
| | | const int pos = VisibleIndexOf(n); |
| | | if (pos < 0) { RebuildVisible(); return; } |
| | | |
| | | const int cnt = CountDescendantsInVisible(n); |
| | | if (cnt > 0) { |
| | | DeleteRowsAt(pos + 1, cnt); |
| | | } |
| | | } |
| | | |
| | | void CExpandableListCtrl::Toggle(Node* n) |
| | | { |
| | | if (!n || n->children.empty()) return; |
| | | n->expanded = !n->expanded; |
| | | RebuildVisible(); |
| | | if (n->expanded) Collapse(n); |
| | | else Expand(n); |
| | | } |
| | | |
| | | CExpandableListCtrl::Node* CExpandableListCtrl::GetNodeByVisibleIndex(int i) const |
| | |
| | | for (int i = 0; i < (int)m_visible.size(); ++i) { |
| | | if (m_visible[i] == n) { |
| | | RedrawItems(i, i); |
| | | UpdateWindow(); |
| | | return; |
| | | } |
| | | } |
| | |
| | | for (int i = 0; i < (int)m_visible.size(); ++i) { |
| | | if (m_visible[i] == n) { |
| | | RedrawItems(i, i); |
| | | UpdateWindow(); |
| | | return; |
| | | } |
| | | } |
| | |
| | | m_rowColors[row] = rc; |
| | | |
| | | RedrawItems(row, row); |
| | | UpdateWindow(); |
| | | } |
| | | |
| | | CRect CExpandableListCtrl::expanderRectForRow(int row) const |
| | |
| | | |
| | | // —— 若点击到需要“全文显示”的列,则向父窗口发送自定义通知 —— // |
| | | if (!m_popupCols.empty()) { |
| | | LPNMITEMACTIVATE pia = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR); |
| | | LPNMITEMACTIVATE pia2 = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR); |
| | | |
| | | // 用 SubItemHitTest 更精准拿到列 |
| | | LVHITTESTINFO ht{}; |
| | | ht.pt = pia->ptAction; |
| | | ht.pt = pia2->ptAction; |
| | | int hit = SubItemHitTest(&ht); |
| | | if (hit >= 0 && ht.iItem >= 0 && ht.iSubItem >= 0) { |
| | | const int row = ht.iItem; |
| | |
| | | DeleteAllItems(); |
| | | SetRedraw(TRUE); |
| | | |
| | | Invalidate(); |
| | | Invalidate(FALSE); |
| | | } |
| | | |
| | | void CExpandableListCtrl::SetPopupFullTextColumns(const std::vector<int>& cols) |
| | |
| | | const int kPadding = 8; // 预留一点边距/省略号余量 |
| | | return sz.cx > (rcCell.Width() - kPadding); |
| | | } |
| | | |
| | | |