chenluhua1980
2025-11-14 22c4fd7bb3ef1d7c6bfc67baf79aed53d22d8636
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
#include "stdafx.h"
#include "JobSlotGrid.h"
#include "ServoMemDC.h"
 
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
 
IMPLEMENT_DYNAMIC(CJobSlotGrid, CWnd)
 
BEGIN_MESSAGE_MAP(CJobSlotGrid, CWnd)
    ON_WM_PAINT()
    ON_WM_ERASEBKGND()
    ON_WM_MOUSEMOVE()
    ON_WM_MOUSELEAVE()
    ON_WM_LBUTTONDOWN()
    ON_WM_LBUTTONUP()
END_MESSAGE_MAP()
 
CJobSlotGrid::CJobSlotGrid() {
    // 初始化默认行列数
    int m_nRows = 12;
    int m_nCols = 16;
 
    // 初始化默认字体
    m_fontText.CreatePointFont(60, _T("Arial"));
 
    // 初始化默认颜色
    COLORREF m_colorHasJob = RGB(0, 200, 0);      // 默认绿色
    COLORREF m_colorNoJob = RGB(220, 220, 220);   // 默认灰色
 
    // 初始化默认画刷
    m_brushHasJob.CreateSolidBrush(m_colorHasJob);
    m_brushNoJob.CreateSolidBrush(m_colorNoJob);
}
 
CJobSlotGrid::~CJobSlotGrid() {
    if (m_fontText.GetSafeHandle()) {
        m_fontText.DeleteObject();
    }
 
    if (m_brushHasJob.GetSafeHandle()) {
        m_brushHasJob.DeleteObject();
    }
 
    if (m_brushNoJob.GetSafeHandle()) {
        m_brushNoJob.DeleteObject();
    }
}
 
void CJobSlotGrid::SetGridSize(int nRows, int nCols)
{
    m_nRows = nRows;
    m_nCols = nCols;
    m_vSlotStatus.assign(nRows, std::vector<bool>(nCols, false));
    m_vSlotClickable.assign(nRows, std::vector<bool>(nCols, false));
 
    // 初始化文本数组
    m_vSlotText.assign(nRows, std::vector<CString>(nCols));
    for (int i = 0; i < nRows; ++i) {
        for (int j = 0; j < nCols; ++j) {
            if (i == 0) {
                m_vSlotText[i][j].Format(_T("%d"), j + 1);
            }
            else if (j == 0) {
                m_vSlotText[i][j].Format(_T("%d"), i + 1);
            }
        }
    }
 
    Invalidate();
}
 
void CJobSlotGrid::SetColors(COLORREF colorHasJob, COLORREF colorNoJob)
{
    m_colorHasJob = colorHasJob;
    m_colorNoJob = colorNoJob;
 
    if (m_brushHasJob.GetSafeHandle()) {
        m_brushHasJob.DeleteObject();
    }
 
    if (m_brushNoJob.GetSafeHandle()) { 
        m_brushNoJob.DeleteObject();
    }
 
    m_brushHasJob.CreateSolidBrush(m_colorHasJob);
    m_brushNoJob.CreateSolidBrush(m_colorNoJob);
 
 
    Invalidate();
}
 
void CJobSlotGrid::SetSlotStatus(int nRow, int nCol, bool bHasJob, BOOL bInvalidate/* = TRUE*/)
{
    if (nRow >= 0 && nRow < m_nRows && nCol >= 0 && nCol < m_nCols) {
        m_vSlotStatus[nRow][nCol] = bHasJob;
        if (bInvalidate) {
            Invalidate();
        }
    }
}
 
void CJobSlotGrid::SetSlotText(int nRow, int nCol, const CString& strText)
{
    if (nRow >= 0 && nRow < m_nRows && nCol >= 0 && nCol < m_nCols) {
        m_vSlotText[nRow][nCol] = strText;
        Invalidate();
    }
}
 
void CJobSlotGrid::SetTextFont(const CString& strFontName, int nPointSize)
{
    // 删除旧字体
    if (m_fontText.GetSafeHandle()) {
        m_fontText.DeleteObject();
    }
 
    // CreatePointFont expects size in 1/10 pt
    m_fontText.CreatePointFont(nPointSize * 10, strFontName);
 
    Invalidate();
}
 
void CJobSlotGrid::ClearAll()
{
    if (m_vSlotStatus.empty()) {
        return;
    }
 
    for (int i = 0; i < m_nRows; ++i) {
        if (i < (int)m_vSlotStatus.size()) {
            std::fill(m_vSlotStatus[i].begin(), m_vSlotStatus[i].end(), false);
        }
    }
    Invalidate();
}
 
void CJobSlotGrid::SetSlotClickable(int nRow, int nCol, bool bClickable)
{
    if (nRow >= 0 && nRow < m_nRows && nCol >= 0 && nCol < m_nCols) {
        m_vSlotClickable[nRow][nCol] = bClickable;
    }
}
 
bool CJobSlotGrid::IsSlotClickable(int nRow, int nCol) const
{
    if (nRow >= 0 && nRow < m_nRows && nCol >= 0 && nCol < m_nCols) {
        return m_vSlotClickable[nRow][nCol];
    }
    return false;
}
 
void CJobSlotGrid::SetSlotClickCallback(SlotClickCallback fnCallback)
{
    m_fnSlotClickCallback = fnCallback;
}
 
BOOL CJobSlotGrid::OnEraseBkgnd(CDC* pDC) {
    return TRUE;
}
 
void CJobSlotGrid::OnMouseMove(UINT nFlags, CPoint point)
{
    TRACKMOUSEEVENT tme = { sizeof(TRACKMOUSEEVENT), TME_LEAVE, m_hWnd };
    ::TrackMouseEvent(&tme);
 
    CRect rect;
    GetClientRect(&rect);
    int nCellWidth = rect.Width() / m_nCols;
    int nCellHeight = rect.Height() / m_nRows;
 
    int nCol = point.x / nCellWidth;
    int nRow = point.y / nCellHeight;
 
    if (nRow != m_ptHover.y || nCol != m_ptHover.x) {
        m_ptHover = CPoint(nCol, nRow);
        Invalidate();
    }
 
    CWnd::OnMouseMove(nFlags, point);
}
 
void CJobSlotGrid::OnMouseLeave()
{
    m_ptHover = CPoint(-1, -1);
    Invalidate();
 
    CWnd::OnMouseLeave();
}
 
void CJobSlotGrid::OnLButtonDown(UINT nFlags, CPoint point)
{
    m_bLButtonDown = true;
    Invalidate();
 
    CWnd::OnLButtonDown(nFlags, point);
}
 
void CJobSlotGrid::OnLButtonUp(UINT nFlags, CPoint point)
{
    m_bLButtonDown = false;
    Invalidate();
 
    // 保持原有逻辑不变
    CRect rect;
    GetClientRect(&rect);
    int nCellWidth = rect.Width() / m_nCols;
    int nCellHeight = rect.Height() / m_nRows;
 
    int nCol = point.x / nCellWidth;
    int nRow = point.y / nCellHeight;
 
    if (IsSlotClickable(nRow, nCol)) {
        if (m_fnSlotClickCallback) {
            m_fnSlotClickCallback(nRow, nCol);
        }
    }
 
    CWnd::OnLButtonUp(nFlags, point);
}
 
void CJobSlotGrid::OnPaint() {
    CPaintDC dc(this);
    CServoMemDC memDC(&dc);
    DrawGrid(&memDC);
}
 
void CJobSlotGrid::DrawGrid(CDC* pDC)
{
    CRect rect;
    GetClientRect(&rect);
    pDC->FillSolidRect(&rect, ::GetSysColor(COLOR_3DFACE)); // 安全背景色,仅内存绘制
 
    if (m_nCols == 0 || m_nRows == 0) {
        return;
    }
 
    // 计算格子尺寸
    int nCellWidth = rect.Width() / m_nCols;
    int nCellHeight = rect.Height() / m_nRows;
 
    // 字体设置
    CFont* pOldFont = pDC->SelectObject(&m_fontText);
    pDC->SetBkMode(TRANSPARENT);
    pDC->SetTextColor(RGB(0, 0, 0));
 
    // 定义颜色常量
    constexpr COLORREF COLOR_HOVER = RGB(200, 230, 255);
    constexpr COLORREF COLOR_CLICK = RGB(0, 120, 215);
 
    for (int i = 0; i < m_nRows; ++i) {
        for (int j = 0; j < m_nCols; ++j) {
            CRect cellRect(j * nCellWidth, i * nCellHeight, (j + 1) * nCellWidth, (i + 1) * nCellHeight);
 
            // 判断状态:悬停 / 按下
            bool bIsHover = (m_ptHover.x == j && m_ptHover.y == i);
            bool bIsClicking = bIsHover && m_bLButtonDown;
 
            // 选择填充颜色
            COLORREF fillColor = m_vSlotStatus[i][j] ? m_colorHasJob : m_colorNoJob;
            if (IsSlotClickable(i, j)) {
                if (bIsClicking) {
                    fillColor = COLOR_CLICK;
                }
                else if (bIsHover) {
                    fillColor = COLOR_HOVER;
                }
            }
 
            // 绘制背景(高效替代 CBrush)
            pDC->FillSolidRect(&cellRect, fillColor);
 
            // 绘制边框
            pDC->DrawEdge(&cellRect, EDGE_SUNKEN, BF_RECT);
 
            // 如果是可点击格子,在左上角画一个小圆点
            if (IsSlotClickable(i, j)) {
                constexpr int DOT_RADIUS = 3;
                int cx = cellRect.left + 5;
                int cy = cellRect.top + 5;
 
                // 保存旧笔和刷子
                CBrush brushDot(RGB(0, 120, 215));
                CBrush* pOldBrush = pDC->SelectObject(&brushDot);
                CPen penDot(PS_SOLID, 1, RGB(0, 120, 215));
                CPen* pOldPen = pDC->SelectObject(&penDot);
 
                // 绘制圆点
                pDC->Ellipse(cx - DOT_RADIUS, cy - DOT_RADIUS, cx + DOT_RADIUS, cy + DOT_RADIUS);
 
                // 恢复
                pDC->SelectObject(pOldBrush);
                pDC->SelectObject(pOldPen);
            }
 
            // 获取文字(安全)
            CString strText;
            if (i < m_vSlotText.size() && j < m_vSlotText[i].size()) {
                strText = m_vSlotText[i][j];
            }
 
            if (!strText.IsEmpty()) {
                // 先计算文字高度(支持换行)
                CRect calcRect = cellRect;
                pDC->DrawText(strText, &calcRect, DT_CENTER | DT_WORDBREAK | DT_NOPREFIX | DT_CALCRECT);
 
                // 重新设定居中绘制区域
                CRect textRect = cellRect;
                textRect.top += (cellRect.Height() - calcRect.Height()) / 2;
 
                // 实际绘制文字
                pDC->DrawText(strText, &textRect, DT_CENTER | DT_WORDBREAK | DT_NOPREFIX);
            }
        }
    }
 
    pDC->SelectObject(pOldFont);
}