Merge pull request #6702 from Laserlicht/camp_drag

[1.7.2] Campaign editor: support drag'n'drop
This commit is contained in:
Ivan Savenko
2026-01-16 12:34:39 +02:00
committed by GitHub
6 changed files with 159 additions and 10 deletions
+85 -10
View File
@@ -34,6 +34,8 @@ CampaignEditor::CampaignEditor():
{
ui->setupUi(this);
setAcceptDrops(true);
setWindowIcon(QIcon{":/icons/menu-game.png"});
ui->actionOpen->setIcon(QIcon{":/icons/document-open.png"});
ui->actionSave->setIcon(QIcon{":/icons/document-save.png"});
@@ -47,6 +49,21 @@ CampaignEditor::CampaignEditor():
campaignScene.reset(new CampaignScene());
ui->campaignView->setScene(campaignScene.get());
// Connect the fileDropped signal from campaignView to handle file drops
connect(ui->campaignView, &CampaignView::fileDropped, this, [this](const QString & filename) {
if(!getAnswerAboutUnsavedChanges())
return;
try
{
loadCampaignFile(filename);
}
catch(const std::exception & e)
{
QMessageBox::critical(this, tr("Failed to open campaign"), tr(e.what()));
}
});
redraw();
@@ -173,17 +190,32 @@ void CampaignEditor::showCampaignEditor(QWidget *parent)
dialog->setAttribute(Qt::WA_DeleteOnClose);
}
void CampaignEditor::on_actionOpen_triggered()
void CampaignEditor::showCampaignEditor(QWidget *parent, const QString &campaignFile)
{
auto * dialog = new CampaignEditor();
dialog->move(parent->geometry().center() - dialog->rect().center());
dialog->setAttribute(Qt::WA_DeleteOnClose);
try
{
dialog->loadCampaignFile(campaignFile);
if(!dialog->campaignState)
{
dialog->close();
return;
}
}
catch(const std::exception & e)
{
QMessageBox::critical(dialog, QObject::tr("Failed to open campaign"), QObject::tr(e.what()));
dialog->close();
}
}
void CampaignEditor::loadCampaignFile(const QString & filenameSelect)
{
if(!getAnswerAboutUnsavedChanges())
return;
auto filenameSelect = QFileDialog::getOpenFileName(this, tr("Open map"),
QString::fromStdString(VCMIDirs::get().userDataPath().make_preferred().string()),
tr("All supported campaigns (*.vcmp *.h3c);;VCMI campaigns(*.vcmp);;HoMM3 campaigns(*.h3c)"));
if(filenameSelect.isEmpty())
return;
campaignState = Helper::openCampaignInternal(filenameSelect);
selectedScenario = *campaignState->allScenarios().begin();
@@ -203,6 +235,20 @@ void CampaignEditor::on_actionOpen_triggered()
redraw();
}
void CampaignEditor::on_actionOpen_triggered()
{
if(!getAnswerAboutUnsavedChanges())
return;
auto filenameSelect = QFileDialog::getOpenFileName(this, tr("Open map"),
QString::fromStdString(VCMIDirs::get().userDataPath().make_preferred().string()),
tr("All supported campaigns (*.vcmp *.h3c);;VCMI campaigns(*.vcmp);;HoMM3 campaigns(*.h3c)"));
if(filenameSelect.isEmpty())
return;
loadCampaignFile(filenameSelect);
}
void CampaignEditor::on_actionOpenSet_triggered()
{
if(!getAnswerAboutUnsavedChanges())
@@ -326,6 +372,35 @@ void CampaignEditor::closeEvent(QCloseEvent *event)
event->ignore();
}
void CampaignEditor::dragEnterEvent(QDragEnterEvent *event)
{
if(event->mimeData()->hasUrls())
event->acceptProposedAction();
}
void CampaignEditor::dropEvent(QDropEvent *event)
{
if(!getAnswerAboutUnsavedChanges())
return;
for(const QUrl& url : event->mimeData()->urls())
{
QString path = url.toLocalFile();
if(path.endsWith(".h3c", Qt::CaseInsensitive) || path.endsWith(".vcmp", Qt::CaseInsensitive))
{
try
{
loadCampaignFile(path);
}
catch(const std::exception & e)
{
QMessageBox::critical(this, tr("Failed to open campaign"), tr(e.what()));
}
break;
}
}
}
std::unique_ptr<CMap> CampaignEditor::tryToOpenMap(QWidget* parent, std::shared_ptr<CampaignState> state, CampaignScenarioID scenario)
{
try
@@ -33,6 +33,7 @@ public:
void redraw();
static void showCampaignEditor(QWidget *parent);
static void showCampaignEditor(QWidget *parent, const QString &campaignFile);
static std::unique_ptr<CMap> tryToOpenMap(QWidget* parent, std::shared_ptr<CampaignState> state, CampaignScenarioID scenario);
private slots:
@@ -50,8 +51,11 @@ private:
void changed();
bool validate();
void saveCampaign();
void loadCampaignFile(const QString & filenameSelect);
void closeEvent(QCloseEvent *event) override;
void dragEnterEvent(QDragEnterEvent *event) override;
void dropEvent(QDropEvent *event) override;
Ui::CampaignEditor *ui;
+50
View File
@@ -19,6 +19,56 @@ CampaignScene::CampaignScene():
CampaignView::CampaignView(QWidget * parent):
QGraphicsView(parent)
{
setAcceptDrops(true);
}
void CampaignView::dragEnterEvent(QDragEnterEvent *event)
{
if(event->mimeData()->hasUrls())
{
for(const QUrl& url : event->mimeData()->urls())
{
QString path = url.toLocalFile();
if(path.endsWith(".h3c", Qt::CaseInsensitive) || path.endsWith(".vcmp", Qt::CaseInsensitive))
{
event->acceptProposedAction();
return;
}
}
}
}
void CampaignView::dragMoveEvent(QDragMoveEvent *event)
{
if(event->mimeData()->hasUrls())
{
for(const QUrl& url : event->mimeData()->urls())
{
QString path = url.toLocalFile();
if(path.endsWith(".h3c", Qt::CaseInsensitive) || path.endsWith(".vcmp", Qt::CaseInsensitive))
{
event->acceptProposedAction();
return;
}
}
}
}
void CampaignView::dropEvent(QDropEvent *event)
{
if(event->mimeData()->hasUrls())
{
for(const QUrl& url : event->mimeData()->urls())
{
QString path = url.toLocalFile();
if(path.endsWith(".h3c", Qt::CaseInsensitive) || path.endsWith(".vcmp", Qt::CaseInsensitive))
{
Q_EMIT fileDropped(path);
event->acceptProposedAction();
return;
}
}
}
}
ClickablePixmapItem::ClickablePixmapItem(const QPixmap &pixmap, const std::function<void()> & clickedCallback, const std::function<void()> & doubleClickedCallback, const std::function<void(QGraphicsSceneContextMenuEvent *)> & contextMenuCallback):
+8
View File
@@ -26,6 +26,14 @@ class CampaignView : public QGraphicsView
public:
CampaignView(QWidget * parent);
protected:
void dragEnterEvent(QDragEnterEvent *event) override;
void dropEvent(QDropEvent *event) override;
void dragMoveEvent(QDragMoveEvent *event) override;
signals:
void fileDropped(const QString & filename);
};
class ClickablePixmapItem : public QGraphicsPixmapItem {
+11
View File
@@ -181,6 +181,12 @@ void MainWindow::dropEvent(QDropEvent* event)
openMap(path);
break;
}
else if (path.endsWith(".h3c", Qt::CaseInsensitive) || path.endsWith(".vcmp", Qt::CaseInsensitive))
{
hide();
openCampaign(path);
break;
}
}
}
@@ -496,6 +502,11 @@ bool MainWindow::openMap(const QString & filenameSelect)
return true;
}
void MainWindow::openCampaign(const QString & filenameSelect)
{
CampaignEditor::showCampaignEditor(this, filenameSelect);
}
void MainWindow::updateRecentMenu(const QString & filenameSelect) {
QSettings s = CLauncherDirs::getSettings(Ui::appName);
QStringList recentFiles = s.value(recentlyOpenedFilesSetting).toStringList();
+1
View File
@@ -51,6 +51,7 @@ public:
void saveMap();
bool openMap(const QString &);
void openCampaign(const QString &);
//MapView * mapView();