| | |
| | | #include "stdafx.h" |
| | | #include "stdafx.h" |
| | | #include "CCarrierSlotGrid.h" |
| | | #include <gdiplus.h> |
| | | #pragma comment(lib, "gdiplus.lib") |
| | |
| | | ON_WM_CREATE() |
| | | ON_WM_SIZE() |
| | | ON_WM_HSCROLL() |
| | | ON_WM_VSCROLL() // ★ 新增 |
| | | ON_WM_VSCROLL() // ★ 新增 |
| | | ON_WM_LBUTTONDOWN() |
| | | ON_WM_LBUTTONUP() |
| | | ON_WM_MOUSEWHEEL() |
| | |
| | | if (GetParent() && GetParent()->GetFont()) SetFont(GetParent()->GetFont()); |
| | | EnsureFonts(); |
| | | EnsureGdiplus(); |
| | | // 确保样式包含滚动条 |
| | | ModifyStyle(0, WS_HSCROLL | WS_VSCROLL, 0); |
| | | |
| | | SetWindowPos(nullptr, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); |
| | | } |
| | | |
| | |
| | | if (GetParent() && GetParent()->GetFont()) SetFont(GetParent()->GetFont()); |
| | | EnsureFonts(); |
| | | EnsureGdiplus(); |
| | | ModifyStyle(0, WS_HSCROLL | WS_VSCROLL, 0); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | |
| | | Invalidate(FALSE); |
| | | } |
| | | |
| | | void CCarrierSlotGrid::SetSlotChecked(int portIndex, int slotIndex, BOOL checked) |
| | | void CCarrierSlotGrid::SetSlotChecked(int portIndex, int slotIndex, BOOL checked, BOOL bNotify/* = FALSE*/) |
| | | { |
| | | if (!SAFE_PORT(portIndex) || !SAFE_SLOT(slotIndex)) return; |
| | | auto& pc = m_ports[portIndex]; |
| | |
| | | auto& cell = pc.slots[slotIndex]; |
| | | if (!cell.hasGlass) return; |
| | | cell.checked = !!checked; |
| | | NotifySelectionChanged(portIndex, slotIndex, cell.checked); |
| | | if(bNotify) NotifySelectionChanged(portIndex, slotIndex, cell.checked); |
| | | Invalidate(FALSE); |
| | | } |
| | | |
| | |
| | | |
| | | CString CCarrierSlotGrid::GetDisplayId(int portIndex, int slotIndex) const |
| | | { |
| | | CString s(_T("—")); |
| | | CString s(_T("—")); |
| | | if (!SAFE_PORT(portIndex) || !SAFE_SLOT(slotIndex)) return s; |
| | | const auto& cell = m_ports[portIndex].slots[slotIndex]; |
| | | if (!cell.hasGlass) return s; |
| | |
| | | return CSize(w, h); |
| | | } |
| | | |
| | | CSize CCarrierSlotGrid::CalcBestWindowSize(BOOL includeNonClient, int nSlotsOverride) const |
| | | CSize CCarrierSlotGrid::CalcBestWindowSize(BOOL includeNonClient, |
| | | int nSlotsOverride, |
| | | int extraPadX, |
| | | int extraPadY) const |
| | | { |
| | | CSize cli = CalcBestClientSize(nSlotsOverride); |
| | | if (!includeNonClient) return cli; |
| | | // 1) 基础客户区尺寸(含我们在客户区画的 1px 边框:左右+2/上下+2) |
| | | const CSize content = CalcBestClientSize(nSlotsOverride); |
| | | |
| | | RECT rc = { 0, 0, cli.cx, cli.cy }; |
| | | // 2) 取 DPI、滚动条尺寸(尽量用 ForDpi,回退到普通) |
| | | UINT dpi = 96; |
| | | #if (_WIN32_WINNT >= 0x0603) |
| | | if (m_hWnd) { |
| | | HMODULE hUser32 = ::GetModuleHandleW(L"user32.dll"); |
| | | if (hUser32) { |
| | | typedef UINT(WINAPI* PFN_GETDPIFORWINDOW)(HWND); |
| | | auto pGetDpiForWindow = (PFN_GETDPIFORWINDOW)::GetProcAddress(hUser32, "GetDpiForWindow"); |
| | | if (pGetDpiForWindow) dpi = pGetDpiForWindow(m_hWnd); |
| | | } |
| | | } |
| | | #endif |
| | | int cxVScroll = ::GetSystemMetrics(SM_CXVSCROLL); |
| | | int cyHScroll = ::GetSystemMetrics(SM_CYHSCROLL); |
| | | #if (_WIN32_WINNT >= 0x0A00) // Win10: 可用 GetSystemMetricsForDpi |
| | | HMODULE hUser32_2 = ::GetModuleHandleW(L"user32.dll"); |
| | | if (hUser32_2) { |
| | | typedef int (WINAPI* PFN_GSMFD)(int, UINT); |
| | | auto pGsmForDpi = (PFN_GSMFD)::GetProcAddress(hUser32_2, "GetSystemMetricsForDpi"); |
| | | if (pGsmForDpi) { |
| | | cxVScroll = pGsmForDpi(SM_CXVSCROLL, dpi); |
| | | cyHScroll = pGsmForDpi(SM_CYHSCROLL, dpi); |
| | | } |
| | | } |
| | | #endif |
| | | |
| | | // 目标是“刚好不出现滚动条”的窗口外框大小: |
| | | // 用当前样式去掉 WS_HSCROLL/WS_VSCROLL 再做 AdjustWindowRectEx |
| | | DWORD style = GetStyle(); |
| | | // 3) DPI 自适应安全余量(避免取整误差/主题差异) |
| | | const int autoPad = max(1, MulDiv(2, (int)dpi, 96)); // 约等于 2px@96DPI |
| | | const int padX = (extraPadX >= 0) ? extraPadX : autoPad; |
| | | const int padY = (extraPadY >= 0) ? extraPadY : autoPad; |
| | | |
| | | // 4) 迭代:考虑滚动条相互影响,直到稳定不需要滚动条 |
| | | int needCx = content.cx + padX; |
| | | int needCy = content.cy + padY; |
| | | |
| | | while (true) { |
| | | bool needV = (GetTotalContentWidth() > needCx); // 宽不够→会出现横向滚动条?(注意:横条占高度) |
| | | bool needH = (m_headerCY + (nSlotsOverride > 0 ? nSlotsOverride : m_nSlots) * m_rowHeight + 2 /*客户区边框*/ > needCy); // 高不够→会出现纵条?(纵条占宽度) |
| | | |
| | | // 注意:出现“纵向条”会减少可用宽度;出现“横向条”会减少可用高度 |
| | | // 我们目标是让“即使扣掉这些占位”后也仍然 >= 内容尺寸 |
| | | int adjCx = content.cx + padX + (needH ? cxVScroll : 0); |
| | | int adjCy = content.cy + padY + (needV ? cyHScroll : 0); |
| | | |
| | | if (adjCx <= needCx && adjCy <= needCy) break; // 稳定:当前 needCx/needCy 足够 |
| | | needCx = max(needCx, adjCx); |
| | | needCy = max(needCy, adjCy); |
| | | } |
| | | |
| | | if (!includeNonClient) return CSize(needCx, needCy); |
| | | |
| | | // 5) 把“理想客户区尺寸”换算成窗口外框尺寸(去掉 WS_H/VSCROLL 做换算) |
| | | RECT rc = { 0, 0, needCx, needCy }; |
| | | DWORD style = GetStyle(); // ✅ 用真实样式,让系统把滚动条非客户区一并算进去 |
| | | DWORD exStyle = GetExStyle(); |
| | | style &= ~(WS_HSCROLL | WS_VSCROLL); |
| | | |
| | | ::AdjustWindowRectEx(&rc, style, FALSE, exStyle); |
| | | return CSize(rc.right - rc.left, rc.bottom - rc.top); |
| | | } |
| | | |
| | | // ---------- 几何 ---------- |
| | | void CCarrierSlotGrid::DisableSystemScrollbars() |
| | | { |
| | | // 去掉样式 |
| | | ModifyStyle(WS_HSCROLL | WS_VSCROLL, 0, 0); |
| | | // 隐藏(兼容性) |
| | | ShowScrollBar(SB_HORZ, FALSE); |
| | | ShowScrollBar(SB_VERT, FALSE); |
| | | // 清空滚动信息 |
| | | SCROLLINFO si{ sizeof(SCROLLINFO) }; si.fMask = SIF_ALL; si.nMin = 0; si.nMax = 0; si.nPage = 0; si.nPos = 0; |
| | | SetScrollInfo(SB_HORZ, &si, TRUE); |
| | | SetScrollInfo(SB_VERT, &si, TRUE); |
| | | // 让非客户区重算 |
| | | SetWindowPos(nullptr, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); |
| | | Invalidate(FALSE); |
| | | } |
| | | |
| | | // 注意:这里用“无滚动条样式”来换算窗口外框尺寸,确保客户区=内容尺寸 + 我们客户区边框 |
| | | void CCarrierSlotGrid::ResizeWindowToFitAll(BOOL includeNonClient, int nSlotsOverride) |
| | | { |
| | | // 计算内容所需客户区(CalcBestClientSize 内已包含我们客户区1px边框的 +2) |
| | | CSize need = CalcBestClientSize(nSlotsOverride); |
| | | |
| | | if (!includeNonClient) { |
| | | SetWindowPos(nullptr, 0, 0, need.cx, need.cy, |
| | | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); |
| | | return; |
| | | } |
| | | |
| | | RECT rc = { 0, 0, need.cx, need.cy }; |
| | | DWORD style = GetStyle() & ~(WS_HSCROLL | WS_VSCROLL); // ← 用“无滚动条”的样式来换算 |
| | | DWORD exStyle = GetExStyle(); |
| | | ::AdjustWindowRectEx(&rc, style, FALSE, exStyle); |
| | | |
| | | int w = rc.right - rc.left; |
| | | int h = rc.bottom - rc.top; |
| | | |
| | | SetWindowPos(nullptr, 0, 0, w, h, |
| | | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); |
| | | } |
| | | |
| | | void CCarrierSlotGrid::SetNoScrollbarsMode(BOOL enable) |
| | | { |
| | | m_noScrollbars = !!enable; |
| | | |
| | | if (m_noScrollbars) { |
| | | // 1) 偏移清零 |
| | | m_scrollX = 0; |
| | | m_scrollY = 0; |
| | | |
| | | // 2) 去掉样式并隐藏条 |
| | | ModifyStyle(WS_HSCROLL | WS_VSCROLL, 0, 0); |
| | | ShowScrollBar(SB_BOTH, FALSE); |
| | | |
| | | // 3) 清空滚动信息(即便资源里原本带了样式,也不再影响) |
| | | SCROLLINFO si{ sizeof(SCROLLINFO) }; si.fMask = SIF_ALL; |
| | | SetScrollInfo(SB_HORZ, &si, TRUE); |
| | | SetScrollInfo(SB_VERT, &si, TRUE); |
| | | |
| | | // 4) 通知系统非客户区刷新,确保条被彻底移除 |
| | | SetWindowPos(nullptr, 0, 0, 0, 0, |
| | | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); |
| | | |
| | | Invalidate(FALSE); |
| | | } |
| | | else { |
| | | // 退出无滚动条模式:仅恢复样式,实际范围会在 UpdateScrollRange 中重新设置 |
| | | ModifyStyle(0, WS_HSCROLL | WS_VSCROLL, 0); |
| | | SetWindowPos(nullptr, 0, 0, 0, 0, |
| | | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); |
| | | UpdateScrollRange(); |
| | | Invalidate(FALSE); |
| | | } |
| | | } |
| | | |
| | | void CCarrierSlotGrid::FitWindowToContentNoScroll(BOOL includeNonClient, int nSlotsOverride) |
| | | { |
| | | // 确保已处于“无滚动条模式”,防止系统在 AdjustWindowRectEx 时预留滚动条非客户区 |
| | | SetNoScrollbarsMode(TRUE); |
| | | |
| | | // 你自己的 CalcBestClientSize 已包含客户区 1px 边框(+2)的修正 |
| | | CSize needCli = CalcBestClientSize(nSlotsOverride); |
| | | |
| | | if (!includeNonClient) { |
| | | SetWindowPos(nullptr, 0, 0, needCli.cx, needCli.cy, |
| | | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); |
| | | return; |
| | | } |
| | | |
| | | RECT rc{ 0, 0, needCli.cx, needCli.cy }; |
| | | // 注意:此时窗口样式已经没有 WS_H/VSCROLL 了——用真实样式换算即可 |
| | | ::AdjustWindowRectEx(&rc, GetStyle(), FALSE, GetExStyle()); |
| | | const int w = rc.right - rc.left; |
| | | const int h = rc.bottom - rc.top; |
| | | |
| | | SetWindowPos(nullptr, 0, 0, w, h, |
| | | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); |
| | | } |
| | | |
| | | // ---------- 几何 ---------- |
| | | CRect CCarrierSlotGrid::GetClientRectNoSB() const |
| | | { |
| | | CRect rc; GetClientRect(&rc); return rc; |
| | |
| | | |
| | | void CCarrierSlotGrid::UpdateScrollRange() |
| | | { |
| | | if (m_noScrollbars) { |
| | | // 确保偏移一直为 0,不设任何滚动信息 |
| | | m_scrollX = 0; |
| | | m_scrollY = 0; |
| | | return; |
| | | } |
| | | |
| | | CRect rc; GetClientRect(&rc); |
| | | |
| | | // 垂直 |
| | | // 垂直 |
| | | const int contentH = m_headerCY + m_nSlots * m_rowHeight; |
| | | const int pageY = max(1, rc.Height()); |
| | | const int maxPosY = max(0, contentH - pageY); |
| | |
| | | siY.nMin = 0; siY.nMax = contentH - 1; siY.nPage = pageY; siY.nPos = m_scrollY; |
| | | SetScrollInfo(SB_VERT, &siY, TRUE); |
| | | |
| | | // ˮƽ |
| | | // 水平 |
| | | const int contentW = GetTotalContentWidth(); |
| | | const int pageX = max(1, rc.Width()); |
| | | const int maxPosX = max(0, contentW - pageX); |
| | |
| | | SetScrollInfo(SB_HORZ, &siX, TRUE); |
| | | } |
| | | |
| | | // ---------- 表头分隔线命中 ---------- |
| | | // ---------- 表头分隔线命中 ---------- |
| | | int CCarrierSlotGrid::HitHeaderEdge(CPoint pt) const |
| | | { |
| | | if (!m_bAllowResize) return -1; // ← 新增 |
| | | if (!m_bAllowResize) return -1; // ← 新增 |
| | | if (!GetHeaderRect().PtInRect(pt)) return -1; |
| | | const int tol = 4; |
| | | int x = GetHeaderRect().left - m_scrollX + m_slotColCX; |
| | |
| | | return -1; |
| | | } |
| | | |
| | | // ---------- 绘制 ---------- |
| | | // ---------- 绘制 ---------- |
| | | BOOL CCarrierSlotGrid::OnEraseBkgnd(CDC* /*pDC*/) { return TRUE; } |
| | | |
| | | void CCarrierSlotGrid::DrawFlatCheckbox(CDC* pDC, const CRect& r, bool checked, bool disabled) |
| | |
| | | { |
| | | CRect rItem = GetHeaderItemRect(i); |
| | | |
| | | // 修改为: |
| | | if (i < GetPortCount()) { // ★ 最后一列不画分隔线 |
| | | // 修改为: |
| | | if (i < GetPortCount()) { // ★ 最后一列不画分隔线 |
| | | CPen pen(PS_SOLID, 1, ::GetSysColor(COLOR_3DSHADOW)); |
| | | pOldPen = pDC->SelectObject(&pen); |
| | | pDC->MoveTo(rItem.right - 1, rItem.top); |
| | |
| | | ? pc.portName |
| | | : (pc.portName + _T(" (") + pc.carrierName + _T(")")); |
| | | |
| | | // 勾选框靠右 |
| | | // 勾选框靠右 |
| | | CRect rcCb = GetHeaderCheckboxRect(i); |
| | | DrawFlatCheckbox(pDC, rcCb, all, pc.allocated); |
| | | |
| | | // 计数贴近勾选框左侧 |
| | | // 计数贴近勾选框左侧 |
| | | CString cnt; cnt.Format(_T("%d/%d"), selected, m_nSlots); |
| | | SIZE szCnt{ 0,0 }; |
| | | { CFont* o = pDC->SelectObject(&m_fntBold); |
| | |
| | | const int gap = 6; |
| | | CRect rcCnt(rcCb.left - gap - szCnt.cx, rItem.top, rcCb.left - gap, rItem.bottom); |
| | | |
| | | // 左侧标题 |
| | | // 左侧标题 |
| | | CRect rt = rItem; rt.DeflateRect(6, 0, (rItem.right - rcCnt.left) + 6, 0); |
| | | pDC->SetBkMode(TRANSPARENT); |
| | | pDC->SelectObject(&m_fntBold); |
| | | pDC->SetTextColor(::GetSysColor(COLOR_BTNTEXT)); |
| | | pDC->DrawText(leftTitle, rt, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS); |
| | | |
| | | // 计数 |
| | | // 计数 |
| | | pDC->SelectObject(&m_fntBold); |
| | | pDC->SetTextColor(::GetSysColor(COLOR_BTNTEXT)); |
| | | pDC->DrawText(cnt, rcCnt, DT_RIGHT | DT_VCENTER | DT_SINGLELINE); |
| | |
| | | pDC->DrawText(tx, rT, DT_CENTER | DT_VCENTER | DT_SINGLELINE); |
| | | } |
| | | |
| | | // 状态点(GDI+ 抗锯齿) |
| | | // 状态点(GDI+ 抗锯齿) |
| | | { |
| | | Graphics g(pDC->GetSafeHdc()); |
| | | g.SetSmoothingMode(SmoothingModeAntiAlias); |
| | |
| | | |
| | | |
| | | |
| | | // ===== 在每个已分配(allocated)的列中央绘制半透明 LOCK 水印(用 HDC+LOGFONT 构造字体)===== |
| | | // ===== 在每个已分配(allocated)的列中央绘制半透明 LOCK 水印(用 HDC+LOGFONT 构造字体)===== |
| | | { |
| | | Gdiplus::Graphics g(pDC->GetSafeHdc()); |
| | | g.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); |
| | |
| | | CRect cli = GetClientRectNoSB(); |
| | | CRect rh = GetHeaderRect(); |
| | | |
| | | // 取当前 UI 字体(优先粗体) |
| | | // 取当前 UI 字体(优先粗体) |
| | | LOGFONT lf{}; |
| | | if ((HFONT)m_fntBold) m_fntBold.GetLogFont(&lf); |
| | | else if ((HFONT)m_fntText) m_fntText.GetLogFont(&lf); |
| | |
| | | { |
| | | if (!m_ports[i].allocated) continue; |
| | | |
| | | // 列矩形(除去表头,考虑水平滚动) |
| | | // 列矩形(除去表头,考虑水平滚动) |
| | | CRect rCol = GetHeaderItemRect(i + 1); |
| | | rCol.top = rh.bottom; |
| | | rCol.bottom = cli.bottom; |
| | | if (rCol.right <= cli.left || rCol.left >= cli.right || rCol.Height() <= 0) continue; |
| | | |
| | | // 自适应一个合适的像素高度 |
| | | // 自适应一个合适的像素高度 |
| | | int availW = rCol.Width() - 12; |
| | | int availH = rCol.Height() - 12; |
| | | int emPx = max(16, min(min(availW / 3, availH / 5), 72)); |
| | | if (emPx < 16) emPx = 16; |
| | | // 字号减半(并给个更低的兜底,避免太小) |
| | | // 字号减半(并给个更低的兜底,避免太小) |
| | | emPx = max(12, emPx / 2); |
| | | |
| | | // 用 LOGFONTW + HDC 构造 GDI+ 字体 |
| | | // 用 LOGFONTW + HDC 构造 GDI+ 字体 |
| | | LOGFONTW lfw{}; |
| | | #ifdef UNICODE |
| | | lfw = *reinterpret_cast<LOGFONTW*>(&lf); |
| | |
| | | lfw.lfPitchAndFamily = lf.lfPitchAndFamily; |
| | | MultiByteToWideChar(CP_ACP, 0, lf.lfFaceName, -1, lfw.lfFaceName, LF_FACESIZE); |
| | | #endif |
| | | lfw.lfHeight = -emPx; // 负值=按像素高度 |
| | | lfw.lfHeight = -emPx; // 负值=按像素高度 |
| | | lfw.lfWeight = FW_BOLD; |
| | | |
| | | Gdiplus::Font gdifont(pDC->GetSafeHdc(), &lfw); // ★ 加上 Gdiplus:: |
| | | Gdiplus::Font gdifont(pDC->GetSafeHdc(), &lfw); // ★ 加上 Gdiplus:: |
| | | Gdiplus::StringFormat fmt; |
| | | fmt.SetAlignment(Gdiplus::StringAlignmentCenter); |
| | | fmt.SetLineAlignment(Gdiplus::StringAlignmentCenter); |
| | | Gdiplus::Color col(140, 120, 100, 60); // 半透明 |
| | | Gdiplus::Color col(140, 120, 100, 60); // 半透明 |
| | | Gdiplus::SolidBrush brush(col); |
| | | Gdiplus::RectF box((Gdiplus::REAL)rCol.left, (Gdiplus::REAL)rCol.top, |
| | | (Gdiplus::REAL)rCol.Width(), (Gdiplus::REAL)rCol.Height()); |
| | |
| | | } |
| | | } |
| | | |
| | | // === 客户区内 1px 灰色边框(不包滚动条,但不会缺角/抢绘制)=== |
| | | // === 客户区内 1px 灰色边框(不包滚动条,但不会缺角/抢绘制)=== |
| | | { |
| | | CRect cli; GetClientRect(&cli); |
| | | |
| | | // 用 FrameRect 更稳(避免右下角丢线) |
| | | // 用 FrameRect 更稳(避免右下角丢线) |
| | | CBrush br; br.CreateSolidBrush(::GetSysColor(COLOR_3DSHADOW)); |
| | | CRect r = cli; |
| | | // 注意:客户区坐标是 [0..width, 0..height];FrameRect 会在内侧画 1px |
| | | // 注意:客户区坐标是 [0..width, 0..height];FrameRect 会在内侧画 1px |
| | | pDC->FrameRect(&r, &br); |
| | | br.DeleteObject(); |
| | | } |
| | |
| | | |
| | | void CCarrierSlotGrid::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pBar) |
| | | { |
| | | if (m_noScrollbars) return; // ← 新增 |
| | | |
| | | UNREFERENCED_PARAMETER(pBar); |
| | | |
| | | SCROLLINFO si = { sizeof(SCROLLINFO) }; |
| | |
| | | case SB_PAGERIGHT: pos += (int)si.nPage; break; |
| | | case SB_THUMBTRACK: |
| | | case SB_THUMBPOSITION: |
| | | pos = (int)si.nTrackPos; // ★ 32 位拖动位置 |
| | | pos = (int)si.nTrackPos; // ★ 32 位拖动位置 |
| | | break; |
| | | default: |
| | | return; |
| | |
| | | |
| | | void CCarrierSlotGrid::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pBar) |
| | | { |
| | | if (m_noScrollbars) return; // ← 新增 |
| | | |
| | | UNREFERENCED_PARAMETER(pBar); |
| | | |
| | | SCROLLINFO si = { sizeof(SCROLLINFO) }; |
| | |
| | | case SB_PAGEDOWN: pos += (int)si.nPage; break; |
| | | case SB_THUMBTRACK: |
| | | case SB_THUMBPOSITION: |
| | | pos = (int)si.nTrackPos; // ★ 32 位拖动位置 |
| | | pos = (int)si.nTrackPos; // ★ 32 位拖动位置 |
| | | break; |
| | | default: |
| | | return; |
| | |
| | | |
| | | BOOL CCarrierSlotGrid::OnMouseWheel(UINT, short zDelta, CPoint) |
| | | { |
| | | if (m_noScrollbars) return FALSE; // ← 新增:彻底不滚 |
| | | |
| | | int delta = (zDelta > 0 ? -1 : +1) * (m_rowHeight * 3); |
| | | m_scrollY = max(0, m_scrollY + delta); |
| | | UpdateScrollRange(); |
| | |
| | | |
| | | void CCarrierSlotGrid::OnLButtonDown(UINT nFlags, CPoint pt) |
| | | { |
| | | // 是否拖动列宽 |
| | | int edge = m_bAllowResize ? HitHeaderEdge(pt) : -1; // ← 修改 |
| | | // 是否拖动列宽 |
| | | int edge = m_bAllowResize ? HitHeaderEdge(pt) : -1; // ← 修改 |
| | | if (edge >= 0) |
| | | { |
| | | m_bResizing = true; |
| | |
| | | return; |
| | | } |
| | | |
| | | // Header 点击(仅复选框区域) |
| | | // Header 点击(仅复选框区域) |
| | | if (GetHeaderRect().PtInRect(pt)) |
| | | { |
| | | for (int i = 1; i <= GetPortCount(); ++i) |
| | |
| | | return; |
| | | } |
| | | |
| | | // Cell 点击 |
| | | // Cell 点击 |
| | | CRect cli = GetClientRectNoSB(); |
| | | if (pt.y < cli.top + m_headerCY) return; |
| | | int yIn = pt.y - (cli.top + m_headerCY) + m_scrollY; |
| | |
| | | } |
| | | else |
| | | { |
| | | int idx = m_resizeEdge - 1; // 调整 Port idx 的宽度 |
| | | int idx = m_resizeEdge - 1; // 调整 Port idx 的宽度 |
| | | int nw = max(m_portColMin, m_portColCXsStart[idx] + dx); |
| | | if (nw != m_portColCXs[idx]) { m_portColCXs[idx] = nw; UpdateScrollRange(); Invalidate(FALSE); } |
| | | } |
| | |
| | | return CWnd::OnSetCursor(pWnd, nHitTest, message); |
| | | } |
| | | |
| | | // ---------- ֪ͨ ---------- |
| | | void CCarrierSlotGrid::NotifySelectionChanged(int /*port*/, int /*slot*/, BOOL /*checked*/) |
| | | void CCarrierSlotGrid::NotifySelectionChanged(int port, int slot, BOOL checked) |
| | | { |
| | | if (GetParent()) |
| | | { |
| | | // 兼容旧的 WM_COMMAND(可留,也可注释掉) |
| | | if (GetParent()) { |
| | | const int code = 0x2001; |
| | | GetParent()->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), code), (LPARAM)m_hWnd); |
| | | } |
| | | // 新的 WM_NOTIFY,带上索引与状态 |
| | | if (GetParent()) { |
| | | CSG_SEL_CHANGE nm{}; |
| | | nm.hdr.hwndFrom = m_hWnd; |
| | | nm.hdr.idFrom = (UINT)GetDlgCtrlID(); |
| | | nm.hdr.code = CSGN_SEL_CHANGED; |
| | | nm.port = port; |
| | | nm.slot = slot; |
| | | nm.checked = checked; |
| | | GetParent()->SendMessage(WM_NOTIFY, nm.hdr.idFrom, (LPARAM)&nm); |
| | | } |
| | | } |
| | | void CCarrierSlotGrid::NotifyMaterialChanged(int /*port*/, int /*slot*/, int /*material*/) |
| | | |
| | | void CCarrierSlotGrid::NotifyMaterialChanged(int port, int slot, int material) |
| | | { |
| | | if (GetParent()) |
| | | { |
| | | if (GetParent()) { |
| | | const int code = 0x2002; |
| | | GetParent()->SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), code), (LPARAM)m_hWnd); |
| | | } |
| | | if (GetParent()) { |
| | | CSG_MAT_CHANGE nm{}; |
| | | nm.hdr.hwndFrom = m_hWnd; |
| | | nm.hdr.idFrom = (UINT)GetDlgCtrlID(); |
| | | nm.hdr.code = CSGN_MAT_CHANGED; |
| | | nm.port = port; |
| | | nm.slot = slot; |
| | | nm.material = material; |
| | | GetParent()->SendMessage(WM_NOTIFY, nm.hdr.idFrom, (LPARAM)&nm); |
| | | } |
| | | } |
| | | |