-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Adds a dialog for setting event arguments when adding or changing an object event. * Adds a display proxy model for the events to have icons, tooltips, and translations. * Adds a model representing types of events that can be selected and added. * Adds a sort proxy model for alphanumerically stable sorting the event types by group and name. * Check if RepeatedModel::headerData section is also less than 0 like official Qt models do, including QStandardItemModel. * Explicitly return invalid QModelIndex in RepeatedModel::headerData when invalid section requested. * Fix a comment typo in RepeatedModel header. * Add a TypeCase user role to the tree model for filtering asset selection. * Put events and properties of the object editor into tabs with events selected by default. * Inline the code editor to the object editor as a master detail view like the timeline editor. * Separate the properties tabs and the code editor using a horizontal splitter so the properties can be collapsed. * Rename some event icons to match event name scheme. * Put the main window event data into a static unique pointer so it doesn't leak. * Add CMakeLists.txt to DISTFILES in the pro so you can select it from "Other FIles" in Qt Creator. While this is poorly documented because it says DISTFILES is Unix only, this is what Qt Creator does when adding the external file through Qt Creator, it does not add the file to the build, only makes it easier to edit. * Updated the ENIGMA submodule so the new events can be used, including fixes to the API.
- Loading branch information
Showing
24 changed files
with
1,004 additions
and
504 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
#include "EventArgumentsDialog.h" | ||
#include "Components/QMenuView.h" | ||
#include "MainWindow.h" | ||
#include "Models/TreeSortFilterProxyModel.h" | ||
|
||
#include <QComboBox> | ||
#include <QDialogButtonBox> | ||
#include <QGridLayout> | ||
#include <QLabel> | ||
#include <QLineEdit> | ||
#include <QMetaProperty> | ||
#include <QPushButton> | ||
#include <QSpinBox> | ||
#include <QToolButton> | ||
|
||
EventArgumentsDialog::EventArgumentsDialog(QWidget *parent, const QStringList &arguments) : QDialog(parent) { | ||
QGridLayout *layout = new QGridLayout(this); | ||
|
||
int row = 0; | ||
for (const auto &arg : arguments) { | ||
QLabel *name = new QLabel(this); | ||
name->setText(arg); | ||
|
||
QWidget *value; | ||
|
||
if (arg == "integer") { | ||
QSpinBox *integer = new QSpinBox(this); | ||
integer->setMinimum(0); | ||
value = integer; | ||
layout->addWidget(value, row, 1); | ||
} else if (arg == "string") { | ||
QLineEdit *lineEdit = new QLineEdit(this); | ||
lineEdit->setText(tr("MyCustomEvent")); | ||
value = lineEdit; | ||
layout->addWidget(value, row, 1); | ||
} else if (arg == "object") { | ||
QHBoxLayout *objLayout = new QHBoxLayout(); | ||
QToolButton *objButton = new QToolButton(this); | ||
|
||
QMenuView *objMenu = new QMenuView(this); | ||
TreeSortFilterProxyModel *treeProxy = new TreeSortFilterProxyModel(this); | ||
treeProxy->SetFilterType(TreeNode::TypeCase::kObject); | ||
treeProxy->setSourceModel(MainWindow::treeModel.get()); | ||
objMenu->setModel(treeProxy); | ||
objButton->setMenu(objMenu); | ||
objButton->setPopupMode(QToolButton::MenuButtonPopup); | ||
|
||
QLineEdit *lineEdit = new QLineEdit(this); | ||
lineEdit->setReadOnly(true); | ||
QModelIndex firstObjIdx = treeProxy | ||
->match(treeProxy->index(0, 0), TreeModel::UserRoles::TypeCaseRole, | ||
TypeCase::kObject, 1, Qt::MatchRecursive) | ||
.first(); | ||
QString firstObj = firstObjIdx.data(Qt::DisplayRole).toString(); | ||
objButton->setIcon(firstObjIdx.data(Qt::DecorationRole).value<QIcon>()); | ||
lineEdit->setText(firstObj); | ||
|
||
connect(objMenu, &QMenuView::triggered, [=](const QModelIndex &index) { | ||
lineEdit->setText(treeProxy->data(index, Qt::DisplayRole).toString()); | ||
objButton->setIcon(treeProxy->data(index, Qt::DecorationRole).value<QIcon>()); | ||
}); | ||
|
||
objLayout->addWidget(lineEdit); | ||
objLayout->addWidget(objButton); | ||
|
||
layout->addItem(objLayout, row, 1); | ||
|
||
value = lineEdit; | ||
} else { | ||
QComboBox *combo = new QComboBox(this); | ||
auto argList = MainWindow::GetEventData()->value_names_for_type(name->text().toStdString()); | ||
for (auto a : argList) { | ||
combo->addItem(QString::fromStdString(a.first)); | ||
} | ||
value = combo; | ||
layout->addWidget(value, row, 1); | ||
} | ||
|
||
widgets_.append(value); | ||
|
||
layout->addWidget(name, row, 0); | ||
row++; | ||
} | ||
|
||
QDialogButtonBox *btn = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); | ||
layout->addWidget(btn, row, 1); | ||
connect(btn, SIGNAL(accepted()), this, SLOT(accept())); | ||
connect(btn, SIGNAL(rejected()), this, SLOT(reject())); | ||
|
||
setWindowTitle(tr("Event arguments")); | ||
} | ||
|
||
const QStringList &EventArgumentsDialog::GetArguments() const { return arguments_; } | ||
|
||
void EventArgumentsDialog::done(int r) { | ||
for (const QWidget *w : widgets_) { | ||
QVariant argument = w->metaObject()->userProperty().read(w); | ||
QString argstr = ""; | ||
if (argument.isValid()) | ||
argstr = argument.toString(); | ||
arguments_.append(argstr); | ||
} | ||
|
||
QDialog::done(r); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
#ifndef EVENTARGUMENTSDIALOG_H | ||
#define EVENTARGUMENTSDIALOG_H | ||
|
||
#include "event_reader/event_parser.h" | ||
|
||
#include <QDialog> | ||
|
||
class EventArgumentsDialog : public QDialog | ||
{ | ||
Q_OBJECT | ||
public: | ||
EventArgumentsDialog(QWidget* parent, const QStringList& arguments); | ||
const QStringList& GetArguments() const; | ||
void done(int r) override; | ||
|
||
private: | ||
QStringList arguments_; | ||
QVector<QWidget*> widgets_; | ||
}; | ||
|
||
#endif // EVENTARGUMENTSDIALOG_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,194 @@ | ||
#include "ObjectEditor.h" | ||
#include "Components/QMenuView.h" | ||
#include "Dialogs/EventArgumentsDialog.h" | ||
#include "MainWindow.h" | ||
#include "Models/EventTypesListModel.h" | ||
#include "Models/RepeatedMessageModel.h" | ||
#include "Models/RepeatedStringModel.h" | ||
|
||
#include "ui_ObjectEditor.h" | ||
|
||
ObjectEditor::ObjectEditor(MessageModel* model, QWidget* parent) : BaseEditor(model, parent), _ui(new Ui::ObjectEditor) { | ||
#include <QSplitter> | ||
|
||
void ObjectEditor::BindEventMenu(QToolButton *btn, bool add) { | ||
QMenuView *eventsMenu = new QMenuView(this); | ||
eventsMenu->setModel(_eventsTypesModel); | ||
eventsMenu->setToolTipsVisible(true); | ||
btn->setMenu(eventsMenu); | ||
connect(eventsMenu, &QMenuView::triggered, [=](const QModelIndex &index) { AddChangeFromMenuEvent(index, add); }); | ||
} | ||
|
||
ObjectEditor::ObjectEditor(MessageModel *model, QWidget *parent) | ||
: BaseEditor(model, parent), _ui(new Ui::ObjectEditor) { | ||
_ui->setupUi(this); | ||
|
||
_nodeMapper->addMapping(_ui->nameEdit, TreeNode::kNameFieldNumber); | ||
|
||
connect(_ui->saveButton, &QAbstractButton::pressed, this, &BaseEditor::OnSave); | ||
|
||
_eventsModel = new EventsListModel(MainWindow::GetEventData(), this); | ||
|
||
EventTypesListModel *m = new EventTypesListModel(MainWindow::GetEventData(), this); | ||
_eventsTypesModel = new EventTypesListSortFilterProxyModel(m); | ||
_eventsTypesModel->setSourceModel(m); | ||
_eventsTypesModel->sort(0); | ||
|
||
BindEventMenu(_ui->addEventButton, true); | ||
BindEventMenu(_ui->changeEventButton, false); | ||
|
||
RebindSubModels(); | ||
} | ||
|
||
ObjectEditor::~ObjectEditor() { delete _ui; } | ||
|
||
void ObjectEditor::AddChangeEventHelper(const Object::EgmEvent &event, bool add) { | ||
if (add) { | ||
AddEvent(event); | ||
} else { | ||
int existingIdx = MapRowFrom(IndexOf(event)); | ||
int selectedIdx = MapRowTo(_ui->eventsList->selectionModel()->currentIndex().row()); | ||
if (existingIdx == -1 && selectedIdx != -1) { | ||
ChangeEvent(selectedIdx, event, false); | ||
} else | ||
qDebug() << "Change event failed"; | ||
} | ||
} | ||
|
||
void ObjectEditor::AddChangeFromMenuEvent(const QModelIndex &index, bool add) { | ||
QStringList args = _eventsTypesModel->data(index, EventTypesListModel::UserRoles::EventArgumentsRole).toStringList(); | ||
EventArgumentsDialog *dialog = nullptr; | ||
if (args.size() > 0) { | ||
dialog = new EventArgumentsDialog(this, args); | ||
dialog->open(); | ||
|
||
connect(dialog, &QDialog::accepted, [=]() { | ||
if (dialog->result() == QDialog::Accepted) { | ||
Object::EgmEvent event; | ||
event.set_id( | ||
_eventsTypesModel->data(index, EventTypesListModel::UserRoles::EventBareIDRole).toString().toStdString()); | ||
for (const QString &arg : dialog->GetArguments()) { | ||
std::string *s = event.add_arguments(); | ||
s->assign(arg.toStdString()); | ||
} | ||
|
||
AddChangeEventHelper(event, add); | ||
} | ||
}); | ||
} else { | ||
Object::EgmEvent event; | ||
event.set_id( | ||
_eventsTypesModel->data(index, EventTypesListModel::UserRoles::EventBareIDRole).toString().toStdString()); | ||
AddChangeEventHelper(event, add); | ||
} | ||
} | ||
|
||
void ObjectEditor::RebindSubModels() { | ||
_objectModel = _model->GetSubModel<MessageModel *>(TreeNode::kObjectFieldNumber); | ||
_eventsModel->setSourceModel(_objectModel->GetSubModel<RepeatedMessageModel *>(Object::kEgmEventsFieldNumber)); | ||
|
||
_sortedEvents = new QSortFilterProxyModel(_eventsModel); | ||
_sortedEvents->setSourceModel(_eventsModel); | ||
_sortedEvents->sort(0); | ||
_sortedEvents->setDynamicSortFilter(true); | ||
_ui->eventsList->setModel(_sortedEvents); | ||
|
||
connect(_ui->eventsList, &QAbstractItemView::clicked, | ||
[=](const QModelIndex &index) { SetCurrentEditor(MapRowTo(index.row())); }); | ||
|
||
connect(_ui->deleteEventButton, &QToolButton::pressed, [=]() { | ||
QModelIndex selection = _ui->eventsList->selectionModel()->currentIndex(); | ||
if (selection.row() != -1) RemoveEvent(MapRowTo(selection.row())); | ||
}); | ||
|
||
for (int event = 0; event < _eventsModel->rowCount(); ++event) { | ||
BindEventEditor(event); | ||
} | ||
|
||
SetCurrentEditor(MapRowFrom(0)); | ||
CheckDisableButtons(); | ||
|
||
BaseEditor::RebindSubModels(); | ||
} | ||
|
||
void ObjectEditor::CheckDisableButtons() { | ||
bool hasEvents = _sortedEvents->rowCount() > 0; | ||
_ui->changeEventButton->setDisabled(!hasEvents); | ||
_ui->deleteEventButton->setDisabled(!hasEvents); | ||
_ui->codeEditor->setDisabled(!hasEvents); | ||
if (!hasEvents) _ui->eventLineEdit->setText(""); | ||
} | ||
|
||
void ObjectEditor::AddEvent(Object::EgmEvent event) { | ||
RepeatedMessageModel *eventsModel = _objectModel->GetSubModel<RepeatedMessageModel *>(Object::kEgmEventsFieldNumber); | ||
int idx = eventsModel->rowCount(); | ||
|
||
if (IndexOf(event) == -1) { | ||
bool insert = eventsModel->insertRow(idx); | ||
if (insert) { | ||
BindEventEditor(idx); | ||
ChangeEvent(idx, event); | ||
} | ||
} else | ||
qDebug() << "Event already exists"; | ||
} | ||
|
||
void ObjectEditor::ChangeEvent(int idx, Object::EgmEvent event, bool changeCode) { | ||
RepeatedMessageModel *eventsModel = _objectModel->GetSubModel<RepeatedMessageModel *>(Object::kEgmEventsFieldNumber); | ||
|
||
eventsModel->SetData(QString::fromStdString(event.id()), idx, Object::EgmEvent::kIdFieldNumber); | ||
|
||
if (changeCode) eventsModel->SetData(QString::fromStdString(event.code()), idx, Object::EgmEvent::kCodeFieldNumber); | ||
|
||
RepeatedStringModel *argsModel = eventsModel->GetSubModel<MessageModel *>(idx)->GetSubModel<RepeatedStringModel *>( | ||
Object::EgmEvent::kArgumentsFieldNumber); | ||
|
||
size_t argc = 0; | ||
argsModel->removeRows(0, argsModel->rowCount()); // clear old arguments | ||
if (event.arguments_size() > 0) { | ||
argsModel->insertRows(argsModel->rowCount(), event.arguments_size()); | ||
for (const auto &arg : event.arguments()) { | ||
argsModel->SetData(QString::fromStdString(arg), argc++); | ||
} | ||
} | ||
|
||
SetCurrentEditor(idx); | ||
CheckDisableButtons(); | ||
} | ||
|
||
void ObjectEditor::RemoveEvent(int idx) { | ||
RepeatedMessageModel *eventsModel = _objectModel->GetSubModel<RepeatedMessageModel *>(Object::kEgmEventsFieldNumber); | ||
eventsModel->removeRow(idx); | ||
_ui->codeEditor->RemoveCodeWidget(idx); | ||
SetCurrentEditor(MapRowFrom(0)); | ||
CheckDisableButtons(); | ||
} | ||
|
||
int ObjectEditor::IndexOf(Object::EgmEvent event) { | ||
std::vector<std::string> args(event.arguments().begin(), event.arguments().end()); | ||
Event e = MainWindow::GetEventData()->get_event(event.id(), args); | ||
for (int i = 0; i < _eventsModel->rowCount(); ++i) { | ||
if (_eventsModel->data(_eventsModel->index(i, 0)).toString() == QString::fromStdString(e.HumanName())) return i; | ||
} | ||
return -1; | ||
} | ||
|
||
void ObjectEditor::BindEventEditor(int idx) { | ||
RepeatedMessageModel *eventsModel = _objectModel->GetSubModel<RepeatedMessageModel *>(Object::kEgmEventsFieldNumber); | ||
CodeWidget *codeWidget = _ui->codeEditor->AddCodeWidget(); | ||
ModelMapper *mapper(new ModelMapper(eventsModel->GetSubModel<MessageModel *>(idx), this)); | ||
mapper->addMapping(codeWidget, Object::EgmEvent::kCodeFieldNumber); | ||
mapper->toFirst(); | ||
} | ||
|
||
void ObjectEditor::SetCurrentEditor(int idx) { | ||
if (idx < _sortedEvents->rowCount()) { | ||
_ui->codeEditor->SetCurrentIndex(idx); | ||
_ui->eventLineEdit->setText(_eventsModel->data(_eventsModel->index(idx, 0)).toString()); | ||
_ui->eventsList->selectionModel()->select(_sortedEvents->index(MapRowFrom(idx), 0), | ||
QItemSelectionModel::QItemSelectionModel::ClearAndSelect); | ||
} | ||
} | ||
|
||
int ObjectEditor::MapRowTo(int row) { return _sortedEvents->mapToSource(_sortedEvents->index(row, 0)).row(); } | ||
|
||
int ObjectEditor::MapRowFrom(int row) { return _sortedEvents->mapFromSource(_eventsModel->index(row, 0)).row(); } |
Oops, something went wrong.