diff --git a/QtGameMaker.pro b/QtGameMaker.pro index 40540fd..ce0494b 100644 --- a/QtGameMaker.pro +++ b/QtGameMaker.pro @@ -7,18 +7,29 @@ DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 HEADERS += \ codeeditorwidget.h \ + constantsmodel.h \ + dialogs/codeeditordialog.h \ dialogs/createspritedialog.h \ dialogs/fontpropertiesdialog.h \ dialogs/imageeditordialog.h \ + dialogs/includedfilesdialog.h \ + dialogs/maskpropertiesdialog.h \ dialogs/objectinformationdialog.h \ dialogs/objectpropertiesdialog.h \ dialogs/pathpropertiesdialog.h \ dialogs/roompropertiesdialog.h \ dialogs/scriptpropertiesdialog.h \ dialogs/timelinepropertiesdialog.h \ + dialogs/triggerconditiondialog.h \ + dialogs/triggersdialog.h \ + dialogs/userdefinedconstantsdialog.h \ + drawingcanvaswidget.h \ futurecpp.h \ + includedfilesmodel.h \ jshighlighter.h \ mainwindow.h \ + pathpointsmodel.h \ + pathpointswidget.h \ projectcontainer.h \ projecttreemodel.h \ dialogs/backgroundpropertiesdialog.h \ @@ -29,21 +40,33 @@ HEADERS += \ dialogs/preferencesdialog.h \ dialogs/soundpropertiesdialog.h \ dialogs/spritepropertiesdialog.h \ - spritesmodel.h + spritesmodel.h \ + triggersmodel.h SOURCES += main.cpp \ codeeditorwidget.cpp \ + constantsmodel.cpp \ + dialogs/codeeditordialog.cpp \ dialogs/createspritedialog.cpp \ dialogs/fontpropertiesdialog.cpp \ dialogs/imageeditordialog.cpp \ + dialogs/includedfilesdialog.cpp \ + dialogs/maskpropertiesdialog.cpp \ dialogs/objectinformationdialog.cpp \ dialogs/objectpropertiesdialog.cpp \ dialogs/pathpropertiesdialog.cpp \ dialogs/roompropertiesdialog.cpp \ dialogs/scriptpropertiesdialog.cpp \ dialogs/timelinepropertiesdialog.cpp \ + dialogs/triggerconditiondialog.cpp \ + dialogs/triggersdialog.cpp \ + dialogs/userdefinedconstantsdialog.cpp \ + drawingcanvaswidget.cpp \ + includedfilesmodel.cpp \ jshighlighter.cpp \ mainwindow.cpp \ + pathpointsmodel.cpp \ + pathpointswidget.cpp \ projectcontainer.cpp \ projecttreemodel.cpp \ dialogs/backgroundpropertiesdialog.cpp \ @@ -54,18 +77,23 @@ SOURCES += main.cpp \ dialogs/preferencesdialog.cpp \ dialogs/soundpropertiesdialog.cpp \ dialogs/spritepropertiesdialog.cpp \ - spritesmodel.cpp + spritesmodel.cpp \ + triggersmodel.cpp FORMS += \ + dialogs/codeeditordialog.ui \ dialogs/createspritedialog.ui \ dialogs/fontpropertiesdialog.ui \ dialogs/imageeditordialog.ui \ + dialogs/includedfilesdialog.ui \ + dialogs/maskpropertiesdialog.ui \ dialogs/objectinformationdialog.ui \ dialogs/objectpropertiesdialog.ui \ dialogs/pathpropertiesdialog.ui \ dialogs/roompropertiesdialog.ui \ - dialogs/scriptpropertiesdialog.ui \ dialogs/timelinepropertiesdialog.ui \ + dialogs/triggersdialog.ui \ + dialogs/userdefinedconstantsdialog.ui \ mainwindow.ui \ dialogs/backgroundpropertiesdialog.ui \ dialogs/editspritedialog.ui \ diff --git a/codeeditorwidget.cpp b/codeeditorwidget.cpp index 4d367d4..9dc5cd0 100644 --- a/codeeditorwidget.cpp +++ b/codeeditorwidget.cpp @@ -3,8 +3,8 @@ #include #include -CodeEditorWidget::CodeEditorWidget(QWidget *parent) - : QPlainTextEdit{parent} +CodeEditorWidget::CodeEditorWidget(QWidget *parent) : + QPlainTextEdit{parent} { lineNumberArea = new LineNumberArea(this); @@ -105,7 +105,9 @@ void CodeEditorWidget::lineNumberAreaPaintEvent(QPaintEvent *event) } } -LineNumberArea::LineNumberArea(CodeEditorWidget *editor) : QWidget(editor), codeEditor(editor) +LineNumberArea::LineNumberArea(CodeEditorWidget *editor) : + QWidget{editor}, + codeEditor{editor} { } diff --git a/constantsmodel.cpp b/constantsmodel.cpp new file mode 100644 index 0000000..0a41401 --- /dev/null +++ b/constantsmodel.cpp @@ -0,0 +1,46 @@ +#include "constantsmodel.h" + +#include "projectcontainer.h" + +namespace { +enum { + ColumnName, + ColumnValue, + NumberOfColumns +}; +} + +ConstantsModel::ConstantsModel(ProjectContainer &project, QObject *parent) : + QAbstractTableModel{parent}, + m_project{project} +{ +} + +int ConstantsModel::rowCount(const QModelIndex &parent) const +{ + return 0; +} + +int ConstantsModel::columnCount(const QModelIndex &parent) const +{ + return NumberOfColumns; +} + +QVariant ConstantsModel::data(const QModelIndex &index, int role) const +{ + return {}; +} + +QVariant ConstantsModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation != Qt::Horizontal || (role != Qt::DisplayRole && role != Qt::EditRole)) + return {}; + + switch (section) + { + case ColumnName: return tr("Name"); + case ColumnValue: return tr("Value"); + } + + return {}; +} diff --git a/constantsmodel.h b/constantsmodel.h new file mode 100644 index 0000000..2c8c8a6 --- /dev/null +++ b/constantsmodel.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +struct ProjectContainer; + +class ConstantsModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + explicit ConstantsModel(ProjectContainer &project, QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + +private: + ProjectContainer &m_project; +}; diff --git a/dialogs/backgroundpropertiesdialog.cpp b/dialogs/backgroundpropertiesdialog.cpp index 3824188..7f49bb8 100644 --- a/dialogs/backgroundpropertiesdialog.cpp +++ b/dialogs/backgroundpropertiesdialog.cpp @@ -13,7 +13,8 @@ BackgroundPropertiesDialog::BackgroundPropertiesDialog(Background &background, P QDialog{parent}, m_ui{std::make_unique()}, m_background{background}, - m_projectModel{projectModel} + m_projectModel{projectModel}, + m_pixmap{m_background.pixmap} { m_ui->setupUi(this); @@ -27,7 +28,6 @@ BackgroundPropertiesDialog::BackgroundPropertiesDialog(Background &background, P m_ui->lineEditName->setText(m_background.name); updateSpriteInfo(); m_ui->checkBoxTileset->setChecked(m_background.tileset); - m_ui->labelPreview->setPixmap(m_background.pixmap); connect(&m_projectModel, &ProjectTreeModel::backgroundNameChanged, this, &BackgroundPropertiesDialog::backgroundNameChanged); @@ -49,6 +49,12 @@ BackgroundPropertiesDialog::~BackgroundPropertiesDialog() = default; void BackgroundPropertiesDialog::accept() { + if (!m_unsavedChanges) + { + QDialog::reject(); + return; + } + if (m_background.name != m_ui->lineEditName->text()) { if (!m_projectModel.rename(m_background, m_ui->lineEditName->text())) @@ -58,8 +64,7 @@ void BackgroundPropertiesDialog::accept() } } - if (m_newPixmap) - m_background.pixmap = std::move(*m_newPixmap); + m_background.pixmap = std::move(m_pixmap); m_background.tileset = m_ui->checkBoxTileset->isChecked(); QDialog::accept(); @@ -108,20 +113,14 @@ void BackgroundPropertiesDialog::loadBackground() return; } - m_ui->labelPreview->setPixmap(pixmap); - - m_newPixmap = std::move(pixmap); - m_unsavedChanges = true; - - updateTitle(); + m_pixmap = std::move(pixmap); + changed(); updateSpriteInfo(); } void BackgroundPropertiesDialog::saveBackground() { - const auto &pixmap = m_newPixmap ? *m_newPixmap : m_background.pixmap; - - if (pixmap.isNull()) + if (m_pixmap.isNull()) { QMessageBox::warning(this, tr("No background available to save!"), tr("No background available to save!")); return; @@ -131,7 +130,7 @@ void BackgroundPropertiesDialog::saveBackground() if (path.isEmpty()) return; - if (!pixmap.save(path)) + if (!m_pixmap.save(path)) { QMessageBox::warning(this, tr("Could not save Background!"), tr("Could not save Background!")); return; @@ -140,7 +139,13 @@ void BackgroundPropertiesDialog::saveBackground() void BackgroundPropertiesDialog::editBackground() { - ImageEditorDialog{this}.exec(); + ImageEditorDialog dialog{m_pixmap, tr("Image Editor: %0").arg(m_background.name), this}; + if (dialog.exec() == QDialog::Accepted) + { + m_pixmap = dialog.pixmap(); + changed(); + updateSpriteInfo(); + } } void BackgroundPropertiesDialog::changed() @@ -175,7 +180,7 @@ void BackgroundPropertiesDialog::updateTitle() void BackgroundPropertiesDialog::updateSpriteInfo() { - const auto &pixmap = m_newPixmap ? *m_newPixmap : m_background.pixmap; - m_ui->labelWidth->setText(tr("Width: %0").arg(pixmap.width())); - m_ui->labelHeight->setText(tr("Height: %0").arg(pixmap.height())); + m_ui->labelWidth->setText(tr("Width: %0").arg(m_pixmap.width())); + m_ui->labelHeight->setText(tr("Height: %0").arg(m_pixmap.height())); + m_ui->labelPreview->setPixmap(m_pixmap); } diff --git a/dialogs/backgroundpropertiesdialog.h b/dialogs/backgroundpropertiesdialog.h index a349ea5..32b4f1e 100644 --- a/dialogs/backgroundpropertiesdialog.h +++ b/dialogs/backgroundpropertiesdialog.h @@ -41,5 +41,5 @@ private: bool m_unsavedChanges{}; - std::optional m_newPixmap; + QPixmap m_pixmap; }; diff --git a/dialogs/backgroundpropertiesdialog.ui b/dialogs/backgroundpropertiesdialog.ui index 9bd2054..0aa5732 100644 --- a/dialogs/backgroundpropertiesdialog.ui +++ b/dialogs/backgroundpropertiesdialog.ui @@ -40,6 +40,9 @@ 0 + + The name of the background + @@ -59,6 +62,9 @@ + + Load the background from a file + &Load Background @@ -70,6 +76,9 @@ + + Save the background to a file + &Save Background @@ -81,6 +90,9 @@ + + Edit the background image + &Edit Background @@ -123,6 +135,9 @@ + + Indicate whether to use this background as a tile set + &Use as tile set diff --git a/dialogs/codeeditordialog.cpp b/dialogs/codeeditordialog.cpp new file mode 100644 index 0000000..12c943a --- /dev/null +++ b/dialogs/codeeditordialog.cpp @@ -0,0 +1,84 @@ +#include "codeeditordialog.h" +#include "ui_codeeditordialog.h" + +#include +#include +#include +#include + +#include "jshighlighter.h" + +CodeEditorDialog::CodeEditorDialog(QWidget *parent) : + QDialog{parent}, + m_ui{std::make_unique()}, + m_labelPosition{new QLabel{this}} +{ + m_ui->setupUi(this); + + m_labelPosition->setFrameStyle(QFrame::Sunken); + m_ui->statusbar->addWidget(m_labelPosition); + + { + QFont font; + font.setFamily("Consolas"); + font.setFixedPitch(true); + font.setPointSize(10); + m_ui->codeEdit->setFont(font); + } + + new JSHighlighter{m_ui->codeEdit->document()}; + + updatePosition(); + + connect(m_ui->actionLoad, &QAction::triggered, + this, &CodeEditorDialog::load); + connect(m_ui->actionSave, &QAction::triggered, + this, &CodeEditorDialog::save); + connect(m_ui->actionPrint, &QAction::triggered, + this, &CodeEditorDialog::print); + + connect(m_ui->codeEdit, &QPlainTextEdit::textChanged, + this, &CodeEditorDialog::updatePosition); + connect(m_ui->codeEdit, &QPlainTextEdit::cursorPositionChanged, + this, &CodeEditorDialog::updatePosition); +} + +CodeEditorDialog::~CodeEditorDialog() = default; + +void CodeEditorDialog::load() +{ + +} + +void CodeEditorDialog::save() +{ + +} + +void CodeEditorDialog::print() +{ + +} + +void CodeEditorDialog::updatePosition() +{ + auto cursor = m_ui->codeEdit->textCursor(); + auto position = cursor.position(); + cursor.movePosition(QTextCursor::StartOfLine); + position -= cursor.position() - 1; + + int lines = 1; + while (cursor.positionInBlock() > 0) + { + cursor.movePosition(QTextCursor::Up); + //lines++; + } + QTextBlock block = cursor.block().previous(); + while (block.isValid()) + { + lines += 1; //block.lineCount(); + block = block.previous(); + } + + m_labelPosition->setText(tr("%0/%1: %2").arg(lines).arg(m_ui->codeEdit->blockCount()).arg(position)); +} diff --git a/dialogs/codeeditordialog.h b/dialogs/codeeditordialog.h new file mode 100644 index 0000000..2958080 --- /dev/null +++ b/dialogs/codeeditordialog.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include + +class QLabel; +namespace Ui { class CodeEditorDialog; } + +class CodeEditorDialog : public QDialog +{ + Q_OBJECT + +public: + explicit CodeEditorDialog(QWidget *parent = nullptr); + ~CodeEditorDialog(); + +private slots: + void load(); + void save(); + void print(); + + void updatePosition(); + +protected: + const std::unique_ptr m_ui; + +private: + QLabel * const m_labelPosition; +}; diff --git a/dialogs/scriptpropertiesdialog.ui b/dialogs/codeeditordialog.ui similarity index 98% rename from dialogs/scriptpropertiesdialog.ui rename to dialogs/codeeditordialog.ui index 7f70b44..d99529b 100644 --- a/dialogs/scriptpropertiesdialog.ui +++ b/dialogs/codeeditordialog.ui @@ -1,7 +1,7 @@ - ScriptPropertiesDialog - + CodeEditorDialog + 0 @@ -11,7 +11,7 @@ - Script Properties + Code Editor @@ -231,6 +231,9 @@ OK + + OK, Save Changes + @@ -391,7 +394,7 @@ actionOk triggered() - ScriptPropertiesDialog + CodeEditorDialog accept() diff --git a/dialogs/createspritedialog.cpp b/dialogs/createspritedialog.cpp index d49520e..1ad7527 100644 --- a/dialogs/createspritedialog.cpp +++ b/dialogs/createspritedialog.cpp @@ -9,10 +9,10 @@ CreateSpriteDialog::CreateSpriteDialog(QWidget *parent) : { m_ui->setupUi(this); - setWindowFlags(windowFlags() - & ~Qt::Dialog - | Qt::Window - | Qt::WindowCloseButtonHint); +#ifdef Q_OS_LINUX + setWindowFlags(windowFlags() & ~Qt::Dialog | Qt::Window); +#endif + setWindowFlag(Qt::WindowCloseButtonHint); if (auto button = m_ui->buttonBox->button(QDialogButtonBox::Ok)) button->setIcon(QIcon{":/qtgameengine/icons/ok.png"}); @@ -29,7 +29,7 @@ CreateSpriteDialog::CreateSpriteDialog(const QSize &size, QWidget *parent) : CreateSpriteDialog::~CreateSpriteDialog() = default; -QSize CreateSpriteDialog::size() const +QSize CreateSpriteDialog::spriteSize() const { return QSize{m_ui->spinBoxWidth->value(), m_ui->spinBoxHeight->value()}; } diff --git a/dialogs/createspritedialog.h b/dialogs/createspritedialog.h index aefa12a..2c143c4 100644 --- a/dialogs/createspritedialog.h +++ b/dialogs/createspritedialog.h @@ -15,7 +15,7 @@ public: explicit CreateSpriteDialog(const QSize &size, QWidget *parent = nullptr); ~CreateSpriteDialog(); - QSize size() const; + QSize spriteSize() const; private: const std::unique_ptr m_ui; diff --git a/dialogs/createspritedialog.ui b/dialogs/createspritedialog.ui index fb643b4..52a2de5 100644 --- a/dialogs/createspritedialog.ui +++ b/dialogs/createspritedialog.ui @@ -50,6 +50,9 @@ 0 + + 16384 + 32 diff --git a/dialogs/editspritedialog.cpp b/dialogs/editspritedialog.cpp index ebc1063..68c33eb 100644 --- a/dialogs/editspritedialog.cpp +++ b/dialogs/editspritedialog.cpp @@ -1,27 +1,30 @@ #include "editspritedialog.h" #include "ui_editspritedialog.h" +#include + #include "projectcontainer.h" #include "spritesmodel.h" #include "createspritedialog.h" #include "imageeditordialog.h" -EditSpriteDialog::EditSpriteDialog(Sprite &sprite, QWidget *parent) : +EditSpriteDialog::EditSpriteDialog(const std::vector &pixmaps, const QString &spriteName, QWidget *parent) : QDialog{parent}, m_ui{std::make_unique()}, - m_sprite{sprite}, - m_model{std::make_unique(this)} + m_pixmaps{pixmaps}, + m_spriteName{spriteName}, + m_model{std::make_unique(m_pixmaps, this)} { m_ui->setupUi(this); - setWindowFlags(windowFlags() - & ~Qt::Dialog - | Qt::Window - | Qt::WindowMinimizeButtonHint - | Qt::WindowMaximizeButtonHint - | Qt::WindowCloseButtonHint); +#ifdef Q_OS_LINUX + setWindowFlags(windowFlags() & ~Qt::Dialog | Qt::Window); +#endif + setWindowFlag(Qt::WindowMinimizeButtonHint); + setWindowFlag(Qt::WindowMaximizeButtonHint); + setWindowFlag(Qt::WindowCloseButtonHint); - setWindowTitle(tr("Sprite editor - %0").arg(m_sprite.name)); + updateTitle(); m_ui->actionNew->setShortcut(QKeySequence::New); m_ui->actionCreateFromFile->setShortcut(QKeySequence::Open); @@ -36,16 +39,95 @@ EditSpriteDialog::EditSpriteDialog(Sprite &sprite, QWidget *parent) : EditSpriteDialog::~EditSpriteDialog() = default; +void EditSpriteDialog::accept() +{ + if (!m_unsavedChanges) + { + QDialog::reject(); + return; + } + + // TODO + + QDialog::accept(); +} + +void EditSpriteDialog::reject() +{ + if (!m_unsavedChanges) + { + QDialog::reject(); + return; + } + + const auto result = QMessageBox::warning( + this, + tr("The Sprite 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 EditSpriteDialog::newSprite() { CreateSpriteDialog dialog{this}; if (dialog.exec() == QDialog::Accepted) { + QPixmap pixmap{dialog.spriteSize()}; + pixmap.fill(Qt::white); + m_model->beginResetModel(); + m_pixmaps = std::vector { std::move(pixmap) }; + m_model->endResetModel(); + qDebug() << m_model->rowCount({}); + changed(); } } void EditSpriteDialog::doubleClicked(const QModelIndex &index) { - ImageEditorDialog{this}.exec(); + if (!index.isValid()) + { + qWarning() << "unexpected invalid index"; + return; + } + + ImageEditorDialog dialog{m_model->pixmap(index), tr("Image Editor: %0").arg(m_spriteName), this}; + if (dialog.exec() == QDialog::Accepted) + { + m_pixmaps[index.row()] = dialog.pixmap(); + emit m_model->dataChanged(index, index, {Qt::DecorationRole}); + changed(); + } +} + +void EditSpriteDialog::changed() +{ + if (!m_unsavedChanges) + { + m_unsavedChanges = true; + updateTitle(); + } +} + +void EditSpriteDialog::updateTitle() +{ + setWindowTitle(tr("Sprite editor - %0%1") + .arg(m_spriteName) + .arg(m_unsavedChanges ? tr("*") : QString{}) + ); } diff --git a/dialogs/editspritedialog.h b/dialogs/editspritedialog.h index 4f180ab..4947d78 100644 --- a/dialogs/editspritedialog.h +++ b/dialogs/editspritedialog.h @@ -1,8 +1,10 @@ #pragma once #include +#include #include +#include namespace Ui { class EditSpriteDialog; } struct Sprite; @@ -13,17 +15,29 @@ class EditSpriteDialog : public QDialog Q_OBJECT public: - explicit EditSpriteDialog(Sprite &sprite, QWidget *parent = nullptr); + explicit EditSpriteDialog(const std::vector &pixmaps, const QString &spriteName, QWidget *parent = nullptr); ~EditSpriteDialog(); + void accept() override; + void reject() override; + + const std::vector &pixmaps() const { return m_pixmaps; } + private slots: void newSprite(); void doubleClicked(const QModelIndex &index); + void changed(); + private: + void updateTitle(); + const std::unique_ptr m_ui; - Sprite &m_sprite; + std::vector m_pixmaps; + const QString m_spriteName; + + bool m_unsavedChanges{}; const std::unique_ptr m_model; }; diff --git a/dialogs/extensionpackagesdialog.cpp b/dialogs/extensionpackagesdialog.cpp index eb31f4d..4ca611e 100644 --- a/dialogs/extensionpackagesdialog.cpp +++ b/dialogs/extensionpackagesdialog.cpp @@ -7,12 +7,10 @@ ExtensionPackagesDialog::ExtensionPackagesDialog(QWidget *parent) : { m_ui->setupUi(this); - setWindowFlags(windowFlags() - & ~Qt::Dialog - | Qt::Window - | Qt::WindowMinimizeButtonHint - | Qt::WindowMaximizeButtonHint - | Qt::WindowCloseButtonHint); +#ifdef Q_OS_LINUX + setWindowFlags(windowFlags() & ~Qt::Dialog | Qt::Window); +#endif + setWindowFlag(Qt::WindowCloseButtonHint); if (auto button = m_ui->buttonBox->button(QDialogButtonBox::Ok)) button->setIcon(QIcon{":/qtgameengine/icons/ok.png"}); diff --git a/dialogs/fontpropertiesdialog.ui b/dialogs/fontpropertiesdialog.ui index ff0e779..c767f61 100644 --- a/dialogs/fontpropertiesdialog.ui +++ b/dialogs/fontpropertiesdialog.ui @@ -31,7 +31,11 @@ - + + + The name of the font + + @@ -130,6 +134,9 @@ + + Set the character range to the normal range (32 - 127) + Normal @@ -137,6 +144,9 @@ + + Set the character range to the digits range (48 - 57) + Digits @@ -144,6 +154,9 @@ + + Set the character range to the complete set (0 - 255) + All @@ -151,6 +164,9 @@ + + Set the character range to the letters range (65 - 122) + Letters diff --git a/dialogs/gameinformationdialog.cpp b/dialogs/gameinformationdialog.cpp index 647cfe0..f7f3020 100644 --- a/dialogs/gameinformationdialog.cpp +++ b/dialogs/gameinformationdialog.cpp @@ -6,12 +6,13 @@ GameInformationDialog::GameInformationDialog(QWidget *parent) : m_ui{std::make_unique()} { m_ui->setupUi(this); - setWindowFlags(windowFlags() - & ~Qt::Dialog - | Qt::Window - | Qt::WindowMinimizeButtonHint - | Qt::WindowMaximizeButtonHint - | Qt::WindowCloseButtonHint); + +#ifdef Q_OS_LINUX + setWindowFlags(windowFlags() & ~Qt::Dialog | Qt::Window); +#endif + setWindowFlag(Qt::WindowMinimizeButtonHint); + setWindowFlag(Qt::WindowMaximizeButtonHint); + setWindowFlag(Qt::WindowCloseButtonHint); } GameInformationDialog::~GameInformationDialog() = default; diff --git a/dialogs/gameinformationdialog.ui b/dialogs/gameinformationdialog.ui index 8583a96..244b01f 100644 --- a/dialogs/gameinformationdialog.ui +++ b/dialogs/gameinformationdialog.ui @@ -13,6 +13,19 @@ Game Information + + + + 50 + 50 + 201 + 16 + + + + Not yet implemented + + diff --git a/dialogs/globalgamesettingsdialog.cpp b/dialogs/globalgamesettingsdialog.cpp index bfa8504..903699b 100644 --- a/dialogs/globalgamesettingsdialog.cpp +++ b/dialogs/globalgamesettingsdialog.cpp @@ -2,6 +2,7 @@ #include "ui_globalgamesettingsdialog.h" #include +#include GlobalGameSettingsDialog::GlobalGameSettingsDialog(QWidget *parent) : QDialog{parent}, @@ -9,17 +10,102 @@ GlobalGameSettingsDialog::GlobalGameSettingsDialog(QWidget *parent) : { m_ui->setupUi(this); - setWindowFlags(windowFlags() - & ~Qt::Dialog - | Qt::Window - | Qt::WindowMinimizeButtonHint - | Qt::WindowMaximizeButtonHint - | Qt::WindowCloseButtonHint); +#ifdef Q_OS_LINUX + setWindowFlags(windowFlags() & ~Qt::Dialog | Qt::Window); +#endif + setWindowFlag(Qt::WindowCloseButtonHint); + + updateTitle(); if (auto button = m_ui->buttonBox->button(QDialogButtonBox::Ok)) button->setIcon(QIcon{":/qtgameengine/icons/ok.png"}); if (auto button = m_ui->buttonBox->button(QDialogButtonBox::Cancel)) button->setIcon(QIcon{":/qtgameengine/icons/delete.png"}); + + connect(m_ui->checkBoxFullscreen, &QCheckBox::toggled, + this, &GlobalGameSettingsDialog::changed); + connect(m_ui->radioButtonFixedScale, &QCheckBox::toggled, + this, &GlobalGameSettingsDialog::changed); + connect(m_ui->radioButtonKeepAspectRatio, &QRadioButton::toggled, + this, &GlobalGameSettingsDialog::changed); + connect(m_ui->radioButtonFullScale, &QRadioButton::toggled, + this, &GlobalGameSettingsDialog::changed); + connect(m_ui->checkBoxInterpolateColors, &QRadioButton::toggled, + this, &GlobalGameSettingsDialog::changed); + // TODO toolButtonColorOutside + connect(m_ui->checkBoxAllowResize, &QCheckBox::toggled, + this, &GlobalGameSettingsDialog::changed); + connect(m_ui->checkBoxAlwaysOntop, &QCheckBox::toggled, + this, &GlobalGameSettingsDialog::changed); + connect(m_ui->checkBoxNoBorder, &QCheckBox::toggled, + this, &GlobalGameSettingsDialog::changed); + connect(m_ui->checkBoxNoWindowButtons, &QCheckBox::toggled, + this, &GlobalGameSettingsDialog::changed); + connect(m_ui->checkBoxDisplayCursor, &QCheckBox::toggled, + this, &GlobalGameSettingsDialog::changed); + connect(m_ui->checkBoxFreezeOnFocusLost, &QCheckBox::toggled, + this, &GlobalGameSettingsDialog::changed); + connect(m_ui->checkBoxDisableScreensavers, &QCheckBox::toggled, + this, &GlobalGameSettingsDialog::changed); } GlobalGameSettingsDialog::~GlobalGameSettingsDialog() = default; + +void GlobalGameSettingsDialog::accept() +{ + if (!m_unsavedChanges) + { + QDialog::reject(); + return; + } + + // TODO + + QDialog::accept(); +} + +void GlobalGameSettingsDialog::reject() +{ + if (!m_unsavedChanges) + { + QDialog::reject(); + return; + } + + const auto result = QMessageBox::warning( + this, + tr("The Global Game Settings 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 GlobalGameSettingsDialog::changed() +{ + if (!m_unsavedChanges) + { + m_unsavedChanges = true; + updateTitle(); + } +} + +void GlobalGameSettingsDialog::updateTitle() +{ + setWindowTitle(tr("Global Game Settings%0") + .arg(m_unsavedChanges ? tr("*") : QString{}) + ); +} diff --git a/dialogs/globalgamesettingsdialog.h b/dialogs/globalgamesettingsdialog.h index 22f86c4..2001a25 100644 --- a/dialogs/globalgamesettingsdialog.h +++ b/dialogs/globalgamesettingsdialog.h @@ -14,6 +14,16 @@ public: explicit GlobalGameSettingsDialog(QWidget *parent = nullptr); ~GlobalGameSettingsDialog(); + void accept() override; + void reject() override; + +private slots: + void changed(); + private: + void updateTitle(); + const std::unique_ptr m_ui; + + bool m_unsavedChanges{}; }; diff --git a/dialogs/globalgamesettingsdialog.ui b/dialogs/globalgamesettingsdialog.ui index 21e7b7b..f4fb4de 100644 --- a/dialogs/globalgamesettingsdialog.ui +++ b/dialogs/globalgamesettingsdialog.ui @@ -109,7 +109,7 @@ - + ... diff --git a/dialogs/imageeditordialog.cpp b/dialogs/imageeditordialog.cpp index 163dec5..58e72fc 100644 --- a/dialogs/imageeditordialog.cpp +++ b/dialogs/imageeditordialog.cpp @@ -1,18 +1,88 @@ #include "imageeditordialog.h" #include "ui_imageeditordialog.h" -ImageEditorDialog::ImageEditorDialog(QWidget *parent) : +#include + +ImageEditorDialog::ImageEditorDialog(const QPixmap &pixmap, const QString &title, QWidget *parent) : QDialog{parent}, - m_ui{std::make_unique()} + m_ui{std::make_unique()}, + m_pixmap{pixmap}, + m_title{title} { m_ui->setupUi(this); - setWindowFlags(windowFlags() - & ~Qt::Dialog - | Qt::Window - | Qt::WindowMinimizeButtonHint - | Qt::WindowMaximizeButtonHint - | Qt::WindowCloseButtonHint); +#ifdef Q_OS_LINUX + setWindowFlags(windowFlags() & ~Qt::Dialog | Qt::Window); +#endif + setWindowFlag(Qt::WindowMinimizeButtonHint); + setWindowFlag(Qt::WindowMaximizeButtonHint); + setWindowFlag(Qt::WindowCloseButtonHint); + + updateTitle(); + + m_ui->scrollArea->setBackgroundRole(QPalette::Dark); + + m_ui->canvas->setPixmap(m_pixmap); } ImageEditorDialog::~ImageEditorDialog() = default; + +void ImageEditorDialog::accept() +{ + if (!m_unsavedChanges) + { + QDialog::reject(); + return; + } + + // TODO + + QDialog::accept(); +} + +void ImageEditorDialog::reject() +{ + if (!m_unsavedChanges) + { + QDialog::reject(); + return; + } + + const auto result = QMessageBox::warning( + this, + tr("The Image 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 ImageEditorDialog::changed() +{ + if (!m_unsavedChanges) + { + m_unsavedChanges = true; + updateTitle(); + } +} + +void ImageEditorDialog::updateTitle() +{ + setWindowTitle(tr("%0%1") + .arg(m_title) + .arg(m_unsavedChanges ? tr("*") : QString{}) + ); +} diff --git a/dialogs/imageeditordialog.h b/dialogs/imageeditordialog.h index 2e44ffb..d2a114c 100644 --- a/dialogs/imageeditordialog.h +++ b/dialogs/imageeditordialog.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include #include @@ -11,9 +13,24 @@ class ImageEditorDialog : public QDialog Q_OBJECT public: - explicit ImageEditorDialog(QWidget *parent = nullptr); + explicit ImageEditorDialog(const QPixmap &pixmap, const QString &title, QWidget *parent = nullptr); ~ImageEditorDialog(); + const QPixmap &pixmap() const { return m_pixmap; } + + void accept() override; + void reject() override; + +private slots: + void changed(); + private: + void updateTitle(); + const std::unique_ptr m_ui; + + QPixmap m_pixmap; + const QString m_title; + + bool m_unsavedChanges{}; }; diff --git a/dialogs/imageeditordialog.ui b/dialogs/imageeditordialog.ui index 671fcb2..5bbc45d 100644 --- a/dialogs/imageeditordialog.ui +++ b/dialogs/imageeditordialog.ui @@ -31,7 +31,7 @@ - + &File @@ -44,7 +44,7 @@ - + &Edit @@ -61,26 +61,26 @@ - + &View - + &Transform - + &Image - - - - - + + + + + @@ -135,7 +135,40 @@ - + + + + + + + + + + + + + + + + 0 + 0 + 0 + 0 + + + + + + + + + + + + + + + @@ -264,8 +297,32 @@ + + + DrawingCanvasWidget + QWidget +
drawingcanvaswidget.h
+
+
- + + + actionCloseSavingChanges + triggered() + ImageEditorDialog + accept() + + + -1 + -1 + + + 285 + 226 + + + + diff --git a/dialogs/includedfilesdialog.cpp b/dialogs/includedfilesdialog.cpp new file mode 100644 index 0000000..a93ce5b --- /dev/null +++ b/dialogs/includedfilesdialog.cpp @@ -0,0 +1,23 @@ +#include "includedfilesdialog.h" +#include "ui_includedfilesdialog.h" + +#include "projectcontainer.h" +#include "includedfilesmodel.h" + +IncludedFilesDialog::IncludedFilesDialog(ProjectContainer &project, QWidget *parent) : + QDialog{parent}, + m_ui{std::make_unique()}, + m_project{project}, + m_model{std::make_unique(m_project, this)} +{ + m_ui->setupUi(this); + + if (auto button = m_ui->buttonBox->button(QDialogButtonBox::Ok)) + button->setIcon(QIcon{":/qtgameengine/icons/ok.png"}); + if (auto button = m_ui->buttonBox->button(QDialogButtonBox::Cancel)) + button->setIcon(QIcon{":/qtgameengine/icons/delete.png"}); + + m_ui->listView->setModel(m_model.get()); +} + +IncludedFilesDialog::~IncludedFilesDialog() = default; diff --git a/dialogs/includedfilesdialog.h b/dialogs/includedfilesdialog.h new file mode 100644 index 0000000..f52e480 --- /dev/null +++ b/dialogs/includedfilesdialog.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +#include + +namespace Ui { class IncludedFilesDialog; } +struct ProjectContainer; +class IncludedFilesModel; + +class IncludedFilesDialog : public QDialog +{ + Q_OBJECT + +public: + explicit IncludedFilesDialog(ProjectContainer &project, QWidget *parent = nullptr); + ~IncludedFilesDialog(); + +private: + const std::unique_ptr m_ui; + + ProjectContainer &m_project; + + const std::unique_ptr m_model; +}; diff --git a/dialogs/includedfilesdialog.ui b/dialogs/includedfilesdialog.ui new file mode 100644 index 0000000..aff0970 --- /dev/null +++ b/dialogs/includedfilesdialog.ui @@ -0,0 +1,112 @@ + + + IncludedFilesDialog + + + + 0 + 0 + 400 + 300 + + + + Included Files + + + + + + + + + Files to include in the stand alone executable: + + + + + + + + + + + + Add + + + + + + + Change + + + + + + + Delete + + + + + + + Clear + + + + + + + + + + + + QDialogButtonBox::Ok + + + true + + + + + + + + + buttonBox + accepted() + IncludedFilesDialog + accept() + + + 199 + 278 + + + 199 + 149 + + + + + buttonBox + rejected() + IncludedFilesDialog + reject() + + + 199 + 278 + + + 199 + 149 + + + + + diff --git a/dialogs/maskpropertiesdialog.cpp b/dialogs/maskpropertiesdialog.cpp new file mode 100644 index 0000000..389a3f2 --- /dev/null +++ b/dialogs/maskpropertiesdialog.cpp @@ -0,0 +1,16 @@ +#include "maskpropertiesdialog.h" +#include "ui_maskpropertiesdialog.h" + +MaskPropertiesDialog::MaskPropertiesDialog(QWidget *parent) : + QDialog{parent}, + m_ui{std::make_unique()} +{ + m_ui->setupUi(this); + +#ifdef Q_OS_LINUX + setWindowFlags(windowFlags() & ~Qt::Dialog | Qt::Window); +#endif + setWindowFlag(Qt::WindowCloseButtonHint); +} + +MaskPropertiesDialog::~MaskPropertiesDialog() = default; diff --git a/dialogs/maskpropertiesdialog.h b/dialogs/maskpropertiesdialog.h new file mode 100644 index 0000000..7e24bdf --- /dev/null +++ b/dialogs/maskpropertiesdialog.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +#include + +namespace Ui { class MaskPropertiesDialog; } + +class MaskPropertiesDialog : public QDialog +{ + Q_OBJECT + +public: + explicit MaskPropertiesDialog(QWidget *parent = nullptr); + ~MaskPropertiesDialog(); + +private: + const std::unique_ptr m_ui; +}; diff --git a/dialogs/maskpropertiesdialog.ui b/dialogs/maskpropertiesdialog.ui new file mode 100644 index 0000000..a9083a2 --- /dev/null +++ b/dialogs/maskpropertiesdialog.ui @@ -0,0 +1,19 @@ + + + MaskPropertiesDialog + + + + 0 + 0 + 400 + 300 + + + + Mask Properties + + + + + diff --git a/dialogs/objectinformationdialog.cpp b/dialogs/objectinformationdialog.cpp index f01ab23..6b1adaa 100644 --- a/dialogs/objectinformationdialog.cpp +++ b/dialogs/objectinformationdialog.cpp @@ -1,9 +1,10 @@ #include "objectinformationdialog.h" #include "ui_objectinformationdialog.h" -ObjectInformationDialog::ObjectInformationDialog(QWidget *parent) : +ObjectInformationDialog::ObjectInformationDialog(ProjectContainer &project, QWidget *parent) : QDialog{parent}, - m_ui{std::make_unique()} + m_ui{std::make_unique()}, + m_project{project} { m_ui->setupUi(this); } diff --git a/dialogs/objectinformationdialog.h b/dialogs/objectinformationdialog.h index 7ce5ad1..2c7b98e 100644 --- a/dialogs/objectinformationdialog.h +++ b/dialogs/objectinformationdialog.h @@ -5,15 +5,18 @@ #include namespace Ui { class ObjectInformationDialog; } +struct ProjectContainer; class ObjectInformationDialog : public QDialog { Q_OBJECT public: - explicit ObjectInformationDialog(QWidget *parent = nullptr); + explicit ObjectInformationDialog(ProjectContainer &project, QWidget *parent = nullptr); ~ObjectInformationDialog(); private: const std::unique_ptr m_ui; + + ProjectContainer &m_project; }; diff --git a/dialogs/objectinformationdialog.ui b/dialogs/objectinformationdialog.ui index d47bb59..8a10df6 100644 --- a/dialogs/objectinformationdialog.ui +++ b/dialogs/objectinformationdialog.ui @@ -96,5 +96,22 @@ - + + + actionOk + triggered() + ObjectInformationDialog + accept() + + + -1 + -1 + + + 199 + 149 + + + + diff --git a/dialogs/pathpropertiesdialog.cpp b/dialogs/pathpropertiesdialog.cpp index 20e40df..dd1ed4d 100644 --- a/dialogs/pathpropertiesdialog.cpp +++ b/dialogs/pathpropertiesdialog.cpp @@ -1,25 +1,63 @@ #include "pathpropertiesdialog.h" #include "ui_pathpropertiesdialog.h" +#include +#include #include #include #include "projectcontainer.h" #include "projecttreemodel.h" +#include "pathpointsmodel.h" PathPropertiesDialog::PathPropertiesDialog(Path &path, ProjectTreeModel &projectModel, QWidget *parent) : QDialog{parent}, m_ui{std::make_unique()}, m_path{path}, m_projectModel{projectModel}, + m_points{path.points}, + m_pointsModel{std::make_unique(m_points, this)}, + 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)}}, 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); + updateTitle(); + { + auto label = new QLabel{tr("Snap X:"), this}; + label->setBuddy(m_spinBoxSnapX); + m_ui->toolBar->insertWidget(m_ui->toolBar->actions().at(18), label); + } + m_spinBoxSnapX->setValue(m_ui->widget->gridX()); + m_spinBoxSnapX->setMaximumWidth(50); + m_ui->toolBar->insertWidget(m_ui->toolBar->actions().at(19), m_spinBoxSnapX); + + { + auto label = new QLabel{tr("Snap Y:"), this}; + label->setBuddy(m_spinBoxSnapY); + m_ui->toolBar->insertWidget(m_ui->toolBar->actions().at(20), label); + } + 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()); + + m_ui->treeView->setModel(m_pointsModel.get()); + + m_ui->radioButtonStraight->setChecked(m_path.type == Path::Type::Straight); + m_ui->radioButtonSmooth->setChecked(m_path.type == Path::Type::Smooth); + m_ui->checkBoxClosed->setChecked(m_path.closed); + m_ui->spinBoxPrecision->setValue(m_path.precision); + + m_ui->widget->setClosed(m_path.closed); + m_labelX->setFrameStyle(QFrame::Sunken); m_ui->statusbar->addWidget(m_labelX, 1); m_labelY->setFrameStyle(QFrame::Sunken); @@ -35,9 +73,41 @@ PathPropertiesDialog::PathPropertiesDialog(Path &path, ProjectTreeModel &project m_ui->lineEditName->setText(m_path.name); + m_ui->treeView->setColumnWidth(1, 75); + + connect(m_ui->widget, &PathPointsWidget::pointInserted, + m_pointsModel.get(), &PathPointsModel::pointInserted); + connect(m_ui->widget, &PathPointsWidget::pointMoved, + m_pointsModel.get(), &PathPointsModel::pointMoved); + connect(&m_projectModel, &ProjectTreeModel::pathNameChanged, this, &PathPropertiesDialog::pathNameChanged); + connect(m_ui->treeView->selectionModel(), &QItemSelectionModel::currentChanged, + this, &PathPropertiesDialog::selectionChanged); + + connect(m_ui->widget, &PathPointsWidget::gridXChanged, + m_spinBoxSnapX, &QSpinBox::setValue); + connect(m_ui->widget, &PathPointsWidget::gridYChanged, + m_spinBoxSnapY, &QSpinBox::setValue); + connect(m_ui->widget, &PathPointsWidget::showGridChanged, + m_ui->actionGrid, &QAction::setChecked); + connect(m_ui->widget, &PathPointsWidget::closedChanged, + m_ui->checkBoxClosed, &QCheckBox::setChecked); + connect(m_ui->widget, &PathPointsWidget::cursorMoved, + this, &PathPropertiesDialog::cursorMoved); + connect(m_ui->widget, &PathPointsWidget::selectedIndexChanged, + this, &PathPropertiesDialog::selectedPointChanged); + + connect(m_spinBoxSnapX, &QSpinBox::valueChanged, + m_ui->widget, &PathPointsWidget::setGridX); + connect(m_spinBoxSnapY, &QSpinBox::valueChanged, + m_ui->widget, &PathPointsWidget::setGridY); + connect(m_ui->actionGrid, &QAction::toggled, + m_ui->widget, &PathPointsWidget::setShowGrid); + connect(m_ui->checkBoxClosed, &QCheckBox::toggled, + m_ui->widget, &PathPointsWidget::setClosed); + connect(m_ui->pushButtonAdd, &QAbstractButton::pressed, this, &PathPropertiesDialog::add); connect(m_ui->pushButtonInsert, &QAbstractButton::pressed, @@ -47,12 +117,35 @@ PathPropertiesDialog::PathPropertiesDialog(Path &path, ProjectTreeModel &project connect(m_ui->lineEditName, &QLineEdit::textChanged, this, &PathPropertiesDialog::changed); + + connect(m_pointsModel.get(), &QAbstractTableModel::rowsInserted, + this, &PathPropertiesDialog::changed); + connect(m_pointsModel.get(), &QAbstractTableModel::rowsRemoved, + this, &PathPropertiesDialog::changed); + connect(m_pointsModel.get(), &QAbstractTableModel::dataChanged, + this, &PathPropertiesDialog::dataChanged); + connect(m_pointsModel.get(), &QAbstractTableModel::dataChanged, + this, &PathPropertiesDialog::changed); + connect(m_ui->radioButtonStraight, &QRadioButton::toggled, + this, &PathPropertiesDialog::changed); + connect(m_ui->radioButtonSmooth, &QRadioButton::toggled, + this, &PathPropertiesDialog::changed); + connect(m_ui->checkBoxClosed, &QCheckBox::toggled, + this, &PathPropertiesDialog::changed); + connect(m_ui->spinBoxPrecision, &QSpinBox::valueChanged, + this, &PathPropertiesDialog::changed); } PathPropertiesDialog::~PathPropertiesDialog() = default; void PathPropertiesDialog::accept() { + if (!m_unsavedChanges) + { + QDialog::reject(); + return; + } + if (m_path.name != m_ui->lineEditName->text()) { if (!m_projectModel.rename(m_path, m_ui->lineEditName->text())) @@ -62,7 +155,22 @@ void PathPropertiesDialog::accept() } } - // TODO + m_path.points = m_points; + + if (m_ui->radioButtonStraight->isChecked()) + m_path.type = Path::Type::Straight; + else if (m_ui->radioButtonSmooth->isChecked()) + m_path.type = Path::Type::Smooth; + else + { + QMessageBox::critical(this, tr("No kind selected!"), tr("No kind selected!")); + return; + } + + m_path.closed = m_ui->checkBoxClosed->isChecked(); + m_path.precision = m_ui->spinBoxPrecision->value(); + + // TODO update points QDialog::accept(); } @@ -97,6 +205,36 @@ void PathPropertiesDialog::reject() } } +void PathPropertiesDialog::selectionChanged(const QModelIndex &index) +{ + if (!index.isValid()) + { + m_ui->widget->setSelectedIndex(std::nullopt); + return; + } + + m_ui->widget->setSelectedIndex(index.row()); + + updatePointFields(); +} + +void PathPropertiesDialog::selectedPointChanged(const std::optional &index) +{ + m_ui->treeView->setCurrentIndex(index ? m_pointsModel->index(*index, 0, {}) : QModelIndex{}); +} + +void PathPropertiesDialog::dataChanged(const QModelIndex &index) +{ + if (index == m_ui->treeView->currentIndex()) + updatePointFields(); +} + +void PathPropertiesDialog::cursorMoved(const QPoint &point) +{ + m_labelX->setText(tr("X: %0").arg(point.x())); + m_labelY->setText(tr("Y: %0").arg(point.y())); +} + void PathPropertiesDialog::add() { QMessageBox::warning(this, tr("Not yet implemented"), tr("Not yet implemented")); @@ -141,3 +279,11 @@ void PathPropertiesDialog::updateTitle() .arg(m_unsavedChanges ? tr("*") : QString{}) ); } + +void PathPropertiesDialog::updatePointFields() +{ + const auto point = m_pointsModel->getPoint(m_ui->treeView->currentIndex()); + m_ui->spinBoxX->setValue(point.point.x()); + m_ui->spinBoxY->setValue(point.point.y()); + m_ui->spinBoxSp->setValue(point.sp); +} diff --git a/dialogs/pathpropertiesdialog.h b/dialogs/pathpropertiesdialog.h index 9db12a3..daada90 100644 --- a/dialogs/pathpropertiesdialog.h +++ b/dialogs/pathpropertiesdialog.h @@ -3,11 +3,15 @@ #include #include +#include +#include "projectcontainer.h" + +class QSpinBox; class QLabel; namespace Ui { class PathPropertiesDialog; } -struct Path; class ProjectTreeModel; +class PathPointsModel; class PathPropertiesDialog : public QDialog { @@ -21,6 +25,11 @@ public: void reject() override; private slots: + void selectionChanged(const QModelIndex &index); + void selectedPointChanged(const std::optional &index); + void dataChanged(const QModelIndex &index); + void cursorMoved(const QPoint &point); + void add(); void insert(); void delete_(); @@ -31,14 +40,22 @@ private slots: private: void updateTitle(); + void updatePointFields(); const std::unique_ptr m_ui; Path &m_path; ProjectTreeModel &m_projectModel; + std::vector m_points; + + const std::unique_ptr m_pointsModel; + bool m_unsavedChanges{}; + QSpinBox * const m_spinBoxSnapX; + QSpinBox * const m_spinBoxSnapY; + QLabel * const m_labelX; QLabel * const m_labelY; QLabel * const m_labelArea; diff --git a/dialogs/pathpropertiesdialog.ui b/dialogs/pathpropertiesdialog.ui index aff912a..4c9fbd7 100644 --- a/dialogs/pathpropertiesdialog.ui +++ b/dialogs/pathpropertiesdialog.ui @@ -6,8 +6,8 @@ 0 0 - 728 - 449 + 921 + 576 @@ -78,6 +78,20 @@ + + + + + + + + + + + + + +
@@ -112,7 +126,11 @@ - + + + The name of the path + + @@ -133,13 +151,19 @@ - + 0 0 + + false + + + true + @@ -170,7 +194,14 @@ - + + + X-coordinate of the point + + + 16384 + + @@ -180,7 +211,14 @@ - + + + Y-coordinate of the point + + + 16384 + + @@ -190,7 +228,14 @@ - + + + Relative speed at this point (100 = default) + + + 16384 + + @@ -211,6 +256,9 @@ + + Add the point to the path + Add @@ -218,6 +266,9 @@ + + Insert the point before the current one + Insert @@ -225,6 +276,9 @@ + + Delete the point from the path + Delete @@ -256,6 +310,9 @@ + + Indicate the type of connections between the points + connection kind @@ -287,7 +344,11 @@ - + + + Precision with which to create smooth curves + + @@ -324,7 +385,14 @@ - + + + + 300 + 0 + + + @@ -394,7 +462,153 @@ Shift the path + + + + :/qtgameengine/icons/flip-horizontal.png:/qtgameengine/icons/flip-horizontal.png + + + Mirror horizontally + + + Mirror the path horizontally + + + + + + :/qtgameengine/icons/flip-vertical.png:/qtgameengine/icons/flip-vertical.png + + + Flip vertically + + + Flip the path vertically + + + + + + :/qtgameengine/icons/rotate.png:/qtgameengine/icons/rotate.png + + + Rotate + + + Rotate the path + + + + + + :/qtgameengine/icons/scale.png:/qtgameengine/icons/scale.png + + + Scale + + + Scale the path + + + + + + :/qtgameengine/icons/arrow-left.png:/qtgameengine/icons/arrow-left.png + + + Shift Left + + + Shift the view to the left + + + + + + :/qtgameengine/icons/arrow-right.png:/qtgameengine/icons/arrow-right.png + + + Shift right + + + Shift the view to the right + + + + + + :/qtgameengine/icons/arrow-up.png:/qtgameengine/icons/arrow-up.png + + + Shift top + + + Shift the view to the top + + + + + + :/qtgameengine/icons/arrow-down.png:/qtgameengine/icons/arrow-down.png + + + Shift bottom + + + Shift the view to the bottom + + + + + + :/qtgameengine/icons/center.png:/qtgameengine/icons/center.png + + + Center view + + + Center view around the path + + + + + true + + + true + + + + :/qtgameengine/icons/grid.png:/qtgameengine/icons/grid.png + + + Grid + + + Toggle the showing of the grid + + + + + + :/qtgameengine/icons/room.png:/qtgameengine/icons/room.png + + + Room + + + Indicate the room to show as background + + + + + PathPointsWidget + QWidget +
pathpointswidget.h
+ 1 +
+
diff --git a/dialogs/preferencesdialog.cpp b/dialogs/preferencesdialog.cpp index d889813..3a1fbaa 100644 --- a/dialogs/preferencesdialog.cpp +++ b/dialogs/preferencesdialog.cpp @@ -1,17 +1,23 @@ #include "preferencesdialog.h" #include "ui_preferencesdialog.h" +#include + PreferencesDialog::PreferencesDialog(QWidget *parent) : QDialog{parent}, m_ui{std::make_unique()} { m_ui->setupUi(this); - setWindowFlags(windowFlags() - & ~Qt::Dialog - | Qt::Window - | Qt::WindowMinimizeButtonHint - | Qt::WindowMaximizeButtonHint - | Qt::WindowCloseButtonHint); + +#ifdef Q_OS_LINUX + setWindowFlags(windowFlags() & ~Qt::Dialog | Qt::Window); +#endif + setWindowFlag(Qt::WindowCloseButtonHint); + + if (auto button = m_ui->buttonBox->button(QDialogButtonBox::Ok)) + button->setIcon(QIcon{":/qtgameengine/icons/ok.png"}); + if (auto button = m_ui->buttonBox->button(QDialogButtonBox::Cancel)) + button->setIcon(QIcon{":/qtgameengine/icons/delete.png"}); } PreferencesDialog::~PreferencesDialog() = default; diff --git a/dialogs/scriptpropertiesdialog.cpp b/dialogs/scriptpropertiesdialog.cpp index 00537a1..46384b8 100644 --- a/dialogs/scriptpropertiesdialog.cpp +++ b/dialogs/scriptpropertiesdialog.cpp @@ -1,27 +1,20 @@ #include "scriptpropertiesdialog.h" -#include "ui_scriptpropertiesdialog.h" +#include "ui_codeeditordialog.h" -#include -#include -#include #include -#include +#include #include +#include #include "projectcontainer.h" #include "projecttreemodel.h" -#include "jshighlighter.h" ScriptPropertiesDialog::ScriptPropertiesDialog(Script &script, ProjectTreeModel &projectModel, QWidget *parent) : - QDialog{parent}, - m_ui{std::make_unique()}, + CodeEditorDialog{parent}, m_script{script}, m_projectModel{projectModel}, - m_lineEditName{new QLineEdit{this}}, - m_labelPosition{new QLabel{this}} + m_lineEditName{new QLineEdit{this}} { - m_ui->setupUi(this); - updateTitle(); { @@ -32,46 +25,19 @@ ScriptPropertiesDialog::ScriptPropertiesDialog(Script &script, ProjectTreeModel m_lineEditName->setMaximumWidth(100); m_ui->toolBar->addWidget(m_lineEditName); - m_labelPosition->setFrameStyle(QFrame::Sunken); - m_ui->statusbar->addWidget(m_labelPosition); - - { - QFont font; - font.setFamily("Consolas"); - font.setFixedPitch(true); - font.setPointSize(10); - m_ui->codeEdit->setFont(font); - } - - new JSHighlighter{m_ui->codeEdit->document()}; m_lineEditName->setText(m_script.name); m_ui->codeEdit->setPlainText(m_script.script); - updatePosition(); - connect(&m_projectModel, &ProjectTreeModel::scriptNameChanged, this, &ScriptPropertiesDialog::scriptNameChanged); - connect(m_ui->actionLoad, &QAction::triggered, - this, &ScriptPropertiesDialog::load); - connect(m_ui->actionSave, &QAction::triggered, - this, &ScriptPropertiesDialog::save); - connect(m_ui->actionPrint, &QAction::triggered, - this, &ScriptPropertiesDialog::print); - connect(m_lineEditName, &QLineEdit::textChanged, this, &ScriptPropertiesDialog::changed); connect(m_ui->codeEdit, &QPlainTextEdit::textChanged, this, &ScriptPropertiesDialog::changed); - connect(m_ui->codeEdit, &QPlainTextEdit::textChanged, - this, &ScriptPropertiesDialog::updatePosition); - connect(m_ui->codeEdit, &QPlainTextEdit::cursorPositionChanged, - this, &ScriptPropertiesDialog::updatePosition); } -ScriptPropertiesDialog::~ScriptPropertiesDialog() = default; - void ScriptPropertiesDialog::accept() { if (m_script.name != m_lineEditName->text()) @@ -85,14 +51,14 @@ void ScriptPropertiesDialog::accept() m_script.script = m_ui->codeEdit->toPlainText(); - QDialog::accept(); + CodeEditorDialog::accept(); } void ScriptPropertiesDialog::reject() { if (!m_unsavedChanges) { - QDialog::reject(); + CodeEditorDialog::reject(); return; } @@ -109,7 +75,7 @@ void ScriptPropertiesDialog::reject() accept(); return; case QMessageBox::Discard: - QDialog::reject(); + CodeEditorDialog::reject(); return; case QMessageBox::Cancel: return; @@ -127,44 +93,6 @@ void ScriptPropertiesDialog::changed() } } -void ScriptPropertiesDialog::load() -{ - -} - -void ScriptPropertiesDialog::save() -{ - -} - -void ScriptPropertiesDialog::print() -{ - -} - -void ScriptPropertiesDialog::updatePosition() -{ - auto cursor = m_ui->codeEdit->textCursor(); - auto position = cursor.position(); - cursor.movePosition(QTextCursor::StartOfLine); - position -= cursor.position() - 1; - - int lines = 1; - while (cursor.positionInBlock() > 0) - { - cursor.movePosition(QTextCursor::Up); - //lines++; - } - QTextBlock block = cursor.block().previous(); - while (block.isValid()) - { - lines += 1; //block.lineCount(); - block = block.previous(); - } - - m_labelPosition->setText(tr("%0/%1: %2").arg(lines).arg(m_ui->codeEdit->blockCount()).arg(position)); -} - void ScriptPropertiesDialog::scriptNameChanged(const Script &script) { if (&script != &m_script) @@ -185,3 +113,4 @@ void ScriptPropertiesDialog::updateTitle() .arg(m_unsavedChanges ? tr("*") : QString{}) ); } + diff --git a/dialogs/scriptpropertiesdialog.h b/dialogs/scriptpropertiesdialog.h index 4ed43ab..ebc389f 100644 --- a/dialogs/scriptpropertiesdialog.h +++ b/dialogs/scriptpropertiesdialog.h @@ -1,22 +1,15 @@ -#pragma once - -#include - -#include +#include "codeeditordialog.h" class QLineEdit; -class QLabel; -namespace Ui { class ScriptPropertiesDialog; } struct Script; class ProjectTreeModel; -class ScriptPropertiesDialog : public QDialog +class ScriptPropertiesDialog : public CodeEditorDialog { Q_OBJECT public: - explicit ScriptPropertiesDialog(Script &script, ProjectTreeModel &projectModel, QWidget *parent = nullptr); - ~ScriptPropertiesDialog(); + ScriptPropertiesDialog(Script &script, ProjectTreeModel &projectModel, QWidget *parent = nullptr); void accept() override; void reject() override; @@ -24,25 +17,15 @@ public: private slots: void changed(); - void load(); - void save(); - void print(); - - void updatePosition(); - void scriptNameChanged(const Script &script); private: void updateTitle(); - const std::unique_ptr m_ui; - Script &m_script; ProjectTreeModel &m_projectModel; bool m_unsavedChanges{}; QLineEdit * const m_lineEditName; - - QLabel * const m_labelPosition; }; diff --git a/dialogs/soundpropertiesdialog.cpp b/dialogs/soundpropertiesdialog.cpp index c6276d5..49ce290 100644 --- a/dialogs/soundpropertiesdialog.cpp +++ b/dialogs/soundpropertiesdialog.cpp @@ -14,7 +14,8 @@ SoundPropertiesDialog::SoundPropertiesDialog(Sound &sound, ProjectTreeModel &pro QDialog{parent}, m_ui{std::make_unique()}, m_sound{sound}, - m_projectModel{projectModel} + m_projectModel{projectModel}, + m_path{m_sound.path} { m_ui->setupUi(this); @@ -33,6 +34,8 @@ SoundPropertiesDialog::SoundPropertiesDialog(Sound &sound, ProjectTreeModel &pro } m_ui->radioButtonNormal->setChecked(m_sound.type == Sound::Type::Sound); m_ui->radioButtonMusic->setChecked(m_sound.type == Sound::Type::Music); + m_ui->radioButton3D->setChecked(false); + m_ui->radioButtonMultimedia->setChecked(false); m_ui->checkBoxChorus->setChecked(m_sound.effects.chorus); m_ui->checkBoxFlanger->setChecked(m_sound.effects.flanger); m_ui->checkBoxGargle->setChecked(m_sound.effects.gargle); @@ -88,6 +91,12 @@ SoundPropertiesDialog::~SoundPropertiesDialog() = default; void SoundPropertiesDialog::accept() { + if (!m_unsavedChanges) + { + QDialog::reject(); + return; + } + if (m_sound.name != m_ui->lineEditName->text()) { if (!m_projectModel.rename(m_sound, m_ui->lineEditName->text())) @@ -97,18 +106,23 @@ void SoundPropertiesDialog::accept() } } - if (m_newPath) - m_sound.path = std::move(*m_newPath); + m_sound.path = std::move(m_path); if (m_ui->radioButtonNormal->isChecked()) m_sound.type = Sound::Type::Sound; else if (m_ui->radioButtonMusic->isChecked()) m_sound.type = Sound::Type::Music; - else + else if (m_ui->radioButton3D->isChecked() || + m_ui->radioButtonMultimedia->isChecked()) { QMessageBox::critical(this, tr("Not implemented"), tr("This kind of sound is not yet supported!")); return; } + else + { + QMessageBox::critical(this, tr("No kind selected!"), tr("No kind selected!")); + return; + } m_sound.effects.chorus = m_ui->checkBoxChorus->isChecked(); m_sound.effects.flanger = m_ui->checkBoxFlanger->isChecked(); @@ -153,7 +167,7 @@ void SoundPropertiesDialog::reject() void SoundPropertiesDialog::loadSound() { - const auto path = QFileDialog::getOpenFileName(this, tr("Open a Sound File...")); + auto path = QFileDialog::getOpenFileName(this, tr("Open a Sound File...")); if (path.isEmpty()) return; @@ -163,16 +177,15 @@ void SoundPropertiesDialog::loadSound() return; } - m_newPath = path; + m_path = std::move(path); changed(); - m_ui->labelFilename->setText(tr("Filename: %0").arg(QFileInfo{path}.fileName())); - m_soundEffect.setSource(QUrl::fromLocalFile(path)); + m_ui->labelFilename->setText(tr("Filename: %0").arg(QFileInfo{m_path}.fileName())); + m_soundEffect.setSource(QUrl::fromLocalFile(m_path)); } void SoundPropertiesDialog::saveSound() { - const auto &path = m_newPath ? *m_newPath : m_sound.path; - if (path.isEmpty()) + if (m_path.isEmpty()) { QMessageBox::warning(this, tr("Could not save Sound!"), tr("Could not save Sound!") + "\n\n" + tr("No sound has been selected yet.")); return; @@ -182,7 +195,7 @@ void SoundPropertiesDialog::saveSound() if (savePath.isEmpty()) return; - if (!QFile::copy(path, savePath)) + if (!QFile::copy(m_path, savePath)) { QMessageBox::warning(this, tr("Could not save Sound!"), tr("Could not save Sound!")); return; diff --git a/dialogs/soundpropertiesdialog.h b/dialogs/soundpropertiesdialog.h index c361506..2ee0957 100644 --- a/dialogs/soundpropertiesdialog.h +++ b/dialogs/soundpropertiesdialog.h @@ -43,7 +43,7 @@ private: bool m_unsavedChanges{}; - std::optional m_newPath; + QString m_path; QSoundEffect m_soundEffect; }; diff --git a/dialogs/soundpropertiesdialog.ui b/dialogs/soundpropertiesdialog.ui index 571defa..dd47047 100644 --- a/dialogs/soundpropertiesdialog.ui +++ b/dialogs/soundpropertiesdialog.ui @@ -31,7 +31,11 @@
- + + + The name of the sound + +
@@ -39,6 +43,9 @@ + + Load the sound from a file + &Load Sound @@ -80,6 +87,9 @@ + + Save the sound to a file + Sa&ve Sound @@ -98,6 +108,9 @@ + + Set the kind of this sound + Kind @@ -107,9 +120,6 @@ Normal Sound - - true - @@ -138,6 +148,9 @@ + + Set the effects to apply for the sound + Effects @@ -194,6 +207,9 @@ + + Indicates the default volume for the sound + 100 @@ -217,6 +233,9 @@ + + Indicates the default pan for the sound + -100 @@ -238,6 +257,9 @@ + + Indicates whether to preload the sound when the game starts + &Preload @@ -263,6 +285,9 @@ + + Edit the sound using an external sound editor + &Edit Sound diff --git a/dialogs/spritepropertiesdialog.cpp b/dialogs/spritepropertiesdialog.cpp index a499345..c1e3c62 100644 --- a/dialogs/spritepropertiesdialog.cpp +++ b/dialogs/spritepropertiesdialog.cpp @@ -9,12 +9,14 @@ #include "projectcontainer.h" #include "projecttreemodel.h" #include "editspritedialog.h" +#include "maskpropertiesdialog.h" SpritePropertiesDialog::SpritePropertiesDialog(Sprite &sprite, ProjectTreeModel &projectModel, QWidget *parent) : QDialog{parent}, m_ui{std::make_unique()}, m_sprite{sprite}, - m_projectModel{projectModel} + m_projectModel{projectModel}, + m_pixmaps{m_sprite.pixmaps} { m_ui->setupUi(this); @@ -31,7 +33,6 @@ SpritePropertiesDialog::SpritePropertiesDialog(Sprite &sprite, ProjectTreeModel m_ui->spinBoxOriginY->setValue(m_sprite.origin.y); m_ui->checkBoxPreciseCollisionChecking->setChecked(m_sprite.preciseCollisionChecking); m_ui->checkBoxSeparateCollisionMasks->setChecked(m_sprite.separateCollisionMasks); - m_ui->labelPreview->setPixmap(m_sprite.pixmaps.empty() ? QPixmap{} : m_sprite.pixmaps.front()); connect(&m_projectModel, &ProjectTreeModel::spriteNameChanged, this, &SpritePropertiesDialog::spriteNameChanged); @@ -44,6 +45,8 @@ SpritePropertiesDialog::SpritePropertiesDialog(Sprite &sprite, ProjectTreeModel this, &SpritePropertiesDialog::editSprite); connect(m_ui->pushButtonCenterOrigin, &QAbstractButton::pressed, this, &SpritePropertiesDialog::centerOrigin); + connect(m_ui->pushButtonModifyCollisionmask, &QAbstractButton::pressed, + this, &SpritePropertiesDialog::modifyMask); connect(m_ui->lineEditName, &QLineEdit::textChanged, this, &SpritePropertiesDialog::changed); @@ -61,6 +64,12 @@ SpritePropertiesDialog::~SpritePropertiesDialog() = default; void SpritePropertiesDialog::accept() { + if (!m_unsavedChanges) + { + QDialog::reject(); + return; + } + if (m_sprite.name != m_ui->lineEditName->text()) { if (!m_projectModel.rename(m_sprite, m_ui->lineEditName->text())) @@ -70,8 +79,7 @@ void SpritePropertiesDialog::accept() } } - if (m_newPixmaps) - m_sprite.pixmaps = std::move(*m_newPixmaps); + m_sprite.pixmaps = std::move(m_pixmaps); m_sprite.origin.x = m_ui->spinBoxOriginX->value(); m_sprite.origin.y = m_ui->spinBoxOriginY->value(); m_sprite.preciseCollisionChecking = m_ui->checkBoxPreciseCollisionChecking->isChecked(); @@ -123,20 +131,14 @@ void SpritePropertiesDialog::loadSprite() return; } - m_ui->labelPreview->setPixmap(pixmap); - - m_newPixmaps = std::vector{ std::move(pixmap) }; - m_unsavedChanges = true; - - updateTitle(); + m_pixmaps = std::vector{ std::move(pixmap) }; + changed(); updateSpriteInfo(); } void SpritePropertiesDialog::saveSprite() { - const auto &pixmaps = m_newPixmaps ? *m_newPixmaps : m_sprite.pixmaps; - - if (pixmaps.empty() || pixmaps.front().isNull()) + if (m_pixmaps.empty() || m_pixmaps.front().isNull()) { QMessageBox::warning(this, tr("No sprites available to save!"), tr("No sprites available to save!")); return; @@ -146,7 +148,7 @@ void SpritePropertiesDialog::saveSprite() if (path.isEmpty()) return; - if (!pixmaps.front().save(path)) + if (!m_pixmaps.front().save(path)) { QMessageBox::warning(this, tr("Could not save Sprite!"), tr("Could not save Sprite!")); return; @@ -155,20 +157,33 @@ void SpritePropertiesDialog::saveSprite() void SpritePropertiesDialog::editSprite() { - EditSpriteDialog{m_sprite}.exec(); + EditSpriteDialog dialog{m_pixmaps, m_sprite.name, this}; + if (dialog.exec() == QDialog::Accepted) + { + m_pixmaps = dialog.pixmaps(); + changed(); + updateSpriteInfo(); + } } void SpritePropertiesDialog::centerOrigin() { - const auto &pixmaps = m_newPixmaps ? *m_newPixmaps : m_sprite.pixmaps; - if (pixmaps.empty() || pixmaps.front().isNull()) + if (m_pixmaps.empty() || m_pixmaps.front().isNull()) { qDebug() << "unexpected empty pixmaps"; + m_ui->spinBoxOriginX->setValue(0); + m_ui->spinBoxOriginY->setValue(0); return; } - m_ui->spinBoxOriginX->setValue(pixmaps.front().width() / 2); - m_ui->spinBoxOriginY->setValue(pixmaps.front().height() / 2); + m_ui->spinBoxOriginX->setValue(m_pixmaps.front().width() / 2); + m_ui->spinBoxOriginY->setValue(m_pixmaps.front().height() / 2); +} + +void SpritePropertiesDialog::modifyMask() +{ + MaskPropertiesDialog dialog{this}; + dialog.exec(); } void SpritePropertiesDialog::changed() @@ -203,8 +218,8 @@ void SpritePropertiesDialog::updateTitle() void SpritePropertiesDialog::updateSpriteInfo() { - const auto &pixmaps = m_newPixmaps ? *m_newPixmaps : m_sprite.pixmaps; - m_ui->labelWidth->setText(tr("Width: %0").arg(pixmaps.empty() ? tr("n/a") : QString::number(pixmaps.front().width()))); - m_ui->labelHeight->setText(tr("Height: %0").arg(pixmaps.empty() ? tr("n/a") : QString::number(pixmaps.front().height()))); - m_ui->labelSubimages->setText(tr("Number of subimages: %0").arg(pixmaps.size())); + m_ui->labelPreview->setPixmap(m_pixmaps.empty() ? QPixmap{} : m_pixmaps.front()); + m_ui->labelWidth->setText(tr("Width: %0").arg(m_pixmaps.empty() ? tr("n/a") : QString::number(m_pixmaps.front().width()))); + m_ui->labelHeight->setText(tr("Height: %0").arg(m_pixmaps.empty() ? tr("n/a") : QString::number(m_pixmaps.front().height()))); + m_ui->labelSubimages->setText(tr("Number of subimages: %0").arg(m_pixmaps.size())); } diff --git a/dialogs/spritepropertiesdialog.h b/dialogs/spritepropertiesdialog.h index 6379bcf..36b88c3 100644 --- a/dialogs/spritepropertiesdialog.h +++ b/dialogs/spritepropertiesdialog.h @@ -26,6 +26,7 @@ private slots: void saveSprite(); void editSprite(); void centerOrigin(); + void modifyMask(); void changed(); @@ -42,5 +43,5 @@ private: bool m_unsavedChanges{}; - std::optional> m_newPixmaps; + std::vector m_pixmaps; }; diff --git a/dialogs/spritepropertiesdialog.ui b/dialogs/spritepropertiesdialog.ui index a270763..cdbf7e0 100644 --- a/dialogs/spritepropertiesdialog.ui +++ b/dialogs/spritepropertiesdialog.ui @@ -33,7 +33,11 @@ - + + + The name of the sprite + + @@ -56,6 +60,9 @@ + + Load the sprite from a file + &Load Sprite @@ -67,6 +74,9 @@ + + Save the sprite to a file + &Save Sprite @@ -78,6 +88,9 @@ + + Edit the sprite + &Edit Sprite @@ -151,7 +164,11 @@ - + + + The x-coordinate in the sprite that is used for the origin + + @@ -167,7 +184,11 @@ - + + + The y-coordinate in the sprite that is used for the origin + + @@ -188,6 +209,9 @@ + + Put the origin in the center of the image + &Center @@ -246,6 +270,9 @@ + + Perform pixel precise colliion checking + &Precise collision checking @@ -253,6 +280,9 @@ + + Have separate collision masks for the subimages + Separa&te Collision Masks @@ -260,6 +290,9 @@ + + Modify additional options for the collision mask + &Modify Mask diff --git a/dialogs/triggerconditiondialog.cpp b/dialogs/triggerconditiondialog.cpp new file mode 100644 index 0000000..e3e75c5 --- /dev/null +++ b/dialogs/triggerconditiondialog.cpp @@ -0,0 +1,12 @@ +#include "triggerconditiondialog.h" + +TriggerConditionDialog::TriggerConditionDialog(QWidget *parent) : + CodeEditorDialog{parent} +{ +#ifdef Q_OS_LINUX + setWindowFlags(windowFlags() & ~Qt::Dialog | Qt::Window); +#endif + setWindowFlag(Qt::WindowMinimizeButtonHint); + setWindowFlag(Qt::WindowMaximizeButtonHint); + setWindowFlag(Qt::WindowCloseButtonHint); +} diff --git a/dialogs/triggerconditiondialog.h b/dialogs/triggerconditiondialog.h new file mode 100644 index 0000000..95c948d --- /dev/null +++ b/dialogs/triggerconditiondialog.h @@ -0,0 +1,11 @@ +#pragma once + +#include "codeeditordialog.h" + +class TriggerConditionDialog : public CodeEditorDialog +{ + Q_OBJECT + +public: + explicit TriggerConditionDialog(QWidget *parent = nullptr); +}; diff --git a/dialogs/triggersdialog.cpp b/dialogs/triggersdialog.cpp new file mode 100644 index 0000000..a98ff6e --- /dev/null +++ b/dialogs/triggersdialog.cpp @@ -0,0 +1,33 @@ +#include "triggersdialog.h" +#include "ui_triggersdialog.h" + +#include "projectcontainer.h" +#include "triggersmodel.h" +#include "triggerconditiondialog.h" + +TriggersDialog::TriggersDialog(ProjectContainer &project, QWidget *parent) : + QDialog{parent}, + m_ui{std::make_unique()}, + m_project{project}, + m_model{std::make_unique(project, this)} +{ + m_ui->setupUi(this); + + if (auto button = m_ui->buttonBox->button(QDialogButtonBox::Ok)) + button->setIcon(QIcon{":/qtgameengine/icons/ok.png"}); + if (auto button = m_ui->buttonBox->button(QDialogButtonBox::Cancel)) + button->setIcon(QIcon{":/qtgameengine/icons/delete.png"}); + + m_ui->listView->setModel(m_model.get()); + + connect(m_ui->pushButtonUseCodeEditor, &QAbstractButton::pressed, + this, &TriggersDialog::openCodeEditor); +} + +TriggersDialog::~TriggersDialog() = default; + +void TriggersDialog::openCodeEditor() +{ + TriggerConditionDialog dialog{this}; + dialog.exec(); +} diff --git a/dialogs/triggersdialog.h b/dialogs/triggersdialog.h new file mode 100644 index 0000000..9cd6017 --- /dev/null +++ b/dialogs/triggersdialog.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include + +namespace Ui { class TriggersDialog; } +struct ProjectContainer; +class TriggersModel; + +class TriggersDialog : public QDialog +{ + Q_OBJECT + +public: + explicit TriggersDialog(ProjectContainer &project, QWidget *parent = nullptr); + ~TriggersDialog(); + +private slots: + void openCodeEditor(); + +private: + const std::unique_ptr m_ui; + + ProjectContainer &m_project; + + const std::unique_ptr m_model; +}; diff --git a/dialogs/triggersdialog.ui b/dialogs/triggersdialog.ui new file mode 100644 index 0000000..9e412b5 --- /dev/null +++ b/dialogs/triggersdialog.ui @@ -0,0 +1,227 @@ + + + TriggersDialog + + + + 0 + 0 + 622 + 515 + + + + Triggers + + + + + + + + + 0 + 0 + + + + + + + + + + Load the collection of triggers from a file. + + + &Load + + + + + + + Delete the selected trigger. Make sure no object uses it! + + + &Delete + + + + + + + Add a trigger to the list + + + &Add + + + + + + + Clear the complete list of triggers + + + &Clear + + + + + + + Save the collection of triggers to a file + + + &Save + + + + + + + + + QDialogButtonBox::Ok + + + true + + + + + + + + + true + + + + + + Name: + + + + + + + + + + Condition: + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + &Use code editor + + + + + + + + + Moment of checking: + + + + + + + + + Begin of step + + + + + + + Middle of step + + + + + + + End of step + + + + + + + + + Constant name: + + + + + + + + + + + + + + + buttonBox + accepted() + TriggersDialog + accept() + + + 93 + 492 + + + 310 + 257 + + + + + buttonBox + rejected() + TriggersDialog + reject() + + + 93 + 492 + + + 310 + 257 + + + + + diff --git a/dialogs/userdefinedconstantsdialog.cpp b/dialogs/userdefinedconstantsdialog.cpp new file mode 100644 index 0000000..ad9f8ee --- /dev/null +++ b/dialogs/userdefinedconstantsdialog.cpp @@ -0,0 +1,23 @@ +#include "userdefinedconstantsdialog.h" +#include "ui_userdefinedconstantsdialog.h" + +#include "projectcontainer.h" +#include "constantsmodel.h" + +UserDefinedConstantsDialog::UserDefinedConstantsDialog(ProjectContainer &project, QWidget *parent) : + QDialog{parent}, + m_ui{std::make_unique()}, + m_project{project}, + m_model{std::make_unique(m_project, this)} +{ + m_ui->setupUi(this); + + if (auto button = m_ui->buttonBox->button(QDialogButtonBox::Ok)) + button->setIcon(QIcon{":/qtgameengine/icons/ok.png"}); + if (auto button = m_ui->buttonBox->button(QDialogButtonBox::Cancel)) + button->setIcon(QIcon{":/qtgameengine/icons/delete.png"}); + + m_ui->treeView->setModel(m_model.get()); +} + +UserDefinedConstantsDialog::~UserDefinedConstantsDialog() = default; diff --git a/dialogs/userdefinedconstantsdialog.h b/dialogs/userdefinedconstantsdialog.h new file mode 100644 index 0000000..9f29f02 --- /dev/null +++ b/dialogs/userdefinedconstantsdialog.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +#include + +namespace Ui { class UserDefinedConstantsDialog; } +struct ProjectContainer; +class ConstantsModel; + +class UserDefinedConstantsDialog : public QDialog +{ + Q_OBJECT + +public: + explicit UserDefinedConstantsDialog(ProjectContainer &project, QWidget *parent = nullptr); + ~UserDefinedConstantsDialog(); + +private: + const std::unique_ptr m_ui; + + ProjectContainer &m_project; + + const std::unique_ptr m_model; +}; diff --git a/dialogs/userdefinedconstantsdialog.ui b/dialogs/userdefinedconstantsdialog.ui new file mode 100644 index 0000000..aa748d1 --- /dev/null +++ b/dialogs/userdefinedconstantsdialog.ui @@ -0,0 +1,150 @@ + + + UserDefinedConstantsDialog + + + + 0 + 0 + 479 + 461 + + + + User-Defined Constants + + + + :/qtgameengine/icons/constants.png:/qtgameengine/icons/constants.png + + + + + + + + + false + + + + + + + + + &Insert + + + + + + + &Sort + + + + + + + &Delete + + + + + + + &Load + + + + + + + &Up + + + + + + + &Add + + + + + + + &Clear + + + + + + + Do&wn + + + + + + + Sa&ve + + + + + + + + + + + + QDialogButtonBox::Ok + + + true + + + + + + + + + + + buttonBox + accepted() + UserDefinedConstantsDialog + accept() + + + 239 + 439 + + + 239 + 230 + + + + + buttonBox + rejected() + UserDefinedConstantsDialog + reject() + + + 239 + 439 + + + 239 + 230 + + + + + diff --git a/drawingcanvaswidget.cpp b/drawingcanvaswidget.cpp new file mode 100644 index 0000000..5ca3630 --- /dev/null +++ b/drawingcanvaswidget.cpp @@ -0,0 +1,37 @@ +#include "drawingcanvaswidget.h" + +#include +#include + +DrawingCanvasWidget::DrawingCanvasWidget(QWidget *parent) : + QWidget{parent} +{ +} + +void DrawingCanvasWidget::setPixmap(QPixmap &pixmap) +{ + m_pixmap = &pixmap; + setFixedSize(m_pixmap->size() * m_scale); + update(); +} + +void DrawingCanvasWidget::setScale(float scale) +{ + if (m_scale == scale) + return; + emit scaleChanged(m_scale = scale); + if (m_pixmap) + setFixedSize(m_pixmap->size() * m_scale); + update(); +} + +void DrawingCanvasWidget::paintEvent(QPaintEvent *ev) +{ + QWidget::paintEvent(ev); + + if (!m_pixmap) + return; + + QPainter painter{this}; + painter.drawPixmap(QRect{QPoint{}, m_pixmap->size() * m_scale}, *m_pixmap); +} diff --git a/drawingcanvaswidget.h b/drawingcanvaswidget.h new file mode 100644 index 0000000..1409ac2 --- /dev/null +++ b/drawingcanvaswidget.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +class QPixmap; + +class DrawingCanvasWidget : public QWidget +{ + Q_OBJECT + +public: + explicit DrawingCanvasWidget(QWidget *parent = nullptr); + + QPixmap *pixmap() { return m_pixmap; } + const QPixmap *pixmap() const { return m_pixmap; } + void setPixmap(QPixmap &pixmap); + + float scale() const { return m_scale; } + void setScale(float scale); + +signals: + void scaleChanged(float scale); + +protected: + void paintEvent(QPaintEvent *ev) override; + +private: + QPixmap *m_pixmap{}; + + float m_scale{4.f}; +}; diff --git a/icons/center.png b/icons/center.png new file mode 100644 index 0000000..a48d76b Binary files /dev/null and b/icons/center.png differ diff --git a/icons/constants.png b/icons/constants.png new file mode 100644 index 0000000..5af4169 Binary files /dev/null and b/icons/constants.png differ diff --git a/icons/flip-horizontal.png b/icons/flip-horizontal.png new file mode 100644 index 0000000..ca35497 Binary files /dev/null and b/icons/flip-horizontal.png differ diff --git a/icons/flip-vertical.png b/icons/flip-vertical.png new file mode 100644 index 0000000..108faba Binary files /dev/null and b/icons/flip-vertical.png differ diff --git a/icons/grid.png b/icons/grid.png new file mode 100644 index 0000000..27d9158 Binary files /dev/null and b/icons/grid.png differ diff --git a/icons/scale.png b/icons/scale.png new file mode 100644 index 0000000..09a40bc Binary files /dev/null and b/icons/scale.png differ diff --git a/includedfilesmodel.cpp b/includedfilesmodel.cpp new file mode 100644 index 0000000..8d35103 --- /dev/null +++ b/includedfilesmodel.cpp @@ -0,0 +1,19 @@ +#include "includedfilesmodel.h" + +#include "projectcontainer.h" + +IncludedFilesModel::IncludedFilesModel(ProjectContainer &project, QObject *parent) : + QAbstractListModel{parent}, + m_project{project} +{ +} + +int IncludedFilesModel::rowCount(const QModelIndex &parent) const +{ + return 0; +} + +QVariant IncludedFilesModel::data(const QModelIndex &index, int role) const +{ + return {}; +} diff --git a/includedfilesmodel.h b/includedfilesmodel.h new file mode 100644 index 0000000..89f1941 --- /dev/null +++ b/includedfilesmodel.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +struct ProjectContainer; + +class IncludedFilesModel : public QAbstractListModel +{ + Q_OBJECT + +public: + explicit IncludedFilesModel(ProjectContainer &project, QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + +private: + ProjectContainer &m_project; +}; diff --git a/mainwindow.cpp b/mainwindow.cpp index 854d3d4..57ae83e 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -6,8 +6,10 @@ #include #include #include +#include #include "projecttreemodel.h" +#include "dialogs/preferencesdialog.h" #include "dialogs/spritepropertiesdialog.h" #include "dialogs/soundpropertiesdialog.h" #include "dialogs/backgroundpropertiesdialog.h" @@ -17,11 +19,13 @@ #include "dialogs/timelinepropertiesdialog.h" #include "dialogs/objectpropertiesdialog.h" #include "dialogs/roompropertiesdialog.h" -#include "dialogs/preferencesdialog.h" +#include "dialogs/objectinformationdialog.h" #include "dialogs/gameinformationdialog.h" #include "dialogs/globalgamesettingsdialog.h" #include "dialogs/extensionpackagesdialog.h" -#include "dialogs/objectinformationdialog.h" +#include "dialogs/userdefinedconstantsdialog.h" +#include "dialogs/triggersdialog.h" +#include "dialogs/includedfilesdialog.h" namespace { template struct PropertiesDialogForDetail; @@ -107,6 +111,9 @@ MainWindow::MainWindow(QWidget *parent) : connect(m_ui->actionGameInformation, &QAction::triggered, this, &MainWindow::showGameInformation); connect(m_ui->actionGlobalGameSettings, &QAction::triggered, this, &MainWindow::showGlobalGameSettings); connect(m_ui->actionExtensionPackages, &QAction::triggered, this, &MainWindow::showExtensionPackages); + connect(m_ui->actionDefineConstants, &QAction::triggered, this, &MainWindow::showDefineConstants); + connect(m_ui->actionDefineTriggers, &QAction::triggered, this, &MainWindow::showDefineTriggers); + connect(m_ui->actionIncludedFiles, &QAction::triggered, this, &MainWindow::showIncludedFiles); connect(m_ui->actionAbout, &QAction::triggered, this, &MainWindow::about); connect(m_ui->actionAboutQt, &QAction::triggered, qApp, &QApplication::aboutQt); @@ -126,10 +133,16 @@ MainWindow::MainWindow(QWidget *parent) : connect(m_projectTreeModel.get(), &ProjectTreeModel::rowsInserted, this, &MainWindow::rowsInserted); + connect(m_projectTreeModel.get(), &QAbstractTableModel::rowsInserted, + this, &MainWindow::changed); connect(m_projectTreeModel.get(), &ProjectTreeModel::rowsAboutToBeRemoved, this, &MainWindow::rowsAboutToBeRemoved); + connect(m_projectTreeModel.get(), &QAbstractTableModel::rowsRemoved, + this, &MainWindow::changed); connect(m_projectTreeModel.get(), &ProjectTreeModel::modelAboutToBeReset, this, &MainWindow::modelAboutToBeReset); + connect(m_projectTreeModel.get(), &QAbstractTableModel::dataChanged, + this, &MainWindow::changed); updateTitle(); } @@ -318,6 +331,15 @@ void MainWindow::modelErrorOccured(const QString &message) QMessageBox::warning(this, tr("Error occured!"), tr("Error occured!") + "\n\n" + message); } +void MainWindow::changed() +{ + if (!m_unsavedChanges) + { + m_unsavedChanges = true; + updateTitle(); + } +} + void MainWindow::newFile() { m_ui->mdiArea->closeAllSubWindows(); @@ -354,7 +376,7 @@ void MainWindow::newFile() m_project = {}; m_projectTreeModel->setProject(&m_project); - m_filePath = {}; + m_filePath = QString{}; m_unsavedChanges = false; updateTitle(); @@ -492,7 +514,8 @@ void MainWindow::exportResources() void MainWindow::preferences() { - PreferencesDialog{this}.exec(); + PreferencesDialog dialog{this}; + dialog.exec(); } void MainWindow::create() @@ -501,7 +524,7 @@ void MainWindow::create() if (!index.isValid()) return; - if (index == m_projectTreeModel->rootFor()) createFor(); + if (index == m_projectTreeModel->rootFor()) createFor(); else if (index == m_projectTreeModel->rootFor()) createFor(); else if (index == m_projectTreeModel->rootFor()) createFor(); else if (index == m_projectTreeModel->rootFor()) createFor(); @@ -583,35 +606,7 @@ void MainWindow::findResource() void MainWindow::showObjectInformation() { - if (m_objectInformationWindow) - m_ui->mdiArea->setActiveSubWindow(m_objectInformationWindow); - else - { - auto dialog = new ObjectInformationDialog; - auto subwindow = m_ui->mdiArea->addSubWindow(dialog); - auto action = m_ui->menuWindow->addAction(dialog->windowTitle()); - action->setCheckable(true); - connect(action, &QAction::triggered, - m_ui->mdiArea, [mdiArea=m_ui->mdiArea,subwindow,action](){ - mdiArea->setActiveSubWindow(subwindow); - action->setChecked(subwindow->windowState().testFlag(Qt::WindowActive)); - }); - connect(subwindow, &QMdiSubWindow::windowStateChanged, - action, [action](Qt::WindowStates oldState, Qt::WindowStates newState){ - action->setChecked(newState.testFlag(Qt::WindowActive)); - }); - connect(dialog, &QWidget::windowTitleChanged, action, &QAction::setText); - connect(dialog, &QDialog::finished, - this, [&objectInformationWindow=m_objectInformationWindow](){ - objectInformationWindow = nullptr; - }); - connect(dialog, &QDialog::finished, - subwindow, &QObject::deleteLater); - connect(dialog, &QDialog::finished, - action, &QObject::deleteLater); - m_objectInformationWindow = subwindow; - dialog->show(); - } + openOrActivateWindow(m_objectInformationWindow, m_project); } template @@ -633,17 +628,37 @@ template void MainWindow::createFor(); void MainWindow::showGameInformation() { - GameInformationDialog{this}.exec(); + GameInformationDialog dialog{this}; + if (dialog.exec() == QDialog::Accepted) + changed(); } void MainWindow::showGlobalGameSettings() { - GlobalGameSettingsDialog{this}.exec(); + GlobalGameSettingsDialog dialog{this}; + if (dialog.exec() == QDialog::Accepted) + changed(); } void MainWindow::showExtensionPackages() { - ExtensionPackagesDialog{this}.exec(); + ExtensionPackagesDialog dialog{this}; + dialog.exec(); +} + +void MainWindow::showDefineConstants() +{ + openOrActivateWindow(m_userDefinedConstantsWindow, m_project); +} + +void MainWindow::showDefineTriggers() +{ + openOrActivateWindow(m_triggersWindow, m_project); +} + +void MainWindow::showIncludedFiles() +{ + openOrActivateWindow(m_includedFilesWindow, m_project); } void MainWindow::about() @@ -688,11 +703,45 @@ void MainWindow::modelAboutToBeReset() void MainWindow::updateTitle() { - setWindowTitle(tr("%0 - Qt Gamemaker 1.0 Ultimate%1") + setWindowTitle(tr("%0%1 - Qt Gamemaker 1.0 Ultimate") .arg(m_filePath.isEmpty() ? "" : QFileInfo{m_filePath}.fileName()) .arg(m_unsavedChanges ? tr("*") : QString{})); } +template +void MainWindow::openOrActivateWindow(QMdiSubWindow * &ptr, Targs &&...args) +{ + if (ptr) + m_ui->mdiArea->setActiveSubWindow(ptr); + else + { + auto dialog = new T{std::forward(args)...}; + auto subwindow = m_ui->mdiArea->addSubWindow(dialog); + auto action = m_ui->menuWindow->addAction(dialog->windowTitle()); + action->setCheckable(true); + connect(action, &QAction::triggered, + m_ui->mdiArea, [mdiArea=m_ui->mdiArea,subwindow,action](){ + mdiArea->setActiveSubWindow(subwindow); + action->setChecked(subwindow->windowState().testFlag(Qt::WindowActive)); + }); + connect(subwindow, &QMdiSubWindow::windowStateChanged, + action, [action](Qt::WindowStates oldState, Qt::WindowStates newState){ + action->setChecked(newState.testFlag(Qt::WindowActive)); + }); + connect(dialog, &QWidget::windowTitleChanged, action, &QAction::setText); + connect(dialog, &QDialog::finished, + this, [&ptr](){ + ptr = nullptr; + }); + connect(dialog, &QDialog::finished, + subwindow, &QObject::deleteLater); + connect(dialog, &QDialog::finished, + action, &QObject::deleteLater); + ptr = subwindow; + dialog->show(); + } +} + template bool MainWindow::doubleClickedFor(const QModelIndex &index) { @@ -725,7 +774,9 @@ bool MainWindow::doubleClickedFor(const QModelIndex &index) }); connect(dialog, &QWidget::windowTitleChanged, action, &QAction::setText); connect(dialog, &QDialog::finished, - this, [&propertyWindows,subwindow](){ + this, [this,&propertyWindows,subwindow](int result){ + if (result == QDialog::Accepted) + changed(); for (auto iter = std::begin(propertyWindows); iter != std::end(propertyWindows); ) { if (iter->second == subwindow) diff --git a/mainwindow.h b/mainwindow.h index ad0ead9..c57bb1f 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -27,6 +27,7 @@ private slots: void doubleClicked(const QModelIndex &index); void selectionChanged(const QModelIndex &index); void modelErrorOccured(const QString &message); + void changed(); void newFile(); void openFile(); @@ -50,18 +51,12 @@ private: template void createFor(); private slots: -// void createSprite(); -// void createSound(); -// void createBackground(); -// void createPath(); -// void createScript(); -// void createFont(); -// void createTimeLine(); -// void createObject(); -// void createRoom(); void showGameInformation(); void showGlobalGameSettings(); void showExtensionPackages(); + void showDefineConstants(); + void showDefineTriggers(); + void showIncludedFiles(); void about(); void rowsInserted(const QModelIndex &parent, int first, int last); @@ -71,6 +66,9 @@ private slots: private: void updateTitle(); + template + void openOrActivateWindow(QMdiSubWindow * &ptr, Targs &&...args); + template std::map &propertyWindowsFor(); @@ -103,4 +101,7 @@ private: std::map m_roomPropertiesWindows; QMdiSubWindow *m_objectInformationWindow{}; + QMdiSubWindow *m_userDefinedConstantsWindow{}; + QMdiSubWindow *m_triggersWindow{}; + QMdiSubWindow *m_includedFilesWindow{}; }; diff --git a/mainwindow.ui b/mainwindow.ui index 5c40c3d..5b17a72 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -459,6 +459,10 @@ + + + :/qtgameengine/icons/object-file.png:/qtgameengine/icons/object-file.png + &Show Object Information @@ -608,6 +612,10 @@ + + + :/qtgameengine/icons/constants.png:/qtgameengine/icons/constants.png + Define Co&nstants... diff --git a/pathpointsmodel.cpp b/pathpointsmodel.cpp new file mode 100644 index 0000000..c27f79e --- /dev/null +++ b/pathpointsmodel.cpp @@ -0,0 +1,100 @@ +#include "pathpointsmodel.h" + +#include + +namespace { +enum { + ColumnPoint, + ColumnSp, + NumberOfColumns +}; +} + +PathPointsModel::PathPointsModel(std::vector &points, QObject *parent) : + QAbstractTableModel{parent}, + m_points{points} +{ +} + +int PathPointsModel::rowCount(const QModelIndex &parent) const +{ + return m_points.size(); +} + +int PathPointsModel::columnCount(const QModelIndex &parent) const +{ + return NumberOfColumns; +} + +QVariant PathPointsModel::data(const QModelIndex &index, int role) const +{ + if (index.row() < 0 || index.row() >= m_points.size()) + { + qWarning() << "invalid row" << index.row(); + return {}; + } + + const auto &point = m_points.at(index.row()); + + switch (index.column()) + { + case ColumnPoint: + switch (role) + { + case Qt::DisplayRole: return tr("(%0,%1)").arg(point.point.x()).arg(point.point.y()); + case Qt::EditRole: return point.point; + default: return {}; + } + case ColumnSp: + switch (role) + { + case Qt::DisplayRole: + case Qt::EditRole: return point.sp; + default: return {}; + } + } + + return {}; +} + +QVariant PathPointsModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation != Qt::Horizontal || (role != Qt::DisplayRole && role != Qt::EditRole)) + return {}; + + switch (section) + { + case ColumnPoint: return tr("point"); + case ColumnSp: return tr("sp"); + } + + return {}; +} + +Path::Point PathPointsModel::getPoint(const QModelIndex &index) const +{ + if (!index.isValid()) + { + qWarning() << "unexpected invalid index" << index; + return {}; + } + + if (index.row() < 0 || index.row() >= m_points.size()) + { + qWarning() << "unexpected row" << index.row(); + return {}; + } + + return m_points.at(index.row()); +} + +void PathPointsModel::pointInserted(std::size_t index) +{ + emit beginInsertRows({}, index, index); + emit endInsertRows(); +} + +void PathPointsModel::pointMoved(std::size_t index) +{ + emit dataChanged(this->index(index, 0), this->index(index, 0), {Qt::DisplayRole, Qt::EditRole}); +} diff --git a/pathpointsmodel.h b/pathpointsmodel.h new file mode 100644 index 0000000..f813747 --- /dev/null +++ b/pathpointsmodel.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include + +#include "projectcontainer.h" + +class PathPointsModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + explicit PathPointsModel(std::vector &points, QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + + Path::Point getPoint(const QModelIndex &index) const; + +public slots: + void pointInserted(std::size_t index); + void pointMoved(std::size_t index); + +private: + std::vector &m_points; +}; diff --git a/pathpointswidget.cpp b/pathpointswidget.cpp new file mode 100644 index 0000000..594a873 --- /dev/null +++ b/pathpointswidget.cpp @@ -0,0 +1,223 @@ +#include "pathpointswidget.h" + +#include +#include +#include + +#include + +PathPointsWidget::PathPointsWidget(QWidget *parent) : + QWidget{parent} +{ + setMouseTracking(true); +} + +PathPointsWidget::PathPointsWidget(std::vector *points, QWidget *parent) : + QWidget{parent}, + m_points{points} +{ +} + +void PathPointsWidget::setPoints(std::vector *points) +{ + m_points = points; + update(); +} + +void PathPointsWidget::setShowGrid(bool showGrid) +{ + if (m_showGrid == showGrid) + return; + emit showGridChanged(m_showGrid = showGrid); + update(); +} + +void PathPointsWidget::setGridX(int gridX) +{ + if (m_gridX == gridX) + return; + emit gridXChanged(m_gridX = gridX); + update(); +} + +void PathPointsWidget::setGridY(int gridY) +{ + if (m_gridY == gridY) + return; + emit gridYChanged(m_gridY = gridY); + update(); +} + +void PathPointsWidget::setClosed(bool closed) +{ + if (m_closed == closed) + return; + emit closedChanged(m_closed = closed); + update(); +} + +void PathPointsWidget::setSelectedIndex(const std::optional &selectedIndex) +{ + if (m_selectedIndex == selectedIndex) + return; + emit selectedIndexChanged(m_selectedIndex = selectedIndex); + update(); +} + +void PathPointsWidget::paintEvent(QPaintEvent *event) +{ + if (m_showGrid) + { + if (!m_gridBrush || m_gridBrush->gridX != m_gridX || m_gridBrush->gridY != m_gridY) + { + QPixmap pixmap{m_gridX, m_gridY}; + + { + QPainter painter{&pixmap}; + painter.setPen(palette().color(m_gridRole)); + painter.drawLine(0, 0, m_gridX, 0); + painter.drawLine(0, 0, 0, m_gridY); + + painter.fillRect(1, 1, m_gridX - 1, m_gridY - 1, palette().color(backgroundRole())); + } + + qDebug() << "setup brush" << pixmap.width() << pixmap.height(); + + m_gridBrush = GridBrush { + .gridX = m_gridX, + .gridY = m_gridY, + .brush = QBrush{std:: move(pixmap)} + }; + } + } + else + m_gridBrush = std::nullopt; + + QPainter painter{this}; + painter.fillRect(rect(), m_gridBrush ? m_gridBrush->brush : palette().color(backgroundRole())); + + if (!m_points) + return; + + const auto drawLine = [&](const auto &point0, const auto &point1){ + painter.setPen(QPen{Qt::black, 2}); + painter.drawLine(point0.point, point1.point); + painter.setPen(QPen{Qt::yellow, 1}); + painter.drawLine(point0.point, point1.point); + }; + + std::adjacent_find(std::cbegin(*m_points), std::cend(*m_points), + [&](const Path::Point &point0, const Path::Point &point1){ + drawLine(point0, point1); + return false; + }); + + if (m_closed && m_points->size() >= 2) + drawLine(m_points->back(), m_points->front()); + + painter.setPen(Qt::black); + std::size_t i{}; + for (const auto &point : *m_points) + { + if (i == m_selectedIndex) + painter.setBrush(Qt::red); + else if (i == 0) + painter.setBrush(Qt::green); + else + painter.setBrush(Qt::blue); + painter.drawEllipse(point.point, 2, 2); + i++; + } +} + +void PathPointsWidget::mousePressEvent(QMouseEvent *event) +{ + QWidget::mousePressEvent(event); + + switch (event->button()) + { + case Qt::LeftButton: + if (!m_points) + return; + + if (!m_points->empty()) + { + auto points = *m_points; + const auto distance = [&](const Path::Point &point){ return QLineF{point.point, event->pos()}.length(); }; + std::sort(std::begin(points), std::end(points), [&](const Path::Point &left, const Path::Point &right){ + return distance(left) < distance(right); + }); + if (distance(points.front()) > 8) + goto createNew; + + const auto iter = std::find_if(std::cbegin(*m_points), std::cend(*m_points), + [&minPoint=points.front().point](const Path::Point &point){ return point.point == minPoint; }); + if (iter == std::cend(*m_points)) + { + qWarning() << "unexpected end"; + goto createNew; + } + + const auto index = std::distance(std::cbegin(*m_points), iter); + emit pointInserted(index); + emit selectedIndexChanged(m_selectedIndex = index); + m_dragIndex = index; + } + else + { +createNew: + m_points->push_back(Path::Point{.point = snapPoint(event->pos()), .sp=100}); + const auto index = m_points->size() - 1; + emit pointInserted(index); + emit selectedIndexChanged(m_selectedIndex = index); + m_dragIndex = index; + } + update(); + break; + } +} + +void PathPointsWidget::mouseReleaseEvent(QMouseEvent *event) +{ + QWidget::mouseReleaseEvent(event); + + switch (event->button()) + { + case Qt::LeftButton: + m_dragIndex = std::nullopt; + break; + } +} + +void PathPointsWidget::mouseMoveEvent(QMouseEvent *event) +{ + QWidget::mouseMoveEvent(event); + + emit cursorMoved(snapPoint(event->pos())); + + if (m_dragIndex) + { + if (event->buttons().testFlag(Qt::LeftButton)) + { + const auto point = snapPoint(event->pos()); + if ((*m_points)[*m_dragIndex].point != point) + { + (*m_points)[*m_dragIndex].point = point; + emit pointMoved(*m_dragIndex); + update(); + } + } + else + m_dragIndex = std::nullopt; + } +} + +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, + }; +} diff --git a/pathpointswidget.h b/pathpointswidget.h new file mode 100644 index 0000000..baccf77 --- /dev/null +++ b/pathpointswidget.h @@ -0,0 +1,84 @@ +#pragma once + +#include +#include + +#include +#include + +#include "projectcontainer.h" + +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(bool closed READ closed WRITE setClosed NOTIFY closedChanged) + +public: + explicit PathPointsWidget(QWidget *parent = nullptr); + explicit PathPointsWidget(std::vector *points, QWidget *parent = nullptr); + + void setPoints(std::vector *points); + + bool showGrid() const { return m_showGrid; } + void setShowGrid(bool showGrid); + + int gridX() const { return m_gridX; } + void setGridX(int gridX); + + int gridY() const { return m_gridY; } + void setGridY(int gridY); + + bool closed() const { return m_closed; } + void setClosed(bool closed); + + const std::optional &selectedIndex() const { return m_selectedIndex; } + 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(); } + +signals: + void showGridChanged(bool showGrid); + void gridXChanged(int gridX); + void gridYChanged(int gridY); + void closedChanged(bool closed); + void selectedIndexChanged(const std::optional &selectedIndex); + + void cursorMoved(const QPoint &point); + + void pointInserted(std::size_t index); + void pointMoved(std::size_t index); + +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; + + std::vector *m_points{}; + + bool m_showGrid{true}; + int m_gridX{16}; + int m_gridY{16}; + + bool m_closed{true}; + + std::optional m_selectedIndex; + std::optional m_dragIndex; + + struct GridBrush { + int gridX; + int gridY; + QBrush brush; + }; + + std::optional m_gridBrush; + + QPalette::ColorRole m_gridRole{QPalette::Dark}; +}; diff --git a/projectcontainer.cpp b/projectcontainer.cpp index f0db2ae..40600f8 100644 --- a/projectcontainer.cpp +++ b/projectcontainer.cpp @@ -143,15 +143,37 @@ QDataStream &operator>>(QDataStream &ds, Background &background) return ds; } +QDataStream &operator<<(QDataStream &ds, const Path::Point &point) +{ + ds << point.point; + ds << point.sp; + return ds; +} + +QDataStream &operator>>(QDataStream &ds, Path::Point &point) +{ + ds >> point.point; + ds >> point.sp; + return ds; +} + QDataStream &operator<<(QDataStream &ds, const Path &path) { ds << path.name; + ds << path.points; + ds << path.type; + ds << path.closed; + ds << path.precision; return ds; } QDataStream &operator>>(QDataStream &ds, Path &path) { ds >> path.name; + ds >> path.points; + ds >> path.type; + ds >> path.closed; + ds >> path.precision; return ds; } diff --git a/projectcontainer.h b/projectcontainer.h index 628df50..6339754 100644 --- a/projectcontainer.h +++ b/projectcontainer.h @@ -7,6 +7,7 @@ #include #include #include +#include struct Sprite { @@ -49,6 +50,15 @@ struct Background struct Path { QString name; + struct Point { + QPoint point; + int sp{100}; + }; + std::vector points; + enum class Type { Straight, Smooth }; + Type type{Type::Straight}; + bool closed{true}; + int precision{4}; }; struct Script @@ -98,23 +108,5 @@ struct ProjectContainer template const std::list &containerFor() const; }; -QDataStream &operator<<(QDataStream &ds, const Sprite &sprite); -QDataStream &operator>>(QDataStream &ds, Sprite &sprite); -QDataStream &operator<<(QDataStream &ds, const Sound &sound); -QDataStream &operator>>(QDataStream &ds, Sound &sound); -QDataStream &operator<<(QDataStream &ds, const Background &background); -QDataStream &operator>>(QDataStream &ds, Background &background); -QDataStream &operator<<(QDataStream &ds, const Path &path); -QDataStream &operator>>(QDataStream &ds, Path &path); -QDataStream &operator<<(QDataStream &ds, const Script &script); -QDataStream &operator>>(QDataStream &ds, Script &script); -QDataStream &operator<<(QDataStream &ds, const Font &font); -QDataStream &operator>>(QDataStream &ds, Font &font); -QDataStream &operator<<(QDataStream &ds, const TimeLine &timeLine); -QDataStream &operator>>(QDataStream &ds, TimeLine &timeLine); -QDataStream &operator<<(QDataStream &ds, const Object &object); -QDataStream &operator>>(QDataStream &ds, Object &object); -QDataStream &operator<<(QDataStream &ds, const Room &room); -QDataStream &operator>>(QDataStream &ds, Room &room); QDataStream &operator<<(QDataStream &ds, const ProjectContainer &project); QDataStream &operator>>(QDataStream &ds, ProjectContainer &project); diff --git a/projecttreemodel.cpp b/projecttreemodel.cpp index 06ab4ae..ff134fc 100644 --- a/projecttreemodel.cpp +++ b/projecttreemodel.cpp @@ -6,7 +6,7 @@ #include #include -#include "futurecpp.h" +//#include "futurecpp.h" #include "projectcontainer.h" namespace { diff --git a/resources.qrc b/resources.qrc index 4282990..7e60e01 100644 --- a/resources.qrc +++ b/resources.qrc @@ -65,5 +65,11 @@ icons/object-file.png icons/room-file.png icons/timeline-file.png + icons/flip-horizontal.png + icons/flip-vertical.png + icons/scale.png + icons/center.png + icons/grid.png + icons/constants.png diff --git a/spritesmodel.cpp b/spritesmodel.cpp index b53472a..e9fedeb 100644 --- a/spritesmodel.cpp +++ b/spritesmodel.cpp @@ -1,29 +1,53 @@ #include "spritesmodel.h" #include +#include -SpritesModel::SpritesModel(QObject *parent) - : QAbstractListModel{parent} +SpritesModel::SpritesModel(const std::vector &pixmaps, QObject *parent) : + QAbstractListModel{parent}, + m_pixmaps{pixmaps} { } int SpritesModel::rowCount(const QModelIndex &parent) const { - return 100; + return m_pixmaps.size(); } QVariant SpritesModel::data(const QModelIndex &index, int role) const { + if (index.column() != 0) + return {}; + switch (role) { case Qt::DisplayRole: case Qt::EditRole: return tr("image %0").arg(index.row()); case Qt::DecorationRole: - QPixmap pixmap{64, 64}; - pixmap.fill(Qt::black); + if (index.row() < 0 || index.row() >= m_pixmaps.size()) + { + qWarning() << "invalid row" << index.row(); + return {}; + } + + const auto &pixmap = m_pixmaps.at(index.row()); + if (pixmap.isNull()) + { + QPixmap pixmap{32, 32}; + pixmap.fill(Qt::white); + return pixmap; + } return pixmap; } return {}; } + +const QPixmap &SpritesModel::pixmap(const QModelIndex &index) const +{ + if (index.row() < 0 || index.row() >= m_pixmaps.size()) + qFatal("index out of range"); + + return m_pixmaps.at(index.row()); +} diff --git a/spritesmodel.h b/spritesmodel.h index f8acf5d..e3d8079 100644 --- a/spritesmodel.h +++ b/spritesmodel.h @@ -1,14 +1,25 @@ #pragma once #include +#include + +#include class SpritesModel : public QAbstractListModel { Q_OBJECT public: - explicit SpritesModel(QObject *parent = nullptr); + explicit SpritesModel(const std::vector &pixmaps, QObject *parent = nullptr); int rowCount(const QModelIndex &parent) const override; QVariant data(const QModelIndex &index, int role) const override; + + const QPixmap &pixmap(const QModelIndex &index) const; + + using QAbstractListModel::beginResetModel; + using QAbstractListModel::endResetModel; + +private: + const std::vector &m_pixmaps; }; diff --git a/triggersmodel.cpp b/triggersmodel.cpp new file mode 100644 index 0000000..d511ea0 --- /dev/null +++ b/triggersmodel.cpp @@ -0,0 +1,19 @@ +#include "triggersmodel.h" + +#include "projectcontainer.h" + +TriggersModel::TriggersModel(ProjectContainer &project, QObject *parent) : + QAbstractListModel{parent}, + m_project{project} +{ +} + +int TriggersModel::rowCount(const QModelIndex &parent) const +{ + return 0; +} + +QVariant TriggersModel::data(const QModelIndex &index, int role) const +{ + return {}; +} diff --git a/triggersmodel.h b/triggersmodel.h new file mode 100644 index 0000000..01b1889 --- /dev/null +++ b/triggersmodel.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +struct ProjectContainer; + +class TriggersModel : public QAbstractListModel +{ + Q_OBJECT + +public: + explicit TriggersModel(ProjectContainer &project, QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + +private: + ProjectContainer &m_project; +};