// LogSearchBar.cpp - 日志列表搜索条实现 #include "stdafx.h" #include "LogSearchBar.h" #include "LangManager.h" BEGIN_MESSAGE_MAP(CLogSearchBar, CWnd) ON_WM_CREATE() ON_WM_SIZE() ON_WM_ERASEBKGND() ON_WM_TIMER() ON_EN_CHANGE(IDC_LOG_SEARCH_EDIT, OnEnChangeSearch) ON_BN_CLICKED(IDC_LOG_SEARCH_PREV, OnBnClickedPrev) ON_BN_CLICKED(IDC_LOG_SEARCH_NEXT, OnBnClickedNext) ON_BN_CLICKED(IDC_LOG_SEARCH_CLEAR, OnBnClickedClear) END_MESSAGE_MAP() CLogSearchBar::CLogSearchBar() { } CLogSearchBar::~CLogSearchBar() { } BOOL CLogSearchBar::Create(CWnd* pParent, CListCtrl* pList) { m_pList = pList; LPCTSTR cls = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW, ::LoadCursor(NULL, IDC_ARROW)); return CWnd::Create(cls, _T(""), WS_CHILD | WS_CLIPCHILDREN, CRect(0, 0, 1, 1), pParent, 0); } int CLogSearchBar::OnCreate(LPCREATESTRUCT lpcs) { if (CWnd::OnCreate(lpcs) == -1) return -1; // 搜索输入框 m_editSearch.Create(WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, CRect(0, 0, 1, 1), this, IDC_LOG_SEARCH_EDIT); m_editSearch.SetCueBanner(L"搜索日志...", TRUE); // 清除按钮 m_btnClear.Create(_T("X"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_FLAT, CRect(0, 0, 1, 1), this, IDC_LOG_SEARCH_CLEAR); // 上一个 / 下一个按钮(系统样式,与浅色背景融合) m_btnPrev.Create(_T("▲"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_FLAT, CRect(0, 0, 1, 1), this, IDC_LOG_SEARCH_PREV); m_btnNext.Create(_T("▼"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_FLAT, CRect(0, 0, 1, 1), this, IDC_LOG_SEARCH_NEXT); // 结果计数 m_staticCount.Create(_T(""), WS_CHILD | WS_VISIBLE | SS_CENTER | SS_CENTERIMAGE, CRect(0, 0, 1, 1), this, IDC_LOG_SEARCH_COUNT); // 统一字体:跟父窗口(主对话框)保持一致 CFont* pFont = GetParent()->GetFont(); if (pFont) { m_staticCount.SetFont(pFont); m_editSearch.SetFont(pFont); m_btnClear.SetFont(pFont); m_btnPrev.SetFont(pFont); m_btnNext.SetFont(pFont); } return 0; } void CLogSearchBar::LayoutControls(int cx, int cy) { if (!m_editSearch.GetSafeHwnd()) return; const int m = 2; const int h = cy - 2 * m; if (h <= 0) return; const int btnW = h; const int cntW = 62; // 搜索框填满剩余空间(barW 由外部控制总宽,这里保证控件铺满) const int editW = max(20, cx - 2 * m - 3 * btnW - 2 * m - cntW); int x = m; m_staticCount.MoveWindow(x, m, cntW, h); x += cntW + m; m_editSearch.MoveWindow(x, m, editW, h); x += editW + m; m_btnClear.MoveWindow(x, m, btnW, h); x += btnW; m_btnPrev.MoveWindow(x, m, btnW, h); x += btnW; m_btnNext.MoveWindow(x, m, btnW, h); } void CLogSearchBar::OnSize(UINT nType, int cx, int cy) { CWnd::OnSize(nType, cx, cy); LayoutControls(cx, cy); } BOOL CLogSearchBar::OnEraseBkgnd(CDC* pDC) { CRect rc; GetClientRect(&rc); pDC->FillSolidRect(rc, ::GetSysColor(COLOR_3DFACE)); return TRUE; } void CLogSearchBar::OnEnChangeSearch() { KillTimer(TIMER_SEARCH); SetTimer(TIMER_SEARCH, SEARCH_DELAY_MS, NULL); } void CLogSearchBar::OnTimer(UINT_PTR nIDEvent) { if (nIDEvent == TIMER_SEARCH) { KillTimer(TIMER_SEARCH); DoSearch(); } CWnd::OnTimer(nIDEvent); } void CLogSearchBar::DoSearch() { if (!m_pList || !m_pList->GetSafeHwnd()) return; CString kw; m_editSearch.GetWindowText(kw); m_Results.clear(); m_nCurrentIndex = -1; if (kw.IsEmpty()) { UpdateCountText(); return; } CString kwLow = kw; kwLow.MakeLower(); int nCols = m_pList->GetHeaderCtrl() ? m_pList->GetHeaderCtrl()->GetItemCount() : 1; int nItems = m_pList->GetItemCount(); for (int i = 0; i < nItems; i++) { for (int c = 0; c < nCols; c++) { CString cell = m_pList->GetItemText(i, c); cell.MakeLower(); if (cell.Find(kwLow) >= 0) { m_Results.push_back(i); break; } } } if (!m_Results.empty()) { m_nCurrentIndex = 0; GotoMatch(m_nCurrentIndex); } UpdateCountText(); } void CLogSearchBar::GotoMatch(int idx) { if (!m_pList || idx < 0 || idx >= (int)m_Results.size()) return; int item = m_Results[idx]; // 取消所有选中 int n = -1; while ((n = m_pList->GetNextItem(-1, LVNI_SELECTED)) != -1) m_pList->SetItemState(n, 0, LVIS_SELECTED | LVIS_FOCUSED); m_pList->SetItemState(item, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); m_pList->EnsureVisible(item, FALSE); } void CLogSearchBar::UpdateCountText() { CString text; if (m_Results.empty()) { CString kw; m_editSearch.GetWindowText(kw); text = kw.IsEmpty() ? _TR("") : _TR("无结果"); } else { text.Format(_T("%d/%d"), m_nCurrentIndex + 1, (int)m_Results.size()); } m_staticCount.SetWindowText(text); } void CLogSearchBar::InvalidateSearch() { KillTimer(TIMER_SEARCH); m_Results.clear(); m_nCurrentIndex = -1; UpdateCountText(); if (m_editSearch.GetSafeHwnd()) { CString kw; m_editSearch.GetWindowText(kw); if (!kw.IsEmpty()) SetTimer(TIMER_SEARCH, SEARCH_DELAY_MS, NULL); } } void CLogSearchBar::OnBnClickedPrev() { if (m_Results.empty()) return; m_nCurrentIndex--; if (m_nCurrentIndex < 0) m_nCurrentIndex = (int)m_Results.size() - 1; GotoMatch(m_nCurrentIndex); UpdateCountText(); } void CLogSearchBar::OnBnClickedNext() { if (m_Results.empty()) return; m_nCurrentIndex++; if (m_nCurrentIndex >= (int)m_Results.size()) m_nCurrentIndex = 0; GotoMatch(m_nCurrentIndex); UpdateCountText(); } void CLogSearchBar::OnBnClickedClear() { m_editSearch.SetWindowText(_T("")); m_editSearch.SetFocus(); // 触发 EN_CHANGE → 定时器 → DoSearch 清空结果 }