diff --git a/.clang-format b/.clang-format index ee0c924b3..4897bcf18 100644 --- a/.clang-format +++ b/.clang-format @@ -18,6 +18,7 @@ AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: true AlwaysBreakTemplateDeclarations: true +AllowShortCaseLabelsOnASingleLine: true BinPackArguments: true BinPackParameters: true BraceWrapping: diff --git a/CMakeLists.txt b/CMakeLists.txt index c3c2667e0..164a0d2c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,12 +29,14 @@ set(RGM_SOURCES main.cpp MainWindow.cpp Dialogs/PreferencesDialog.cpp + Dialogs/TimelineChangeMoment.cpp Editors/BaseEditor.cpp Editors/CodeEditor.cpp Editors/BackgroundEditor.cpp Editors/ObjectEditor.cpp Editors/SoundEditor.cpp Editors/ScriptEditor.cpp + Editors/ShaderEditor.cpp Editors/FontEditor.cpp Editors/PathEditor.cpp Editors/TimelineEditor.cpp @@ -55,6 +57,7 @@ set(RGM_SOURCES Components/QMenuView.cpp Widgets/BackgroundView.cpp Widgets/CodeWidget.cpp + Widgets/StackedCodeWidget.cpp Widgets/ColorPicker.cpp Widgets/RoomView.cpp Widgets/AssetView.cpp @@ -71,12 +74,14 @@ set(RGM_HEADERS MainWindow.h Dialogs/PreferencesDialog.h Dialogs/PreferencesKeys.h + Dialogs/TimelineChangeMoment.h Editors/CodeEditor.h Editors/BaseEditor.h Editors/BackgroundEditor.h Editors/ObjectEditor.h Editors/SoundEditor.h Editors/ScriptEditor.h + Editors/ShaderEditor.h Editors/FontEditor.h Editors/PathEditor.h Editors/TimelineEditor.h @@ -98,6 +103,7 @@ set(RGM_HEADERS Components/Logger.h Widgets/BackgroundView.h Widgets/CodeWidget.h + Widgets/StackedCodeWidget.h Widgets/ColorPicker.h Widgets/RoomView.h Widgets/AssetView.h @@ -109,6 +115,7 @@ set(RGM_UI MainWindow.ui Dialogs/PreferencesDialog.ui Dialogs/AddImageDialog.ui + Dialogs/TimelineChangeMoment.ui Editors/CodeEditor.ui Editors/BackgroundEditor.ui Editors/ObjectEditor.ui @@ -184,14 +191,14 @@ target_link_libraries(${EXE} PRIVATE yaml-cpp) # Find libEGM # Debug -find_library(LIB_EGM_D NAMES EGMd PATHS ${ENIGMA_DIR}) +find_library(LIB_EGM_D NAMES EGMd EGM PATHS ${ENIGMA_DIR}) # Release find_library(LIB_EGM NAMES EGM PATHS ${ENIGMA_DIR}) target_link_libraries(${EXE} PRIVATE "$,${LIB_EGM_D},${LIB_EGM}>") # Find libProtocols # Debug -find_library(LIB_PROTO_D NAMES Protocolsd PATHS ${ENIGMA_DIR}) +find_library(LIB_PROTO_D NAMES Protocolsd Protocols PATHS ${ENIGMA_DIR}) # Release find_library(LIB_PROTO NAMES Protocols PATHS ${ENIGMA_DIR}) target_link_libraries(${EXE} PRIVATE "$,${LIB_PROTO_D},${LIB_PROTO}>") diff --git a/Dialogs/TimelineChangeMoment.cpp b/Dialogs/TimelineChangeMoment.cpp new file mode 100644 index 000000000..d7ed79279 --- /dev/null +++ b/Dialogs/TimelineChangeMoment.cpp @@ -0,0 +1,18 @@ +#include "TimelineChangeMoment.h" +#include "ui_TimelineChangeMoment.h" + +TimelineChangeMoment::TimelineChangeMoment(QWidget *parent) : + QDialog(parent), + ui(new Ui::TimelineChangeMoment) { + ui->setupUi(this); +} + +TimelineChangeMoment::~TimelineChangeMoment() { delete ui; } + +int TimelineChangeMoment::GetSpinBoxValue() { + return ui->spinBox->value(); +} + +void TimelineChangeMoment::SetSpinBoxValue(int val) { + ui->spinBox->setValue(val); +} diff --git a/Dialogs/TimelineChangeMoment.h b/Dialogs/TimelineChangeMoment.h new file mode 100644 index 000000000..e4d8c8eb7 --- /dev/null +++ b/Dialogs/TimelineChangeMoment.h @@ -0,0 +1,24 @@ +#ifndef TIMELINECHANGEMOMENT_H +#define TIMELINECHANGEMOMENT_H + +#include + +namespace Ui { +class TimelineChangeMoment; +} + +class TimelineChangeMoment : public QDialog +{ + Q_OBJECT + +public: + explicit TimelineChangeMoment(QWidget *parent = nullptr); + ~TimelineChangeMoment(); + int GetSpinBoxValue(); + void SetSpinBoxValue(int val); + +private: + Ui::TimelineChangeMoment *ui; +}; + +#endif // TIMELINECHANGEMOMENT_H diff --git a/Dialogs/TimelineChangeMoment.ui b/Dialogs/TimelineChangeMoment.ui new file mode 100644 index 000000000..2a9973e6e --- /dev/null +++ b/Dialogs/TimelineChangeMoment.ui @@ -0,0 +1,78 @@ + + + TimelineChangeMoment + + + + 0 + 0 + 206 + 89 + + + + Dialog + + + + + + + + New Step Value + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + TimelineChangeMoment + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + TimelineChangeMoment + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/Editors/BackgroundEditor.h b/Editors/BackgroundEditor.h index 5d4070da1..bfc1566c7 100644 --- a/Editors/BackgroundEditor.h +++ b/Editors/BackgroundEditor.h @@ -14,7 +14,7 @@ class BackgroundEditor : public BaseEditor { public: explicit BackgroundEditor(ProtoModelPtr model, QWidget *parent); - ~BackgroundEditor(); + ~BackgroundEditor() override; private slots: void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVariant &oldValue = QVariant(0), diff --git a/Editors/BaseEditor.cpp b/Editors/BaseEditor.cpp index a80b172cf..8c29ba5e3 100644 --- a/Editors/BaseEditor.cpp +++ b/Editors/BaseEditor.cpp @@ -1,15 +1,18 @@ #include "BaseEditor.h" #include -#include #include +#include BaseEditor::BaseEditor(ProtoModelPtr treeNodeModel, QWidget* parent) - : QWidget(parent), nodeMapper(new ModelMapper(treeNodeModel, this)) { + : QWidget(parent), nodeMapper(new ModelMapper(treeNodeModel, this)), _model(treeNodeModel) { buffers::TreeNode* n = static_cast(treeNodeModel->GetBuffer()); resMapper = new ModelMapper(treeNodeModel->GetSubModel(ResTypeFields[n->type_case()]), this); + // Backup should be deleted by Qt's garbage collector when this editor is closed resMapper->GetModel()->BackupModel(this); + + connect(_model, &QAbstractItemModel::modelReset, [this]() { this->RebindSubModels(); }); } void BaseEditor::closeEvent(QCloseEvent* event) { @@ -50,6 +53,12 @@ void BaseEditor::dataChanged(const QModelIndex& topLeft, const QModelIndex& /*bo this->setWindowTitle(QString::fromStdString(n->name())); emit ResourceRenamed(n->type_case(), oldValue.toString(), QString::fromStdString(n->name())); } + resMapper->SetDirty(true); +} + +void BaseEditor::RebindSubModels() { + resMapper->toFirst(); + nodeMapper->toFirst(); } void BaseEditor::OnSave() { diff --git a/Editors/BaseEditor.h b/Editors/BaseEditor.h index 617684ac8..3aeede14b 100644 --- a/Editors/BaseEditor.h +++ b/Editors/BaseEditor.h @@ -7,12 +7,12 @@ #include static const QHash ResTypeFields = { - {TypeCase::kFolder, TreeNode::kFolderFieldNumber}, {TypeCase::kSprite, TreeNode::kSpriteFieldNumber}, - {TypeCase::kSound, TreeNode::kSoundFieldNumber}, {TypeCase::kBackground, TreeNode::kBackgroundFieldNumber}, - {TypeCase::kPath, TreeNode::kPathFieldNumber}, {TypeCase::kFont, TreeNode::kFontFieldNumber}, - {TypeCase::kScript, TreeNode::kScriptFieldNumber}, {TypeCase::kTimeline, TreeNode::kTimelineFieldNumber}, - {TypeCase::kObject, TreeNode::kObjectFieldNumber}, {TypeCase::kRoom, TreeNode::kRoomFieldNumber}, - {TypeCase::kSettings, TreeNode::kSettingsFieldNumber}}; + {TypeCase::kFolder, TreeNode::kFolderFieldNumber}, {TypeCase::kSprite, TreeNode::kSpriteFieldNumber}, + {TypeCase::kSound, TreeNode::kSoundFieldNumber}, {TypeCase::kBackground, TreeNode::kBackgroundFieldNumber}, + {TypeCase::kPath, TreeNode::kPathFieldNumber}, {TypeCase::kFont, TreeNode::kFontFieldNumber}, + {TypeCase::kScript, TreeNode::kScriptFieldNumber}, {TypeCase::kTimeline, TreeNode::kTimelineFieldNumber}, + {TypeCase::kObject, TreeNode::kObjectFieldNumber}, {TypeCase::kRoom, TreeNode::kRoomFieldNumber}, + {TypeCase::kSettings, TreeNode::kSettingsFieldNumber}, {TypeCase::kShader, TreeNode::kShaderFieldNumber}}; class BaseEditor : public QWidget { Q_OBJECT @@ -31,11 +31,13 @@ class BaseEditor : public QWidget { public slots: virtual void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVariant &oldValue = QVariant(0), const QVector &roles = QVector()); + virtual void RebindSubModels(); void OnSave(); protected: - ModelMapper* nodeMapper; - ModelMapper* resMapper; + ModelMapper *nodeMapper; + ModelMapper *resMapper; + ProtoModelPtr _model; }; #endif // BASEEDTIOR_H diff --git a/Editors/CodeEditor.cpp b/Editors/CodeEditor.cpp index 793501cd0..23de41358 100644 --- a/Editors/CodeEditor.cpp +++ b/Editors/CodeEditor.cpp @@ -1,26 +1,69 @@ #include "CodeEditor.h" +#include "Widgets/StackedCodeWidget.h" #include "ui_CodeEditor.h" -CodeEditor::CodeEditor(ProtoModelPtr model, QWidget *parent) : BaseEditor(model, parent), ui(new Ui::CodeEditor) { +CodeEditor::CodeEditor(QWidget* parent, bool removeSaveBtn) : ui(new Ui::CodeEditor) { ui->setupUi(this); - connect(ui->actionSave, &QAction::triggered, this, &BaseEditor::OnSave); + if (removeSaveBtn) ui->mainToolBar->removeAction(ui->actionSave); + + layout()->setAlignment(Qt::AlignTop); + + connect(ui->actionCut, &QAction::triggered, ui->stackedWidget, &StackedCodeWidget::cut); + connect(ui->actionCopy, &QAction::triggered, ui->stackedWidget, &StackedCodeWidget::copy); + connect(ui->actionRedo, &QAction::triggered, ui->stackedWidget, &StackedCodeWidget::redo); + connect(ui->actionUndo, &QAction::triggered, ui->stackedWidget, &StackedCodeWidget::undo); + connect(ui->actionPaste, &QAction::triggered, ui->stackedWidget, &StackedCodeWidget::paste); + connect(ui->actionPrint, &QAction::triggered, ui->stackedWidget, &StackedCodeWidget::printSource); + connect(ui->actionGoToLine, &QAction::triggered, ui->stackedWidget, &StackedCodeWidget::gotoLineDialog); + connect(ui->actionNewSource, &QAction::triggered, ui->stackedWidget, &StackedCodeWidget::newSource); + connect(ui->actionLoadSource, &QAction::triggered, ui->stackedWidget, &StackedCodeWidget::loadSource); + connect(ui->actionSaveSource, &QAction::triggered, ui->stackedWidget, &StackedCodeWidget::saveSource); + //connect(ui->actionFindAndReplace, &QAction::triggered, ui->stackedWidget, &StackedCodeWidget::findAndReplace); cursorPositionLabel = new QLabel(ui->statusBar); lineCountLabel = new QLabel(ui->statusBar); + connect(ui->stackedWidget, &QStackedWidget::currentChanged, [this]() { + if (ui->stackedWidget->count() > 0) { + this->updateCursorPositionLabel(); + this->updateLineCountLabel(); + } + }); + ui->statusBar->addWidget(cursorPositionLabel); ui->statusBar->addWidget(lineCountLabel); +} - // make sure we set the status labels at least once - updateCursorPositionLabel(); - updateLineCountLabel(); +CodeEditor::~CodeEditor() { delete ui; } - connect(ui->codeWidget, &CodeWidget::cursorPositionChanged, this, &CodeEditor::setCursorPositionLabel); - connect(ui->codeWidget, &CodeWidget::lineCountChanged, this, &CodeEditor::setLineCountLabel); +void CodeEditor::SetDisabled(bool disabled) { + (disabled) ? ui->stackedWidget->hide() : ui->stackedWidget->show(); + (disabled) ? ui->statusBar->hide() : ui->statusBar->show(); + for (QAction* action : ui->mainToolBar->actions()) { + action->setDisabled(disabled); + } } -CodeEditor::~CodeEditor() { delete ui; } +int CodeEditor::GetCurrentIndex() { return ui->stackedWidget->currentIndex(); } + +void CodeEditor::SetCurrentIndex(int index) { ui->stackedWidget->setCurrentIndex(index); } + +void CodeEditor::RemoveCodeWidget(int index) { ui->stackedWidget->removeWidget(ui->stackedWidget->widget(index)); } + +void CodeEditor::ClearCodeWidgets() { + for (int i = 0; i < ui->stackedWidget->count(); ++i) { + ui->stackedWidget->removeWidget(ui->stackedWidget->widget(i)); + } +} + +CodeWidget* CodeEditor::AddCodeWidget() { + CodeWidget* codeWidget = new CodeWidget(ui->stackedWidget); + ui->stackedWidget->addWidget(codeWidget); + connect(codeWidget, &CodeWidget::cursorPositionChanged, this, &CodeEditor::setCursorPositionLabel); + connect(codeWidget, &CodeWidget::lineCountChanged, this, &CodeEditor::setLineCountLabel); + return codeWidget; +} void CodeEditor::setCursorPositionLabel(int line, int index) { this->cursorPositionLabel->setText(tr("Ln %0, Col %1").arg(line).arg(index)); @@ -29,8 +72,10 @@ void CodeEditor::setCursorPositionLabel(int line, int index) { void CodeEditor::setLineCountLabel(int lines) { this->lineCountLabel->setText(tr("Lines %0").arg(lines)); } void CodeEditor::updateCursorPositionLabel() { - auto cursorPosition = this->ui->codeWidget->cursorPosition(); + auto cursorPosition = static_cast(ui->stackedWidget->currentWidget())->cursorPosition(); this->setCursorPositionLabel(cursorPosition.first, cursorPosition.second); } -void CodeEditor::updateLineCountLabel() { this->setLineCountLabel(this->ui->codeWidget->lineCount()); } +void CodeEditor::updateLineCountLabel() { + this->setLineCountLabel(static_cast(ui->stackedWidget->currentWidget())->lineCount()); +} diff --git a/Editors/CodeEditor.h b/Editors/CodeEditor.h index 176363b66..a69671c79 100644 --- a/Editors/CodeEditor.h +++ b/Editors/CodeEditor.h @@ -1,7 +1,7 @@ #ifndef CODEEDITOR_H #define CODEEDITOR_H -#include "BaseEditor.h" +#include "Widgets/CodeWidget.h" #include @@ -9,12 +9,19 @@ namespace Ui { class CodeEditor; } -class CodeEditor : public BaseEditor { +class CodeEditor : public QWidget { Q_OBJECT public: - explicit CodeEditor(ProtoModelPtr model, QWidget *parent); + explicit CodeEditor(QWidget *parent, bool removeSaveBtn = false); ~CodeEditor(); + void SetDisabled(bool disabled); + CodeWidget *AddCodeWidget(); + int GetCurrentIndex(); + void SetCurrentIndex(int index); + void RemoveCodeWidget(int index); + void ClearCodeWidgets(); + Ui::CodeEditor *ui; public slots: void setCursorPositionLabel(int line, int index); @@ -22,9 +29,6 @@ class CodeEditor : public BaseEditor { void updateCursorPositionLabel(); void updateLineCountLabel(); - protected: - Ui::CodeEditor *ui; - private: QLabel *cursorPositionLabel, *lineCountLabel; }; diff --git a/Editors/CodeEditor.ui b/Editors/CodeEditor.ui index 541cf5f9a..0e5a9f169 100644 --- a/Editors/CodeEditor.ui +++ b/Editors/CodeEditor.ui @@ -23,9 +23,9 @@ :/resources/script.png:/resources/script.png - + - 0 + 2 0 @@ -41,12 +41,18 @@ + + true + 0 0 + + Qt::Horizontal + 24 @@ -75,10 +81,31 @@ - + + + ArrowCursor + + + + + 0 + 0 + 711 + 341 + + + + - + + + + 0 + 0 + + + @@ -228,6 +255,11 @@ CodeWidget QWidget
Widgets/CodeWidget.h
+ + + StackedCodeWidget + QWidget +
Widgets/StackedCodeWidget.h
1 cut() @@ -246,166 +278,5 @@ - - - actionCut - triggered() - codeWidget - cut() - - - -1 - -1 - - - 367 - 214 - - - - - actionCopy - triggered() - codeWidget - copy() - - - -1 - -1 - - - 367 - 214 - - - - - actionPaste - triggered() - codeWidget - paste() - - - -1 - -1 - - - 367 - 214 - - - - - actionUndo - triggered() - codeWidget - undo() - - - -1 - -1 - - - 367 - 214 - - - - - actionRedo - triggered() - codeWidget - redo() - - - -1 - -1 - - - 367 - 214 - - - - - actionNewSource - triggered() - codeWidget - newSource() - - - -1 - -1 - - - 367 - 214 - - - - - actionLoadSource - triggered() - codeWidget - loadSource() - - - -1 - -1 - - - 367 - 214 - - - - - actionSaveSource - triggered() - codeWidget - saveSource() - - - -1 - -1 - - - 367 - 214 - - - - - actionPrint - triggered() - codeWidget - printSource() - - - -1 - -1 - - - 367 - 214 - - - - - actionGoToLine - triggered() - codeWidget - gotoLineDialog() - - - -1 - -1 - - - 367 - 214 - - - - + diff --git a/Editors/FontEditor.h b/Editors/FontEditor.h index e07494199..19172b840 100644 --- a/Editors/FontEditor.h +++ b/Editors/FontEditor.h @@ -12,7 +12,7 @@ class FontEditor : public BaseEditor { public: explicit FontEditor(ProtoModelPtr model, QWidget* parent); - ~FontEditor(); + ~FontEditor() override; private: Ui::FontEditor* ui; diff --git a/Editors/ObjectEditor.h b/Editors/ObjectEditor.h index 67a252eb2..b011af01b 100644 --- a/Editors/ObjectEditor.h +++ b/Editors/ObjectEditor.h @@ -12,7 +12,7 @@ class ObjectEditor : public BaseEditor { public: explicit ObjectEditor(ProtoModelPtr model, QWidget* parent); - ~ObjectEditor(); + ~ObjectEditor() override; private: Ui::ObjectEditor* ui; diff --git a/Editors/PathEditor.h b/Editors/PathEditor.h index e9f444207..c7c948e0a 100644 --- a/Editors/PathEditor.h +++ b/Editors/PathEditor.h @@ -12,7 +12,7 @@ class PathEditor : public BaseEditor { public: explicit PathEditor(ProtoModelPtr model, QWidget* parent); - ~PathEditor(); + ~PathEditor() override; private: Ui::PathEditor* ui; diff --git a/Editors/RoomEditor.cpp b/Editors/RoomEditor.cpp index 9006dc7a4..0aa3f8ac5 100644 --- a/Editors/RoomEditor.cpp +++ b/Editors/RoomEditor.cpp @@ -20,9 +20,6 @@ RoomEditor::RoomEditor(ProtoModelPtr model, QWidget* parent) : BaseEditor(model, ui->setupUi(this); connect(ui->actionSave, &QAction::triggered, this, &BaseEditor::OnSave); - ProtoModelPtr roomModel = model->GetSubModel(TreeNode::kRoomFieldNumber); - ui->roomView->SetResourceModel(roomModel); - nodeMapper->addMapping(ui->roomName, TreeNode::kNameFieldNumber); nodeMapper->toFirst(); @@ -37,9 +34,7 @@ RoomEditor::RoomEditor(ProtoModelPtr model, QWidget* parent) : BaseEditor(model, resMapper->addMapping(ui->clearViewportCheckBox, Room::kClearViewBackgroundFieldNumber); resMapper->toFirst(); - RepeatedProtoModelPtr vm = roomModel->GetRepeatedSubModel(Room::kViewsFieldNumber); - ImmediateDataWidgetMapper* viewMapper = new ImmediateDataWidgetMapper(this); - viewMapper->setModel(vm); + viewMapper = new ImmediateDataWidgetMapper(this); viewMapper->addMapping(ui->viewVisibleCheckBox, View::kVisibleFieldNumber); viewMapper->addMapping(ui->cameraXSpinBox, View::kXviewFieldNumber); @@ -70,19 +65,35 @@ RoomEditor::RoomEditor(ProtoModelPtr model, QWidget* parent) : BaseEditor(model, connect(ui->currentViewComboBox, static_cast(&QComboBox::currentIndexChanged), [=](int index) { viewMapper->setCurrentIndex(index); }); + // filter the room preview scroll area to paint + // the checkerboard transparency pattern and to + // track the cursor position for the status bar + ui->roomPreview->widget()->installEventFilter(this); + // track the cursor position on the transparency pattern for the status bar + ui->roomPreview->widget()->setMouseTracking(true); + // also need to track it on the room preview itself so the event is received + ui->roomView->setMouseTracking(true); + + cursorPositionLabel = new QLabel(); + this->updateCursorPositionLabel(ui->roomPreview->widget()->cursor().pos()); + assetNameLabel = new QLabel("obj_xxx"); + + ui->statusBar->addWidget(cursorPositionLabel); + ui->statusBar->addWidget(assetNameLabel); + + RebindSubModels(); +} + +RoomEditor::~RoomEditor() { delete ui; } + +void RoomEditor::RebindSubModels() { + roomModel = _model->GetSubModel(TreeNode::kRoomFieldNumber); + ui->roomView->SetResourceModel(roomModel); + RepeatedProtoModelPtr im = roomModel->GetRepeatedSubModel(Room::kInstancesFieldNumber); QSortFilterProxyModel* imp = new QSortFilterProxyModel(this); imp->setSourceModel(im); ui->instancesListView->setModel(imp); - connect(ui->instancesListView->selectionModel(), &QItemSelectionModel::selectionChanged, - [=](const QItemSelection& selected, const QItemSelection& /*deselected*/) { - if (selected.empty()) return; - ui->tilesListView->clearSelection(); - auto selectedIndex = selected.indexes().first(); - auto currentInstanceModel = - roomModel->GetSubModel(Room::kInstancesFieldNumber, selectedIndex.row()); - ui->layersPropertiesView->setModel(currentInstanceModel); - }); for (int c = 0; c < im->columnCount(); ++c) { if (c != Room::Instance::kNameFieldNumber && c != Room::Instance::kObjectTypeFieldNumber && @@ -91,23 +102,14 @@ RoomEditor::RoomEditor(ProtoModelPtr model, QWidget* parent) : BaseEditor(model, else ui->instancesListView->resizeColumnToContents(c); } - + ui->instancesListView->header()->swapSections(Room::Instance::kNameFieldNumber, - Room::Instance::kObjectTypeFieldNumber); + Room::Instance::kObjectTypeFieldNumber); RepeatedProtoModelPtr tm = roomModel->GetRepeatedSubModel(Room::kTilesFieldNumber); QSortFilterProxyModel* tmp = new QSortFilterProxyModel(this); tmp->setSourceModel(tm); ui->tilesListView->setModel(tmp); - connect(ui->tilesListView->selectionModel(), &QItemSelectionModel::selectionChanged, - [=](const QItemSelection& selected, const QItemSelection& /*deselected*/) { - if (selected.empty()) return; - ui->instancesListView->clearSelection(); - auto selectedIndex = selected.indexes().first(); - auto currentInstanceModel = - roomModel->GetSubModel(Room::kTilesFieldNumber, selectedIndex.row()); - ui->layersPropertiesView->setModel(currentInstanceModel); - }); for (int c = 0; c < tm->columnCount(); ++c) { if (c != Room::Tile::kBackgroundNameFieldNumber && c != Room::Tile::kIdFieldNumber && @@ -117,27 +119,31 @@ RoomEditor::RoomEditor(ProtoModelPtr model, QWidget* parent) : BaseEditor(model, ui->tilesListView->resizeColumnToContents(c); } - ui->tilesListView->header()->swapSections(Room::Tile::kNameFieldNumber, - Room::Tile::kBackgroundNameFieldNumber); + ui->tilesListView->header()->swapSections(Room::Tile::kNameFieldNumber, Room::Tile::kBackgroundNameFieldNumber); - // filter the room preview scroll area to paint - // the checkerboard transparency pattern and to - // track the cursor position for the status bar - ui->roomPreview->widget()->installEventFilter(this); - // track the cursor position on the transparency pattern for the status bar - ui->roomPreview->widget()->setMouseTracking(true); - // also need to track it on the room preview itself so the event is received - ui->roomView->setMouseTracking(true); + RepeatedProtoModelPtr vm = roomModel->GetRepeatedSubModel(Room::kViewsFieldNumber); + viewMapper->setModel(vm); - cursorPositionLabel = new QLabel(); - this->updateCursorPositionLabel(ui->roomPreview->widget()->cursor().pos()); - assetNameLabel = new QLabel("obj_xxx"); + connect(ui->instancesListView->selectionModel(), &QItemSelectionModel::selectionChanged, + [=](const QItemSelection& selected, const QItemSelection& /*deselected*/) { + if (selected.empty()) return; + ui->tilesListView->clearSelection(); + auto selectedIndex = selected.indexes().first(); + auto currentInstanceModel = roomModel->GetSubModel(Room::kInstancesFieldNumber, selectedIndex.row()); + ui->layersPropertiesView->setModel(currentInstanceModel); + }); - ui->statusBar->addWidget(cursorPositionLabel); - ui->statusBar->addWidget(assetNameLabel); -} + connect(ui->tilesListView->selectionModel(), &QItemSelectionModel::selectionChanged, + [=](const QItemSelection& selected, const QItemSelection& /*deselected*/) { + if (selected.empty()) return; + ui->instancesListView->clearSelection(); + auto selectedIndex = selected.indexes().first(); + auto currentInstanceModel = roomModel->GetSubModel(Room::kTilesFieldNumber, selectedIndex.row()); + ui->layersPropertiesView->setModel(currentInstanceModel); + }); -RoomEditor::~RoomEditor() { delete ui; } + BaseEditor::RebindSubModels(); +} bool RoomEditor::eventFilter(QObject* obj, QEvent* event) { if (obj == ui->roomPreview->widget()) { diff --git a/Editors/RoomEditor.h b/Editors/RoomEditor.h index 34ae54674..919d2330f 100644 --- a/Editors/RoomEditor.h +++ b/Editors/RoomEditor.h @@ -17,12 +17,13 @@ class RoomEditor : public BaseEditor { public: explicit RoomEditor(ProtoModelPtr model, QWidget* parent); - ~RoomEditor(); + ~RoomEditor() override; void setZoom(qreal zoom); public slots: - void SelectedObjectChanged(QAction *action); + void RebindSubModels() override; + void SelectedObjectChanged(QAction* action); private slots: void on_actionZoomIn_triggered(); @@ -33,6 +34,8 @@ class RoomEditor : public BaseEditor { private: Ui::RoomEditor* ui; QLabel *cursorPositionLabel, *assetNameLabel; + ProtoModelPtr roomModel; + ImmediateDataWidgetMapper* viewMapper; bool eventFilter(QObject* obj, QEvent* event) override; }; diff --git a/Editors/ScriptEditor.cpp b/Editors/ScriptEditor.cpp index 5fcbbe072..fae819d38 100644 --- a/Editors/ScriptEditor.cpp +++ b/Editors/ScriptEditor.cpp @@ -5,12 +5,21 @@ using namespace buffers::resources; -ScriptEditor::ScriptEditor(ProtoModelPtr model, QWidget *parent) : CodeEditor(model, parent) { - resMapper->addMapping(ui->codeWidget, Script::kCodeFieldNumber); +ScriptEditor::ScriptEditor(ProtoModelPtr model, QWidget* parent) + : BaseEditor(model, parent), codeEditor(new CodeEditor(this)) { + QLayout* layout = new QVBoxLayout(this); + layout->addWidget(codeEditor); + layout->setMargin(0); + setLayout(layout); + resize(codeEditor->geometry().width(), codeEditor->geometry().height()); + + Ui::CodeEditor* ui = codeEditor->ui; + connect(ui->actionSave, &QAction::triggered, this, &BaseEditor::OnSave); + + CodeWidget* codeWidget = codeEditor->AddCodeWidget(); + resMapper->addMapping(codeWidget, Script::kCodeFieldNumber); resMapper->toFirst(); - this->updateCursorPositionLabel(); - this->updateLineCountLabel(); + codeEditor->updateCursorPositionLabel(); + codeEditor->updateLineCountLabel(); } - -ScriptEditor::~ScriptEditor() {} diff --git a/Editors/ScriptEditor.h b/Editors/ScriptEditor.h index 2265b5106..0635beb6f 100644 --- a/Editors/ScriptEditor.h +++ b/Editors/ScriptEditor.h @@ -1,14 +1,17 @@ #ifndef SCRIPTEDITOR_H #define SCRIPTEDITOR_H +#include "Editors/BaseEditor.h" #include "Editors/CodeEditor.h" -class ScriptEditor : public CodeEditor { +class ScriptEditor : public BaseEditor { Q_OBJECT public: - ScriptEditor(ProtoModelPtr model, QWidget *parent = nullptr); - ~ScriptEditor(); + ScriptEditor(ProtoModelPtr model, QWidget* parent = nullptr); + + private: + CodeEditor* codeEditor; }; #endif // SCRIPTEDITOR_H diff --git a/Editors/ShaderEditor.cpp b/Editors/ShaderEditor.cpp new file mode 100644 index 000000000..02213963c --- /dev/null +++ b/Editors/ShaderEditor.cpp @@ -0,0 +1,44 @@ +#include "ShaderEditor.h" +#include "ui_CodeEditor.h" + +#include "Shader.pb.h" + +#include +#include +#include + +using namespace buffers::resources; + +ShaderEditor::ShaderEditor(ProtoModelPtr model, QWidget* parent) + : BaseEditor(model, parent), codeEditor(new CodeEditor(this)) { + QLayout* layout = new QVBoxLayout(this); + layout->addWidget(codeEditor); + layout->setMargin(0); + setLayout(layout); + resize(codeEditor->geometry().width(), codeEditor->geometry().height()); + + Ui::CodeEditor* ui = codeEditor->ui; + connect(ui->actionSave, &QAction::triggered, this, &BaseEditor::OnSave); + + QLabel* shaderLabel = new QLabel(tr("Shader Type: "), ui->mainToolBar); + QComboBox* shaderType = new QComboBox(ui->mainToolBar); + shaderType->addItems({tr("Vertex"), tr("Fragment")}); + + ui->mainToolBar->addSeparator(); + ui->mainToolBar->addWidget(shaderLabel); + ui->mainToolBar->addWidget(shaderType); + + CodeWidget* vertexWidget = codeEditor->AddCodeWidget(); + CodeWidget* fragWidget = codeEditor->AddCodeWidget(); + + resMapper->addMapping(fragWidget, Shader::kFragmentCodeFieldNumber); + resMapper->addMapping(vertexWidget, Shader::kVertexCodeFieldNumber); + resMapper->toFirst(); + + connect(shaderType, QOverload::of(&QComboBox::currentIndexChanged), + [=](int index) { ui->stackedWidget->setCurrentIndex(index); }); + + ui->stackedWidget->setCurrentIndex(0); + codeEditor->updateCursorPositionLabel(); + codeEditor->updateLineCountLabel(); +} diff --git a/Editors/ShaderEditor.h b/Editors/ShaderEditor.h new file mode 100644 index 000000000..5aa9d44b9 --- /dev/null +++ b/Editors/ShaderEditor.h @@ -0,0 +1,17 @@ +#ifndef SHADEREDITOR_H +#define SHADEREDITOR_H + +#include "Editors/BaseEditor.h" +#include "Editors/CodeEditor.h" + +class ShaderEditor : public BaseEditor { + Q_OBJECT + + public: + ShaderEditor(ProtoModelPtr model, QWidget* parent = nullptr); + + private: + CodeEditor* codeEditor; +}; + +#endif // SHADEREDITOR_H diff --git a/Editors/SoundEditor.cpp b/Editors/SoundEditor.cpp index fb4fc4bf2..6a703e42c 100644 --- a/Editors/SoundEditor.cpp +++ b/Editors/SoundEditor.cpp @@ -7,40 +7,39 @@ #include "Components/Utility.h" #include -#include -#include #include +#include +#include -SoundEditor::SoundEditor(ProtoModelPtr model, QWidget* parent) : - BaseEditor(model, parent), ui(new Ui::SoundEditor), mediaPlayer(new QMediaPlayer(this)), - playlist(new QMediaPlaylist(mediaPlayer)), userPaused(false) { +SoundEditor::SoundEditor(ProtoModelPtr model, QWidget* parent) + : BaseEditor(model, parent), + ui(new Ui::SoundEditor), + mediaPlayer(new QMediaPlayer(this)), + playlist(new QMediaPlaylist(mediaPlayer)), + userPaused(false) { ui->setupUi(this); nodeMapper->addMapping(ui->nameEdit, TreeNode::kNameFieldNumber); - nodeMapper->toFirst(); - resMapper->addMapping(ui->volumeSpinBox, Sound::kVolumeFieldNumber); - resMapper->toFirst(); ui->volumeSlider->setValue(static_cast(ui->volumeSpinBox->value() * 100)); connect(ui->saveButton, &QAbstractButton::pressed, this, &BaseEditor::OnSave); - ProtoModelPtr soundModel = model->GetSubModel(TreeNode::kSoundFieldNumber); playlist->setPlaybackMode(QMediaPlaylist::CurrentItemOnce); - playlist->addMedia(QUrl::fromLocalFile(soundModel->data(Sound::kDataFieldNumber).toString())); mediaPlayer->setPlaylist(playlist); // Update the signals every 50ms instead of Qt's default 1000ms to keep slider up to date mediaPlayer->setNotifyInterval(50); connect(mediaPlayer, &QMediaPlayer::positionChanged, [=]() { - if (mediaPlayer->duration() > 0) { - int percent = static_cast(100*(static_cast(mediaPlayer->position()) / mediaPlayer->duration())); - ui->playbackSlider->setSliderPosition(percent); - } else ui->playbackSlider->setSliderPosition(0); - - QTime timestamp(0,0); - ui->playbackPositionLabel->setText(timestamp.addMSecs(static_cast(mediaPlayer->position())).toString()); + if (mediaPlayer->duration() > 0) { + int percent = static_cast(100 * (static_cast(mediaPlayer->position()) / mediaPlayer->duration())); + ui->playbackSlider->setSliderPosition(percent); + } else + ui->playbackSlider->setSliderPosition(0); + + QTime timestamp(0, 0); + ui->playbackPositionLabel->setText(timestamp.addMSecs(static_cast(mediaPlayer->position())).toString()); }); connect(mediaPlayer, &QMediaPlayer::mediaChanged, [=]() { @@ -55,12 +54,20 @@ SoundEditor::SoundEditor(ProtoModelPtr model, QWidget* parent) : ui->playButton->setIcon(ArtManager::GetIcon(":/actions/pause.png")); }); - playlist->addMedia(QUrl::fromLocalFile(soundModel->data(Sound::kDataFieldNumber).toString())); mediaPlayer->setPlaylist(playlist); + + RebindSubModels(); } SoundEditor::~SoundEditor() { delete ui; } +void SoundEditor::RebindSubModels() { + playlist->clear(); + soundModel = _model->GetSubModel(TreeNode::kSoundFieldNumber); + playlist->addMedia(QUrl::fromLocalFile(soundModel->data(Sound::kDataFieldNumber).toString())); + BaseEditor::RebindSubModels(); +} + void SoundEditor::on_playButton_clicked() { if (mediaPlayer->state() == QMediaPlayer::PausedState || mediaPlayer->state() == QMediaPlayer::StoppedState) { mediaPlayer->play(); @@ -83,7 +90,7 @@ void SoundEditor::on_playbackSlider_sliderPressed() { } void SoundEditor::on_playbackSlider_sliderReleased() { - mediaPlayer->setPosition(static_cast((ui->playbackSlider->value()/100.f)*mediaPlayer->duration())); + mediaPlayer->setPosition(static_cast((ui->playbackSlider->value() / 100.f) * mediaPlayer->duration())); if (mediaPlayer->state() == QMediaPlayer::PausedState && !userPaused) mediaPlayer->play(); } @@ -98,7 +105,7 @@ void SoundEditor::on_volumeSpinBox_valueChanged(double arg1) { } void SoundEditor::on_saveAsButton_clicked() { - // TODO: implement this when egm is done + // TODO: implement this when egm is done } void SoundEditor::on_loadButton_clicked() { @@ -114,7 +121,8 @@ void SoundEditor::on_loadButton_clicked() { // QString newData = GetModelData(Sound::kDataFieldNumber).toString(); // TODO: Copy data into our egm and reset the path // SetModelData(Sound::kDataFieldNumber, lastData); - } else qDebug() << "Failed to load gmx sound"; + } else + qDebug() << "Failed to load gmx sound"; } else { // TODO: Copy data into our egm SetModelData(Sound::kDataFieldNumber, fName); @@ -130,6 +138,4 @@ void SoundEditor::on_editButton_clicked() { // TODO: editor settings } -void SoundEditor::on_stopButton_clicked() { - mediaPlayer->stop(); -} +void SoundEditor::on_stopButton_clicked() { mediaPlayer->stop(); } diff --git a/Editors/SoundEditor.h b/Editors/SoundEditor.h index e077f3228..c8fc077f7 100644 --- a/Editors/SoundEditor.h +++ b/Editors/SoundEditor.h @@ -15,25 +15,29 @@ class SoundEditor : public BaseEditor { public: explicit SoundEditor(ProtoModelPtr model, QWidget* parent); - ~SoundEditor(); - -private slots: - void on_playButton_clicked(); - void on_loopButton_clicked(); - void on_playbackSlider_sliderPressed(); - void on_playbackSlider_sliderReleased(); - void on_volumeSlider_sliderMoved(int position); - void on_volumeSpinBox_valueChanged(double arg1); - void on_saveAsButton_clicked(); - void on_loadButton_clicked(); - void on_editButton_clicked(); - void on_stopButton_clicked(); - -private: + ~SoundEditor() override; + + public slots: + void RebindSubModels() override; + + private slots: + void on_playButton_clicked(); + void on_loopButton_clicked(); + void on_playbackSlider_sliderPressed(); + void on_playbackSlider_sliderReleased(); + void on_volumeSlider_sliderMoved(int position); + void on_volumeSpinBox_valueChanged(double arg1); + void on_saveAsButton_clicked(); + void on_loadButton_clicked(); + void on_editButton_clicked(); + void on_stopButton_clicked(); + + private: Ui::SoundEditor* ui; QMediaPlayer* mediaPlayer; - QMediaPlaylist *playlist; //it's only one song but Qt puts looping stuff here + QMediaPlaylist* playlist; //it's only one song but Qt puts looping stuff here bool userPaused; + ProtoModelPtr soundModel; }; #endif // SOUNDEDITOR_H diff --git a/Editors/SpriteEditor.cpp b/Editors/SpriteEditor.cpp index 946a98d4f..a9c7e563a 100644 --- a/Editors/SpriteEditor.cpp +++ b/Editors/SpriteEditor.cpp @@ -6,18 +6,24 @@ SpriteEditor::SpriteEditor(ProtoModelPtr model, QWidget* parent) : BaseEditor(model, parent), ui(new Ui::SpriteEditor) { ui->setupUi(this); - connect(ui->actionSave, &QAction::triggered, this, &BaseEditor::OnSave); + RebindSubModels(); +} + +SpriteEditor::~SpriteEditor() { delete ui; } - spriteModel = - new SpriteModel(static_cast(model->GetSubModel(TreeNode::kSpriteFieldNumber)->GetBuffer())->mutable_subimages(), this); +void SpriteEditor::RebindSubModels() { + spriteModel = new SpriteModel( + static_cast(_model->GetSubModel(TreeNode::kSpriteFieldNumber)->GetBuffer()) + ->mutable_subimages(), + this); connect(spriteModel, &SpriteModel::MismatchedImageSize, this, &SpriteEditor::LoadedMismatchedImage); ui->subImageList->setModel(spriteModel); ui->subImageList->setIconSize(spriteModel->GetIconSize()); -} -SpriteEditor::~SpriteEditor() { delete ui; } + BaseEditor::RebindSubModels(); +} void SpriteEditor::LoadedMismatchedImage(QSize /*expectedSize*/, QSize /*actualSize*/) { QMessageBox::critical(this, tr("Failed to load image"), tr("Error mismatched size"), QMessageBox::Ok); diff --git a/Editors/SpriteEditor.h b/Editors/SpriteEditor.h index ebf35450a..9ef92b8d2 100644 --- a/Editors/SpriteEditor.h +++ b/Editors/SpriteEditor.h @@ -14,9 +14,12 @@ class SpriteEditor : public BaseEditor { public: explicit SpriteEditor(ProtoModelPtr model, QWidget* parent); - ~SpriteEditor(); + ~SpriteEditor() override; void LoadedMismatchedImage(QSize expectedSize, QSize actualSize); + public slots: + void RebindSubModels() override; + private: Ui::SpriteEditor* ui; SpriteModel* spriteModel; diff --git a/Editors/TimelineEditor.cpp b/Editors/TimelineEditor.cpp index 9606867cc..967d02eb2 100644 --- a/Editors/TimelineEditor.cpp +++ b/Editors/TimelineEditor.cpp @@ -1,10 +1,175 @@ #include "TimelineEditor.h" +#include "ui_CodeEditor.h" #include "ui_TimelineEditor.h" +#include "CodeEditor.h" +#include "Dialogs/TimelineChangeMoment.h" + +#include +#include +#include +#include +#include + TimelineEditor::TimelineEditor(ProtoModelPtr model, QWidget* parent) - : BaseEditor(model, parent), ui(new Ui::TimelineEditor) { - ui->setupUi(this); + : BaseEditor(model, parent), codeEditor(new CodeEditor(this, true)), ui(new Ui::TimelineEditor) { + QLayout* layout = new QVBoxLayout(this); + QSplitter* splitter = new QSplitter(this); + + QWidget* momentWidget = new QWidget(this); + ui->setupUi(momentWidget); + + nodeMapper->addMapping(ui->nameEdit, TreeNode::kNameFieldNumber); + + connect(ui->saveButton, &QAbstractButton::pressed, this, &BaseEditor::OnSave); + + splitter->addWidget(momentWidget); + splitter->addWidget(codeEditor); + + layout->addWidget(splitter); + + layout->setMargin(0); + setLayout(layout); + + // Prefer resizing the code editor over the moments editor + splitter->setStretchFactor(0, 0); + splitter->setStretchFactor(1, 1); + + // Tell frankensteined widget to resize to proper size + resize(codeEditor->geometry().width() + momentWidget->geometry().width(), codeEditor->geometry().height()); + + connect(ui->momentsList, &QAbstractItemView::clicked, [=](const QModelIndex& index) { + SetCurrentEditor(index.row()); + ui->stepBox->setValue(momentsModel->data(index.row(), Timeline::Moment::kStepFieldNumber).toInt()); + }); + + connect(ui->addMomentButton, &QPushButton::pressed, [=]() { + AddMoment(ui->stepBox->value()); + codeEditor->setDisabled(false); + SetCurrentEditor(momentsModel->rowCount() - 1); + ui->stepBox->setValue(ui->stepBox->value() + 1); + }); + + connect(ui->stepBox, QOverload::of(&QSpinBox::valueChanged), [=](int value) { CheckDisableButtons(value); }); + + connect(ui->changeMomentButton, &QPushButton::pressed, [=]() { + TimelineChangeMoment dialog(this); + dialog.setWindowTitle((QString)("Change moment " + QString::number(ui->stepBox->value()) + " to ")); + dialog.SetSpinBoxValue(ui->stepBox->value()); + if (dialog.exec()) ChangeMoment(IndexOf(ui->stepBox->value()), dialog.GetSpinBoxValue()); + + ui->momentsList->reset(); + + ui->changeMomentButton->setDown(false); // Qt buggy af + }); + + connect(ui->deleteMomentButton, &QPushButton::pressed, [=]() { + RemoveMoment(IndexOf(ui->stepBox->value())); + + ui->momentsList->reset(); + + if (momentsModel->rowCount() == 0) { + CheckDisableButtons(-1); + codeEditor->setDisabled(true); + } else + SetCurrentEditor(momentsModel->rowCount() - 1); + + CheckDisableButtons(ui->stepBox->value()); + }); + + RebindSubModels(); } TimelineEditor::~TimelineEditor() { delete ui; } + +void TimelineEditor::RebindSubModels() { + codeEditor->ClearCodeWidgets(); + + ProtoModelPtr timelineModel = _model->GetSubModel(TreeNode::kTimelineFieldNumber); + momentsModel = timelineModel->GetRepeatedSubModel(Timeline::kMomentsFieldNumber); + + for (int moment = 0; moment < momentsModel->rowCount(); ++moment) { + BindMomentEditor(moment); + } + + ui->momentsList->setModel(momentsModel); + ui->momentsList->setModelColumn(Timeline::Moment::kStepFieldNumber); + + if (momentsModel->rowCount() == 0) codeEditor->setDisabled(true); + + CheckDisableButtons(ui->stepBox->value()); + + BaseEditor::RebindSubModels(); +} + +void TimelineEditor::AddMoment(int step) { + int insertIndex = FindInsertIndex(step); + momentsModel->insertRow(insertIndex); + momentsModel->setData(insertIndex, Timeline::Moment::kStepFieldNumber, step); + BindMomentEditor(insertIndex); +} + +void TimelineEditor::ChangeMoment(int oldIndex, int step) { + if (IndexOf(step) != -1) { + QMessageBox error; + error.critical(this, "Error Changing Moment", "Moment already exists"); + } else { + int newIndex = FindInsertIndex(step); + momentsModel->moveRows(oldIndex, 1, newIndex); + momentsModel->setData((newIndex < oldIndex) ? newIndex : newIndex - 1, Timeline::Moment::kStepFieldNumber, step); + } +} + +void TimelineEditor::RemoveMoment(int modelIndex) { + RepeatedProtoModel::RowRemovalOperation remover(momentsModel); + remover.RemoveRow(modelIndex); + codeEditor->RemoveCodeWidget(modelIndex); +} + +int TimelineEditor::FindInsertIndex(int step) { + int index = 0; + while (index < momentsModel->rowCount() && + step > momentsModel->data(index, Timeline::Moment::kStepFieldNumber).toInt()) { + ++index; + } + + return index; +} + +int TimelineEditor::IndexOf(int step) { + for (int r = 0; r < momentsModel->rowCount(); ++r) { + if (momentsModel->data(r, Timeline::Moment::kStepFieldNumber).toInt() == step) { + return r; + } + } + + return -1; +} + +void TimelineEditor::BindMomentEditor(int modelIndex) { + CodeWidget* codeWidget = codeEditor->AddCodeWidget(); + ModelMapper* mapper(new ModelMapper(momentsModel->GetSubModel(modelIndex), this)); + mapper->addMapping(codeWidget, Timeline::Moment::kCodeFieldNumber); + mapper->toFirst(); +} + +void TimelineEditor::SetCurrentEditor(int modelIndex) { + codeEditor->SetCurrentIndex(modelIndex); + ui->momentsList->selectionModel()->select(momentsModel->index(modelIndex, Timeline::Moment::kStepFieldNumber), + QItemSelectionModel::QItemSelectionModel::ClearAndSelect); +} + +void TimelineEditor::CheckDisableButtons(int value) { + for (int i = 0; i < momentsModel->rowCount(); ++i) { + if (momentsModel->data(i, Timeline::Moment::kStepFieldNumber).toInt() == value) { + ui->addMomentButton->setDisabled(true); + ui->changeMomentButton->setDisabled(false); + ui->deleteMomentButton->setDisabled(false); + return; + } + } + ui->addMomentButton->setDisabled(false); + ui->changeMomentButton->setDisabled(true); + ui->deleteMomentButton->setDisabled(true); +} diff --git a/Editors/TimelineEditor.h b/Editors/TimelineEditor.h index cff445079..966f6fcea 100644 --- a/Editors/TimelineEditor.h +++ b/Editors/TimelineEditor.h @@ -2,20 +2,37 @@ #define TIMELINEEDITOR_H #include "BaseEditor.h" +#include "CodeEditor.h" namespace Ui { class TimelineEditor; -} +class CodeEditor; +} // namespace Ui class TimelineEditor : public BaseEditor { Q_OBJECT public: explicit TimelineEditor(ProtoModelPtr model, QWidget* parent); - ~TimelineEditor(); + ~TimelineEditor() override; + + public slots: + void RebindSubModels() override; private: + void CheckDisableButtons(int value); + void AddMoment(int step); + void ChangeMoment(int oldIndex, int step); + void RemoveMoment(int modelIndex); + int FindInsertIndex(int step); + int IndexOf(int step); + void BindMomentEditor(int modelIndex); + void SetCurrentEditor(int modelIndex); + + CodeEditor* codeEditor; Ui::TimelineEditor* ui; + ProtoModelPtr timelineModel; + RepeatedProtoModelPtr momentsModel; }; #endif // TIMELINEEDITOR_H diff --git a/Editors/TimelineEditor.ui b/Editors/TimelineEditor.ui index 51e0795a7..382b495cc 100644 --- a/Editors/TimelineEditor.ui +++ b/Editors/TimelineEditor.ui @@ -1,211 +1,213 @@ - - - TimelineEditor - - - - 0 - 0 - 314 - 395 - - - - - 0 - 0 - - - - Timeline - - - - :/resources/timeline.png:/resources/timeline.png - - - - 4 - - - 6 - - - 6 - - - 6 - - - 6 - - - - - 4 - - - - - - - - - - - - Qt::LeftToRight - - - Name - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - 4 - - - - - Add Moment - - - Add Moment - - - - :/actions/add.png:/actions/add.png - - - - - - - Change Moment - - - Change Moment - - - - :/events/event.png:/events/event.png - - - - - - - Delete Moment - - - Delete Moment - - - - :/actions/delete.png:/actions/delete.png - - - false - - - Qt::ToolButtonIconOnly - - - Qt::NoArrow - - - - - - - Undo - - - Undo - - - - :/actions/undo.png:/actions/undo.png - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Edit Moment - - - Edit Moment - - - - :/actions/edit.png:/actions/edit.png - - - - - - - - - - 0 - 0 - - - - false - - - - Step 0 - - - - - Step 1 - - - - - - - - Save - - - - :/actions/accept.png:/actions/accept.png - - - true - - - - - - - - - - + + + TimelineEditor + + + + 0 + 0 + 256 + 400 + + + + + 0 + 0 + + + + Timeline + + + + :/resources/timeline.png:/resources/timeline.png + + + Qt::LeftToRight + + + + 2 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 4 + + + + + + + + + + + + Qt::LeftToRight + + + Name + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + 0 + 0 + + + + + 20 + 0 + + + + + 30 + 16777215 + + + + Step + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + + Add Moment + + + Add Moment + + + + :/actions/add.png:/actions/add.png + + + + + + + Change Moment + + + Change Moment + + + + :/events/event.png:/events/event.png + + + + + + + Delete Moment + + + Delete Moment + + + + :/actions/delete.png:/actions/delete.png + + + false + + + Qt::ToolButtonIconOnly + + + Qt::NoArrow + + + + + + + + + + 0 + 0 + + + + true + + + QAbstractScrollArea::AdjustIgnored + + + true + + + QAbstractItemView::InternalMove + + + Qt::MoveAction + + + + + + + Save + + + + :/actions/accept.png:/actions/accept.png + + + true + + + + + + + + + + diff --git a/MainWindow.cpp b/MainWindow.cpp index f9a1c1c7d..fa030e1a5 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -10,6 +10,7 @@ #include "Editors/PathEditor.h" #include "Editors/RoomEditor.h" #include "Editors/ScriptEditor.h" +#include "Editors/ShaderEditor.h" #include "Editors/SettingsEditor.h" #include "Editors/SoundEditor.h" #include "Editors/SpriteEditor.h" @@ -207,6 +208,7 @@ void MainWindow::openSubWindow(buffers::TreeNode *item) { {TypeCase::kPath, EditorFactory}, {TypeCase::kFont, EditorFactory}, {TypeCase::kScript, EditorFactory}, + {TypeCase::kShader, EditorFactory}, {TypeCase::kTimeline, EditorFactory}, {TypeCase::kObject, EditorFactory}, {TypeCase::kRoom, EditorFactory}, diff --git a/Models/ModelMapper.cpp b/Models/ModelMapper.cpp index 2b21699f3..f471b5945 100644 --- a/Models/ModelMapper.cpp +++ b/Models/ModelMapper.cpp @@ -2,8 +2,8 @@ #include "Editors/BaseEditor.h" -ModelMapper::ModelMapper(ProtoModelPtr model, BaseEditor *parent) : parentWidget(parent), model(model) { - mapper = new ImmediateDataWidgetMapper(parent); +ModelMapper::ModelMapper(ProtoModelPtr model, BaseEditor *parent) : QObject(parent), model(model) { + mapper = new ImmediateDataWidgetMapper(this); mapper->setOrientation(Qt::Vertical); mapper->setModel(model); parent->connect(model, &ProtoModel::dataChanged, parent, &BaseEditor::dataChanged); @@ -23,9 +23,7 @@ void ModelMapper::toFirst() { mapper->toFirst(); } void ModelMapper::ReplaceBuffer(google::protobuf::Message *buffer) { model->ReplaceBuffer(buffer); } -bool ModelMapper::RestoreBackup() { - return model->RestoreBackup(); -} +bool ModelMapper::RestoreBackup() { return model->RestoreBackup(); } void ModelMapper::SetDirty(bool dirty) { model->SetDirty(dirty); } diff --git a/Models/ModelMapper.h b/Models/ModelMapper.h index 09edb56a6..bd5efbf73 100644 --- a/Models/ModelMapper.h +++ b/Models/ModelMapper.h @@ -1,19 +1,19 @@ #ifndef MODELMAPPER_H #define MODELMAPPER_H -#include "Models/ProtoModel.h" #include "Models/ImmediateMapper.h" +#include "Models/ProtoModel.h" #include class BaseEditor; -class ModelMapper { -public: - ModelMapper(ProtoModelPtr model, BaseEditor* parent); +class ModelMapper : public QObject { + public: + ModelMapper(ProtoModelPtr model, BaseEditor *parent); // mapper - void addMapping(QWidget* widget, int section); + void addMapping(QWidget *widget, int section); void clearMapping(); void toFirst(); @@ -26,7 +26,7 @@ class ModelMapper { int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; bool setData(const QModelIndex &index, const QVariant &value, int role); - QVariant data(int row, int column=0) const; + QVariant data(int row, int column = 0) const; QVariant data(const QModelIndex &index, int role) const; RepeatedProtoModelPtr GetRepeatedSubModel(int fieldNum); ProtoModelPtr GetSubModel(int fieldNum); @@ -36,10 +36,9 @@ class ModelMapper { QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const; Qt::ItemFlags flags(const QModelIndex &index) const; -protected: - BaseEditor* parentWidget; + protected: ProtoModelPtr model; - ImmediateDataWidgetMapper* mapper; + ImmediateDataWidgetMapper *mapper; }; -#endif // MODELMAPPER_H +#endif // MODELMAPPER_H diff --git a/Models/ProtoModel.cpp b/Models/ProtoModel.cpp index 843b339f6..310d60fa3 100644 --- a/Models/ProtoModel.cpp +++ b/Models/ProtoModel.cpp @@ -6,7 +6,7 @@ using namespace google::protobuf; using CppType = FieldDescriptor::CppType; -ProtoModel::ProtoModel(google::protobuf::Message *protobuf, QObject* parent) : ProtoModel(protobuf, (ProtoModelPtr)nullptr) { +ProtoModel::ProtoModel(Message *protobuf, QObject *parent) : ProtoModel(protobuf, static_cast(nullptr)) { QAbstractItemModel::setParent(parent); } @@ -18,16 +18,22 @@ ProtoModel::ProtoModel(Message *protobuf, ProtoModelPtr parent) emit QAbstractItemModel::dataChanged(topLeft, bottomRight, roles); }); + RebuildSubModels(); +} + +void ProtoModel::RebuildSubModels() { + subModels.Clear(); + const Descriptor *desc = protobuf->GetDescriptor(); const Reflection *refl = protobuf->GetReflection(); for (int i = 0; i < desc->field_count(); i++) { - const google::protobuf::FieldDescriptor *field = desc->field(i); + const FieldDescriptor *field = desc->field(i); if (field->cpp_type() == CppType::CPPTYPE_MESSAGE) { if (field->is_repeated()) { - repeatedModels[field->number()] = new RepeatedProtoModel(protobuf, field, this); + subModels.repeatedModels[field->number()] = new RepeatedProtoModel(protobuf, field, this); } else { - const google::protobuf::OneofDescriptor *oneof = field->containing_oneof(); + const OneofDescriptor *oneof = field->containing_oneof(); if (oneof) { if (refl->HasOneof(*protobuf, oneof)) { field = refl->GetOneofFieldDescriptor(*protobuf, oneof); @@ -36,31 +42,35 @@ ProtoModel::ProtoModel(Message *protobuf, ProtoModelPtr parent) continue; // don't allocate if not set } } - subModels[field->number()] = new ProtoModel(refl->MutableMessage(protobuf, field), this); + subModels.protoModels[field->number()] = new ProtoModel(refl->MutableMessage(protobuf, field), this); } } else if (field->cpp_type() == CppType::CPPTYPE_STRING && field->is_repeated()) { for (int j = 0; j < refl->FieldSize(*protobuf, field); j++) { - repeatedStrings[field->number()].append(QString::fromStdString(refl->GetRepeatedString(*protobuf, field, j))); + subModels.strings[field->number()].append(QString::fromStdString(refl->GetRepeatedString(*protobuf, field, j))); } } } } -ProtoModel::~ProtoModel() {} +ProtoModelPtr ProtoModel::GetParentModel() const { return parentModel; } -ProtoModelPtr ProtoModel::GetParentModel() const { - return parentModel; -} - -ProtoModelPtr ProtoModel::BackupModel(QObject* parent) { +ProtoModelPtr ProtoModel::BackupModel(QObject *parent) { backupProtobuf.reset(protobuf->New()); backupProtobuf->CopyFrom(*protobuf); modelBackup = new ProtoModel(backupProtobuf.get(), parent); return modelBackup; } -ProtoModelPtr ProtoModel::GetBackupModel() { - return modelBackup; +ProtoModelPtr ProtoModel::GetBackupModel() { return modelBackup; } + +ProtoModel::SubModels &ProtoModel::GetSubModels() { return subModels; } + +void ProtoModel::ReplaceBuffer(Message *buffer) { + beginResetModel(); + SetDirty(true); + protobuf->CopyFrom(*buffer); + RebuildSubModels(); + endResetModel(); } bool ProtoModel::RestoreBackup() { @@ -69,14 +79,10 @@ bool ProtoModel::RestoreBackup() { return true; } -void ProtoModel::ReplaceBuffer(Message *buffer) { - SetDirty(true); - protobuf->CopyFrom(*buffer); -} - -google::protobuf::Message *ProtoModel::GetBuffer() { return protobuf; } +Message *ProtoModel::GetBuffer() { return protobuf; } -int ProtoModel::rowCount(const QModelIndex & /*parent*/) const { +int ProtoModel::rowCount(const QModelIndex &parent) const { + if (parent.isValid()) return 0; const Descriptor *desc = protobuf->GetDescriptor(); return desc->field_count(); } @@ -101,37 +107,22 @@ bool ProtoModel::setData(const QModelIndex &index, const QVariant &value, int ro case CppType::CPPTYPE_MESSAGE: { break; } - case CppType::CPPTYPE_INT32: - refl->SetInt32(protobuf, field, value.toInt()); - break; - case CppType::CPPTYPE_INT64: - refl->SetInt64(protobuf, field, value.toLongLong()); - break; - case CppType::CPPTYPE_UINT32: - refl->SetUInt32(protobuf, field, value.toUInt()); - break; - case CppType::CPPTYPE_UINT64: - refl->SetUInt64(protobuf, field, value.toULongLong()); - break; - case CppType::CPPTYPE_DOUBLE: - refl->SetDouble(protobuf, field, value.toDouble()); - break; - case CppType::CPPTYPE_FLOAT: - refl->SetFloat(protobuf, field, value.toFloat()); - break; - case CppType::CPPTYPE_BOOL: - refl->SetBool(protobuf, field, value.toBool()); - break; + case CppType::CPPTYPE_INT32: refl->SetInt32(protobuf, field, value.toInt()); break; + case CppType::CPPTYPE_INT64: refl->SetInt64(protobuf, field, value.toLongLong()); break; + case CppType::CPPTYPE_UINT32: refl->SetUInt32(protobuf, field, value.toUInt()); break; + case CppType::CPPTYPE_UINT64: refl->SetUInt64(protobuf, field, value.toULongLong()); break; + case CppType::CPPTYPE_DOUBLE: refl->SetDouble(protobuf, field, value.toDouble()); break; + case CppType::CPPTYPE_FLOAT: refl->SetFloat(protobuf, field, value.toFloat()); break; + case CppType::CPPTYPE_BOOL: refl->SetBool(protobuf, field, value.toBool()); break; case CppType::CPPTYPE_ENUM: refl->SetEnum(protobuf, field, field->enum_type()->FindValueByNumber(value.toInt())); break; - case CppType::CPPTYPE_STRING: - refl->SetString(protobuf, field, value.toString().toStdString()); - break; + case CppType::CPPTYPE_STRING: refl->SetString(protobuf, field, value.toString().toStdString()); break; } SetDirty(true); emit dataChanged(index, index, oldValue); + return true; } @@ -154,50 +145,40 @@ QVariant ProtoModel::data(const QModelIndex &index, int role) const { } switch (field->cpp_type()) { - case CppType::CPPTYPE_MESSAGE: - R_EXPECT(false, QVariant()) << "The requested field " << index << " is a message"; - case CppType::CPPTYPE_INT32: - return refl->GetInt32(*protobuf, field); - case CppType::CPPTYPE_INT64: - return static_cast(refl->GetInt64(*protobuf, field)); - case CppType::CPPTYPE_UINT32: - return refl->GetUInt32(*protobuf, field); - case CppType::CPPTYPE_UINT64: - return static_cast(refl->GetUInt64(*protobuf, field)); - case CppType::CPPTYPE_DOUBLE: - return refl->GetDouble(*protobuf, field); - case CppType::CPPTYPE_FLOAT: - return refl->GetFloat(*protobuf, field); - case CppType::CPPTYPE_BOOL: - return refl->GetBool(*protobuf, field); - case CppType::CPPTYPE_ENUM: - return refl->GetInt32(*protobuf, field); - case CppType::CPPTYPE_STRING: - return QString::fromStdString(refl->GetString(*protobuf, field)); + case CppType::CPPTYPE_MESSAGE: R_EXPECT(false, QVariant()) << "The requested field " << index << " is a message"; + case CppType::CPPTYPE_INT32: return refl->GetInt32(*protobuf, field); + case CppType::CPPTYPE_INT64: return static_cast(refl->GetInt64(*protobuf, field)); + case CppType::CPPTYPE_UINT32: return refl->GetUInt32(*protobuf, field); + case CppType::CPPTYPE_UINT64: return static_cast(refl->GetUInt64(*protobuf, field)); + case CppType::CPPTYPE_DOUBLE: return refl->GetDouble(*protobuf, field); + case CppType::CPPTYPE_FLOAT: return refl->GetFloat(*protobuf, field); + case CppType::CPPTYPE_BOOL: return refl->GetBool(*protobuf, field); + case CppType::CPPTYPE_ENUM: return refl->GetInt32(*protobuf, field); + case CppType::CPPTYPE_STRING: return QString::fromStdString(refl->GetString(*protobuf, field)); } return QVariant(); } -RepeatedProtoModelPtr ProtoModel::GetRepeatedSubModel(int fieldNum) { return repeatedModels[fieldNum]; } +RepeatedProtoModelPtr ProtoModel::GetRepeatedSubModel(int fieldNum) { return subModels.repeatedModels[fieldNum]; } ProtoModelPtr ProtoModel::GetSubModel(int fieldNum) { - if (subModels.contains(fieldNum)) - return subModels[fieldNum]; + if (subModels.protoModels.contains(fieldNum)) + return subModels.protoModels[fieldNum]; else return nullptr; } ProtoModelPtr ProtoModel::GetSubModel(int fieldNum, int index) { - if (repeatedModels.contains(fieldNum) && repeatedModels[fieldNum]->rowCount() > index) - return repeatedModels[fieldNum]->GetSubModel(index); + if (subModels.repeatedModels.contains(fieldNum) && subModels.repeatedModels[fieldNum]->rowCount() > index) + return subModels.repeatedModels[fieldNum]->GetSubModel(index); else return nullptr; } QString ProtoModel::GetString(int fieldNum, int index) const { - if (repeatedStrings.contains(fieldNum) && repeatedStrings[fieldNum].count() > index) - return repeatedStrings[fieldNum][index]; + if (subModels.strings.contains(fieldNum) && subModels.strings[fieldNum].count() > index) + return subModels.strings[fieldNum][index]; else return ""; } @@ -227,17 +208,20 @@ Qt::ItemFlags ProtoModel::flags(const QModelIndex &index) const { return flags; } -void UpdateReferences(ProtoModelPtr model, const QString& type, const QString& oldName, const QString& newName) { +void ProtoModel::SubModels::Clear() { + this->protoModels.clear(); + this->repeatedModels.clear(); + this->strings.clear(); +} +void UpdateReferences(ProtoModelPtr model, const QString &type, const QString &oldName, const QString &newName) { if (model == nullptr) return; int rows = model->rowCount(); for (int row = 0; row < rows; row++) { - - google::protobuf::Message* protobuf = model->GetBuffer(); + Message *protobuf = model->GetBuffer(); const Descriptor *desc = protobuf->GetDescriptor(); - const Reflection *refl = protobuf->GetReflection(); const FieldDescriptor *field = desc->FindFieldByNumber(row); if (field != nullptr) { if (field->cpp_type() == CppType::CPPTYPE_MESSAGE) { @@ -246,7 +230,8 @@ void UpdateReferences(ProtoModelPtr model, const QString& type, const QString& o for (int col = 0; col < cols; col++) { UpdateReferences(model->GetSubModel(row, col), type, oldName, newName); } - } else UpdateReferences(model->GetSubModel(row), type, oldName, newName); + } else + UpdateReferences(model->GetSubModel(row), type, oldName, newName); } else if (field->cpp_type() == CppType::CPPTYPE_STRING && !field->is_repeated()) { const QString refType = QString::fromStdString(field->options().GetExtension(buffers::resource_ref)); if (refType == type && model->data(row, 0).toString() == oldName) { @@ -259,15 +244,20 @@ void UpdateReferences(ProtoModelPtr model, const QString& type, const QString& o QString ResTypeAsString(TypeCase type) { switch (type) { - case TypeCase::kFolder: return "treenode"; - case TypeCase::kBackground: return "background"; - case TypeCase::kFont: return "font"; - case TypeCase::kObject: return "object"; - case TypeCase::kPath: return "path"; - case TypeCase::kRoom: return "room"; - case TypeCase::kSound: return "sound"; - case TypeCase::kSprite: return "sprite"; - case TypeCase::kTimeline: return "timeline"; + case TypeCase::kFolder: return "treenode"; + case TypeCase::kBackground: return "background"; + case TypeCase::kFont: return "font"; + case TypeCase::kObject: return "object"; + case TypeCase::kPath: return "path"; + case TypeCase::kRoom: return "room"; + case TypeCase::kSound: return "sound"; + case TypeCase::kSprite: return "sprite"; + case TypeCase::kShader: return "shader"; + case TypeCase::kScript: return "script"; + case TypeCase::kSettings: return "settings"; + case TypeCase::kInclude: return "include"; + case TypeCase::kTimeline: return "timeline"; + case TypeCase::TYPE_NOT_SET: return "unknown"; } return "unknown"; } diff --git a/Models/ProtoModel.h b/Models/ProtoModel.h index a5b57bf68..14dd1ca1a 100644 --- a/Models/ProtoModel.h +++ b/Models/ProtoModel.h @@ -5,6 +5,7 @@ #include "RepeatedProtoModel.h" +#include #include #include @@ -22,22 +23,38 @@ using Timeline = buffers::resources::Timeline; class ProtoModel : public QAbstractItemModel { Q_OBJECT + private: + struct SubModels { + void Clear(); + QHash protoModels; + QHash> strings; + QHash repeatedModels; + }; + + bool dirty; + Message *protobuf; + SubModels subModels; + ProtoModelPtr parentModel; + ProtoModelPtr modelBackup; + QScopedPointer backupProtobuf; + public: - explicit ProtoModel(google::protobuf::Message *protobuf, QObject* parent); - explicit ProtoModel(google::protobuf::Message *protobuf, ProtoModelPtr parent); - ~ProtoModel(); + explicit ProtoModel(Message *protobuf, QObject *parent); + explicit ProtoModel(Message *protobuf, ProtoModelPtr parent); + void RebuildSubModels(); ProtoModelPtr GetParentModel() const; - ProtoModelPtr BackupModel(QObject* parent); + ProtoModelPtr BackupModel(QObject *parent); ProtoModelPtr GetBackupModel(); + SubModels &GetSubModels(); bool RestoreBackup(); - void ReplaceBuffer(google::protobuf::Message *buffer); - google::protobuf::Message *GetBuffer(); + void ReplaceBuffer(Message *buffer); + Message *GetBuffer(); void SetDirty(bool dirty); bool IsDirty(); int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; - QVariant data(int row, int column=0) const; + QVariant data(int row, int column = 0) const; QVariant data(const QModelIndex &index, int role) const override; RepeatedProtoModelPtr GetRepeatedSubModel(int fieldNum); ProtoModelPtr GetSubModel(int fieldNum); @@ -49,21 +66,11 @@ class ProtoModel : public QAbstractItemModel { Qt::ItemFlags flags(const QModelIndex &index) const override; signals: - void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, - const QVariant &oldValue = QVariant(0), const QVector &roles = QVector()); - - private: - ProtoModelPtr parentModel; - ProtoModelPtr modelBackup; - QHash subModels; - QHash> repeatedStrings; - QHash repeatedModels; - bool dirty; - google::protobuf::Message *protobuf; - QScopedPointer backupProtobuf; + void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVariant &oldValue = QVariant(0), + const QVector &roles = QVector()); }; -void UpdateReferences(ProtoModelPtr model, const QString& type, const QString& oldName, const QString& newName); +void UpdateReferences(ProtoModelPtr model, const QString &type, const QString &oldName, const QString &newName); QString ResTypeAsString(TypeCase type); #endif // RESOURCEMODEL_H diff --git a/Models/RepeatedProtoModel.cpp b/Models/RepeatedProtoModel.cpp index 9c420bb39..0ab04f4ce 100644 --- a/Models/RepeatedProtoModel.cpp +++ b/Models/RepeatedProtoModel.cpp @@ -13,18 +13,15 @@ RepeatedProtoModel::RepeatedProtoModel(Message *protobuf, const FieldDescriptor } } -ProtoModelPtr RepeatedProtoModel::GetParentModel() const { - return parentModel; -} +ProtoModelPtr RepeatedProtoModel::GetParentModel() const { return parentModel; } -int RepeatedProtoModel::rowCount(const QModelIndex & /*parent*/) const { +int RepeatedProtoModel::rowCount(const QModelIndex &parent) const { + if (parent.isValid()) return 0; const Reflection *refl = protobuf->GetReflection(); return refl->FieldSize(*protobuf, field); } -void RepeatedProtoModel::AddModel(ProtoModelPtr model) { - models.append(model); -} +void RepeatedProtoModel::AddModel(ProtoModelPtr model) { models.append(model); } ProtoModelPtr RepeatedProtoModel::GetSubModel(int index) { if (index < models.count()) @@ -33,19 +30,16 @@ ProtoModelPtr RepeatedProtoModel::GetSubModel(int index) { return nullptr; } -QVector& RepeatedProtoModel::GetMutableModelList() { - return models; -} +QVector &RepeatedProtoModel::GetMutableModelList() { return models; } bool RepeatedProtoModel::empty() const { return this->rowCount() <= 0; } -int RepeatedProtoModel::columnCount(const QModelIndex & /*parent*/) const { +int RepeatedProtoModel::columnCount(const QModelIndex &parent) const { + if (parent.isValid()) return 0; const Descriptor *desc = protobuf->GetDescriptor(); return desc->field_count(); } -//bool RepeatedProtoModel::setData(const QModelIndex &index, const QVariant &value, int role) {} - QVariant RepeatedProtoModel::data(int row, int column) const { return data(this->index(row, column, QModelIndex()), Qt::DisplayRole); } @@ -72,7 +66,7 @@ QVariant RepeatedProtoModel::data(const QModelIndex &index, int role) const { } } } else if (role == Qt::DecorationRole && field->name() == "tiles" && - index.column() == Room::Tile::kBackgroundNameFieldNumber) { + index.column() == Room::Tile::kBackgroundNameFieldNumber) { auto bkg = MainWindow::resourceMap->GetResourceByName(TreeNode::kBackground, data.toString()); if (bkg != nullptr) { bkg = bkg->GetSubModel(TreeNode::kBackgroundFieldNumber); @@ -87,6 +81,16 @@ QVariant RepeatedProtoModel::data(const QModelIndex &index, int role) const { return QVariant(); } +bool RepeatedProtoModel::setData(int subModelIndex, int dataField, const QVariant &value) { + return setData(index(subModelIndex, dataField), value); +} + +bool RepeatedProtoModel::setData(const QModelIndex &index, const QVariant &value, int role) { + R_EXPECT(index.isValid(), false) << "Supplied index was invalid:" << index; + GetParentModel()->SetDirty(true); + return models[index.row()]->setData(models[index.row()]->index(index.column(), 0), value, role); +} + QModelIndex RepeatedProtoModel::parent(const QModelIndex & /*index*/) const { return QModelIndex(); } QVariant RepeatedProtoModel::headerData(int section, Qt::Orientation orientation, int role) const { @@ -104,9 +108,65 @@ Qt::ItemFlags RepeatedProtoModel::flags(const QModelIndex &index) const { return QAbstractItemModel::flags(index); } -void RepeatedProtoModel::RowRemovalOperation::RemoveRow(int row) { - rows.insert(row); +bool RepeatedProtoModel::moveRows(int source, int count, int destination) { + int left = source; + int right = source + count; + if (left < 0 || destination < 0) return false; + if (right > rowCount() || destination > rowCount()) return false; + if (destination >= left && destination < right) return false; + + beginMoveRows(QModelIndex(), source, source + count - 1, QModelIndex(), destination); + + if (destination < left) { + SwapBack(destination, left, right); + } else { // Verified above that we're dest < left or dest >= right + SwapBack(left, right, destination); + } + + endMoveRows(); + GetParentModel()->SetDirty(true); + return true; } + +bool RepeatedProtoModel::moveRows(const QModelIndex & /*sourceParent*/, int sourceRow, int count, + const QModelIndex & /*destinationParent*/, int destinationChild) { + return moveRows(sourceRow, count, destinationChild); +} + +bool RepeatedProtoModel::insertRows(int row, int count, const QModelIndex &parent) { + if (row > rowCount()) return false; + + beginInsertRows(parent, row, row + count); + + int p = rowCount(); + + Message *parentBuffer = parentModel->GetBuffer(); + for (int r = 0; r < count; ++r) { + Message *m = parentBuffer->GetReflection()->AddMessage(parentBuffer, field); + models.append(new ProtoModel(m, parentModel)); + } + + SwapBack(row, p, rowCount()); + + endInsertRows(); + + GetParentModel()->SetDirty(true); + + return true; +} + +void RepeatedProtoModel::SwapBack(int left, int part, int right) { + MutableRepeatedFieldRef f = protobuf->GetReflection()->GetMutableRepeatedFieldRef(protobuf, field); + if (left >= part || part >= right) return; + int npart = (part - left) % (right - part); + while (part > left) { + std::swap(models[--part], models[--right]); + f.SwapElements(part, right); + } + SwapBack(left, left + npart, right); +} + +void RepeatedProtoModel::RowRemovalOperation::RemoveRow(int row) { rows.insert(row); } void RepeatedProtoModel::RowRemovalOperation::RemoveRows(int row, int count) { for (int i = row; i < row + count; ++i) rows.insert(i); } @@ -118,8 +178,8 @@ RepeatedProtoModel::RowRemovalOperation::~RowRemovalOperation() { // Compute ranges for our deleted rows. struct Range { int first, last; - Range(): first(), last() {} - Range(int f, int l): first(f), last(l) {} + Range() : first(), last() {} + Range(int f, int l) : first(f), last(l) {} int size() { return last - first + 1; } }; std::vector ranges; @@ -143,14 +203,16 @@ RepeatedProtoModel::RowRemovalOperation::~RowRemovalOperation() { while (right < range.first) { field.SwapElements(left, right); std::swap(list[left], list[right]); - left++; right++; + left++; + right++; } right = range.last + 1; } while (right < field.size()) { field.SwapElements(left, right); std::swap(list[left], list[right]); - left++; right++; + left++; + right++; } // Send the endRemoveRows operations in the reverse order, removing the @@ -161,4 +223,6 @@ RepeatedProtoModel::RowRemovalOperation::~RowRemovalOperation() { for (int j = range.first; j <= range.last; ++j) field.RemoveLast(); model->endRemoveRows(); } + + model->GetParentModel()->SetDirty(true); } diff --git a/Models/RepeatedProtoModel.h b/Models/RepeatedProtoModel.h index d537301ea..0e44ef905 100644 --- a/Models/RepeatedProtoModel.h +++ b/Models/RepeatedProtoModel.h @@ -24,19 +24,31 @@ class RepeatedProtoModel : public QAbstractItemModel { public: RepeatedProtoModel(Message *protobuf, const FieldDescriptor *field, ProtoModelPtr parent); ProtoModelPtr GetParentModel() const; + void SetParentModel(ProtoModelPtr parent); + void SetBuffer(Message *buffer, const FieldDescriptor *field); void AddModel(ProtoModelPtr model); ProtoModelPtr GetSubModel(int index); QVector& GetMutableModelList(); int rowCount(const QModelIndex &parent = QModelIndex()) const override; bool empty() const; int columnCount(const QModelIndex &parent = QModelIndex()) const override; - //bool setData(const QModelIndex &index, const QVariant &value, int role) override; + bool setData(int subModelIndex, int dataField, const QVariant& value); + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::DisplayRole) override; QVariant data(int row, int column = 0) const; QVariant data(const QModelIndex &index, int role) const override; QModelIndex parent(const QModelIndex &index) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; + bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; + bool moveRows(int source, int count, int destination); + bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, + int destinationChild) override; + + // Takes the elements in range [part, right) and move them to `left` by swapping. + // Rearranges elements so that all those at or after the partition point are + // moved to the beginning of the range (`left`). + void SwapBack(int left, int part, int right); class RowRemovalOperation { RepeatedProtoModelPtr model; diff --git a/RadialGM.pro b/RadialGM.pro index a4a8a0780..6285c04d2 100644 --- a/RadialGM.pro +++ b/RadialGM.pro @@ -57,6 +57,10 @@ LIBS += -L$$PWD/Submodules/enigma-dev/CommandLine/libEGM/ \ -lgrpc++ SOURCES += \ + Dialogs/TimelineChangeMoment.cpp \ + Editors/ShaderEditor.cpp \ + Widgets/StackedCodeWidget.cpp \ + Widgets/StackedCodewidget.cpp \ main.cpp \ MainWindow.cpp \ Dialogs/PreferencesDialog.cpp \ @@ -93,6 +97,8 @@ SOURCES += \ Models/TreeSortFilterProxyModel.cpp HEADERS += \ + Dialogs/TimelineChangeMoment.h \ + Editors/ShaderEditor.h \ MainWindow.h \ Dialogs/PreferencesDialog.h \ Editors/BaseEditor.h \ @@ -120,6 +126,7 @@ HEADERS += \ Plugins/RGMPlugin.h \ Plugins/ServerPlugin.h \ Components/RecentFiles.h \ + Widgets/StackedCodeWidget.h \ main.h \ Dialogs/PreferencesKeys.h \ Editors/CodeEditor.h \ @@ -132,6 +139,8 @@ HEADERS += \ Models/TreeSortFilterProxyModel.h FORMS += \ + Dialogs/TimelineChangeMoment.ui \ + Editors/TimelineEditor.ui \ MainWindow.ui \ Dialogs/PreferencesDialog.ui \ Dialogs/AddImageDialog.ui \ @@ -139,7 +148,6 @@ FORMS += \ Editors/ObjectEditor.ui \ Editors/FontEditor.ui \ Editors/PathEditor.ui \ - Editors/TimelineEditor.ui \ Editors/RoomEditor.ui \ Editors/SpriteEditor.ui \ Editors/SoundEditor.ui \ diff --git a/Widgets/CodeWidget.h b/Widgets/CodeWidget.h index 0af695493..eca56c11a 100644 --- a/Widgets/CodeWidget.h +++ b/Widgets/CodeWidget.h @@ -14,7 +14,7 @@ class CodeWidget : public QWidget { Q_PROPERTY(QString code READ code WRITE setCode NOTIFY codeChanged USER true) public: - explicit CodeWidget(QWidget* parent); + explicit CodeWidget(QWidget* parent = nullptr); ~CodeWidget(); QString code() const; diff --git a/Widgets/StackedCodeWidget.cpp b/Widgets/StackedCodeWidget.cpp new file mode 100644 index 000000000..58a00fe6f --- /dev/null +++ b/Widgets/StackedCodeWidget.cpp @@ -0,0 +1,56 @@ +#include "StackedCodeWidget.h" +#include "CodeWidget.h" + +#include +#include +#include +#include + +StackedCodeWidget::StackedCodeWidget(QWidget* parent) : QStackedWidget(parent) {} + +void StackedCodeWidget::newSource() { + static_cast(currentWidget())->newSource(); +} + +void StackedCodeWidget::loadSource() { + static_cast(currentWidget())->loadSource(); +} + +void StackedCodeWidget::saveSource() { + static_cast(currentWidget())->saveSource(); +} + +void StackedCodeWidget::printSource() { + static_cast(currentWidget())->printSource(); +} + +void StackedCodeWidget::gotoLineDialog() { + static_cast(currentWidget())->gotoLineDialog(); +} + +void StackedCodeWidget::print(QPrinter* printer) { + static_cast(currentWidget())->print(printer); +} + +void StackedCodeWidget::undo() { + static_cast(currentWidget())->undo(); +} + +void StackedCodeWidget::redo() { + static_cast(currentWidget())->redo(); +} + +void StackedCodeWidget::cut() { + static_cast(currentWidget())->cut(); +} +void StackedCodeWidget::copy() { + static_cast(currentWidget())->copy(); +} + +void StackedCodeWidget::paste() { + static_cast(currentWidget())->paste(); +} + +void StackedCodeWidget::gotoLine(int line) { + static_cast(currentWidget())->gotoLine(line); +} diff --git a/Widgets/StackedCodeWidget.h b/Widgets/StackedCodeWidget.h new file mode 100644 index 000000000..e7605b835 --- /dev/null +++ b/Widgets/StackedCodeWidget.h @@ -0,0 +1,27 @@ +#ifndef STACKEDCODEWIDGET_H +#define STACKEDCODEWIDGET_H + +#include +#include + +class StackedCodeWidget : public QStackedWidget { + Q_OBJECT +public: + StackedCodeWidget(QWidget* parent); + +public slots: + void newSource(); + void loadSource(); + void saveSource(); + void printSource(); + void gotoLineDialog(); + void print(QPrinter* printer); + void undo(); + void redo(); + void cut(); + void copy(); + void paste(); + void gotoLine(int line); +}; + +#endif // STACKEDCODEWIDGET_H