C++實現(xiàn)自定義撤銷重做功能的示例代碼
前言
在使用c++做界面開發(fā)的時候,需要涉及到到撤銷重做操作,尤其是實現(xiàn)白板功能時需要自己實現(xiàn)一套撤銷重做功能,如果是qt則有QUndoable對象,可以直接拿來用。但是如果是使用gdi繪圖,則可能需要自己實現(xiàn)了。
一、完整代碼
由于需要的功能相對簡單,這里就不做原理以及接口設(shè)計思路說明了,直接展示完整代碼。
Undoable.h
#ifndef AC_UNDOABLE_H #define AC_UNDOABLE_H #include<functional> #include<vector> namespace AC { /// <summary> /// 撤銷重做管理對象 /// 最少行數(shù)實現(xiàn),采用vector+下標(biāo)浮動實現(xiàn),性能也是很好的。 /// ceate by xin 2022.7.5 /// </summary> class Undoable { public: /// <summary> /// 撤銷 /// </summary> void Undo(); /// <summary> /// 重做 /// </summary> void Redo(); /// <summary> /// 清除 /// </summary> void Clear(); /// <summary> /// 添加操作 /// </summary> /// <param name="undo">撤銷</param> /// <param name="redo">重做</param> void Add(std::function<void()> undo, std::function<void()> redo); private: //記錄的步驟 std::vector<std::pair<std::function<void()>, std::function<void()>>> _steps; //當(dāng)前操作的下標(biāo) int _index = -1; }; } #endif
Undoable.cpp
#include "Undoable.h" namespace AC { void Undoable::Undo(){ if (_index > -1) _steps[_index--].first(); } void Undoable::Redo(){ if (_index < (int)_steps.size() - 1) _steps[++_index].second(); } void Undoable::Clear(){ _steps.clear(); _index = -1; } void Undoable::Add(std::function<void()> undo, std::function<void()> redo){ if (++_index < (int)_steps.size()) _steps.resize(_index); _steps.push_back({ undo ,redo }); } }
二、使用示例
1、基本用法
//1、定義撤銷重做管理對象 AC::Undoable undoable; //2、添加操作 undoable.Add( [=]() { //撤銷邏輯 }, [=]() { //重做邏輯 } ); //3、撤銷重做 //執(zhí)行撤銷 undoable.Undo(); //執(zhí)行重做 undoable.Redo();
2、gdi畫線撤銷
#include<Windows.h> #include<Windowsx.h> #include<vector> #include "Undoable.h" //筆畫集合 std::vector<std::vector<POINT>*> strokes; //窗口上下文 HDC hdc; int xPos = 0; int yPos = 0; //撤銷重做管理對象 AC::Undoable undoable; //窗口過程 static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_LBUTTONDOWN: //記錄筆畫起點 xPos = GET_X_LPARAM(lParam); yPos = GET_Y_LPARAM(lParam); strokes.push_back(new std::vector<POINT>()); strokes.back()->push_back({ xPos ,yPos }); MoveToEx(hdc, xPos, yPos, NULL); break; case WM_MOUSEMOVE: if ((wParam & MK_LBUTTON) != 0) //繪制筆畫 { xPos = GET_X_LPARAM(lParam); yPos = GET_Y_LPARAM(lParam); LineTo(hdc, xPos, yPos); strokes.back()->push_back({ xPos ,yPos }); } break; case WM_LBUTTONUP: //記錄筆畫撤銷重做 { auto currentStroke = strokes.back(); //補全一個點 if (strokes.back()->size()==1) { xPos = GET_X_LPARAM(lParam); yPos = GET_Y_LPARAM(lParam); LineTo(hdc, xPos, yPos); } //記錄操作 undoable.Add( [=]() { //撤銷邏輯 strokes.pop_back(); RECT rect; GetClientRect(hwnd, &rect); InvalidateRect(hwnd, &rect, 0); }, [=]() { //重做邏輯 strokes.push_back(currentStroke); RECT rect; GetClientRect(hwnd, &rect); InvalidateRect(hwnd, &rect, 0); } ); } break; case WM_PAINT: { //重繪筆畫 RECT rect; GetClientRect(hwnd, &rect); FillRect(hdc, &rect, (HBRUSH)GetStockObject(WHITE_BRUSH)); for (auto i : strokes) { MoveToEx(hdc, i->front().x, i->front().y, NULL); for (auto j : *i) { LineTo(hdc, j.x, j.y); } } } break; case WM_KEYDOWN: //響應(yīng)消息 if (wParam == 'Z') { //執(zhí)行撤銷 undoable.Undo(); } if (wParam == 'Y') { //執(zhí)行重做 undoable.Redo(); } break; case WM_DESTROY: PostQuitMessage(0); break; } return DefWindowProc(hwnd, msg, wParam, lParam); } void main() { //創(chuàng)建win32窗口 WNDCLASS wndclass; wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = GetModuleHandle(NULL); wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName = 0; wndclass.lpszClassName = L"Win32Window"; if (!RegisterClass(&wndclass)) { MessageBox(NULL, TEXT("創(chuàng)建窗口失敗!"), L"", MB_ICONERROR); } auto hwnd = CreateWindowW( L"Win32Window", L"重做撤銷", WS_OVERLAPPEDWINDOW, 0, 0, 640, 360, NULL, NULL, GetModuleHandle(NULL), NULL ); //顯示窗口 ShowWindow(hwnd, SW_SHOW); //獲取窗口上下文 hdc = GetDC(hwnd); HPEN pen = ::CreatePen(PS_SOLID, 9, RGB(255, 0, 0));//創(chuàng)建一支紅色的畫筆 auto oldPen = SelectObject(hdc, pen); MSG msg; HACCEL hAccelTable = NULL; // 主消息循環(huán): while (GetMessage(&msg, NULL, 0, 0)) { switch (msg.message) { case WM_PAINT: break; case WM_DESTROY: PostQuitMessage(0); break; } if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } //銷毀資源 SelectObject(hdc, oldPen); DeleteObject(oldPen); ReleaseDC(hwnd, hdc); DestroyWindow(hwnd); }
效果預(yù)覽
總結(jié)
以上就是今天要講的內(nèi)容,本文展示的撤銷重做管理對象包含的功能可以滿足大部分使用場景,而且由于實現(xiàn)非常簡潔,很容易修改和拓展。這個對象經(jīng)過筆者多個項目修改演變而成,最初的實現(xiàn)是使用兩個棧來記錄操作,其實并不是最優(yōu)的,變長數(shù)組加下標(biāo)記錄應(yīng)該是更好的方式。
到此這篇關(guān)于C++實現(xiàn)自定義撤銷重做功能的示例代碼的文章就介紹到這了,更多相關(guān)C++撤銷重做功能內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
學(xué)生成績管理系統(tǒng)C++實現(xiàn)代碼
這篇文章主要為大家詳細介紹了學(xué)生成績管理系統(tǒng)C++實現(xiàn)代碼,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-12-12FFmpeg實戰(zhàn)之分離出PCM數(shù)據(jù)
PCM(Pulse?Code?Modulation,脈沖編碼調(diào)制)音頻數(shù)據(jù)是未經(jīng)壓縮的音頻采樣數(shù)據(jù)裸流,它是由模擬信號經(jīng)過采樣、量化、編碼轉(zhuǎn)換成的標(biāo)準(zhǔn)數(shù)字音頻數(shù)據(jù)。本文將通過FFmpeg實現(xiàn)分離PCM數(shù)據(jù),感興趣的可以了解一下2023-02-02