1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-22 22:13:35 +02:00

First draft of undo/redo. Can move objects and undo new object placement.

Signed-off-by: Tomasz Zieliński <tomaszzielinskijedenzwielu@gmail.com>
This commit is contained in:
Tomasz Zieliński 2022-09-05 18:27:09 +02:00
parent d1471fb3ed
commit 81ec08b55e
7 changed files with 152 additions and 14 deletions

View File

@ -155,14 +155,17 @@ MapRect CMapOperation::extendTileAroundSafely(const int3 & centerPos) const
} }
CMapUndoManager::CMapUndoManager() : undoRedoLimit(10) CMapUndoManager::CMapUndoManager() :
undoRedoLimit(10),
undoCallback([](bool, bool) {})
{ {
//TODO: unlimited undo
} }
void CMapUndoManager::undo() void CMapUndoManager::undo()
{ {
doOperation(undoStack, redoStack, true); doOperation(undoStack, redoStack, true);
} }
void CMapUndoManager::redo() void CMapUndoManager::redo()
@ -174,6 +177,7 @@ void CMapUndoManager::clearAll()
{ {
undoStack.clear(); undoStack.clear();
redoStack.clear(); redoStack.clear();
onUndoRedo();
} }
int CMapUndoManager::getUndoRedoLimit() const int CMapUndoManager::getUndoRedoLimit() const
@ -219,6 +223,7 @@ void CMapUndoManager::doOperation(TStack & fromStack, TStack & toStack, bool doU
} }
toStack.push_front(std::move(operation)); toStack.push_front(std::move(operation));
fromStack.pop_front(); fromStack.pop_front();
onUndoRedo();
} }
const CMapOperation * CMapUndoManager::peek(const TStack & stack) const const CMapOperation * CMapUndoManager::peek(const TStack & stack) const
@ -227,6 +232,18 @@ const CMapOperation * CMapUndoManager::peek(const TStack & stack) const
return stack.front().get(); return stack.front().get();
} }
void CMapUndoManager::onUndoRedo()
{
//true if there's anything on the stack
undoCallback((bool)peekUndo(), (bool)peekRedo());
}
void CMapUndoManager::setUndoCallback(std::function<void(bool, bool)> functor)
{
undoCallback = functor;
onUndoRedo(); //inform immediately
}
CMapEditManager::CMapEditManager(CMap * map) CMapEditManager::CMapEditManager(CMap * map)
: map(map), terrainSel(map), objectSel(map) : map(map), terrainSel(map), objectSel(map)
{ {
@ -322,6 +339,7 @@ void CComposedOperation::undo()
void CComposedOperation::redo() void CComposedOperation::redo()
{ {
//TODO: double-chekcif the order is correct
for(auto & operation : operations) for(auto & operation : operations)
{ {
operation->redo(); operation->redo();
@ -1070,7 +1088,7 @@ void CInsertObjectOperation::execute()
void CInsertObjectOperation::undo() void CInsertObjectOperation::undo()
{ {
//TODO map->removeObject(obj);
} }
void CInsertObjectOperation::redo() void CInsertObjectOperation::redo()
@ -1083,20 +1101,24 @@ std::string CInsertObjectOperation::getLabel() const
return "Insert Object"; return "Insert Object";
} }
CMoveObjectOperation::CMoveObjectOperation(CMap * map, CGObjectInstance * obj, const int3 & position) CMoveObjectOperation::CMoveObjectOperation(CMap * map, CGObjectInstance * obj, const int3 & targetPosition)
: CMapOperation(map), obj(obj), pos(position) : CMapOperation(map),
obj(obj),
initialPos(obj->pos),
targetPos(targetPosition)
{ {
} }
void CMoveObjectOperation::execute() void CMoveObjectOperation::execute()
{ {
map->moveObject(obj, pos); map->moveObject(obj, targetPos);
logGlobal->debug("Moved object %s to position %s", obj->instanceName, targetPos.toString());
} }
void CMoveObjectOperation::undo() void CMoveObjectOperation::undo()
{ {
//TODO map->moveObject(obj, initialPos);
logGlobal->debug("Moved object %s back to position %s", obj->instanceName, initialPos.toString());
} }
void CMoveObjectOperation::redo() void CMoveObjectOperation::redo()

View File

@ -138,6 +138,8 @@ public:
/// The undo redo limit is a number which says how many undo/redo items can be saved. The default /// The undo redo limit is a number which says how many undo/redo items can be saved. The default
/// value is 10. If the value is 0, no undo/redo history will be maintained. /// value is 10. If the value is 0, no undo/redo history will be maintained.
/// FIXME: unlimited undo please
int getUndoRedoLimit() const; int getUndoRedoLimit() const;
void setUndoRedoLimit(int value); void setUndoRedoLimit(int value);
@ -146,6 +148,9 @@ public:
void addOperation(std::unique_ptr<CMapOperation> && operation); /// Client code does not need to call this method. void addOperation(std::unique_ptr<CMapOperation> && operation); /// Client code does not need to call this method.
//poor man's signal
void setUndoCallback(std::function<void(bool, bool)> functor);
private: private:
typedef std::list<std::unique_ptr<CMapOperation> > TStack; typedef std::list<std::unique_ptr<CMapOperation> > TStack;
@ -155,6 +160,9 @@ private:
TStack undoStack; TStack undoStack;
TStack redoStack; TStack redoStack;
int undoRedoLimit; int undoRedoLimit;
void onUndoRedo();
std::function<void(bool allowUndo, bool allowRedo)> undoCallback;
}; };
/// The map edit manager provides functionality for drawing terrain and placing /// The map edit manager provides functionality for drawing terrain and placing
@ -425,7 +433,7 @@ private:
class CMoveObjectOperation : public CMapOperation class CMoveObjectOperation : public CMapOperation
{ {
public: public:
CMoveObjectOperation(CMap * map, CGObjectInstance * obj, const int3 & position); CMoveObjectOperation(CMap * map, CGObjectInstance * obj, const int3 & targetPosition);
void execute() override; void execute() override;
void undo() override; void undo() override;
@ -434,7 +442,8 @@ public:
private: private:
CGObjectInstance * obj; CGObjectInstance * obj;
int3 pos; int3 initialPos;
int3 targetPos;
}; };
/// The CRemoveObjectOperation class removes object from the map /// The CRemoveObjectOperation class removes object from the map

View File

@ -554,6 +554,28 @@ void MainWindow::on_actionLevel_triggered()
} }
} }
void MainWindow::on_actionUndo_triggered()
{
QString str("Undo clicked");
statusBar()->showMessage(str, 1000);
if (controller.map())
{
controller.undo();
}
}
void MainWindow::on_actionRedo_triggered()
{
QString str("Redo clicked");
statusBar()->showMessage(str, 1000);
if (controller.map())
{
controller.redo();
}
}
void MainWindow::on_actionPass_triggered(bool checked) void MainWindow::on_actionPass_triggered(bool checked)
{ {
if(controller.map()) if(controller.map())
@ -769,3 +791,12 @@ void MainWindow::on_actionPlayers_settings_triggered()
mapSettingsDialog->setModal(true); mapSettingsDialog->setModal(true);
} }
void MainWindow::enableUndo(bool enable)
{
ui->actionUndo->setEnabled(enable);
}
void MainWindow::enableRedo(bool enable)
{
ui->actionRedo->setEnabled(enable);
}

View File

@ -48,6 +48,10 @@ private slots:
void on_actionLevel_triggered(); void on_actionLevel_triggered();
void on_actionSave_triggered(); void on_actionSave_triggered();
void on_actionUndo_triggered();
void on_actionRedo_triggered();
void on_actionPass_triggered(bool checked); void on_actionPass_triggered(bool checked);
@ -84,6 +88,8 @@ public slots:
void treeViewSelected(const QModelIndex &selected, const QModelIndex &deselected); void treeViewSelected(const QModelIndex &selected, const QModelIndex &deselected);
void loadInspector(CGObjectInstance * obj); void loadInspector(CGObjectInstance * obj);
void mapChanged(); void mapChanged();
void enableUndo(bool enable);
void enableRedo(bool enable);
private: private:
void preparePreview(const QModelIndex &index, bool createNew); void preparePreview(const QModelIndex &index, bool createNew);

View File

@ -51,7 +51,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1024</width> <width>1024</width>
<height>22</height> <height>18</height>
</rect> </rect>
</property> </property>
<widget class="QMenu" name="menuFile"> <widget class="QMenu" name="menuFile">
@ -70,7 +70,15 @@
<addaction name="actionMapSettings"/> <addaction name="actionMapSettings"/>
<addaction name="actionPlayers_settings"/> <addaction name="actionPlayers_settings"/>
</widget> </widget>
<widget class="QMenu" name="menuEdit">
<property name="title">
<string>Edit</string>
</property>
<addaction name="actionUndo"/>
<addaction name="actionRedo"/>
</widget>
<addaction name="menuFile"/> <addaction name="menuFile"/>
<addaction name="menuEdit"/>
<addaction name="menuMap"/> <addaction name="menuMap"/>
</widget> </widget>
<widget class="QToolBar" name="toolBar"> <widget class="QToolBar" name="toolBar">
@ -647,7 +655,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>128</width> <width>128</width>
<height>271</height> <height>356</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -690,7 +698,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>128</width> <width>128</width>
<height>271</height> <height>356</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -709,7 +717,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>128</width> <width>128</width>
<height>271</height> <height>356</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -823,6 +831,37 @@
<string>Players settings</string> <string>Players settings</string>
</property> </property>
</action> </action>
<action name="actionUndo">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Undo</string>
</property>
<property name="iconText">
<string>Undo</string>
</property>
<property name="shortcut">
<string>Ctrl+Z</string>
</property>
<property name="shortcutVisibleInContextMenu">
<bool>true</bool>
</property>
</action>
<action name="actionRedo">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Redo</string>
</property>
<property name="shortcut">
<string>Ctrl+Y</string>
</property>
<property name="shortcutVisibleInContextMenu">
<bool>true</bool>
</property>
</action>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>
@ -833,4 +872,8 @@
</customwidgets> </customwidgets>
<resources/> <resources/>
<connections/> <connections/>
<slots>
<signal>enableUndo(bool)</signal>
<signal>enableRedo(bool)</signal>
</slots>
</ui> </ui>

View File

@ -46,6 +46,13 @@ void MapController::setMap(std::unique_ptr<CMap> cmap)
_scenes[1].reset(new MapScene(1)); _scenes[1].reset(new MapScene(1));
resetMapHandler(); resetMapHandler();
sceneForceUpdate(); sceneForceUpdate();
_map->getEditManager()->getUndoManager().setUndoCallback([this](bool allowUndo, bool allowRedo)
{
main->enableUndo(allowUndo);
main->enableRedo(allowRedo);
}
);
} }
void MapController::sceneForceUpdate() void MapController::sceneForceUpdate()
@ -211,3 +218,19 @@ void MapController::commitObjectCreate(int level)
main->mapChanged(); main->mapChanged();
} }
void MapController::undo()
{
_map->getEditManager()->getUndoManager().undo();
resetMapHandler();
sceneForceUpdate();
main->mapChanged();
}
void MapController::redo()
{
_map->getEditManager()->getUndoManager().redo();
resetMapHandler();
sceneForceUpdate();
main->mapChanged();
}

View File

@ -27,6 +27,7 @@ public:
void sceneForceUpdate(int level); void sceneForceUpdate(int level);
void commitTerrainChange(int level, const Terrain & terrain); void commitTerrainChange(int level, const Terrain & terrain);
void commitObjectErase(const CGObjectInstance* obj);
void commitObjectErase(int level); void commitObjectErase(int level);
void commitObstacleFill(int level); void commitObstacleFill(int level);
void commitChangeWithoutRedraw(); void commitChangeWithoutRedraw();
@ -36,6 +37,9 @@ public:
bool discardObject(int level) const; bool discardObject(int level) const;
void createObject(int level, CGObjectInstance * obj) const; void createObject(int level, CGObjectInstance * obj) const;
void undo();
void redo();
private: private:
std::unique_ptr<CMap> _map; std::unique_ptr<CMap> _map;