mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-22 22:13:35 +02:00
- Erase operation (hotkey: DELETE)
- Interface is updated when there is an object selected or not - Undo for erase object
This commit is contained in:
parent
d0a6bf9521
commit
d3da75eece
@ -233,7 +233,8 @@ CMapHeader::~CMapHeader()
|
||||
|
||||
CMap::CMap()
|
||||
: checksum(0), grailPos(-1, -1, -1), grailRadius(0), terrain(nullptr),
|
||||
guardingCreaturePositions(nullptr)
|
||||
guardingCreaturePositions(nullptr),
|
||||
uid_counter(0)
|
||||
{
|
||||
allHeroes.resize(allowedHeroes.size());
|
||||
allowedAbilities = VLC->skillh->getDefaultAllowed();
|
||||
@ -603,22 +604,36 @@ void CMap::addNewQuestInstance(CQuest* quest)
|
||||
quests.push_back(quest);
|
||||
}
|
||||
|
||||
void CMap::setUniqueInstanceName(CGObjectInstance* obj)
|
||||
{
|
||||
//this gives object unique name even if objects are removed later
|
||||
|
||||
auto uid = uid_counter++;
|
||||
|
||||
boost::format fmt("%s_%d");
|
||||
fmt % obj->typeName % uid;
|
||||
obj->instanceName = fmt.str();
|
||||
}
|
||||
|
||||
void CMap::addNewObject(CGObjectInstance * obj)
|
||||
{
|
||||
|
||||
if(obj->id != ObjectInstanceID((si32)objects.size()))
|
||||
throw std::runtime_error("Invalid object instance id");
|
||||
|
||||
if(obj->instanceName == "")
|
||||
throw std::runtime_error("Object instance name missing");
|
||||
|
||||
auto it = instanceNames.find(obj->instanceName);
|
||||
if(it != instanceNames.end())
|
||||
if (vstd::contains(instanceNames, obj->instanceName))
|
||||
throw std::runtime_error("Object instance name duplicated: "+obj->instanceName);
|
||||
|
||||
objects.push_back(obj);
|
||||
instanceNames[obj->instanceName] = obj;
|
||||
addBlockVisTiles(obj);
|
||||
|
||||
//TODO: how about deafeated heroes recruited again?
|
||||
//TODO: How about objects restored with map editor?
|
||||
|
||||
obj->afterAddToMap(this);
|
||||
}
|
||||
|
||||
|
@ -362,6 +362,7 @@ public:
|
||||
|
||||
void addNewQuestInstance(CQuest * quest);
|
||||
|
||||
void setUniqueInstanceName(CGObjectInstance* obj);
|
||||
///Use only this method when creating new map object instances
|
||||
void addNewObject(CGObjectInstance * obj);
|
||||
void moveObject(CGObjectInstance * obj, const int3 & dst);
|
||||
@ -411,6 +412,7 @@ public:
|
||||
private:
|
||||
/// a 3-dimensional array of terrain tiles, access is as follows: x, y, level. where level=1 is underground
|
||||
TerrainTile*** terrain;
|
||||
si32 uid_counter; //TODO: initialize when loading an old map
|
||||
|
||||
public:
|
||||
template <typename Handler>
|
||||
@ -488,5 +490,6 @@ public:
|
||||
h & CGTownInstance::universitySkills;
|
||||
|
||||
h & instanceNames;
|
||||
h & uid_counter;
|
||||
}
|
||||
};
|
||||
|
@ -190,6 +190,7 @@ void CMapUndoManager::setUndoRedoLimit(int value)
|
||||
assert(value >= 0);
|
||||
undoStack.resize(std::min(undoStack.size(), static_cast<TStack::size_type>(value)));
|
||||
redoStack.resize(std::min(redoStack.size(), static_cast<TStack::size_type>(value)));
|
||||
onUndoRedo();
|
||||
}
|
||||
|
||||
const CMapOperation * CMapUndoManager::peekRedo() const
|
||||
@ -207,6 +208,7 @@ void CMapUndoManager::addOperation(std::unique_ptr<CMapOperation> && operation)
|
||||
undoStack.push_front(std::move(operation));
|
||||
if(undoStack.size() > undoRedoLimit) undoStack.pop_back();
|
||||
redoStack.clear();
|
||||
onUndoRedo();
|
||||
}
|
||||
|
||||
void CMapUndoManager::doOperation(TStack & fromStack, TStack & toStack, bool doUndo)
|
||||
@ -1077,11 +1079,9 @@ CInsertObjectOperation::CInsertObjectOperation(CMap * map, CGObjectInstance * ob
|
||||
|
||||
void CInsertObjectOperation::execute()
|
||||
{
|
||||
obj->id = ObjectInstanceID((si32)map->objects.size());
|
||||
obj->id = ObjectInstanceID(map->objects.size());
|
||||
|
||||
boost::format fmt("%s_%d");
|
||||
fmt % obj->typeName % obj->id.getNum();
|
||||
obj->instanceName = fmt.str();
|
||||
map->setUniqueInstanceName(obj);
|
||||
|
||||
map->addNewObject(obj);
|
||||
}
|
||||
@ -1112,13 +1112,11 @@ CMoveObjectOperation::CMoveObjectOperation(CMap * map, CGObjectInstance * obj, c
|
||||
void CMoveObjectOperation::execute()
|
||||
{
|
||||
map->moveObject(obj, targetPos);
|
||||
logGlobal->debug("Moved object %s to position %s", obj->instanceName, targetPos.toString());
|
||||
}
|
||||
|
||||
void CMoveObjectOperation::undo()
|
||||
{
|
||||
map->moveObject(obj, initialPos);
|
||||
logGlobal->debug("Moved object %s back to position %s", obj->instanceName, initialPos.toString());
|
||||
}
|
||||
|
||||
void CMoveObjectOperation::redo()
|
||||
@ -1137,6 +1135,23 @@ CRemoveObjectOperation::CRemoveObjectOperation(CMap * map, CGObjectInstance * ob
|
||||
|
||||
}
|
||||
|
||||
CRemoveObjectOperation::~CRemoveObjectOperation()
|
||||
{
|
||||
//when operation is destroyed and wasn't undone, the object is lost forever
|
||||
|
||||
if (!obj)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//do not destroy an object that belongs to map
|
||||
if (!vstd::contains(map->instanceNames, obj->instanceName))
|
||||
{
|
||||
delete obj;
|
||||
obj = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void CRemoveObjectOperation::execute()
|
||||
{
|
||||
map->removeObject(obj);
|
||||
@ -1144,7 +1159,16 @@ void CRemoveObjectOperation::execute()
|
||||
|
||||
void CRemoveObjectOperation::undo()
|
||||
{
|
||||
//TODO
|
||||
try
|
||||
{
|
||||
//set new id, but do not rename object
|
||||
obj->id = ObjectInstanceID((si32)map->objects.size());
|
||||
map->addNewObject(obj);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
logGlobal->error(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void CRemoveObjectOperation::redo()
|
||||
|
@ -451,6 +451,7 @@ class CRemoveObjectOperation : public CMapOperation
|
||||
{
|
||||
public:
|
||||
CRemoveObjectOperation(CMap * map, CGObjectInstance * obj);
|
||||
~CRemoveObjectOperation();
|
||||
|
||||
void execute() override;
|
||||
void undo() override;
|
||||
|
@ -657,6 +657,10 @@ void MainWindow::on_toolArea_clicked(bool checked)
|
||||
ui->mapView->selectionTool = MapView::SelectionTool::None;
|
||||
}
|
||||
|
||||
void MainWindow::on_actionErase_triggered()
|
||||
{
|
||||
on_toolErase_clicked();
|
||||
}
|
||||
|
||||
void MainWindow::on_toolErase_clicked()
|
||||
{
|
||||
@ -799,4 +803,16 @@ void MainWindow::enableUndo(bool enable)
|
||||
void MainWindow::enableRedo(bool enable)
|
||||
{
|
||||
ui->actionRedo->setEnabled(enable);
|
||||
}
|
||||
|
||||
void MainWindow::onSelectionMade(int level, bool anythingSelected)
|
||||
{
|
||||
if (level == mapLevel)
|
||||
{
|
||||
auto info = QString::asprintf("Selection on layer %s: %s", level, anythingSelected ? "true" : "false");
|
||||
setStatusMessage(info);
|
||||
|
||||
ui->actionErase->setEnabled(anythingSelected);
|
||||
ui->toolErase->setEnabled(anythingSelected);
|
||||
}
|
||||
}
|
@ -48,6 +48,8 @@ private slots:
|
||||
void on_actionLevel_triggered();
|
||||
|
||||
void on_actionSave_triggered();
|
||||
|
||||
void on_actionErase_triggered();
|
||||
|
||||
void on_actionUndo_triggered();
|
||||
|
||||
@ -90,6 +92,7 @@ public slots:
|
||||
void mapChanged();
|
||||
void enableUndo(bool enable);
|
||||
void enableRedo(bool enable);
|
||||
void onSelectionMade(int level, bool anythingSelected);
|
||||
|
||||
private:
|
||||
void preparePreview(const QModelIndex &index, bool createNew);
|
||||
|
@ -76,6 +76,7 @@
|
||||
</property>
|
||||
<addaction name="actionUndo"/>
|
||||
<addaction name="actionRedo"/>
|
||||
<addaction name="actionErase"/>
|
||||
</widget>
|
||||
<addaction name="menuFile"/>
|
||||
<addaction name="menuEdit"/>
|
||||
@ -99,6 +100,7 @@
|
||||
<addaction name="actionGrid"/>
|
||||
<addaction name="actionPass"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionErase"/>
|
||||
<addaction name="actionCut"/>
|
||||
<addaction name="actionCopy"/>
|
||||
<addaction name="actionPaste"/>
|
||||
@ -600,6 +602,9 @@
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QPushButton" name="toolErase">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
@ -655,7 +660,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>128</width>
|
||||
<height>356</height>
|
||||
<height>344</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@ -698,7 +703,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>128</width>
|
||||
<height>356</height>
|
||||
<height>344</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@ -717,7 +722,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>128</width>
|
||||
<height>356</height>
|
||||
<height>344</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@ -862,6 +867,17 @@
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionErase">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Erase</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Del</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
|
@ -18,6 +18,19 @@ MapController::MapController(MainWindow * m): main(m)
|
||||
{
|
||||
_scenes[0].reset(new MapScene(0));
|
||||
_scenes[1].reset(new MapScene(1));
|
||||
connectScenes();
|
||||
}
|
||||
|
||||
void MapController::connectScenes()
|
||||
{
|
||||
for (int level = 0; level <= 1; level++)
|
||||
{
|
||||
//selections for both layers will be handled separately
|
||||
QObject::connect(_scenes[level].get(), &MapScene::selected, [this, level](bool anythingSelected)
|
||||
{
|
||||
main->onSelectionMade(level, anythingSelected);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
MapController::~MapController()
|
||||
@ -47,6 +60,8 @@ void MapController::setMap(std::unique_ptr<CMap> cmap)
|
||||
resetMapHandler();
|
||||
sceneForceUpdate();
|
||||
|
||||
connectScenes();
|
||||
|
||||
_map->getEditManager()->getUndoManager().setUndoCallback([this](bool allowUndo, bool allowRedo)
|
||||
{
|
||||
main->enableUndo(allowUndo);
|
||||
@ -99,7 +114,7 @@ void MapController::commitObjectErase(int level)
|
||||
for(auto * obj : _scenes[level]->selectionObjectsView.getSelection())
|
||||
{
|
||||
_map->getEditManager()->removeObject(obj);
|
||||
delete obj;
|
||||
//object ownership goes to EditManager, which can handle undo operation
|
||||
}
|
||||
_scenes[level]->selectionObjectsView.clear();
|
||||
resetMapHandler();
|
||||
|
@ -46,6 +46,8 @@ private:
|
||||
std::unique_ptr<MapHandler> _mapHandler;
|
||||
MainWindow * main;
|
||||
mutable std::array<std::unique_ptr<MapScene>, 2> _scenes;
|
||||
|
||||
void connectScenes();
|
||||
};
|
||||
|
||||
#endif // MAPCONTROLLER_H
|
||||
|
@ -281,7 +281,9 @@ MapScene::MapScene(int lev):
|
||||
terrainView(this),
|
||||
objectsView(this),
|
||||
selectionObjectsView(this),
|
||||
level(lev)
|
||||
level(lev),
|
||||
isTerrainSelected(false),
|
||||
isObjectSelected(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -314,3 +316,15 @@ void MapScene::updateViews()
|
||||
selectionTerrainView.show(true);
|
||||
selectionObjectsView.show(true);
|
||||
}
|
||||
|
||||
void MapScene::terrainSelected(bool anythingSelected)
|
||||
{
|
||||
isTerrainSelected = anythingSelected;
|
||||
emit selected(isTerrainSelected || isObjectSelected);
|
||||
}
|
||||
|
||||
void MapScene::objectSelected(bool anythingSelected)
|
||||
{
|
||||
isObjectSelected = anythingSelected;
|
||||
emit selected(isTerrainSelected || isObjectSelected);
|
||||
}
|
@ -13,6 +13,7 @@ class MapController;
|
||||
|
||||
class MapScene : public QGraphicsScene
|
||||
{
|
||||
Q_OBJECT;
|
||||
public:
|
||||
MapScene(int lev);
|
||||
|
||||
@ -27,9 +28,19 @@ public:
|
||||
SelectionObjectsLayer selectionObjectsView;
|
||||
|
||||
const int level;
|
||||
|
||||
signals:
|
||||
void selected(bool anything);
|
||||
|
||||
public slots:
|
||||
void terrainSelected(bool anything);
|
||||
void objectSelected(bool anything);
|
||||
|
||||
private:
|
||||
std::list<AbstractLayer *> getAbstractLayers();
|
||||
|
||||
bool isTerrainSelected;
|
||||
bool isObjectSelected;
|
||||
};
|
||||
|
||||
class MapView : public QGraphicsView
|
||||
|
@ -133,6 +133,7 @@ void SelectionTerrainLayer::update()
|
||||
area.clear();
|
||||
areaAdd.clear();
|
||||
areaErase.clear();
|
||||
selectionMade();
|
||||
|
||||
pixmap.reset(new QPixmap(map->width * 32, map->height * 32));
|
||||
pixmap->fill(QColor(0, 0, 0, 0));
|
||||
@ -173,6 +174,7 @@ void SelectionTerrainLayer::select(const int3 & tile)
|
||||
areaAdd.insert(tile);
|
||||
areaErase.erase(tile);
|
||||
}
|
||||
selectionMade();
|
||||
}
|
||||
|
||||
void SelectionTerrainLayer::erase(const int3 & tile)
|
||||
@ -186,6 +188,7 @@ void SelectionTerrainLayer::erase(const int3 & tile)
|
||||
areaErase.insert(tile);
|
||||
areaAdd.erase(tile);
|
||||
}
|
||||
selectionMade();
|
||||
}
|
||||
|
||||
void SelectionTerrainLayer::clear()
|
||||
@ -193,6 +196,7 @@ void SelectionTerrainLayer::clear()
|
||||
areaErase = area;
|
||||
areaAdd.clear();
|
||||
area.clear();
|
||||
selectionMade();
|
||||
}
|
||||
|
||||
const std::set<int3> & SelectionTerrainLayer::selection() const
|
||||
@ -200,6 +204,11 @@ const std::set<int3> & SelectionTerrainLayer::selection() const
|
||||
return area;
|
||||
}
|
||||
|
||||
void SelectionTerrainLayer::selectionMade()
|
||||
{
|
||||
scene->objectSelected(!area.empty());
|
||||
}
|
||||
|
||||
TerrainLayer::TerrainLayer(MapScene * s): AbstractLayer(s)
|
||||
{
|
||||
}
|
||||
@ -352,6 +361,7 @@ void SelectionObjectsLayer::update()
|
||||
return;
|
||||
|
||||
selectedObjects.clear();
|
||||
selectionMade();
|
||||
shift = QPoint();
|
||||
if(newObject)
|
||||
delete newObject;
|
||||
@ -465,14 +475,19 @@ void SelectionObjectsLayer::selectObjects(int x1, int y1, int x2, int y2)
|
||||
for(int i = x1; i < x2; ++i)
|
||||
{
|
||||
for(auto & o : handler->getObjects(i, j, scene->level))
|
||||
selectObject(o.obj);
|
||||
selectObject(o.obj, false); //do not inform about each object added
|
||||
}
|
||||
}
|
||||
selectionMade();
|
||||
}
|
||||
|
||||
void SelectionObjectsLayer::selectObject(CGObjectInstance * obj)
|
||||
void SelectionObjectsLayer::selectObject(CGObjectInstance * obj, bool inform /* = true */)
|
||||
{
|
||||
selectedObjects.insert(obj);
|
||||
if (inform)
|
||||
{
|
||||
selectionMade();
|
||||
}
|
||||
}
|
||||
|
||||
bool SelectionObjectsLayer::isSelected(const CGObjectInstance * obj) const
|
||||
@ -488,6 +503,12 @@ std::set<CGObjectInstance*> SelectionObjectsLayer::getSelection() const
|
||||
void SelectionObjectsLayer::clear()
|
||||
{
|
||||
selectedObjects.clear();
|
||||
selectionMade();
|
||||
shift.setX(0);
|
||||
shift.setY(0);
|
||||
}
|
||||
|
||||
void SelectionObjectsLayer::selectionMade()
|
||||
{
|
||||
scene->objectSelected(!selectedObjects.empty());
|
||||
}
|
@ -67,6 +67,8 @@ public:
|
||||
|
||||
private:
|
||||
std::set<int3> area, areaAdd, areaErase;
|
||||
|
||||
void selectionMade(); //TODO: consider making it a signal
|
||||
};
|
||||
|
||||
|
||||
@ -113,7 +115,7 @@ public:
|
||||
|
||||
CGObjectInstance * selectObjectAt(int x, int y) const;
|
||||
void selectObjects(int x1, int y1, int x2, int y2);
|
||||
void selectObject(CGObjectInstance *);
|
||||
void selectObject(CGObjectInstance *, bool inform = true);
|
||||
bool isSelected(const CGObjectInstance *) const;
|
||||
std::set<CGObjectInstance*> getSelection() const;
|
||||
void moveSelection(int x, int y);
|
||||
@ -125,6 +127,8 @@ public:
|
||||
|
||||
private:
|
||||
std::set<CGObjectInstance *> selectedObjects;
|
||||
|
||||
void selectionMade(); //TODO: consider making it a signal
|
||||
};
|
||||
|
||||
#endif // SCENELAYER_H
|
||||
|
Loading…
Reference in New Issue
Block a user