From 25e522fd8aa927344aaa5d4a6423e5e067c091ac Mon Sep 17 00:00:00 2001 From: 0xFEEDC0DE64 Date: Sat, 15 Jan 2022 04:48:32 +0100 Subject: [PATCH] Implemented a few room property inputs --- QtGameMaker.pro | 6 +- icons/isometric.png | Bin 0 -> 11945 bytes icons/lock.png | Bin 0 -> 5326 bytes icons/sort-x.png | Bin 0 -> 13147 bytes icons/sort-y.png | Bin 0 -> 4511 bytes icons/unlock.png | Bin 0 -> 5641 bytes resources.qrc | 5 + src/editor/dialogs/addeventdialog.ui | 10 +- .../dialogs/backgroundpropertiesdialog.ui | 10 +- src/editor/dialogs/codeeditordialog.ui | 26 +- src/editor/dialogs/createspritedialog.ui | 4 +- src/editor/dialogs/editspritedialog.ui | 12 +- src/editor/dialogs/fontpropertiesdialog.ui | 4 +- ...dialog.cpp => genericcodeeditordialog.cpp} | 6 +- src/editor/dialogs/genericcodeeditordialog.h | 11 + src/editor/dialogs/imageeditordialog.ui | 18 +- src/editor/dialogs/includedfilesdialog.ui | 10 +- src/editor/dialogs/objectinformationdialog.ui | 10 +- src/editor/dialogs/objectpropertiesdialog.cpp | 13 +- src/editor/dialogs/objectpropertiesdialog.h | 2 +- src/editor/dialogs/objectpropertiesdialog.ui | 6 +- src/editor/dialogs/pathpropertiesdialog.cpp | 209 +++++-- src/editor/dialogs/pathpropertiesdialog.h | 19 + src/editor/dialogs/pathpropertiesdialog.ui | 74 ++- src/editor/dialogs/roompropertiesdialog.cpp | 300 +++++++++- src/editor/dialogs/roompropertiesdialog.h | 36 ++ src/editor/dialogs/roompropertiesdialog.ui | 522 +++++++++++++++++- src/editor/dialogs/soundpropertiesdialog.ui | 14 +- src/editor/dialogs/spritepropertiesdialog.ui | 10 +- .../dialogs/timelinepropertiesdialog.ui | 22 +- src/editor/dialogs/triggerconditiondialog.h | 11 - src/editor/dialogs/triggersdialog.cpp | 4 +- src/editor/dialogs/triggersdialog.ui | 12 +- .../dialogs/userdefinedconstantsdialog.ui | 22 +- src/editor/mainwindow.cpp | 10 +- src/editor/mainwindow.ui | 74 +-- src/editor/widgets/pathpointswidget.cpp | 49 +- src/editor/widgets/pathpointswidget.h | 36 +- src/editor/widgets/roomeditwidget.cpp | 112 ++++ src/editor/widgets/roomeditwidget.h | 65 +++ src/projectcontainer.cpp | 34 +- src/projectcontainer.h | 13 + 42 files changed, 1507 insertions(+), 294 deletions(-) create mode 100644 icons/isometric.png create mode 100644 icons/lock.png create mode 100644 icons/sort-x.png create mode 100644 icons/sort-y.png create mode 100644 icons/unlock.png rename src/editor/dialogs/{triggerconditiondialog.cpp => genericcodeeditordialog.cpp} (59%) create mode 100644 src/editor/dialogs/genericcodeeditordialog.h delete mode 100644 src/editor/dialogs/triggerconditiondialog.h create mode 100644 src/editor/widgets/roomeditwidget.cpp create mode 100644 src/editor/widgets/roomeditwidget.h diff --git a/QtGameMaker.pro b/QtGameMaker.pro index 7c1db4a..8123270 100644 --- a/QtGameMaker.pro +++ b/QtGameMaker.pro @@ -22,6 +22,8 @@ INCLUDEPATH += \ HEADERS += \ src/closeeventfilter.h \ + src/editor/dialogs/genericcodeeditordialog.h \ + src/editor/widgets/roomeditwidget.h \ src/futurecpp.h \ src/projectcontainer.h \ src/editor/jshighlighter.h \ @@ -50,7 +52,6 @@ HEADERS += \ src/editor/dialogs/soundpropertiesdialog.h \ src/editor/dialogs/spritepropertiesdialog.h \ src/editor/dialogs/timelinepropertiesdialog.h \ - src/editor/dialogs/triggerconditiondialog.h \ src/editor/dialogs/triggersdialog.h \ src/editor/dialogs/userdefinedconstantsdialog.h \ src/editor/models/actionscontainermodel.h \ @@ -70,6 +71,8 @@ HEADERS += \ SOURCES += \ src/closeeventfilter.cpp \ + src/editor/dialogs/genericcodeeditordialog.cpp \ + src/editor/widgets/roomeditwidget.cpp \ src/main.cpp \ src/projectcontainer.cpp \ src/editor/jshighlighter.cpp \ @@ -98,7 +101,6 @@ SOURCES += \ src/editor/dialogs/soundpropertiesdialog.cpp \ src/editor/dialogs/spritepropertiesdialog.cpp \ src/editor/dialogs/timelinepropertiesdialog.cpp \ - src/editor/dialogs/triggerconditiondialog.cpp \ src/editor/dialogs/triggersdialog.cpp \ src/editor/dialogs/userdefinedconstantsdialog.cpp \ src/editor/models/actionscontainermodel.cpp \ diff --git a/icons/isometric.png b/icons/isometric.png new file mode 100644 index 0000000000000000000000000000000000000000..7b6ec02726ca13c44c3ade05f10aca45ce8a5eb7 GIT binary patch literal 11945 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 V)en%}`WiGY>FMg{vd$@?2>=1V3ef-n literal 0 HcmV?d00001 diff --git a/icons/sort-x.png b/icons/sort-x.png new file mode 100644 index 0000000000000000000000000000000000000000..0fa57efefc5f5c6c541dd0c35140278701a35ed4 GIT binary patch literal 13147 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 literal 0 HcmV?d00001 diff --git a/icons/sort-y.png b/icons/sort-y.png new file mode 100644 index 0000000000000000000000000000000000000000..451cde35854c43e8fbd156b815283222499c6f78 GIT binary patch literal 4511 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 XVAJ%Co=mI+4YYW=`njxgN@xNA6b>^T literal 0 HcmV?d00001 diff --git a/icons/unlock.png b/icons/unlock.png new file mode 100644 index 0000000000000000000000000000000000000000..de6d1d295f062d496470acbfd006c41f3e3d2370 GIT binary patch literal 5641 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 literal 0 HcmV?d00001 diff --git a/resources.qrc b/resources.qrc index 7f166bc..8a7be93 100644 --- a/resources.qrc +++ b/resources.qrc @@ -82,5 +82,10 @@ icons/action.png icons/code-action.png icons/event-draw.png + icons/lock.png + icons/unlock.png + icons/sort-x.png + icons/sort-y.png + icons/isometric.png diff --git a/src/editor/dialogs/addeventdialog.ui b/src/editor/dialogs/addeventdialog.ui index acaca55..f42ec56 100644 --- a/src/editor/dialogs/addeventdialog.ui +++ b/src/editor/dialogs/addeventdialog.ui @@ -22,7 +22,7 @@ Dra&w - + :/qtgameengine/icons/event-draw.png:/qtgameengine/icons/event-draw.png @@ -54,7 +54,7 @@ &Step - + :/qtgameengine/icons/event-step.png:/qtgameengine/icons/event-step.png @@ -65,7 +65,7 @@ C&reate - + :/qtgameengine/icons/event-create.png:/qtgameengine/icons/event-create.png @@ -90,7 +90,7 @@ &Destroy - + :/qtgameengine/icons/event-destroy.png:/qtgameengine/icons/event-destroy.png @@ -131,7 +131,7 @@ - + diff --git a/src/editor/dialogs/backgroundpropertiesdialog.ui b/src/editor/dialogs/backgroundpropertiesdialog.ui index 74f7a03..0c855fe 100644 --- a/src/editor/dialogs/backgroundpropertiesdialog.ui +++ b/src/editor/dialogs/backgroundpropertiesdialog.ui @@ -14,7 +14,7 @@ Dialog - + :/qtgameengine/icons/background-file.png:/qtgameengine/icons/background-file.png @@ -69,7 +69,7 @@ &Load Background - + :/qtgameengine/icons/open.png:/qtgameengine/icons/open.png @@ -83,7 +83,7 @@ &Save Background - + :/qtgameengine/icons/save.png:/qtgameengine/icons/save.png @@ -97,7 +97,7 @@ &Edit Background - + :/qtgameengine/icons/edit.png:/qtgameengine/icons/edit.png @@ -178,7 +178,7 @@ - + diff --git a/src/editor/dialogs/codeeditordialog.ui b/src/editor/dialogs/codeeditordialog.ui index 71f1c3c..fb771a8 100644 --- a/src/editor/dialogs/codeeditordialog.ui +++ b/src/editor/dialogs/codeeditordialog.ui @@ -14,7 +14,7 @@ Code Editor - + :/qtgameengine/icons/script-file.png:/qtgameengine/icons/script-file.png @@ -225,7 +225,7 @@ - + :/qtgameengine/icons/ok.png:/qtgameengine/icons/ok.png @@ -237,7 +237,7 @@ - + :/qtgameengine/icons/open.png:/qtgameengine/icons/open.png @@ -249,7 +249,7 @@ - + :/qtgameengine/icons/save.png:/qtgameengine/icons/save.png @@ -261,7 +261,7 @@ - + :/qtgameengine/icons/print.png:/qtgameengine/icons/print.png @@ -276,7 +276,7 @@ false - + :/qtgameengine/icons/undo.png:/qtgameengine/icons/undo.png @@ -291,7 +291,7 @@ false - + :/qtgameengine/icons/redo.png:/qtgameengine/icons/redo.png @@ -306,7 +306,7 @@ false - + :/qtgameengine/icons/cut.png:/qtgameengine/icons/cut.png @@ -321,7 +321,7 @@ false - + :/qtgameengine/icons/copy.png:/qtgameengine/icons/copy.png @@ -333,7 +333,7 @@ - + :/qtgameengine/icons/paste.png:/qtgameengine/icons/paste.png @@ -351,7 +351,7 @@ false - + :/qtgameengine/icons/find.png:/qtgameengine/icons/find.png @@ -369,7 +369,7 @@ true - + :/qtgameengine/icons/check.png:/qtgameengine/icons/check.png @@ -388,7 +388,7 @@ - + diff --git a/src/editor/dialogs/createspritedialog.ui b/src/editor/dialogs/createspritedialog.ui index aff8b14..1e84c41 100644 --- a/src/editor/dialogs/createspritedialog.ui +++ b/src/editor/dialogs/createspritedialog.ui @@ -57,7 +57,7 @@ 0 - 16384 + 2147483647 32 @@ -67,7 +67,7 @@ - 16384 + 2147483647 32 diff --git a/src/editor/dialogs/editspritedialog.ui b/src/editor/dialogs/editspritedialog.ui index 4c5f468..78ef083 100644 --- a/src/editor/dialogs/editspritedialog.ui +++ b/src/editor/dialogs/editspritedialog.ui @@ -168,7 +168,7 @@ - + :/qtgameengine/icons/new.png:/qtgameengine/icons/new.png @@ -177,7 +177,7 @@ - + :/qtgameengine/icons/open.png:/qtgameengine/icons/open.png @@ -186,7 +186,7 @@ - + :/qtgameengine/icons/create-group.png:/qtgameengine/icons/create-group.png @@ -198,7 +198,7 @@ - + :/qtgameengine/icons/save.png:/qtgameengine/icons/save.png @@ -223,7 +223,7 @@ - + :/qtgameengine/icons/ok.png:/qtgameengine/icons/ok.png @@ -232,7 +232,7 @@ - + diff --git a/src/editor/dialogs/fontpropertiesdialog.ui b/src/editor/dialogs/fontpropertiesdialog.ui index e6be99c..69ba919 100644 --- a/src/editor/dialogs/fontpropertiesdialog.ui +++ b/src/editor/dialogs/fontpropertiesdialog.ui @@ -14,7 +14,7 @@ Font Properties - + :/qtgameengine/icons/font-file.png:/qtgameengine/icons/font-file.png @@ -220,7 +220,7 @@ - + diff --git a/src/editor/dialogs/triggerconditiondialog.cpp b/src/editor/dialogs/genericcodeeditordialog.cpp similarity index 59% rename from src/editor/dialogs/triggerconditiondialog.cpp rename to src/editor/dialogs/genericcodeeditordialog.cpp index bff7cac..1b6b03c 100644 --- a/src/editor/dialogs/triggerconditiondialog.cpp +++ b/src/editor/dialogs/genericcodeeditordialog.cpp @@ -1,7 +1,7 @@ -#include "triggerconditiondialog.h" +#include "genericcodeeditordialog.h" -TriggerConditionDialog::TriggerConditionDialog(QWidget *parent) : - CodeEditorDialog{tr("Trigger condition"), parent} +GenericCodeEditorDialog::GenericCodeEditorDialog(const QString &title, QWidget *parent) : + CodeEditorDialog{title, parent} { #ifdef Q_OS_LINUX setWindowFlags((windowFlags() & ~Qt::Dialog) | Qt::Window); diff --git a/src/editor/dialogs/genericcodeeditordialog.h b/src/editor/dialogs/genericcodeeditordialog.h new file mode 100644 index 0000000..42759a2 --- /dev/null +++ b/src/editor/dialogs/genericcodeeditordialog.h @@ -0,0 +1,11 @@ +#pragma once + +#include "codeeditordialog.h" + +class GenericCodeEditorDialog : public CodeEditorDialog +{ + Q_OBJECT + +public: + explicit GenericCodeEditorDialog(const QString &title, QWidget *parent = nullptr); +}; diff --git a/src/editor/dialogs/imageeditordialog.ui b/src/editor/dialogs/imageeditordialog.ui index d2df2ce..513f8ce 100644 --- a/src/editor/dialogs/imageeditordialog.ui +++ b/src/editor/dialogs/imageeditordialog.ui @@ -176,7 +176,7 @@ - + :/qtgameengine/icons/new.png:/qtgameengine/icons/new.png @@ -188,7 +188,7 @@ - + :/qtgameengine/icons/save.png:/qtgameengine/icons/save.png @@ -200,7 +200,7 @@ - + :/qtgameengine/icons/arrow-left.png:/qtgameengine/icons/arrow-left.png @@ -212,7 +212,7 @@ - + :/qtgameengine/icons/arrow-right.png:/qtgameengine/icons/arrow-right.png @@ -224,7 +224,7 @@ - + :/qtgameengine/icons/ok.png:/qtgameengine/icons/ok.png @@ -236,7 +236,7 @@ - + :/qtgameengine/icons/open.png:/qtgameengine/icons/open.png @@ -245,7 +245,7 @@ - + :/qtgameengine/icons/undo.png:/qtgameengine/icons/undo.png @@ -254,7 +254,7 @@ - + :/qtgameengine/icons/redo.png:/qtgameengine/icons/redo.png @@ -305,7 +305,7 @@ - + diff --git a/src/editor/dialogs/includedfilesdialog.ui b/src/editor/dialogs/includedfilesdialog.ui index f36e070..72e0368 100644 --- a/src/editor/dialogs/includedfilesdialog.ui +++ b/src/editor/dialogs/includedfilesdialog.ui @@ -42,7 +42,7 @@ &Add - + :/qtgameengine/icons/add.png:/qtgameengine/icons/add.png @@ -56,7 +56,7 @@ &Change - + :/qtgameengine/icons/replace.png:/qtgameengine/icons/replace.png @@ -70,7 +70,7 @@ &Delete - + :/qtgameengine/icons/delete.png:/qtgameengine/icons/delete.png @@ -84,7 +84,7 @@ &Clear - + :/qtgameengine/icons/new.png:/qtgameengine/icons/new.png @@ -107,7 +107,7 @@ - + diff --git a/src/editor/dialogs/objectinformationdialog.ui b/src/editor/dialogs/objectinformationdialog.ui index 8a10df6..205fdf1 100644 --- a/src/editor/dialogs/objectinformationdialog.ui +++ b/src/editor/dialogs/objectinformationdialog.ui @@ -14,7 +14,7 @@ Information about all Objects - + :/qtgameengine/icons/object-file.png:/qtgameengine/icons/object-file.png @@ -58,7 +58,7 @@ - + :/qtgameengine/icons/ok.png:/qtgameengine/icons/ok.png @@ -70,7 +70,7 @@ - + :/qtgameengine/icons/save.png:/qtgameengine/icons/save.png @@ -82,7 +82,7 @@ - + :/qtgameengine/icons/print.png:/qtgameengine/icons/print.png @@ -94,7 +94,7 @@ - + diff --git a/src/editor/dialogs/objectpropertiesdialog.cpp b/src/editor/dialogs/objectpropertiesdialog.cpp index e7083b8..947f736 100644 --- a/src/editor/dialogs/objectpropertiesdialog.cpp +++ b/src/editor/dialogs/objectpropertiesdialog.cpp @@ -18,7 +18,7 @@ ObjectPropertiesDialog::ObjectPropertiesDialog(Object &object, ProjectTreeModel m_projectModel{projectModel}, m_events{m_object.events}, m_eventsModel{std::make_unique(m_events)}, - m_spritesMenu{new QMenu{m_ui->toolButtonSprite}}, + m_menuSprites{new QMenu{this}}, m_spriteName{object.spriteName} { m_ui->setupUi(this); @@ -33,7 +33,8 @@ ObjectPropertiesDialog::ObjectPropertiesDialog(Object &object, ProjectTreeModel m_ui->lineEditName->setText(m_object.name); m_ui->lineEditSprite->setText(m_spriteName.isEmpty() ? tr("") : m_spriteName); updateSpritePreview(); - m_ui->toolButtonSprite->setMenu(m_spritesMenu); + m_menuSprites->setParent(m_ui->toolButtonSprite); + m_ui->toolButtonSprite->setMenu(m_menuSprites); m_ui->checkBoxVisible->setChecked(m_object.visible); m_ui->checkBoxSolid->setChecked(m_object.solid); m_ui->spinBoxDepth->setValue(m_object.depth); @@ -83,7 +84,7 @@ ObjectPropertiesDialog::ObjectPropertiesDialog(Object &object, ProjectTreeModel connect(m_ui->checkBoxPersistent, &QCheckBox::toggled, this, &ObjectPropertiesDialog::changed); - connect(m_spritesMenu, &QMenu::aboutToShow, + connect(m_menuSprites, &QMenu::aboutToShow, this, &ObjectPropertiesDialog::spritesMenuAboutToShow); connect(m_ui->listViewEvents->selectionModel(), &QItemSelectionModel::currentChanged, @@ -289,10 +290,10 @@ void ObjectPropertiesDialog::spritePixmapsChanged(const Sprite &sprite) void ObjectPropertiesDialog::spritesMenuAboutToShow() { - m_spritesMenu->clear(); - m_spritesMenu->addAction(tr(""), this, &ObjectPropertiesDialog::clearSprite); + m_menuSprites->clear(); + m_menuSprites->addAction(tr(""), this, &ObjectPropertiesDialog::clearSprite); for (const auto &sprite : m_projectModel.project()->sprites) - m_spritesMenu->addAction(sprite.pixmaps.empty() ? QPixmap{} : sprite.pixmaps.front(), + m_menuSprites->addAction(sprite.pixmaps.empty() ? QPixmap{} : sprite.pixmaps.front(), sprite.name, this, [&sprite,this](){ setSprite(sprite); }); diff --git a/src/editor/dialogs/objectpropertiesdialog.h b/src/editor/dialogs/objectpropertiesdialog.h index 39ee415..bdf4c46 100644 --- a/src/editor/dialogs/objectpropertiesdialog.h +++ b/src/editor/dialogs/objectpropertiesdialog.h @@ -60,7 +60,7 @@ private: const std::unique_ptr m_eventsModel; - QMenu * const m_spritesMenu; + QMenu * const m_menuSprites; QString m_spriteName; diff --git a/src/editor/dialogs/objectpropertiesdialog.ui b/src/editor/dialogs/objectpropertiesdialog.ui index 493a475..4e391f1 100644 --- a/src/editor/dialogs/objectpropertiesdialog.ui +++ b/src/editor/dialogs/objectpropertiesdialog.ui @@ -14,7 +14,7 @@ Object Properties - + :/qtgameengine/icons/object-file.png:/qtgameengine/icons/object-file.png @@ -305,7 +305,7 @@ Show Information - + :/qtgameengine/icons/info.png:/qtgameengine/icons/info.png @@ -416,7 +416,7 @@ - + diff --git a/src/editor/dialogs/pathpropertiesdialog.cpp b/src/editor/dialogs/pathpropertiesdialog.cpp index 2718b7f..62041f0 100644 --- a/src/editor/dialogs/pathpropertiesdialog.cpp +++ b/src/editor/dialogs/pathpropertiesdialog.cpp @@ -3,6 +3,8 @@ #include #include +#include +#include #include #include #include @@ -16,39 +18,60 @@ PathPropertiesDialog::PathPropertiesDialog(Path &path, ProjectTreeModel &project m_ui{std::make_unique()}, m_path{path}, m_projectModel{projectModel}, - m_points{path.points}, + m_points{m_path.points}, m_pointsModel{std::make_unique(m_points, this)}, m_spinBoxSnapX{new QSpinBox{this}}, m_spinBoxSnapY{new QSpinBox{this}}, + m_menuRooms{new QMenu{this}}, m_labelX{new QLabel{tr("x: %0").arg(0)}}, m_labelY{new QLabel{tr("y: %0").arg(0)}}, m_labelArea{new QLabel{tr("Area: (%0,%1)->(%2,%3)").arg(0).arg(0).arg(0).arg(0)}} { m_ui->setupUi(this); - m_ui->widget->setPoints(&m_points); + m_ui->pathPointsWidget->setPoints(&m_points); + m_ui->pathPointsWidget->setSnapX(m_path.snapX); + m_ui->pathPointsWidget->setSnapY(m_path.snapY); + m_ui->pathPointsWidget->setGridEnabled(m_path.gridEnabled); updateTitle(); { - auto label = new QLabel{tr("Snap X:"), this}; - label->setBuddy(m_spinBoxSnapX); - m_ui->toolBar->insertWidget(m_ui->toolBar->actions().at(18), label); + int index{18}; + + { + auto label = new QLabel{tr("Snap &X:"), this}; + label->setBuddy(m_spinBoxSnapX); + m_ui->toolBar->insertWidget(m_ui->toolBar->actions().at(index++), label); + } + m_spinBoxSnapX->setValue(m_ui->pathPointsWidget->snapX()); + m_spinBoxSnapX->setMaximumWidth(50); + m_ui->toolBar->insertWidget(m_ui->toolBar->actions().at(index++), m_spinBoxSnapX); + + { + auto label = new QLabel{tr("Snap &Y:"), this}; + label->setBuddy(m_spinBoxSnapY); + m_ui->toolBar->insertWidget(m_ui->toolBar->actions().at(index++), label); + } + m_spinBoxSnapY->setValue(m_ui->pathPointsWidget->snapY()); + m_spinBoxSnapY->setMaximumWidth(50); + m_ui->toolBar->insertWidget(m_ui->toolBar->actions().at(index++), m_spinBoxSnapY); } - m_spinBoxSnapX->setValue(m_ui->widget->gridX()); - m_spinBoxSnapX->setMaximumWidth(50); - m_ui->toolBar->insertWidget(m_ui->toolBar->actions().at(19), m_spinBoxSnapX); + + m_ui->actionGridEnabled->setChecked(m_ui->pathPointsWidget->gridEnabled()); { - auto label = new QLabel{tr("Snap Y:"), this}; - label->setBuddy(m_spinBoxSnapY); - m_ui->toolBar->insertWidget(m_ui->toolBar->actions().at(20), label); + auto toolButton = new QToolButton{this}; + toolButton->setText(tr("Show")); + toolButton->setWhatsThis(tr("Indicate the room to show as background")); + toolButton->setIcon(QIcon{":/qtgameengine/icons/room.png"}); + toolButton->setPopupMode(QToolButton::InstantPopup); + toolButton->setMenu(m_menuRooms); + m_ui->toolBar->addWidget(toolButton); } - m_spinBoxSnapY->setValue(m_ui->widget->gridY()); - m_spinBoxSnapY->setMaximumWidth(50); - m_ui->toolBar->insertWidget(m_ui->toolBar->actions().at(21), m_spinBoxSnapY); - m_ui->actionGrid->setChecked(m_ui->widget->showGrid()); + connect(m_menuRooms, &QMenu::aboutToShow, + this, &PathPropertiesDialog::roomsMenuAboutToShow); m_ui->treeView->setModel(m_pointsModel.get()); @@ -57,7 +80,7 @@ PathPropertiesDialog::PathPropertiesDialog(Path &path, ProjectTreeModel &project m_ui->checkBoxClosed->setChecked(m_path.closed); m_ui->spinBoxPrecision->setValue(m_path.precision); - m_ui->widget->setClosed(m_path.closed); + m_ui->pathPointsWidget->setClosed(m_path.closed); m_labelX->setFrameStyle(QFrame::Sunken); m_ui->statusbar->addWidget(m_labelX, 1); @@ -76,9 +99,9 @@ PathPropertiesDialog::PathPropertiesDialog(Path &path, ProjectTreeModel &project m_ui->treeView->setColumnWidth(1, 75); - connect(m_ui->widget, &PathPointsWidget::pointInserted, + connect(m_ui->pathPointsWidget, &PathPointsWidget::pointInserted, m_pointsModel.get(), &PathPointsModel::pointInserted); - connect(m_ui->widget, &PathPointsWidget::pointMoved, + connect(m_ui->pathPointsWidget, &PathPointsWidget::pointMoved, m_pointsModel.get(), &PathPointsModel::pointMoved); connect(&m_projectModel, &ProjectTreeModel::pathNameChanged, @@ -87,27 +110,61 @@ PathPropertiesDialog::PathPropertiesDialog(Path &path, ProjectTreeModel &project connect(m_ui->treeView->selectionModel(), &QItemSelectionModel::currentChanged, this, &PathPropertiesDialog::selectionChanged); - connect(m_ui->widget, &PathPointsWidget::gridXChanged, + connect(m_ui->actionUndo, &QAction::triggered, + this, &PathPropertiesDialog::undo); + connect(m_ui->actionClear, &QAction::triggered, + this, &PathPropertiesDialog::clearPath); + connect(m_ui->actionReverse, &QAction::triggered, + this, &PathPropertiesDialog::reversePath); + connect(m_ui->actionShift, &QAction::triggered, + this, &PathPropertiesDialog::shiftPath); + connect(m_ui->actionMirrorHorizontally, &QAction::triggered, + this, &PathPropertiesDialog::mirrorPathHorizontally); + connect(m_ui->actionMirrorVertically, &QAction::triggered, + this, &PathPropertiesDialog::mirrorPathVertically); + connect(m_ui->actionRotate, &QAction::triggered, + this, &PathPropertiesDialog::rotatePath); + connect(m_ui->actionScale, &QAction::triggered, + this, &PathPropertiesDialog::scalePath); + connect(m_ui->actionShiftLeft, &QAction::triggered, + this, &PathPropertiesDialog::shiftViewLeft); + connect(m_ui->actionShiftRight, &QAction::triggered, + this, &PathPropertiesDialog::shiftViewRight); + connect(m_ui->actionShiftUp, &QAction::triggered, + this, &PathPropertiesDialog::shiftViewUp); + connect(m_ui->actionShiftDown, &QAction::triggered, + this, &PathPropertiesDialog::shiftViewDown); + connect(m_ui->actionCenterView, &QAction::triggered, + this, &PathPropertiesDialog::centerView); + + connect(m_ui->pathPointsWidget, &PathPointsWidget::snapXChanged, m_spinBoxSnapX, &QSpinBox::setValue); - connect(m_ui->widget, &PathPointsWidget::gridYChanged, + connect(m_ui->pathPointsWidget, &PathPointsWidget::snapYChanged, m_spinBoxSnapY, &QSpinBox::setValue); - connect(m_ui->widget, &PathPointsWidget::showGridChanged, - m_ui->actionGrid, &QAction::setChecked); - connect(m_ui->widget, &PathPointsWidget::closedChanged, + connect(m_ui->pathPointsWidget, &PathPointsWidget::gridEnabledChanged, + m_ui->actionGridEnabled, &QAction::setChecked); + connect(m_ui->pathPointsWidget, &PathPointsWidget::closedChanged, m_ui->checkBoxClosed, &QCheckBox::setChecked); - connect(m_ui->widget, &PathPointsWidget::cursorMoved, + connect(m_ui->pathPointsWidget, &PathPointsWidget::cursorMoved, this, &PathPropertiesDialog::cursorMoved); - connect(m_ui->widget, &PathPointsWidget::selectedIndexChanged, + connect(m_ui->pathPointsWidget, &PathPointsWidget::selectedIndexChanged, this, &PathPropertiesDialog::selectedPointChanged); connect(m_spinBoxSnapX, &QSpinBox::valueChanged, - m_ui->widget, &PathPointsWidget::setGridX); + m_ui->pathPointsWidget, &PathPointsWidget::setSnapX); connect(m_spinBoxSnapY, &QSpinBox::valueChanged, - m_ui->widget, &PathPointsWidget::setGridY); - connect(m_ui->actionGrid, &QAction::toggled, - m_ui->widget, &PathPointsWidget::setShowGrid); + m_ui->pathPointsWidget, &PathPointsWidget::setSnapY); + connect(m_ui->actionGridEnabled, &QAction::toggled, + m_ui->pathPointsWidget, &PathPointsWidget::setGridEnabled); connect(m_ui->checkBoxClosed, &QCheckBox::toggled, - m_ui->widget, &PathPointsWidget::setClosed); + m_ui->pathPointsWidget, &PathPointsWidget::setClosed); + + connect(m_spinBoxSnapX, &QSpinBox::valueChanged, + this, &PathPropertiesDialog::changed); + connect(m_spinBoxSnapY, &QSpinBox::valueChanged, + this, &PathPropertiesDialog::changed); + connect(m_ui->actionGridEnabled, &QAction::toggled, + this, &PathPropertiesDialog::changed); connect(m_ui->spinBoxX, &QSpinBox::valueChanged, this, &PathPropertiesDialog::pointFieldsChanged); @@ -129,17 +186,17 @@ PathPropertiesDialog::PathPropertiesDialog(Path &path, ProjectTreeModel &project connect(m_pointsModel.get(), &QAbstractTableModel::rowsInserted, this, &PathPropertiesDialog::changed); connect(m_pointsModel.get(), &QAbstractTableModel::rowsInserted, - m_ui->widget, qOverload<>(&PathPointsWidget::update)); + m_ui->pathPointsWidget, qOverload<>(&PathPointsWidget::update)); connect(m_pointsModel.get(), &QAbstractTableModel::rowsRemoved, this, &PathPropertiesDialog::changed); connect(m_pointsModel.get(), &QAbstractTableModel::rowsRemoved, - m_ui->widget, qOverload<>(&PathPointsWidget::update)); + m_ui->pathPointsWidget, qOverload<>(&PathPointsWidget::update)); connect(m_pointsModel.get(), &QAbstractTableModel::dataChanged, this, &PathPropertiesDialog::dataChanged); connect(m_pointsModel.get(), &QAbstractTableModel::dataChanged, this, &PathPropertiesDialog::changed); connect(m_pointsModel.get(), &QAbstractTableModel::dataChanged, - m_ui->widget, qOverload<>(&PathPointsWidget::update)); + m_ui->pathPointsWidget, qOverload<>(&PathPointsWidget::update)); connect(m_ui->radioButtonStraight, &QRadioButton::toggled, this, &PathPropertiesDialog::changed); connect(m_ui->radioButtonSmooth, &QRadioButton::toggled, @@ -183,8 +240,9 @@ void PathPropertiesDialog::accept() m_path.closed = m_ui->checkBoxClosed->isChecked(); m_path.precision = m_ui->spinBoxPrecision->value(); - - // TODO update points + m_path.snapX = m_spinBoxSnapX->value(); + m_path.snapY = m_spinBoxSnapY->value(); + m_path.gridEnabled = m_ui->actionGridEnabled->isChecked(); QDialog::accept(); } @@ -223,11 +281,11 @@ void PathPropertiesDialog::selectionChanged(const QModelIndex &index) { if (!index.isValid()) { - m_ui->widget->setSelectedIndex(std::nullopt); + m_ui->pathPointsWidget->setSelectedIndex(std::nullopt); return; } - m_ui->widget->setSelectedIndex(index.row()); + m_ui->pathPointsWidget->setSelectedIndex(index.row()); updatePointFields(); } @@ -284,6 +342,71 @@ void PathPropertiesDialog::deletePoint() } } +void PathPropertiesDialog::undo() +{ + QMessageBox::warning(this, tr("Not yet implemented"), tr("Not yet implemented")); +} + +void PathPropertiesDialog::clearPath() +{ + QMessageBox::warning(this, tr("Not yet implemented"), tr("Not yet implemented")); +} + +void PathPropertiesDialog::reversePath() +{ + QMessageBox::warning(this, tr("Not yet implemented"), tr("Not yet implemented")); +} + +void PathPropertiesDialog::shiftPath() +{ + QMessageBox::warning(this, tr("Not yet implemented"), tr("Not yet implemented")); +} + +void PathPropertiesDialog::mirrorPathHorizontally() +{ + QMessageBox::warning(this, tr("Not yet implemented"), tr("Not yet implemented")); +} + +void PathPropertiesDialog::mirrorPathVertically() +{ + QMessageBox::warning(this, tr("Not yet implemented"), tr("Not yet implemented")); +} + +void PathPropertiesDialog::rotatePath() +{ + QMessageBox::warning(this, tr("Not yet implemented"), tr("Not yet implemented")); +} + +void PathPropertiesDialog::scalePath() +{ + QMessageBox::warning(this, tr("Not yet implemented"), tr("Not yet implemented")); +} + +void PathPropertiesDialog::shiftViewLeft() +{ + QMessageBox::warning(this, tr("Not yet implemented"), tr("Not yet implemented")); +} + +void PathPropertiesDialog::shiftViewRight() +{ + QMessageBox::warning(this, tr("Not yet implemented"), tr("Not yet implemented")); +} + +void PathPropertiesDialog::shiftViewUp() +{ + QMessageBox::warning(this, tr("Not yet implemented"), tr("Not yet implemented")); +} + +void PathPropertiesDialog::shiftViewDown() +{ + QMessageBox::warning(this, tr("Not yet implemented"), tr("Not yet implemented")); +} + +void PathPropertiesDialog::centerView() +{ + QMessageBox::warning(this, tr("Not yet implemented"), tr("Not yet implemented")); +} + void PathPropertiesDialog::pointFieldsChanged() { const auto index = m_ui->treeView->currentIndex(); @@ -321,6 +444,18 @@ void PathPropertiesDialog::pathNameChanged(const Path &path) updateTitle(); } +void PathPropertiesDialog::roomsMenuAboutToShow() +{ + m_menuRooms->clear(); + qDebug() << "called" << tr("No Room"); + m_menuRooms->addAction(tr("No Room")); + for (const auto &room : m_projectModel.project()->rooms) + { + qDebug() << "called" << room.name; + m_menuRooms->addAction(room.name); + } +} + void PathPropertiesDialog::updateTitle() { setWindowTitle(tr("Path Properties: %0%1") diff --git a/src/editor/dialogs/pathpropertiesdialog.h b/src/editor/dialogs/pathpropertiesdialog.h index 21c2299..f0a615a 100644 --- a/src/editor/dialogs/pathpropertiesdialog.h +++ b/src/editor/dialogs/pathpropertiesdialog.h @@ -9,6 +9,7 @@ class QSpinBox; class QLabel; +class QMenu; namespace Ui { class PathPropertiesDialog; } class ProjectTreeModel; class PathPointsModel; @@ -34,11 +35,27 @@ private slots: void insertPoint(); void deletePoint(); + void undo(); + void clearPath(); + void reversePath(); + void shiftPath(); + void mirrorPathHorizontally(); + void mirrorPathVertically(); + void rotatePath(); + void scalePath(); + void shiftViewLeft(); + void shiftViewRight(); + void shiftViewUp(); + void shiftViewDown(); + void centerView(); + void pointFieldsChanged(); void changed(); void pathNameChanged(const Path &path); + void roomsMenuAboutToShow(); + private: void updateTitle(); void updatePointFields(); @@ -59,6 +76,8 @@ private: QSpinBox * const m_spinBoxSnapX; QSpinBox * const m_spinBoxSnapY; + QMenu * const m_menuRooms; + QLabel * const m_labelX; QLabel * const m_labelY; QLabel * const m_labelArea; diff --git a/src/editor/dialogs/pathpropertiesdialog.ui b/src/editor/dialogs/pathpropertiesdialog.ui index b276450..2bd5b28 100644 --- a/src/editor/dialogs/pathpropertiesdialog.ui +++ b/src/editor/dialogs/pathpropertiesdialog.ui @@ -14,7 +14,7 @@ Path Properties - + :/qtgameengine/icons/path-file.png:/qtgameengine/icons/path-file.png @@ -79,19 +79,18 @@ - + - - + + - - + @@ -158,6 +157,9 @@ 0 + + true + @@ -196,7 +198,7 @@ X-coordinate of the point - 16384 + 2147483647 @@ -216,7 +218,7 @@ Y-coordinate of the point - 16384 + 2147483647 @@ -236,7 +238,7 @@ Relative speed at this point (100 = default) - 16384 + 2147483647 @@ -391,7 +393,7 @@ - + 300 @@ -410,7 +412,7 @@ - + :/qtgameengine/icons/ok.png:/qtgameengine/icons/ok.png @@ -422,7 +424,7 @@ - + :/qtgameengine/icons/undo.png:/qtgameengine/icons/undo.png @@ -434,7 +436,7 @@ - + :/qtgameengine/icons/new.png:/qtgameengine/icons/new.png @@ -446,7 +448,7 @@ - + :/qtgameengine/icons/rotate.png:/qtgameengine/icons/rotate.png @@ -458,7 +460,7 @@ - + :/qtgameengine/icons/move.png:/qtgameengine/icons/move.png @@ -470,7 +472,7 @@ - + :/qtgameengine/icons/flip-horizontal.png:/qtgameengine/icons/flip-horizontal.png @@ -480,9 +482,9 @@ Mirror the path horizontally - + - + :/qtgameengine/icons/flip-vertical.png:/qtgameengine/icons/flip-vertical.png @@ -494,7 +496,7 @@ - + :/qtgameengine/icons/rotate.png:/qtgameengine/icons/rotate.png @@ -506,7 +508,7 @@ - + :/qtgameengine/icons/scale.png:/qtgameengine/icons/scale.png @@ -518,7 +520,7 @@ - + :/qtgameengine/icons/arrow-left.png:/qtgameengine/icons/arrow-left.png @@ -530,7 +532,7 @@ - + :/qtgameengine/icons/arrow-right.png:/qtgameengine/icons/arrow-right.png @@ -540,9 +542,9 @@ Shift the view to the right - + - + :/qtgameengine/icons/arrow-up.png:/qtgameengine/icons/arrow-up.png @@ -552,9 +554,9 @@ Shift the view to the top - + - + :/qtgameengine/icons/arrow-down.png:/qtgameengine/icons/arrow-down.png @@ -566,7 +568,7 @@ - + :/qtgameengine/icons/center.png:/qtgameengine/icons/center.png @@ -576,7 +578,7 @@ Center view around the path - + true @@ -584,7 +586,7 @@ true - + :/qtgameengine/icons/grid.png:/qtgameengine/icons/grid.png @@ -594,18 +596,6 @@ Toggle the showing of the grid - - - - :/qtgameengine/icons/room.png:/qtgameengine/icons/room.png - - - Room - - - Indicate the room to show as background - - @@ -616,7 +606,7 @@ - + diff --git a/src/editor/dialogs/roompropertiesdialog.cpp b/src/editor/dialogs/roompropertiesdialog.cpp index 024da6a..5e79760 100644 --- a/src/editor/dialogs/roompropertiesdialog.cpp +++ b/src/editor/dialogs/roompropertiesdialog.cpp @@ -1,14 +1,306 @@ #include "roompropertiesdialog.h" #include "ui_roompropertiesdialog.h" +#include +#include +#include +#include +#include + +#include "projectcontainer.h" +#include "models/projecttreemodel.h" +#include "genericcodeeditordialog.h" + RoomPropertiesDialog::RoomPropertiesDialog(Room &room, ProjectTreeModel &projectModel, QWidget *parent) : QDialog{parent}, - m_ui{std::make_unique()} + m_ui{std::make_unique()}, + m_room{room}, + m_projectModel{projectModel}, + m_creationCode{m_room.creationCode}, + m_spinBoxSnapX{new QSpinBox{this}}, + m_spinBoxSnapY{new QSpinBox{this}}, + m_labelX{new QLabel{tr("x: %0").arg(0)}}, + m_labelY{new QLabel{tr("y: %0").arg(0)}} { - Q_UNUSED(room) - Q_UNUSED(projectModel) - m_ui->setupUi(this); + + updateTitle(); + + m_ui->roomEditWidget->setFixedWidth(m_room.width); + m_ui->roomEditWidget->setFixedHeight(m_room.height); + m_ui->roomEditWidget->setSnapX(m_room.snapX); + m_ui->roomEditWidget->setSnapY(m_room.snapY); + m_ui->roomEditWidget->setGridEnabled(m_room.gridEnabled); + m_ui->roomEditWidget->setIsometricGrid(m_room.isometricGrid); + + { + int index{11}; + + { + auto label = new QLabel{tr("Snap &X:"), this}; + label->setBuddy(m_spinBoxSnapX); + m_ui->toolBar->insertWidget(m_ui->toolBar->actions().at(index++), label); + } + m_spinBoxSnapX->setValue(m_ui->roomEditWidget->snapX()); + m_spinBoxSnapX->setMaximumWidth(50); + m_ui->toolBar->insertWidget(m_ui->toolBar->actions().at(index++), m_spinBoxSnapX); + + { + auto label = new QLabel{tr("Snap &Y:"), this}; + label->setBuddy(m_spinBoxSnapY); + m_ui->toolBar->insertWidget(m_ui->toolBar->actions().at(index++), label); + } + m_spinBoxSnapY->setValue(m_ui->roomEditWidget->snapY()); + m_spinBoxSnapY->setMaximumWidth(50); + m_ui->toolBar->insertWidget(m_ui->toolBar->actions().at(index++), m_spinBoxSnapY); + } + + m_ui->actionGridEnabled->setChecked(m_ui->roomEditWidget->gridEnabled()); + m_ui->actionIsometricGrid->setChecked(m_ui->roomEditWidget->isometricGrid()); + + { + auto toolButton = new QToolButton{this}; + toolButton->setText(tr("Show")); + toolButton->setIcon(QIcon{":/qtgameengine/icons/find.png"}); + toolButton->setPopupMode(QToolButton::InstantPopup); + toolButton->addAction(m_ui->actionShowObjects); + toolButton->addAction(m_ui->actionShowTiles); + toolButton->addAction(m_ui->actionShowBackgrounds); + toolButton->addAction(m_ui->actionShowForegrounds); + toolButton->addAction(m_ui->actionShowViews); + m_ui->toolBar->addWidget(toolButton); + } + + m_ui->scrollArea->setBackgroundRole(QPalette::Dark); + + m_labelX->setFrameStyle(QFrame::Sunken); + m_ui->statusbar->addWidget(m_labelX, 1); + m_labelY->setFrameStyle(QFrame::Sunken); + m_ui->statusbar->addWidget(m_labelY, 1); + + { + auto frame = new QFrame{this}; + frame->setFrameStyle(QFrame::Sunken); + m_ui->statusbar->addPermanentWidget(frame, 4); + } + + m_ui->lineEditName->setText(m_room.name); + m_ui->lineEditCaption->setText(m_room.caption); + m_ui->spinBoxWidth->setValue(m_ui->roomEditWidget->width()); + m_ui->spinBoxHeight->setValue(m_ui->roomEditWidget->height()); + m_ui->spinBoxSpeed->setValue(m_room.speed); + m_ui->checkBoxPersistent->setChecked(m_room.persistent); + + connect(&m_projectModel, &ProjectTreeModel::roomNameChanged, + this, &RoomPropertiesDialog::roomNameChanged); + + connect(m_ui->actionUndo, &QAction::triggered, + this, &RoomPropertiesDialog::undo); + connect(m_ui->actionClear, &QAction::triggered, + this, &RoomPropertiesDialog::undo); + connect(m_ui->actionShift, &QAction::triggered, + this, &RoomPropertiesDialog::undo); + connect(m_ui->actionSortX, &QAction::triggered, + this, &RoomPropertiesDialog::undo); + connect(m_ui->actionSortY, &QAction::triggered, + this, &RoomPropertiesDialog::undo); + connect(m_ui->actionLock, &QAction::triggered, + this, &RoomPropertiesDialog::undo); + connect(m_ui->actionUnlock, &QAction::triggered, + this, &RoomPropertiesDialog::undo); + + connect(m_spinBoxSnapX, &QSpinBox::valueChanged, + m_ui->roomEditWidget, &RoomEditWidget::setSnapX); + connect(m_spinBoxSnapY, &QSpinBox::valueChanged, + m_ui->roomEditWidget, &RoomEditWidget::setSnapY); + connect(m_ui->actionGridEnabled, &QAction::toggled, + m_ui->roomEditWidget, &RoomEditWidget::setGridEnabled); + connect(m_ui->actionIsometricGrid, &QAction::toggled, + m_ui->roomEditWidget, &RoomEditWidget::setIsometricGrid); + + connect(m_ui->spinBoxWidth, &QSpinBox::valueChanged, + m_ui->roomEditWidget, &RoomEditWidget::setFixedWidth); + connect(m_ui->spinBoxHeight, &QSpinBox::valueChanged, + m_ui->roomEditWidget, &RoomEditWidget::setFixedHeight); + + connect(m_spinBoxSnapX, &QSpinBox::valueChanged, + this, &RoomPropertiesDialog::changed); + connect(m_spinBoxSnapY, &QSpinBox::valueChanged, + this, &RoomPropertiesDialog::changed); + connect(m_ui->actionGridEnabled, &QAction::toggled, + this, &RoomPropertiesDialog::changed); + connect(m_ui->actionIsometricGrid, &QAction::toggled, + this, &RoomPropertiesDialog::changed); + + connect(m_ui->lineEditName, &QLineEdit::textChanged, + this, &RoomPropertiesDialog::changed); + connect(m_ui->lineEditCaption, &QLineEdit::textChanged, + this, &RoomPropertiesDialog::changed); + connect(m_ui->spinBoxWidth, &QSpinBox::valueChanged, + this, &RoomPropertiesDialog::changed); + connect(m_ui->spinBoxHeight, &QSpinBox::valueChanged, + this, &RoomPropertiesDialog::changed); + connect(m_ui->spinBoxSpeed, &QSpinBox::valueChanged, + this, &RoomPropertiesDialog::changed); + connect(m_ui->checkBoxPersistent, &QCheckBox::toggled, + this, &RoomPropertiesDialog::changed); + + connect(m_ui->pushButtonCreationCode, &QAbstractButton::clicked, + this, &RoomPropertiesDialog::editCreationCode); + + connect(m_ui->roomEditWidget, &RoomEditWidget::snapXChanged, + m_spinBoxSnapX, &QSpinBox::setValue); + connect(m_ui->roomEditWidget, &RoomEditWidget::snapYChanged, + m_spinBoxSnapY, &QSpinBox::setValue); + connect(m_ui->roomEditWidget, &RoomEditWidget::gridEnabledChanged, + m_ui->actionGridEnabled, &QAction::setChecked); + connect(m_ui->roomEditWidget, &RoomEditWidget::isometricGridChanged, + m_ui->actionIsometricGrid, &QAction::setChecked); + connect(m_ui->roomEditWidget, &RoomEditWidget::cursorMoved, + this, &RoomPropertiesDialog::cursorMoved); } RoomPropertiesDialog::~RoomPropertiesDialog() = default; + +void RoomPropertiesDialog::accept() +{ + if (!m_unsavedChanges) + { + QDialog::reject(); + return; + } + + if (m_room.name != m_ui->lineEditName->text()) + { + if (!m_projectModel.rename(m_room, m_ui->lineEditName->text())) + { + QMessageBox::critical(this, tr("Renaming Room failed!"), tr("Renaming Room failed!")); + return; + } + } + + m_room.caption = m_ui->lineEditCaption->text(); + m_room.width = m_ui->spinBoxWidth->value(); + m_room.height = m_ui->spinBoxHeight->value(); + m_room.speed = m_ui->spinBoxSpeed->value(); + m_room.persistent = m_ui->checkBoxPersistent->isChecked(); + m_room.creationCode = m_creationCode; + m_room.snapX = m_spinBoxSnapX->value(); + m_room.snapY = m_spinBoxSnapY->value(); + m_room.gridEnabled = m_ui->actionGridEnabled->isChecked(); + m_room.isometricGrid = m_ui->actionIsometricGrid->isChecked(); + + QDialog::accept(); +} + +void RoomPropertiesDialog::reject() +{ + if (!m_unsavedChanges) + { + QDialog::reject(); + return; + } + + const auto result = QMessageBox::warning( + this, + tr("The Room has been modified."), + tr("Do you want to save your changes?"), + QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, + QMessageBox::Save + ); + switch (result) + { + case QMessageBox::Save: + accept(); + return; + case QMessageBox::Discard: + QDialog::reject(); + return; + case QMessageBox::Cancel: + return; + default: + qWarning() << "unexpected dialog result" << result; + } +} + +void RoomPropertiesDialog::undo() +{ + QMessageBox::warning(this, tr("Not yet implemented"), tr("Not yet implemented")); +} + +void RoomPropertiesDialog::clearInstances() +{ + QMessageBox::warning(this, tr("Not yet implemented"), tr("Not yet implemented")); +} + +void RoomPropertiesDialog::shiftInstances() +{ + QMessageBox::warning(this, tr("Not yet implemented"), tr("Not yet implemented")); +} + +void RoomPropertiesDialog::sortInstancesX() +{ + QMessageBox::warning(this, tr("Not yet implemented"), tr("Not yet implemented")); +} + +void RoomPropertiesDialog::sortInstancesY() +{ + QMessageBox::warning(this, tr("Not yet implemented"), tr("Not yet implemented")); +} + +void RoomPropertiesDialog::lockInstances() +{ + QMessageBox::warning(this, tr("Not yet implemented"), tr("Not yet implemented")); +} + +void RoomPropertiesDialog::unlockInstances() +{ + QMessageBox::warning(this, tr("Not yet implemented"), tr("Not yet implemented")); +} + +void RoomPropertiesDialog::editCreationCode() +{ + GenericCodeEditorDialog dialog{tr("Room Creation Code"), this}; + dialog.setScript(m_creationCode); + if (dialog.exec() == QDialog::Accepted) + { + m_creationCode = dialog.script(); + changed(); + } +} + +void RoomPropertiesDialog::changed() +{ + if (!m_unsavedChanges) + { + m_unsavedChanges = true; + updateTitle(); + } +} + +void RoomPropertiesDialog::roomNameChanged(const Room &room) +{ + if (&room != &m_room) + return; + + { + QSignalBlocker blocker{m_ui->lineEditName}; + m_ui->lineEditName->setText(room.name); + } + + updateTitle(); +} + +void RoomPropertiesDialog::cursorMoved(const QPoint &point) +{ + m_labelX->setText(tr("X: %0").arg(point.x())); + m_labelY->setText(tr("Y: %0").arg(point.y())); +} + +void RoomPropertiesDialog::updateTitle() +{ + setWindowTitle(tr("Room Properties: %0%1") + .arg(m_room.name) + .arg(m_unsavedChanges ? tr("*") : QString{}) + ); +} diff --git a/src/editor/dialogs/roompropertiesdialog.h b/src/editor/dialogs/roompropertiesdialog.h index 4799276..48e4e76 100644 --- a/src/editor/dialogs/roompropertiesdialog.h +++ b/src/editor/dialogs/roompropertiesdialog.h @@ -4,6 +4,8 @@ #include +class QSpinBox; +class QLabel; namespace Ui { class RoomPropertiesDialog; } struct Room; class ProjectTreeModel; @@ -16,6 +18,40 @@ public: explicit RoomPropertiesDialog(Room &room, ProjectTreeModel &projectModel, QWidget *parent = nullptr); ~RoomPropertiesDialog(); + void accept() override; + void reject() override; + +private slots: + void undo(); + void clearInstances(); + void shiftInstances(); + void sortInstancesX(); + void sortInstancesY(); + void lockInstances(); + void unlockInstances(); + void editCreationCode(); + + void changed(); + + void roomNameChanged(const Room &room); + + void cursorMoved(const QPoint &point); + private: + void updateTitle(); + const std::unique_ptr m_ui; + + Room &m_room; + ProjectTreeModel &m_projectModel; + + QString m_creationCode; + + bool m_unsavedChanges{}; + + QSpinBox * const m_spinBoxSnapX; + QSpinBox * const m_spinBoxSnapY; + + QLabel * const m_labelX; + QLabel * const m_labelY; }; diff --git a/src/editor/dialogs/roompropertiesdialog.ui b/src/editor/dialogs/roompropertiesdialog.ui index 7c3f317..8cd6f35 100644 --- a/src/editor/dialogs/roompropertiesdialog.ui +++ b/src/editor/dialogs/roompropertiesdialog.ui @@ -6,20 +6,532 @@ 0 0 - 400 - 300 + 921 + 576 Room Properties - + :/qtgameengine/icons/room-file.png:/qtgameengine/icons/room-file.png + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::Panel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + toolBar + + + false + + + + 16 + 16 + + + + + + + + + + + + + + + + + + + + + + + + + + + + QTabWidget::West + + + 0 + + + + &Objects + + + + 10 + + + 10 + + + 10 + + + 10 + + + + + + &Settings + + + + + + + + &Name: + + + lineEditName + + + + + + + + + + + + &Caption for the room: + + + lineEditCaption + + + + + + + + + + + + &Width: + + + spinBoxWidth + + + + + + + 2147483647 + + + 640 + + + + + + + &Height: + + + spinBoxHeight + + + + + + + 2147483647 + + + 480 + + + + + + + S&peed: + + + spinBoxSpeed + + + + + + + 2147483647 + + + 30 + + + + + + + + + P&ersistent + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + CreationCode + + + + :/qtgameengine/icons/script.png:/qtgameengine/icons/script.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + &Tiles + + + + + &Backgrounds + + + + + &Views + + + + + + + + + + + + 0 + 0 + 0 + 0 + + + + + + + + + + + + + + + + + :/qtgameengine/icons/ok.png:/qtgameengine/icons/ok.png + + + OK + + + Close the form, saving the changes + + + + + + :/qtgameengine/icons/undo.png:/qtgameengine/icons/undo.png + + + Undo + + + Undo the last changes in the room + + + + + + :/qtgameengine/icons/new.png:/qtgameengine/icons/new.png + + + Clear + + + Clear all instances from the room + + + + + + :/qtgameengine/icons/arrow-right.png:/qtgameengine/icons/arrow-right.png + + + Shift + + + Shift all instances by a given amount + + + + + + :/qtgameengine/icons/sort-x.png:/qtgameengine/icons/sort-x.png + + + Sort X + + + Sort all instances by x-coordinate + + + + + + :/qtgameengine/icons/sort-y.png:/qtgameengine/icons/sort-y.png + + + Sort Y + + + Sort all instances by y-coordinate + + + + + + :/qtgameengine/icons/lock.png:/qtgameengine/icons/lock.png + + + Lock + + + Lock all instances + + + + + + :/qtgameengine/icons/unlock.png:/qtgameengine/icons/unlock.png + + + Unlock + + + Unlock all instances + + + + + true + + + true + + + + :/qtgameengine/icons/grid.png:/qtgameengine/icons/grid.png + + + Grid + + + Toggle the showing of the grid + + + + + true + + + + :/qtgameengine/icons/isometric.png:/qtgameengine/icons/isometric.png + + + Isometric + + + Turn the grid into an isometric grid + + + + + true + + + true + + + Show Objects + + + + + true + + + true + + + Show Tiles + + + + + true + + + true + + + Show Backgrounds + + + + + true + + + true + + + Show Foregrounds + + + + + true + + + Show Views + + + + + RoomEditWidget + QWidget +
widgets/roomeditwidget.h
+ 1 +
+
- + - + + + actionOk + triggered() + RoomPropertiesDialog + accept() + + + -1 + -1 + + + 363 + 224 + + + + diff --git a/src/editor/dialogs/soundpropertiesdialog.ui b/src/editor/dialogs/soundpropertiesdialog.ui index 847ba71..e40cde2 100644 --- a/src/editor/dialogs/soundpropertiesdialog.ui +++ b/src/editor/dialogs/soundpropertiesdialog.ui @@ -14,7 +14,7 @@ Sound Properties - + :/qtgameengine/icons/sound-file.png:/qtgameengine/icons/sound-file.png @@ -50,7 +50,7 @@ &Load Sound - + :/qtgameengine/icons/open.png:/qtgameengine/icons/open.png @@ -64,7 +64,7 @@ - + :/qtgameengine/icons/run.png:/qtgameengine/icons/run.png @@ -78,7 +78,7 @@ - + :/qtgameengine/icons/exit.png:/qtgameengine/icons/exit.png @@ -94,7 +94,7 @@ Sa&ve Sound - + :/qtgameengine/icons/save.png:/qtgameengine/icons/save.png @@ -292,7 +292,7 @@ &Edit Sound - + :/qtgameengine/icons/sound.png:/qtgameengine/icons/sound.png @@ -325,7 +325,7 @@ - + diff --git a/src/editor/dialogs/spritepropertiesdialog.ui b/src/editor/dialogs/spritepropertiesdialog.ui index 4f8308f..641aaea 100644 --- a/src/editor/dialogs/spritepropertiesdialog.ui +++ b/src/editor/dialogs/spritepropertiesdialog.ui @@ -14,7 +14,7 @@ Sprite Properties - + :/qtgameengine/icons/sprite-file.png:/qtgameengine/icons/sprite-file.png @@ -67,7 +67,7 @@ &Load Sprite - + :/qtgameengine/icons/open.png:/qtgameengine/icons/open.png @@ -81,7 +81,7 @@ &Save Sprite - + :/qtgameengine/icons/save.png:/qtgameengine/icons/save.png @@ -95,7 +95,7 @@ &Edit Sprite - + :/qtgameengine/icons/edit.png:/qtgameengine/icons/edit.png @@ -339,7 +339,7 @@ - + diff --git a/src/editor/dialogs/timelinepropertiesdialog.ui b/src/editor/dialogs/timelinepropertiesdialog.ui index 4312d3d..a0f83ff 100644 --- a/src/editor/dialogs/timelinepropertiesdialog.ui +++ b/src/editor/dialogs/timelinepropertiesdialog.ui @@ -14,7 +14,7 @@ Time Line Properties - + :/qtgameengine/icons/timeline-file.png:/qtgameengine/icons/timeline-file.png @@ -61,7 +61,7 @@ &Add - + :/qtgameengine/icons/add.png:/qtgameengine/icons/add.png @@ -75,7 +75,7 @@ &Change - + :/qtgameengine/icons/replace.png:/qtgameengine/icons/replace.png @@ -89,7 +89,7 @@ &Delete - + :/qtgameengine/icons/delete.png:/qtgameengine/icons/delete.png @@ -103,7 +103,7 @@ &Shift - + :/qtgameengine/icons/arrow-right.png:/qtgameengine/icons/arrow-right.png @@ -117,7 +117,7 @@ C&lear - + :/qtgameengine/icons/new.png:/qtgameengine/icons/new.png @@ -131,7 +131,7 @@ D&uplicate - + :/qtgameengine/icons/copy.png:/qtgameengine/icons/copy.png @@ -158,7 +158,7 @@ S&pread - + :/qtgameengine/icons/scale.png:/qtgameengine/icons/scale.png @@ -172,7 +172,7 @@ &Merge - + :/qtgameengine/icons/merge.png:/qtgameengine/icons/merge.png @@ -201,7 +201,7 @@ Show &Information - + :/qtgameengine/icons/info.png:/qtgameengine/icons/info.png @@ -278,7 +278,7 @@ - + diff --git a/src/editor/dialogs/triggerconditiondialog.h b/src/editor/dialogs/triggerconditiondialog.h deleted file mode 100644 index 95c948d..0000000 --- a/src/editor/dialogs/triggerconditiondialog.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "codeeditordialog.h" - -class TriggerConditionDialog : public CodeEditorDialog -{ - Q_OBJECT - -public: - explicit TriggerConditionDialog(QWidget *parent = nullptr); -}; diff --git a/src/editor/dialogs/triggersdialog.cpp b/src/editor/dialogs/triggersdialog.cpp index 368cd76..b1f8d4a 100644 --- a/src/editor/dialogs/triggersdialog.cpp +++ b/src/editor/dialogs/triggersdialog.cpp @@ -6,7 +6,7 @@ #include "projectcontainer.h" #include "models/triggersmodel.h" -#include "triggerconditiondialog.h" +#include "genericcodeeditordialog.h" TriggersDialog::TriggersDialog(ProjectContainer &project, QWidget *parent) : QDialog{parent}, @@ -66,7 +66,7 @@ TriggersDialog::~TriggersDialog() = default; void TriggersDialog::openCodeEditor() { - TriggerConditionDialog dialog{this}; + GenericCodeEditorDialog dialog{tr("Trigger condition"), this}; dialog.setScript(m_ui->plainTextEdit->toPlainText()); if (dialog.exec() == QDialog::Accepted) { diff --git a/src/editor/dialogs/triggersdialog.ui b/src/editor/dialogs/triggersdialog.ui index 57f1f8a..9c9606d 100644 --- a/src/editor/dialogs/triggersdialog.ui +++ b/src/editor/dialogs/triggersdialog.ui @@ -40,7 +40,7 @@ &Load - + :/qtgameengine/icons/open.png:/qtgameengine/icons/open.png @@ -54,7 +54,7 @@ &Delete - + :/qtgameengine/icons/delete.png:/qtgameengine/icons/delete.png @@ -68,7 +68,7 @@ &Add - + :/qtgameengine/icons/add.png:/qtgameengine/icons/add.png @@ -82,7 +82,7 @@ &Clear - + :/qtgameengine/icons/new.png:/qtgameengine/icons/new.png @@ -96,7 +96,7 @@ &Save - + :/qtgameengine/icons/save.png:/qtgameengine/icons/save.png @@ -246,7 +246,7 @@ - + diff --git a/src/editor/dialogs/userdefinedconstantsdialog.ui b/src/editor/dialogs/userdefinedconstantsdialog.ui index 5d746d1..df1486d 100644 --- a/src/editor/dialogs/userdefinedconstantsdialog.ui +++ b/src/editor/dialogs/userdefinedconstantsdialog.ui @@ -14,7 +14,7 @@ User-Defined Constants - + :/qtgameengine/icons/constants.png:/qtgameengine/icons/constants.png @@ -39,7 +39,7 @@ &Insert - + :/qtgameengine/icons/add.png:/qtgameengine/icons/add.png @@ -53,7 +53,7 @@ &Sort - + :/qtgameengine/icons/sort.png:/qtgameengine/icons/sort.png @@ -67,7 +67,7 @@ &Delete - + :/qtgameengine/icons/delete.png:/qtgameengine/icons/delete.png @@ -81,7 +81,7 @@ &Load - + :/qtgameengine/icons/open.png:/qtgameengine/icons/open.png @@ -95,7 +95,7 @@ &Up - + :/qtgameengine/icons/arrow-up.png:/qtgameengine/icons/arrow-up.png @@ -109,7 +109,7 @@ &Add - + :/qtgameengine/icons/add.png:/qtgameengine/icons/add.png @@ -123,7 +123,7 @@ &Clear - + :/qtgameengine/icons/new.png:/qtgameengine/icons/new.png @@ -137,7 +137,7 @@ Do&wn - + :/qtgameengine/icons/arrow-down.png:/qtgameengine/icons/arrow-down.png @@ -151,7 +151,7 @@ Sa&ve - + :/qtgameengine/icons/save.png:/qtgameengine/icons/save.png @@ -174,7 +174,7 @@ - + diff --git a/src/editor/mainwindow.cpp b/src/editor/mainwindow.cpp index b5d40e7..f42142c 100644 --- a/src/editor/mainwindow.cpp +++ b/src/editor/mainwindow.cpp @@ -55,12 +55,10 @@ MainWindow::MainWindow(const QString &filePath, QWidget *parent) : { m_ui->setupUi(this); - { - QList sizes; - sizes.append(0.5 * m_ui->splitter->width()); - sizes.append(0.5 * m_ui->splitter->width()); - m_ui->splitter->setSizes(sizes); - } + m_ui->splitter->setSizes({ + m_ui->splitter->width() / 2, + m_ui->splitter->width() / 2 + }); m_ui->actionNew->setShortcut(QKeySequence::New); m_ui->actionOpen->setShortcut(QKeySequence::Open); diff --git a/src/editor/mainwindow.ui b/src/editor/mainwindow.ui index 730120c..85deec2 100644 --- a/src/editor/mainwindow.ui +++ b/src/editor/mainwindow.ui @@ -264,7 +264,7 @@ - + :/qtgameengine/icons/new.png:/qtgameengine/icons/new.png @@ -273,7 +273,7 @@ - + :/qtgameengine/icons/open.png:/qtgameengine/icons/open.png @@ -282,7 +282,7 @@ - + :/qtgameengine/icons/save.png:/qtgameengine/icons/save.png @@ -291,7 +291,7 @@ - + :/qtgameengine/icons/save-as.png:/qtgameengine/icons/save-as.png @@ -300,7 +300,7 @@ - + :/qtgameengine/icons/create-executable.png:/qtgameengine/icons/create-executable.png @@ -309,7 +309,7 @@ - + :/qtgameengine/icons/publish-game.png:/qtgameengine/icons/publish-game.png @@ -318,7 +318,7 @@ - + :/qtgameengine/icons/import-resources.png:/qtgameengine/icons/import-resources.png @@ -327,7 +327,7 @@ - + :/qtgameengine/icons/export-resources.png:/qtgameengine/icons/export-resources.png @@ -347,7 +347,7 @@ - + :/qtgameengine/icons/preferences.png:/qtgameengine/icons/preferences.png @@ -359,7 +359,7 @@ - + :/qtgameengine/icons/exit.png:/qtgameengine/icons/exit.png @@ -379,7 +379,7 @@ - + :/qtgameengine/icons/create.png:/qtgameengine/icons/create.png @@ -388,7 +388,7 @@ - + :/qtgameengine/icons/duplicate.png:/qtgameengine/icons/duplicate.png @@ -400,7 +400,7 @@ - + :/qtgameengine/icons/create-group.png:/qtgameengine/icons/create-group.png @@ -414,7 +414,7 @@ - + :/qtgameengine/icons/delete.png:/qtgameengine/icons/delete.png @@ -423,7 +423,7 @@ - + :/qtgameengine/icons/rename.png:/qtgameengine/icons/rename.png @@ -432,7 +432,7 @@ - + :/qtgameengine/icons/properties.png:/qtgameengine/icons/properties.png @@ -444,7 +444,7 @@ - + :/qtgameengine/icons/find.png:/qtgameengine/icons/find.png @@ -463,7 +463,7 @@ - + :/qtgameengine/icons/object-file.png:/qtgameengine/icons/object-file.png @@ -472,7 +472,7 @@ - + :/qtgameengine/icons/sprite.png:/qtgameengine/icons/sprite.png @@ -484,7 +484,7 @@ - + :/qtgameengine/icons/sound.png:/qtgameengine/icons/sound.png @@ -496,7 +496,7 @@ - + :/qtgameengine/icons/background.png:/qtgameengine/icons/background.png @@ -508,7 +508,7 @@ - + :/qtgameengine/icons/path.png:/qtgameengine/icons/path.png @@ -520,7 +520,7 @@ - + :/qtgameengine/icons/script.png:/qtgameengine/icons/script.png @@ -532,7 +532,7 @@ - + :/qtgameengine/icons/font.png:/qtgameengine/icons/font.png @@ -544,7 +544,7 @@ - + :/qtgameengine/icons/timeline.png:/qtgameengine/icons/timeline.png @@ -556,7 +556,7 @@ - + :/qtgameengine/icons/object.png:/qtgameengine/icons/object.png @@ -568,7 +568,7 @@ - + :/qtgameengine/icons/room.png:/qtgameengine/icons/room.png @@ -580,7 +580,7 @@ - + :/qtgameengine/icons/game-information.png:/qtgameengine/icons/game-information.png @@ -592,7 +592,7 @@ - + :/qtgameengine/icons/global-game-settings.png:/qtgameengine/icons/global-game-settings.png @@ -604,7 +604,7 @@ - + :/qtgameengine/icons/extension-packages.png:/qtgameengine/icons/extension-packages.png @@ -616,7 +616,7 @@ - + :/qtgameengine/icons/constants.png:/qtgameengine/icons/constants.png @@ -644,7 +644,7 @@ - + :/qtgameengine/icons/run.png:/qtgameengine/icons/run.png @@ -656,7 +656,7 @@ - + :/qtgameengine/icons/debug.png:/qtgameengine/icons/debug.png @@ -673,7 +673,7 @@ - + :/qtgameengine/icons/help.png:/qtgameengine/icons/help.png @@ -783,7 +783,7 @@ - + :/qtgameengine/icons/tile.png:/qtgameengine/icons/tile.png @@ -792,7 +792,7 @@ - + :/qtgameengine/icons/cascade.png:/qtgameengine/icons/cascade.png @@ -827,7 +827,7 @@ - + diff --git a/src/editor/widgets/pathpointswidget.cpp b/src/editor/widgets/pathpointswidget.cpp index 98eba03..92568fe 100644 --- a/src/editor/widgets/pathpointswidget.cpp +++ b/src/editor/widgets/pathpointswidget.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -24,27 +25,27 @@ void PathPointsWidget::setPoints(std::vector *points) update(); } -void PathPointsWidget::setShowGrid(bool showGrid) +void PathPointsWidget::setSnapX(int snapX) { - if (m_showGrid == showGrid) + if (m_snapX == snapX) return; - emit showGridChanged(m_showGrid = showGrid); + emit snapXChanged(m_snapX = snapX); update(); } -void PathPointsWidget::setGridX(int gridX) +void PathPointsWidget::setSnapY(int snapY) { - if (m_gridX == gridX) + if (m_snapY == snapY) return; - emit gridXChanged(m_gridX = gridX); + emit snapYChanged(m_snapY = snapY); update(); } -void PathPointsWidget::setGridY(int gridY) +void PathPointsWidget::setGridEnabled(bool gridEnabled) { - if (m_gridY == gridY) + if (m_gridEnabled == gridEnabled) return; - emit gridYChanged(m_gridY = gridY); + emit gridEnabledChanged(m_gridEnabled = gridEnabled); update(); } @@ -64,28 +65,36 @@ void PathPointsWidget::setSelectedIndex(const std::optional &select update(); } +void PathPointsWidget::setGridRole(QPalette::ColorRole gridRole) +{ + if (gridRole == m_gridRole) + return; + m_gridRole = gridRole; + update(); +} + void PathPointsWidget::paintEvent(QPaintEvent *event) { Q_UNUSED(event) - if (m_showGrid) + if (m_gridEnabled) { - if (!m_gridBrush || m_gridBrush->gridX != m_gridX || m_gridBrush->gridY != m_gridY) + if (!m_gridBrush || m_gridBrush->snapX != m_snapX || m_gridBrush->snapY != m_snapY) { - QPixmap pixmap{m_gridX, m_gridY}; + QPixmap pixmap{m_snapX, m_snapY}; { QPainter painter{&pixmap}; painter.setPen(palette().color(m_gridRole)); - painter.drawLine(0, 0, m_gridX, 0); - painter.drawLine(0, 0, 0, m_gridY); + painter.drawLine(0, 0, m_snapX, 0); + painter.drawLine(0, 0, 0, m_snapY); - painter.fillRect(1, 1, m_gridX - 1, m_gridY - 1, palette().color(backgroundRole())); + painter.fillRect(1, 1, m_snapX - 1, m_snapY - 1, palette().color(backgroundRole())); } m_gridBrush = GridBrush { - .gridX = m_gridX, - .gridY = m_gridY, + .snapX = m_snapX, + .snapY = m_snapY, .brush = QBrush{std:: move(pixmap)} }; } @@ -216,10 +225,8 @@ void PathPointsWidget::mouseMoveEvent(QMouseEvent *event) QPoint PathPointsWidget::snapPoint(const QPoint &point) const { - if (!m_showGrid) - return point; return QPoint{ - (point.x() + (m_gridX / 2)) / m_gridX * m_gridX, - (point.y() + (m_gridY / 2)) / m_gridY * m_gridY, + m_snapX > 1 ? ((point.x() + (m_snapX / 2)) / m_snapX * m_snapX) : point.x(), + m_snapY > 1 ? ((point.y() + (m_snapY / 2)) / m_snapY * m_snapY) : point.y(), }; } diff --git a/src/editor/widgets/pathpointswidget.h b/src/editor/widgets/pathpointswidget.h index baccf77..d2f0b98 100644 --- a/src/editor/widgets/pathpointswidget.h +++ b/src/editor/widgets/pathpointswidget.h @@ -11,9 +11,9 @@ class PathPointsWidget : public QWidget { Q_OBJECT - Q_PROPERTY(bool showGrid READ showGrid WRITE setShowGrid NOTIFY showGridChanged) - Q_PROPERTY(int gridX READ gridX WRITE setGridX NOTIFY gridXChanged) - Q_PROPERTY(int gridY READ gridY WRITE setGridY NOTIFY gridYChanged) + Q_PROPERTY(int snapX READ snapX WRITE setSnapX NOTIFY snapXChanged) + Q_PROPERTY(int snapY READ snapY WRITE setSnapY NOTIFY snapYChanged) + Q_PROPERTY(bool gridEnabled READ gridEnabled WRITE setGridEnabled NOTIFY gridEnabledChanged) Q_PROPERTY(bool closed READ closed WRITE setClosed NOTIFY closedChanged) public: @@ -22,14 +22,14 @@ public: void setPoints(std::vector *points); - bool showGrid() const { return m_showGrid; } - void setShowGrid(bool showGrid); + int snapX() const { return m_snapX; } + void setSnapX(int snapX); - int gridX() const { return m_gridX; } - void setGridX(int gridX); + int snapY() const { return m_snapY; } + void setSnapY(int snapY); - int gridY() const { return m_gridY; } - void setGridY(int gridY); + bool gridEnabled() const { return m_gridEnabled; } + void setGridEnabled(bool gridEnabled); bool closed() const { return m_closed; } void setClosed(bool closed); @@ -38,12 +38,12 @@ public: void setSelectedIndex(const std::optional &selectedIndex); QPalette::ColorRole gridRole() const { return m_gridRole; } - void setGridRole(QPalette::ColorRole gridRole) { if (gridRole == m_gridRole) return; m_gridRole = gridRole; update(); } + void setGridRole(QPalette::ColorRole gridRole); signals: - void showGridChanged(bool showGrid); - void gridXChanged(int gridX); - void gridYChanged(int gridY); + void snapXChanged(int snapX); + void snapYChanged(int snapY); + void gridEnabledChanged(bool gridEnabled); void closedChanged(bool closed); void selectedIndexChanged(const std::optional &selectedIndex); @@ -63,9 +63,9 @@ private: std::vector *m_points{}; - bool m_showGrid{true}; - int m_gridX{16}; - int m_gridY{16}; + int m_snapX{16}; + int m_snapY{16}; + bool m_gridEnabled{true}; bool m_closed{true}; @@ -73,8 +73,8 @@ private: std::optional m_dragIndex; struct GridBrush { - int gridX; - int gridY; + int snapX; + int snapY; QBrush brush; }; diff --git a/src/editor/widgets/roomeditwidget.cpp b/src/editor/widgets/roomeditwidget.cpp new file mode 100644 index 0000000..cae259a --- /dev/null +++ b/src/editor/widgets/roomeditwidget.cpp @@ -0,0 +1,112 @@ +#include "roomeditwidget.h" + +#include +#include +#include +#include + +RoomEditWidget::RoomEditWidget(QWidget *parent) : + QWidget{parent} +{ + setBackgroundRole(QPalette::Light); + setMouseTracking(true); + setFixedSize(640, 480); +} + +void RoomEditWidget::setSnapX(int snapX) +{ + if (m_snapX == snapX) + return; + emit snapXChanged(m_snapX = snapX); + update(); +} + +void RoomEditWidget::setSnapY(int snapY) +{ + if (m_snapY == snapY) + return; + emit snapYChanged(m_snapY = snapY); + update(); +} + +void RoomEditWidget::setGridEnabled(bool gridEnabled) +{ + if (m_gridEnabled == gridEnabled) + return; + emit gridEnabledChanged(m_gridEnabled = gridEnabled); + update(); +} + +void RoomEditWidget::setIsometricGrid(bool isometricGrid) +{ + if (m_isometricGrid == isometricGrid) + return; + emit isometricGridChanged(m_isometricGrid = isometricGrid); + update(); +} + +void RoomEditWidget::setGridRole(QPalette::ColorRole gridRole) +{ + if (gridRole == m_gridRole) + return; + m_gridRole = gridRole; + update(); +} + +void RoomEditWidget::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event) + + if (m_gridEnabled) + { + if (!m_gridBrush || m_gridBrush->snapX != m_snapX || m_gridBrush->snapY != m_snapY) + { + QPixmap pixmap{m_snapX, m_snapY}; + + { + QPainter painter{&pixmap}; + painter.setPen(palette().color(m_gridRole)); + painter.drawLine(0, 0, m_snapX, 0); + painter.drawLine(0, 0, 0, m_snapY); + + painter.fillRect(1, 1, m_snapX - 1, m_snapY - 1, palette().color(backgroundRole())); + } + + m_gridBrush = GridBrush { + .snapX = m_snapX, + .snapY = m_snapY, + .brush = QBrush{std:: move(pixmap)} + }; + } + } + else + m_gridBrush = std::nullopt; + + QPainter painter{this}; + painter.fillRect(rect(), m_gridBrush ? m_gridBrush->brush : palette().color(backgroundRole())); +} + +void RoomEditWidget::mousePressEvent(QMouseEvent *event) +{ + QWidget::mousePressEvent(event); +} + +void RoomEditWidget::mouseReleaseEvent(QMouseEvent *event) +{ + QWidget::mouseReleaseEvent(event); +} + +void RoomEditWidget::mouseMoveEvent(QMouseEvent *event) +{ + QWidget::mouseMoveEvent(event); + + emit cursorMoved(snapPoint(event->pos())); +} + +QPoint RoomEditWidget::snapPoint(const QPoint &point) const +{ + return QPoint{ + m_snapX > 1 ? ((point.x() + (m_snapX / 2)) / m_snapX * m_snapX) : point.x(), + m_snapY > 1 ? ((point.y() + (m_snapY / 2)) / m_snapY * m_snapY) : point.y(), + }; +} diff --git a/src/editor/widgets/roomeditwidget.h b/src/editor/widgets/roomeditwidget.h new file mode 100644 index 0000000..ffaf8a3 --- /dev/null +++ b/src/editor/widgets/roomeditwidget.h @@ -0,0 +1,65 @@ +#pragma once + +#include +#include + +#include + +class RoomEditWidget : public QWidget +{ + Q_OBJECT + Q_PROPERTY(int snapX READ snapX WRITE setSnapX NOTIFY snapXChanged) + Q_PROPERTY(int snapY READ snapY WRITE setSnapY NOTIFY snapYChanged) + Q_PROPERTY(bool gridEnabled READ gridEnabled WRITE setGridEnabled NOTIFY gridEnabledChanged) + Q_PROPERTY(bool isometricGrid READ isometricGrid WRITE setIsometricGrid NOTIFY isometricGridChanged) + +public: + explicit RoomEditWidget(QWidget *parent = nullptr); + + int snapX() const { return m_snapX; } + void setSnapX(int snapX); + + int snapY() const { return m_snapY; } + void setSnapY(int snapY); + + bool gridEnabled() const { return m_gridEnabled; } + void setGridEnabled(bool gridEnabled); + + bool isometricGrid() const { return m_isometricGrid; } + void setIsometricGrid(bool isometricGrid); + + QPalette::ColorRole gridRole() const { return m_gridRole; } + void setGridRole(QPalette::ColorRole gridRole); + +signals: + void snapXChanged(int snapX); + void snapYChanged(int snapY); + void gridEnabledChanged(bool gridEnabled); + void isometricGridChanged(bool isometricGrid); + + void cursorMoved(const QPoint &point); + +protected: + void paintEvent(QPaintEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + +private: + QPoint snapPoint(const QPoint &point) const; + + int m_snapX{16}; + int m_snapY{16}; + bool m_gridEnabled{true}; + bool m_isometricGrid{false}; + + struct GridBrush { + int snapX; + int snapY; + QBrush brush; + }; + + std::optional m_gridBrush; + + QPalette::ColorRole m_gridRole{QPalette::Dark}; +}; diff --git a/src/projectcontainer.cpp b/src/projectcontainer.cpp index 271e5c7..7d36b88 100644 --- a/src/projectcontainer.cpp +++ b/src/projectcontainer.cpp @@ -222,7 +222,10 @@ QDataStream &operator<<(QDataStream &ds, const Path &path) << path.points << path.type << path.closed - << path.precision; + << path.precision + << path.snapX + << path.snapY + << path.gridEnabled; return ds; } @@ -232,7 +235,10 @@ QDataStream &operator>>(QDataStream &ds, Path &path) >> path.points >> path.type >> path.closed - >> path.precision; + >> path.precision + >> path.snapX + >> path.snapY + >> path.gridEnabled; return ds; } @@ -318,13 +324,33 @@ QDataStream &operator>>(QDataStream &ds, Object &object) QDataStream &operator<<(QDataStream &ds, const Room &room) { - ds << room.name; + ds << room.name + << room.caption + << room.width + << room.height + << room.speed + << room.persistent + << room.creationCode + << room.snapX + << room.snapY + << room.gridEnabled + << room.isometricGrid; return ds; } QDataStream &operator>>(QDataStream &ds, Room &room) { - ds >> room.name; + ds >> room.name + >> room.caption + >> room.width + >> room.height + >> room.speed + >> room.persistent + >> room.creationCode + >> room.snapX + >> room.snapY + >> room.gridEnabled + >> room.isometricGrid; return ds; } diff --git a/src/projectcontainer.h b/src/projectcontainer.h index 9db1410..26d664b 100644 --- a/src/projectcontainer.h +++ b/src/projectcontainer.h @@ -63,6 +63,9 @@ struct Path Type type{Type::Straight}; bool closed{true}; int precision{4}; + int snapX{16}; + int snapY{16}; + bool gridEnabled{true}; }; struct Script @@ -127,6 +130,16 @@ struct Object struct Room { QString name; + QString caption; + int width{640}; + int height{480}; + int speed{30}; + bool persistent{}; + QString creationCode; + int snapX{16}; + int snapY{16}; + bool gridEnabled{true}; + bool isometricGrid{}; }; struct ProjectContainer