数字涂色项目Undo/Redo功能异常:undoneActions栈问题排查
撤销重做功能BUG排查:仅能恢复最后一次撤销的操作
我在数字涂色项目中实现了撤销(UndoLastPaint)和重做(MoveForward)功能,用来在用户误涂时恢复界面状态。目前撤销功能正常,但重做功能只能恢复最后一次撤销的方块,哪怕连续执行了多次撤销也没用。怀疑是undoneActions栈的问题,求排查帮助。
相关代码
void UndoLastPaint(HWND hWnd) { if (!paintedCoordinates.empty()) { // Retrieve the coordinates of the last painted square int x = paintedCoordinates.back().first; int y = paintedCoordinates.back().second; // Check if the square was previously painted bool wasPainted = false; COLORREF color = RGB(255, 255, 255); // Default color if the square was not painted for (int i = paintedCoordinates.size() - 2; i >= 0; --i) { if (paintedCoordinates[i].first == x && paintedCoordinates[i].second == y) { wasPainted = true; color = paintedColors[i]; // Store color from the loop // Store the undone action PaintAction undoneAction; undoneAction.x = x; undoneAction.y = y; undoneAction.color = color; undoneActions.push(undoneAction); break; } } if (wasPainted) { // Restore the previous color of the square RECT squareRect = { x * squareSize, y * squareSize, (x + 1) * squareSize, (y + 1) * squareSize }; HDC hdc = GetDC(hWnd); HBRUSH brush = CreateSolidBrush(color); FillRect(hdc, &squareRect, brush); ReleaseDC(hWnd, hdc); } else { // Calculate the rectangle occupied by the last painted square RECT squareRect = { x * squareSize, y * squareSize, (x + 1) * squareSize, (y + 1) * squareSize }; // Remove the last painted square from the screen InvalidateRect(hWnd, &squareRect, TRUE); UpdateWindow(hWnd); } // Remove the last painted square from the data collection paintedCoordinates.pop_back(); paintedColors.pop_back(); } } void MoveForward(HWND hWnd) { if (!undoneActions.empty()) { // Retrieve the coordinates and color of the last undone action int x = undoneActions.top().x; int y = undoneActions.top().y; COLORREF color = undoneActions.top().color; // Remove the next undone action from the stack undoneActions.pop(); // Paint the square at the next undone action PaintSquare(hWnd, x, y); // Store the repainted square in the data collection paintedCoordinates.push_back({ x, y }); paintedColors.push_back(color); } }
问题根源
你的UndoLastPaint函数只在方块之前被涂过色(wasPainted = true)的场景下,才会往undoneActions栈添加记录。但如果是第一次涂空白方块(wasPainted = false),这步撤销完全没有生成对应的重做记录,导致连续撤销时,这类操作不会被存入栈,自然无法被重做。
另外,当前记录到undoneActions里的颜色是恢复后的颜色,而不是撤销前的涂色,这会导致重做时涂错颜色。
修复方案
1. 修改UndoLastPaint函数,确保所有撤销动作都被记录
void UndoLastPaint(HWND hWnd) { if (!paintedCoordinates.empty()) { // 获取最后一次涂色的坐标和涂上去的颜色 int x = paintedCoordinates.back().first; int y = paintedCoordinates.back().second; COLORREF undoBeforeColor = paintedColors.back(); bool wasPainted = false; COLORREF restoreColor = RGB(255, 255, 255); // 默认恢复为白色 for (int i = paintedCoordinates.size() - 2; i >= 0; --i) { if (paintedCoordinates[i].first == x && paintedCoordinates[i].second == y) { wasPainted = true; restoreColor = paintedColors[i]; break; } } // 不管之前有没有涂色,都记录撤销动作——要重做的是撤销前的颜色(undoBeforeColor) PaintAction undoneAction; undoneAction.x = x; undoneAction.y = y; undoneAction.color = undoBeforeColor; undoneActions.push(undoneAction); if (wasPainted) { // 恢复之前的颜色 RECT squareRect = { x * squareSize, y * squareSize, (x + 1) * squareSize, (y + 1) * squareSize }; HDC hdc = GetDC(hWnd); HBRUSH brush = CreateSolidBrush(restoreColor); FillRect(hdc, &squareRect, brush); DeleteObject(brush); // 必须删除GDI对象,避免内存泄漏 ReleaseDC(hWnd, hdc); } else { // 恢复空白 RECT squareRect = { x * squareSize, y * squareSize, (x + 1) * squareSize, (y + 1) * squareSize }; InvalidateRect(hWnd, &squareRect, TRUE); UpdateWindow(hWnd); } paintedCoordinates.pop_back(); paintedColors.pop_back(); } }
2. 确保MoveForward使用正确的颜色涂色
检查PaintSquare函数,如果它是用当前选中的颜色而不是传入的参数,需要修改函数定义和调用:
// 修改PaintSquare,支持传入颜色参数 void PaintSquare(HWND hWnd, int x, int y, COLORREF color) { RECT squareRect = { x * squareSize, y * squareSize, (x + 1) * squareSize, (y + 1) * squareSize }; HDC hdc = GetDC(hWnd); HBRUSH brush = CreateSolidBrush(color); FillRect(hdc, &squareRect, brush); DeleteObject(brush); ReleaseDC(hWnd, hdc); } // 修改MoveForward里的调用 void MoveForward(HWND hWnd) { if (!undoneActions.empty()) { int x = undoneActions.top().x; int y = undoneActions.top().y; COLORREF color = undoneActions.top().color; undoneActions.pop(); PaintSquare(hWnd, x, y, color); // 传入记录的颜色 paintedCoordinates.push_back({ x, y }); paintedColors.push_back(color); } }
3. 额外修复:GDI资源泄漏
原代码中创建HBRUSH后没有调用DeleteObject,会导致GDI资源耗尽,修复后的代码已经补上了这一步。
内容的提问来源于stack exchange,提问作者tetiana.mlk
相关产品推荐
相关产品推荐

