C++實(shí)現(xiàn)自定義撤銷(xiāo)重做功能的示例代碼
前言
在使用c++做界面開(kāi)發(fā)的時(shí)候,需要涉及到到撤銷(xiāo)重做操作,尤其是實(shí)現(xiàn)白板功能時(shí)需要自己實(shí)現(xiàn)一套撤銷(xiāo)重做功能,如果是qt則有QUndoable對(duì)象,可以直接拿來(lái)用。但是如果是使用gdi繪圖,則可能需要自己實(shí)現(xiàn)了。
一、完整代碼
由于需要的功能相對(duì)簡(jiǎn)單,這里就不做原理以及接口設(shè)計(jì)思路說(shuō)明了,直接展示完整代碼。
Undoable.h
#ifndef AC_UNDOABLE_H #define AC_UNDOABLE_H #include<functional> #include<vector> namespace AC { /// <summary> /// 撤銷(xiāo)重做管理對(duì)象 /// 最少行數(shù)實(shí)現(xiàn),采用vector+下標(biāo)浮動(dòng)實(shí)現(xiàn),性能也是很好的。 /// ceate by xin 2022.7.5 /// </summary> class Undoable { public: /// <summary> /// 撤銷(xiāo) /// </summary> void Undo(); /// <summary> /// 重做 /// </summary> void Redo(); /// <summary> /// 清除 /// </summary> void Clear(); /// <summary> /// 添加操作 /// </summary> /// <param name="undo">撤銷(xiāo)</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、定義撤銷(xiāo)重做管理對(duì)象 AC::Undoable undoable; //2、添加操作 undoable.Add( [=]() { //撤銷(xiāo)邏輯 }, [=]() { //重做邏輯 } ); //3、撤銷(xiāo)重做 //執(zhí)行撤銷(xiāo) undoable.Undo(); //執(zhí)行重做 undoable.Redo();
2、gdi畫(huà)線(xiàn)撤銷(xiāo)
#include<Windows.h> #include<Windowsx.h> #include<vector> #include "Undoable.h" //筆畫(huà)集合 std::vector<std::vector<POINT>*> strokes; //窗口上下文 HDC hdc; int xPos = 0; int yPos = 0; //撤銷(xiāo)重做管理對(duì)象 AC::Undoable undoable; //窗口過(guò)程 static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_LBUTTONDOWN: //記錄筆畫(huà)起點(diǎn) 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) //繪制筆畫(huà) { xPos = GET_X_LPARAM(lParam); yPos = GET_Y_LPARAM(lParam); LineTo(hdc, xPos, yPos); strokes.back()->push_back({ xPos ,yPos }); } break; case WM_LBUTTONUP: //記錄筆畫(huà)撤銷(xiāo)重做 { auto currentStroke = strokes.back(); //補(bǔ)全一個(gè)點(diǎn) if (strokes.back()->size()==1) { xPos = GET_X_LPARAM(lParam); yPos = GET_Y_LPARAM(lParam); LineTo(hdc, xPos, yPos); } //記錄操作 undoable.Add( [=]() { //撤銷(xiāo)邏輯 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: { //重繪筆畫(huà) 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í)行撤銷(xiāo) 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"重做撤銷(xiāo)", 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)建一支紅色的畫(huà)筆 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); } } //銷(xiāo)毀資源 SelectObject(hdc, oldPen); DeleteObject(oldPen); ReleaseDC(hwnd, hdc); DestroyWindow(hwnd); }
效果預(yù)覽
總結(jié)
以上就是今天要講的內(nèi)容,本文展示的撤銷(xiāo)重做管理對(duì)象包含的功能可以滿(mǎn)足大部分使用場(chǎng)景,而且由于實(shí)現(xiàn)非常簡(jiǎn)潔,很容易修改和拓展。這個(gè)對(duì)象經(jīng)過(guò)筆者多個(gè)項(xiàng)目修改演變而成,最初的實(shí)現(xiàn)是使用兩個(gè)棧來(lái)記錄操作,其實(shí)并不是最優(yōu)的,變長(zhǎng)數(shù)組加下標(biāo)記錄應(yīng)該是更好的方式。
到此這篇關(guān)于C++實(shí)現(xiàn)自定義撤銷(xiāo)重做功能的示例代碼的文章就介紹到這了,更多相關(guān)C++撤銷(xiāo)重做功能內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言實(shí)現(xiàn)洗牌與發(fā)牌游戲
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言洗牌與發(fā)牌游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-12-12C語(yǔ)言 循環(huán)詳解及簡(jiǎn)單代碼示例
本文主要介紹C語(yǔ)言的循環(huán)知識(shí),這里整理了循環(huán)的基礎(chǔ)資料并附簡(jiǎn)單的代碼示例詳細(xì)講解,有需要的小伙伴可以參考下2016-08-08學(xué)生成績(jī)管理系統(tǒng)C++實(shí)現(xiàn)代碼
這篇文章主要為大家詳細(xì)介紹了學(xué)生成績(jī)管理系統(tǒng)C++實(shí)現(xiàn)代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-12-12C語(yǔ)言實(shí)現(xiàn)電話(huà)訂餐管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)電話(huà)訂餐管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01C語(yǔ)言巧用二分查找實(shí)現(xiàn)猜數(shù)游戲
二分查找也稱(chēng)折半查找(Binary?Search),它是一種效率較高的查找方法。但是,折半查找要求線(xiàn)性表必須采用順序存儲(chǔ)結(jié)構(gòu),而且表中元素按關(guān)鍵字有序排列,本篇文章教你用二分查找編寫(xiě)猜數(shù)字游戲2022-02-02C語(yǔ)言實(shí)現(xiàn)空戰(zhàn)游戲
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)空戰(zhàn)游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05FFmpeg實(shí)戰(zhàn)之分離出PCM數(shù)據(jù)
PCM(Pulse?Code?Modulation,脈沖編碼調(diào)制)音頻數(shù)據(jù)是未經(jīng)壓縮的音頻采樣數(shù)據(jù)裸流,它是由模擬信號(hào)經(jīng)過(guò)采樣、量化、編碼轉(zhuǎn)換成的標(biāo)準(zhǔn)數(shù)字音頻數(shù)據(jù)。本文將通過(guò)FFmpeg實(shí)現(xiàn)分離PCM數(shù)據(jù),感興趣的可以了解一下2023-02-02