diff --git a/mapeditor/mainwindow.cpp b/mapeditor/mainwindow.cpp index 3acf2d678..73b6d1524 100644 --- a/mapeditor/mainwindow.cpp +++ b/mapeditor/mainwindow.cpp @@ -550,7 +550,7 @@ void MainWindow::loadObjectsTree() //model objectsModel.setHorizontalHeaderLabels(QStringList() << tr("Type")); - objectBrowser = new ObjectBrowser(this); + objectBrowser = new ObjectBrowserProxyModel(this); objectBrowser->setSourceModel(&objectsModel); objectBrowser->setDynamicSortFilter(false); objectBrowser->setRecursiveFilteringEnabled(true); @@ -853,12 +853,11 @@ void MainWindow::on_toolErase_clicked() ui->tabWidget->setCurrentIndex(0); } -void MainWindow::preparePreview(const QModelIndex &index, bool createNew) +void MainWindow::preparePreview(const QModelIndex &index) { scenePreview->clear(); auto data = objectsModel.itemFromIndex(objectBrowser->mapToSource(index))->data().toJsonObject(); - if(!data.empty()) { auto preview = data["preview"]; @@ -866,29 +865,12 @@ void MainWindow::preparePreview(const QModelIndex &index, bool createNew) { QPixmap objPreview = pixmapFromJson(preview); scenePreview->addPixmap(objPreview); - - auto objId = data["id"].toInt(); - auto objSubId = data["subid"].toInt(); - auto templateId = data["template"].toInt(); - - if(controller.discardObject(mapLevel) || createNew) - { - auto factory = VLC->objtypeh->getHandlerFor(objId, objSubId); - auto templ = factory->getTemplates()[templateId]; - controller.createObject(mapLevel, factory->create(templ)); - } } } } void MainWindow::treeViewSelected(const QModelIndex & index, const QModelIndex & deselected) -{ - preparePreview(index, false); -} - - -void MainWindow::on_treeView_activated(const QModelIndex &index) { ui->toolBrush->setChecked(false); ui->toolBrush2->setChecked(false); @@ -896,11 +878,10 @@ void MainWindow::on_treeView_activated(const QModelIndex &index) ui->toolArea->setChecked(false); ui->toolLasso->setChecked(false); ui->mapView->selectionTool = MapView::SelectionTool::None; - - preparePreview(index, true); + + preparePreview(index); } - void MainWindow::on_terrainFilterCombo_currentTextChanged(const QString &arg1) { if(!objectBrowser) diff --git a/mapeditor/mainwindow.h b/mapeditor/mainwindow.h index 3028c6b25..a728c87dc 100644 --- a/mapeditor/mainwindow.h +++ b/mapeditor/mainwindow.h @@ -8,7 +8,7 @@ #include "resourceExtractor/ResourceConverter.h" class CMap; -class ObjectBrowser; +class ObjectBrowserProxyModel; class CGObjectInstance; namespace Ui @@ -74,8 +74,6 @@ private slots: void on_toolErase_clicked(); - void on_treeView_activated(const QModelIndex &index); - void on_terrainFilterCombo_currentTextChanged(const QString &arg1); void on_filter_textChanged(const QString &arg1); @@ -113,7 +111,7 @@ public slots: void displayStatus(const QString& message, int timeout = 2000); private: - void preparePreview(const QModelIndex &index, bool createNew); + void preparePreview(const QModelIndex & index); void addGroupIntoCatalog(const std::string & groupName, bool staticOnly); void addGroupIntoCatalog(const std::string & groupName, bool useCustomName, bool staticOnly, int ID); @@ -133,7 +131,7 @@ private: private: Ui::MainWindow * ui; - ObjectBrowser * objectBrowser = nullptr; + ObjectBrowserProxyModel * objectBrowser = nullptr; QGraphicsScene * scenePreview; QString filename; diff --git a/mapeditor/mainwindow.ui b/mapeditor/mainwindow.ui index 1b266eaec..6da92787e 100644 --- a/mapeditor/mainwindow.ui +++ b/mapeditor/mainwindow.ui @@ -303,7 +303,7 @@ - + 0 @@ -319,8 +319,11 @@ QAbstractItemView::NoEditTriggers + + false + - QAbstractItemView::NoDragDrop + QAbstractItemView::DragOnly QAbstractItemView::SelectItems @@ -1206,6 +1209,11 @@ QGraphicsView
mapview.h
+ + ObjectBrowser + QTreeView +
objectbrowser.h
+
diff --git a/mapeditor/mapview.cpp b/mapeditor/mapview.cpp index d9460d7a4..c719b80f1 100644 --- a/mapeditor/mapview.cpp +++ b/mapeditor/mapview.cpp @@ -13,6 +13,7 @@ #include "mainwindow.h" #include #include "mapcontroller.h" +#include "../lib/mapObjects/CObjectClassesHandler.h" MinimapView::MinimapView(QWidget * parent): QGraphicsView(parent) @@ -64,12 +65,10 @@ MapView::MapView(QWidget * parent): void MapView::cameraChanged(const QPointF & pos) { - //ui->mapView->translate(pos.x(), pos.y()); horizontalScrollBar()->setValue(pos.x()); verticalScrollBar()->setValue(pos.y()); } - void MapView::setController(MapController * ctrl) { controller = ctrl; @@ -85,7 +84,12 @@ void MapView::mouseMoveEvent(QMouseEvent *mouseEvent) auto pos = mapToScene(mouseEvent->pos()); //TODO: do we need to check size? int3 tile(pos.x() / 32, pos.y() / 32, sc->level); - + + //if scene will be scrolled without mouse movement, selection, object moving and rubber band will not be updated + //to change this behavior, all this logic should be placed in viewportEvent + if(rubberBand) + rubberBand->setGeometry(QRect(mapFromScene(mouseStart), mouseEvent->pos()).normalized()); + if(tile == tilePrev) //do not redraw return; @@ -161,13 +165,7 @@ void MapView::mouseMoveEvent(QMouseEvent *mouseEvent) if(sh.x || sh.y) { - if(sc->selectionObjectsView.newObject) - { - sc->selectionObjectsView.shift = QPoint(tile.x, tile.y); - sc->selectionObjectsView.selectObject(sc->selectionObjectsView.newObject); - sc->selectionObjectsView.selectionMode = SelectionObjectsLayer::MOVEMENT; - } - else if(mouseEvent->buttons() & Qt::LeftButton) + if(!sc->selectionObjectsView.newObject && (mouseEvent->buttons() & Qt::LeftButton)) { if(sc->selectionObjectsView.selectionMode == SelectionObjectsLayer::SELECTION) { @@ -296,6 +294,11 @@ void MapView::mousePressEvent(QMouseEvent *event) { sc->selectionObjectsView.clear(); sc->selectionObjectsView.selectionMode = SelectionObjectsLayer::SELECTION; + + if(!rubberBand) + rubberBand = new QRubberBand(QRubberBand::Rectangle, this); + rubberBand->setGeometry(QRect(mapFromScene(mouseStart), QSize())); + rubberBand->show(); } } sc->selectionObjectsView.shift = QPoint(0, 0); @@ -314,6 +317,9 @@ void MapView::mouseReleaseEvent(QMouseEvent *event) auto * sc = static_cast(scene()); if(!sc || !controller->map()) return; + + if(rubberBand) + rubberBand->hide(); switch(selectionTool) { @@ -324,19 +330,7 @@ void MapView::mouseReleaseEvent(QMouseEvent *event) bool tab = false; if(sc->selectionObjectsView.selectionMode == SelectionObjectsLayer::MOVEMENT) { - if(sc->selectionObjectsView.newObject) - { - QString errorMsg; - if(controller->canPlaceObject(sc->level, sc->selectionObjectsView.newObject, errorMsg)) - controller->commitObjectCreate(sc->level); - else - { - QMessageBox::information(this, "Can't place object", errorMsg); - break; - } - } - else - controller->commitObjectShift(sc->level); + controller->commitObjectShift(sc->level); } else { @@ -355,11 +349,105 @@ void MapView::mouseReleaseEvent(QMouseEvent *event) } } +void MapView::dragEnterEvent(QDragEnterEvent * event) +{ + if(!controller || !controller->map()) + return; + + auto * sc = static_cast(scene()); + if(!sc) + return; + + if(event->mimeData()->hasFormat("application/vcmi.object")) + { + auto encodedData = event->mimeData()->data("application/vcmi.object"); + QDataStream stream(&encodedData, QIODevice::ReadOnly); + QVariant vdata; + stream >> vdata; + auto data = vdata.toJsonObject(); + if(!data.empty()) + { + auto preview = data["preview"]; + if(preview != QJsonValue::Undefined) + { + auto objId = data["id"].toInt(); + auto objSubId = data["subid"].toInt(); + auto templateId = data["template"].toInt(); + auto factory = VLC->objtypeh->getHandlerFor(objId, objSubId); + auto templ = factory->getTemplates()[templateId]; + controller->discardObject(sc->level); + controller->createObject(sc->level, factory->create(templ)); + } + } + + event->acceptProposedAction(); + } +} + +void MapView::dropEvent(QDropEvent * event) +{ + if(!controller || !controller->map()) + return; + + auto * sc = static_cast(scene()); + if(!sc) + return; + + if(sc->selectionObjectsView.newObject) + { + QString errorMsg; + if(controller->canPlaceObject(sc->level, sc->selectionObjectsView.newObject, errorMsg)) + { + controller->commitObjectCreate(sc->level); + } + else + { + controller->discardObject(sc->level); + QMessageBox::information(this, "Can't place object", errorMsg); + } + } + + event->acceptProposedAction(); +} + +void MapView::dragMoveEvent(QDragMoveEvent * event) +{ + auto * sc = static_cast(scene()); + if(!sc) + return; + + auto rect = event->answerRect(); + auto pos = mapToScene(rect.bottomRight()); //TODO: do we need to check size? + int3 tile(pos.x() / 32 + 1, pos.y() / 32 + 1, sc->level); + + if(sc->selectionObjectsView.newObject) + { + sc->selectionObjectsView.shift = QPoint(tile.x, tile.y); + sc->selectionObjectsView.selectObject(sc->selectionObjectsView.newObject); + sc->selectionObjectsView.selectionMode = SelectionObjectsLayer::MOVEMENT; + sc->selectionObjectsView.draw(); + } + + event->acceptProposedAction(); +} + +void MapView::dragLeaveEvent(QDragLeaveEvent * event) +{ + if(!controller || !controller->map()) + return; + + auto * sc = static_cast(scene()); + if(!sc) + return; + + controller->discardObject(sc->level); +} + + bool MapView::viewportEvent(QEvent *event) { if(auto * sc = static_cast(scene())) { - //auto rect = sceneRect(); auto rect = mapToScene(viewport()->geometry()).boundingRect(); controller->miniScene(sc->level)->viewport.setViewport(rect.x() / 32, rect.y() / 32, rect.width() / 32, rect.height() / 32); } diff --git a/mapeditor/mapview.h b/mapeditor/mapview.h index 13caabb97..427271a29 100644 --- a/mapeditor/mapview.h +++ b/mapeditor/mapview.h @@ -98,6 +98,10 @@ public slots: void mouseMoveEvent(QMouseEvent * mouseEvent) override; void mousePressEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; + void dragEnterEvent(QDragEnterEvent * event) override; + void dragMoveEvent(QDragMoveEvent *event) override; + void dragLeaveEvent(QDragLeaveEvent *event) override; + void dropEvent(QDropEvent * event) override; void cameraChanged(const QPointF & pos); @@ -110,6 +114,7 @@ protected: private: MapController * controller = nullptr; + QRubberBand * rubberBand = nullptr; QPointF mouseStart; int3 tileStart; int3 tilePrev; @@ -127,7 +132,7 @@ public: public slots: void mouseMoveEvent(QMouseEvent * mouseEvent) override; - void mousePressEvent(QMouseEvent* event) override; + void mousePressEvent(QMouseEvent * event) override; signals: void cameraPositionChanged(const QPointF & newPosition); diff --git a/mapeditor/objectbrowser.cpp b/mapeditor/objectbrowser.cpp index 416b8abfa..b0cc1f707 100644 --- a/mapeditor/objectbrowser.cpp +++ b/mapeditor/objectbrowser.cpp @@ -12,12 +12,12 @@ #include "objectbrowser.h" #include "../lib/mapObjects/CObjectClassesHandler.h" -ObjectBrowser::ObjectBrowser(QObject *parent) +ObjectBrowserProxyModel::ObjectBrowserProxyModel(QObject *parent) : QSortFilterProxyModel{parent}, terrain(Terrain::ANY_TERRAIN) { } -bool ObjectBrowser::filterAcceptsRow(int source_row, const QModelIndex & source_parent) const +bool ObjectBrowserProxyModel::filterAcceptsRow(int source_row, const QModelIndex & source_parent) const { bool result = QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); @@ -57,7 +57,7 @@ bool ObjectBrowser::filterAcceptsRow(int source_row, const QModelIndex & source_ return result; } -bool ObjectBrowser::filterAcceptsRowText(int source_row, const QModelIndex &source_parent) const +bool ObjectBrowserProxyModel::filterAcceptsRowText(int source_row, const QModelIndex &source_parent) const { if(source_parent.isValid()) { @@ -76,3 +76,64 @@ bool ObjectBrowser::filterAcceptsRowText(int source_row, const QModelIndex &sour return (filter.isNull() || filter.isEmpty() || item->text().contains(filter, Qt::CaseInsensitive)); } +Qt::ItemFlags ObjectBrowserProxyModel::flags(const QModelIndex & index) const +{ + Qt::ItemFlags defaultFlags = QSortFilterProxyModel::flags(index); + + if (index.isValid()) + return Qt::ItemIsDragEnabled | defaultFlags; + + return defaultFlags; +} + +QStringList ObjectBrowserProxyModel::mimeTypes() const +{ + QStringList types; + types << "application/vcmi.object"; + return types; +} + +QMimeData * ObjectBrowserProxyModel::mimeData(const QModelIndexList & indexes) const +{ + assert(indexes.size() == 1); + + auto * standardModel = qobject_cast(sourceModel()); + assert(standardModel); + + QModelIndex index = indexes.front(); + QByteArray encodedData; + + QDataStream stream(&encodedData, QIODevice::WriteOnly); + + if(!index.isValid()) + return nullptr; + + auto text = standardModel->itemFromIndex(mapToSource(index))->data(); + stream << text; + + QMimeData * mimeData = new QMimeData; + mimeData->setData("application/vcmi.object", encodedData); + return mimeData; +} + +ObjectBrowser::ObjectBrowser(QWidget * parent): + QTreeView(parent) +{ + setDropIndicatorShown(false); +} + +void ObjectBrowser::startDrag(Qt::DropActions supportedActions) +{ + QDrag *drag = new QDrag(this); + auto indexes = selectedIndexes(); + if(indexes.isEmpty()) + return; + + QMimeData * mimeData = model()->mimeData(indexes); + if(!mimeData) + return; + + drag->setMimeData(mimeData); + + Qt::DropAction dropAction = drag->exec(); +} diff --git a/mapeditor/objectbrowser.h b/mapeditor/objectbrowser.h index 6c700ae62..2282cf695 100644 --- a/mapeditor/objectbrowser.h +++ b/mapeditor/objectbrowser.h @@ -13,10 +13,16 @@ #include #include "../lib/Terrain.h" -class ObjectBrowser : public QSortFilterProxyModel +class ObjectBrowserProxyModel : public QSortFilterProxyModel { public: - explicit ObjectBrowser(QObject *parent = nullptr); + explicit ObjectBrowserProxyModel(QObject *parent = nullptr); + + Qt::ItemFlags flags(const QModelIndex &index) const override; + + QStringList mimeTypes() const override; + + QMimeData * mimeData(const QModelIndexList & indexes) const override; TerrainId terrain; QString filter; @@ -25,3 +31,12 @@ protected: bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const override; bool filterAcceptsRowText(int source_row, const QModelIndex &source_parent) const; }; + +class ObjectBrowser : public QTreeView +{ +public: + ObjectBrowser(QWidget * parent); + +protected: + void startDrag(Qt::DropActions supportedActions) override; +}; diff --git a/mapeditor/scenelayer.cpp b/mapeditor/scenelayer.cpp index e9d39eb97..8edcaa665 100644 --- a/mapeditor/scenelayer.cpp +++ b/mapeditor/scenelayer.cpp @@ -390,7 +390,7 @@ void SelectionObjectsLayer::draw() //show translation if(selectionMode == SelectionMode::MOVEMENT && (shift.x() || shift.y())) { - painter.setOpacity(0.5); + painter.setOpacity(0.7); auto newPos = QPoint(obj->getPosition().x, obj->getPosition().y) + shift; handler->drawObjectAt(painter, obj, newPos.x(), newPos.y()); } diff --git a/mapeditor/scenelayer.h b/mapeditor/scenelayer.h index 49d1ff083..7b99b4df1 100644 --- a/mapeditor/scenelayer.h +++ b/mapeditor/scenelayer.h @@ -144,7 +144,6 @@ public: void deselectObject(CGObjectInstance *); bool isSelected(const CGObjectInstance *) const; std::set getSelection() const; - void moveSelection(int x, int y); void clear(); QPoint shift;