Port changes from Custom Attributes redesign

This commit is contained in:
GriffinR 2024-10-10 21:14:03 -04:00
parent 09694891f3
commit fe38e42591
19 changed files with 823 additions and 367 deletions

View File

@ -0,0 +1,172 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CustomAttributesDialog</class>
<widget class="QDialog" name="CustomAttributesDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>410</width>
<height>192</height>
</rect>
</property>
<property name="windowTitle">
<string>Add New Custom Attribute</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QFrame" name="centralwidget">
<layout class="QFormLayout" name="formLayout">
<property name="topMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_Name">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="lineEdit_Name">
<property name="toolTip">
<string>The key name for the new JSON field</string>
</property>
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_Type">
<property name="text">
<string>Type</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="NoScrollComboBox" name="comboBox_Type">
<property name="toolTip">
<string>The data type for the new JSON field</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_Value">
<property name="text">
<string>Value</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QStackedWidget" name="stackedWidget_Value">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>The value for the new JSON field</string>
</property>
<widget class="QWidget" name="page_String">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="lineEdit_Value"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_Number">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QSpinBox" name="spinBox_Value"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_Boolean">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QCheckBox" name="checkBox_Value">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QLabel" name="label_NameError">
<property name="visible">
<bool>false</bool>
</property>
<property name="styleSheet">
<string notr="true">color: rgb(255, 0, 0)</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>NoScrollComboBox</class>
<extends>QComboBox</extends>
<header>noscrollcombobox.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CustomAttributesFrame</class>
<widget class="QFrame" name="CustomAttributesFrame">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Custom Attributes</string>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frame_Buttons">
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Plain</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="button_Add">
<property name="text">
<string>Add</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="button_Delete">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="CustomAttributesTable" name="tableWidget"/>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>CustomAttributesTable</class>
<extends>QTableWidget</extends>
<header>customattributestable.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -2244,113 +2244,69 @@
</widget>
</item>
<item>
<widget class="QFrame" name="frame_keyValue">
<widget class="QScrollArea" name="scrollArea_CustomAttributes">
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
<enum>QFrame::Shadow::Plain</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_10">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Custom Fields</string>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::Shape::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Shadow::Plain</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<property name="leftMargin">
<number>0</number>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents_CustomAttributes">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1011</width>
<height>806</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_21">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="CustomAttributesFrame" name="mapCustomAttributesFrame">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="topMargin">
<number>0</number>
<property name="frameShape">
<enum>QFrame::Shape::StyledPanel</enum>
</property>
<property name="rightMargin">
<number>0</number>
<property name="frameShadow">
<enum>QFrame::Shadow::Raised</enum>
</property>
<property name="bottomMargin">
<number>0</number>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<item>
<widget class="QPushButton" name="pushButton_AddCustomHeaderField">
<property name="text">
<string>Add</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_DeleteCustomHeaderField">
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QTableWidget" name="tableWidget_CustomHeaderFields">
<property name="toolTip">
<string>Custom fields will be added to the map.json file for the current map.</string>
</property>
<attribute name="horizontalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderHighlightSections">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderShowSortIndicator" stdset="0">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<column>
<property name="text">
<string>Type</string>
<property name="sizeHint" stdset="0">
<size>
<width>1</width>
<height>767</height>
</size>
</property>
</column>
<column>
<property name="text">
<string>Key</string>
</property>
</column>
<column>
<property name="text">
<string>Value</string>
</property>
</column>
</widget>
</item>
</layout>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
@ -3349,6 +3305,12 @@
<header>maplisttoolbar.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>CustomAttributesFrame</class>
<extends>QFrame</extends>
<header>customattributesframe.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../resources/images.qrc"/>

View File

@ -160,10 +160,10 @@ public:
virtual void setDefaultValues(Project *project);
virtual QSet<QString> getExpectedFields() = 0;
void readCustomValues(QJsonObject values);
void addCustomValuesTo(OrderedJson::object *obj);
const QMap<QString, QJsonValue> getCustomValues() { return this->customValues; }
void setCustomValues(const QMap<QString, QJsonValue> newCustomValues) { this->customValues = newCustomValues; }
void readCustomAttributes(const QJsonObject &json);
void addCustomAttributesTo(OrderedJson::object *obj) const;
const QMap<QString, QJsonValue> getCustomAttributes() const { return this->customAttributes; }
void setCustomAttributes(const QMap<QString, QJsonValue> newCustomAttributes) { this->customAttributes = newCustomAttributes; }
virtual void loadPixmap(Project *project);
@ -206,7 +206,7 @@ protected:
int spriteHeight = 16;
bool usingSprite = false;
QMap<QString, QJsonValue> customValues;
QMap<QString, QJsonValue> customAttributes;
QPixmap pixmap;
DraggablePixmapItem *pixmapItem = nullptr;

View File

@ -107,7 +107,7 @@ public:
void updatePrimaryTileset(QString tilesetLabel, bool forceLoad = false);
void updateSecondaryTileset(QString tilesetLabel, bool forceLoad = false);
void toggleBorderVisibility(bool visible, bool enableScriptCallback = true);
void updateCustomMapHeaderValues(QTableWidget *);
void updateCustomMapAttributes();
DraggablePixmapItem *addMapEvent(Event *event);
bool eventLimitReached(Map *, Event::Type);

View File

@ -274,9 +274,6 @@ private slots:
void on_actionAbout_Porymap_triggered();
void on_actionOpen_Log_File_triggered();
void on_actionOpen_Config_Folder_triggered();
void on_pushButton_AddCustomHeaderField_clicked();
void on_pushButton_DeleteCustomHeaderField_clicked();
void on_tableWidget_CustomHeaderFields_cellChanged(int row, int column);
void on_horizontalSlider_MetatileZoom_valueChanged(int value);
void on_horizontalSlider_CollisionZoom_valueChanged(int value);
void on_pushButton_NewWildMonGroup_clicked();
@ -406,7 +403,7 @@ private:
Event::Group getEventGroupFromTabWidget(QWidget *tab);
bool closeSupplementaryWindows();
void setWindowDisabled(bool);
void resetMapCustomAttributesTable();
void initTilesetEditor();
bool initRegionMapEditor(bool silent = false);
bool askToFixRegionMapEditor();

View File

@ -0,0 +1,34 @@
#ifndef CUSTOMATTRIBUTESDIALOG_H
#define CUSTOMATTRIBUTESDIALOG_H
#include <QDialog>
#include <QAbstractButton>
#include "customattributestable.h"
namespace Ui {
class CustomAttributesDialog;
}
class CustomAttributesDialog : public QDialog
{
Q_OBJECT
public:
explicit CustomAttributesDialog(CustomAttributesTable *table);
~CustomAttributesDialog();
private:
Ui::CustomAttributesDialog *ui;
CustomAttributesTable *const m_table;
void setInputType(int inputType);
void onNameChanged(const QString &);
bool validateName(bool allowEmpty = false);
void clickedButton(QAbstractButton *button);
void addNewAttribute();
QVariant getValue() const;
};
#endif // CUSTOMATTRIBUTESDIALOG_H

View File

@ -0,0 +1,36 @@
#ifndef CUSTOMATTRIBUTESFRAME_H
#define CUSTOMATTRIBUTESFRAME_H
/*
The frame containing the Custom Attributes table and its Add/Delete buttons.
Shared by the map's Header tab and Events.
*/
#include "customattributestable.h"
#include <QObject>
#include <QFrame>
namespace Ui {
class CustomAttributesFrame;
}
class CustomAttributesFrame : public QFrame
{
Q_OBJECT
public:
explicit CustomAttributesFrame(QWidget *parent = nullptr);
~CustomAttributesFrame();
CustomAttributesTable* table() const;
private:
Ui::CustomAttributesFrame *ui;
void addAttribute();
void deleteAttribute();
void updateDeleteButton();
};
#endif // CUSTOMATTRIBUTESFRAME_H

View File

@ -1,25 +1,44 @@
#ifndef CUSTOMATTRIBUTESTABLE_H
#define CUSTOMATTRIBUTESTABLE_H
#include "events.h"
#include <QObject>
#include <QFrame>
#include <QJsonValue>
#include <QTableWidget>
class CustomAttributesTable : public QFrame
class CustomAttributesTable : public QTableWidget
{
public:
explicit CustomAttributesTable(Event *event, QWidget *parent = nullptr);
~CustomAttributesTable();
Q_OBJECT
static const QMap<QString, QJsonValue> getAttributes(QTableWidget * table);
static QJsonValue pickType(QWidget * parent, bool * ok = nullptr);
static void addAttribute(QTableWidget * table, QString key, QJsonValue value, bool isNew = false);
static bool deleteSelectedAttributes(QTableWidget * table);
public:
explicit CustomAttributesTable(QWidget *parent = nullptr);
~CustomAttributesTable() {};
QMap<QString, QJsonValue> getAttributes() const;
void setAttributes(const QMap<QString, QJsonValue> &attributes);
void addNewAttribute(const QString &key, const QJsonValue &value);
bool deleteSelectedAttributes();
bool isEmpty() const;
bool isSelectionEmpty() const;
QSet<QString> keys() const { return m_keys; }
QSet<QString> restrictedKeys() const { return m_restrictedKeys; }
void setRestrictedKeys(const QSet<QString> &keys) { m_restrictedKeys = keys; }
signals:
void edited();
protected:
virtual void resizeEvent(QResizeEvent *event) override;
private:
Event *event;
QTableWidget *table;
QSet<QString> m_keys; // All keys currently in the table
QSet<QString> m_restrictedKeys; // All keys not allowed in the table
QPair<QString, QJsonValue> getAttribute(int row) const;
int addAttribute(const QString &key, const QJsonValue &value);
void removeAttribute(const QString &key);
void resizeVertically();
};

View File

@ -52,6 +52,8 @@ public:
QFrame *frame_contents;
QVBoxLayout *layout_contents;
CustomAttributesFrame *custom_attributes;
protected:
bool populated = false;
bool initialized = false;

View File

@ -67,6 +67,8 @@ SOURCES += src/core/advancemapparser.cpp \
src/ui/aboutporymap.cpp \
src/ui/colorinputwidget.cpp \
src/ui/connectionslistitem.cpp \
src/ui/customattributesdialog.cpp \
src/ui/customattributestable.cpp \
src/ui/customscriptseditor.cpp \
src/ui/customscriptslistitem.cpp \
src/ui/divingmappixmapitem.cpp \
@ -83,7 +85,7 @@ SOURCES += src/core/advancemapparser.cpp \
src/ui/regionmaplayoutpixmapitem.cpp \
src/ui/regionmapentriespixmapitem.cpp \
src/ui/cursortilerect.cpp \
src/ui/customattributestable.cpp \
src/ui/customattributesframe.cpp \
src/ui/eventframes.cpp \
src/ui/eventfilters.cpp \
src/ui/filterchildrenproxymodel.cpp \
@ -176,6 +178,8 @@ HEADERS += include/core/advancemapparser.h \
include/lib/orderedjson.h \
include/ui/aboutporymap.h \
include/ui/connectionslistitem.h \
include/ui/customattributesdialog.h \
include/ui/customattributestable.h \
include/ui/customscriptseditor.h \
include/ui/customscriptslistitem.h \
include/ui/divingmappixmapitem.h \
@ -192,7 +196,7 @@ HEADERS += include/core/advancemapparser.h \
include/ui/regionmaplayoutpixmapitem.h \
include/ui/regionmapentriespixmapitem.h \
include/ui/cursortilerect.h \
include/ui/customattributestable.h \
include/ui/customattributesframe.h \
include/ui/eventframes.h \
include/ui/eventfilters.h \
include/ui/filterchildrenproxymodel.h \
@ -258,6 +262,7 @@ HEADERS += include/core/advancemapparser.h \
FORMS += forms/mainwindow.ui \
forms/colorinputwidget.ui \
forms/connectionslistitem.ui \
forms/customattributesframe.ui \
forms/gridsettingsdialog.ui \
forms/mapheaderform.ui \
forms/maplisttoolbar.ui \
@ -282,6 +287,7 @@ FORMS += forms/mainwindow.ui \
forms/projectsettingseditor.ui \
forms/customscriptseditor.ui \
forms/customscriptslistitem.ui \
forms/customattributesdialog.ui \
forms/updatepromoter.ui \
forms/wildmonchart.ui \
forms/wildmonsearch.ui

View File

@ -53,20 +53,20 @@ void Event::setDefaultValues(Project *) {
this->setElevation(projectConfig.defaultElevation);
}
void Event::readCustomValues(QJsonObject values) {
this->customValues.clear();
QSet<QString> expectedFields = this->getExpectedFields();
for (QString key : values.keys()) {
if (!expectedFields.contains(key)) {
this->customValues[key] = values[key];
void Event::readCustomAttributes(const QJsonObject &json) {
this->customAttributes.clear();
const QSet<QString> expectedFields = this->getExpectedFields();
for (auto i = json.constBegin(); i != json.constEnd(); i++) {
if (!expectedFields.contains(i.key())) {
this->customAttributes[i.key()] = i.value();
}
}
}
void Event::addCustomValuesTo(OrderedJson::object *obj) {
for (QString key : this->customValues.keys()) {
if (!obj->contains(key)) {
(*obj)[key] = OrderedJson::fromQJsonValue(this->customValues[key]);
void Event::addCustomAttributesTo(OrderedJson::object *obj) const {
for (auto i = this->customAttributes.constBegin(); i != this->customAttributes.constEnd(); i++) {
if (!obj->contains(i.key())) {
(*obj)[i.key()] = OrderedJson::fromQJsonValue(i.value());
}
}
}
@ -197,7 +197,7 @@ Event *ObjectEvent::duplicate() {
copy->setSightRadiusBerryTreeID(this->getSightRadiusBerryTreeID());
copy->setScript(this->getScript());
copy->setFlag(this->getFlag());
copy->setCustomValues(this->getCustomValues());
copy->setCustomAttributes(this->getCustomAttributes());
return copy;
}
@ -227,7 +227,7 @@ OrderedJson::object ObjectEvent::buildEventJson(Project *) {
objectJson["trainer_sight_or_berry_tree_id"] = this->getSightRadiusBerryTreeID();
objectJson["script"] = this->getScript();
objectJson["flag"] = this->getFlag();
this->addCustomValuesTo(&objectJson);
this->addCustomAttributesTo(&objectJson);
return objectJson;
}
@ -245,7 +245,7 @@ bool ObjectEvent::loadFromJson(QJsonObject json, Project *) {
this->setScript(ParseUtil::jsonToQString(json["script"]));
this->setFlag(ParseUtil::jsonToQString(json["flag"]));
this->readCustomValues(json);
this->readCustomAttributes(json);
return true;
}
@ -371,7 +371,7 @@ Event *CloneObjectEvent::duplicate() {
copy->setGfx(this->getGfx());
copy->setTargetID(this->getTargetID());
copy->setTargetMap(this->getTargetMap());
copy->setCustomValues(this->getCustomValues());
copy->setCustomAttributes(this->getCustomAttributes());
return copy;
}
@ -394,7 +394,7 @@ OrderedJson::object CloneObjectEvent::buildEventJson(Project *project) {
cloneJson["target_local_id"] = this->getTargetID();
const QString mapName = this->getTargetMap();
cloneJson["target_map"] = project->mapNamesToMapConstants.value(mapName, mapName);
this->addCustomValuesTo(&cloneJson);
this->addCustomAttributesTo(&cloneJson);
return cloneJson;
}
@ -411,7 +411,7 @@ bool CloneObjectEvent::loadFromJson(QJsonObject json, Project *project) {
logWarn(QString("Unknown Target Map constant '%1'.").arg(mapConstant));
this->setTargetMap(project->mapConstantsToMapNames.value(mapConstant, mapConstant));
this->readCustomValues(json);
this->readCustomAttributes(json);
return true;
}
@ -478,7 +478,7 @@ Event *WarpEvent::duplicate() {
copy->setDestinationMap(this->getDestinationMap());
copy->setDestinationWarpID(this->getDestinationWarpID());
copy->setCustomValues(this->getCustomValues());
copy->setCustomAttributes(this->getCustomAttributes());
return copy;
}
@ -501,7 +501,7 @@ OrderedJson::object WarpEvent::buildEventJson(Project *project) {
warpJson["dest_map"] = project->mapNamesToMapConstants.value(mapName, mapName);
warpJson["dest_warp_id"] = this->getDestinationWarpID();
this->addCustomValuesTo(&warpJson);
this->addCustomAttributesTo(&warpJson);
return warpJson;
}
@ -518,7 +518,7 @@ bool WarpEvent::loadFromJson(QJsonObject json, Project *project) {
logWarn(QString("Unknown Destination Map constant '%1'.").arg(mapConstant));
this->setDestinationMap(project->mapConstantsToMapNames.value(mapConstant, mapConstant));
this->readCustomValues(json);
this->readCustomAttributes(json);
return true;
}
@ -560,7 +560,7 @@ Event *TriggerEvent::duplicate() {
copy->setScriptVarValue(this->getScriptVarValue());
copy->setScriptLabel(this->getScriptLabel());
copy->setCustomValues(this->getCustomValues());
copy->setCustomAttributes(this->getCustomAttributes());
return copy;
}
@ -584,7 +584,7 @@ OrderedJson::object TriggerEvent::buildEventJson(Project *) {
triggerJson["var_value"] = this->getScriptVarValue();
triggerJson["script"] = this->getScriptLabel();
this->addCustomValuesTo(&triggerJson);
this->addCustomAttributesTo(&triggerJson);
return triggerJson;
}
@ -597,7 +597,7 @@ bool TriggerEvent::loadFromJson(QJsonObject json, Project *) {
this->setScriptVarValue(ParseUtil::jsonToQString(json["var_value"]));
this->setScriptLabel(ParseUtil::jsonToQString(json["script"]));
this->readCustomValues(json);
this->readCustomAttributes(json);
return true;
}
@ -634,7 +634,7 @@ Event *WeatherTriggerEvent::duplicate() {
copy->setElevation(this->getElevation());
copy->setWeather(this->getWeather());
copy->setCustomValues(this->getCustomValues());
copy->setCustomAttributes(this->getCustomAttributes());
return copy;
}
@ -656,7 +656,7 @@ OrderedJson::object WeatherTriggerEvent::buildEventJson(Project *) {
weatherJson["elevation"] = this->getElevation();
weatherJson["weather"] = this->getWeather();
this->addCustomValuesTo(&weatherJson);
this->addCustomAttributesTo(&weatherJson);
return weatherJson;
}
@ -667,7 +667,7 @@ bool WeatherTriggerEvent::loadFromJson(QJsonObject json, Project *) {
this->setElevation(ParseUtil::jsonToInt(json["elevation"]));
this->setWeather(ParseUtil::jsonToQString(json["weather"]));
this->readCustomValues(json);
this->readCustomAttributes(json);
return true;
}
@ -701,7 +701,7 @@ Event *SignEvent::duplicate() {
copy->setFacingDirection(this->getFacingDirection());
copy->setScriptLabel(this->getScriptLabel());
copy->setCustomValues(this->getCustomValues());
copy->setCustomAttributes(this->getCustomAttributes());
return copy;
}
@ -724,7 +724,7 @@ OrderedJson::object SignEvent::buildEventJson(Project *) {
signJson["player_facing_dir"] = this->getFacingDirection();
signJson["script"] = this->getScriptLabel();
this->addCustomValuesTo(&signJson);
this->addCustomAttributesTo(&signJson);
return signJson;
}
@ -736,7 +736,7 @@ bool SignEvent::loadFromJson(QJsonObject json, Project *) {
this->setFacingDirection(ParseUtil::jsonToQString(json["player_facing_dir"]));
this->setScriptLabel(ParseUtil::jsonToQString(json["script"]));
this->readCustomValues(json);
this->readCustomAttributes(json);
return true;
}
@ -774,7 +774,7 @@ Event *HiddenItemEvent::duplicate() {
copy->setQuantity(this->getQuantity());
copy->setQuantity(this->getQuantity());
copy->setCustomValues(this->getCustomValues());
copy->setCustomAttributes(this->getCustomAttributes());
return copy;
}
@ -803,7 +803,7 @@ OrderedJson::object HiddenItemEvent::buildEventJson(Project *) {
hiddenItemJson["underfoot"] = this->getUnderfoot();
}
this->addCustomValuesTo(&hiddenItemJson);
this->addCustomAttributesTo(&hiddenItemJson);
return hiddenItemJson;
}
@ -821,7 +821,7 @@ bool HiddenItemEvent::loadFromJson(QJsonObject json, Project *) {
this->setUnderfoot(ParseUtil::jsonToBool(json["underfoot"]));
}
this->readCustomValues(json);
this->readCustomAttributes(json);
return true;
}
@ -867,7 +867,7 @@ Event *SecretBaseEvent::duplicate() {
copy->setElevation(this->getElevation());
copy->setBaseID(this->getBaseID());
copy->setCustomValues(this->getCustomValues());
copy->setCustomAttributes(this->getCustomAttributes());
return copy;
}
@ -889,7 +889,7 @@ OrderedJson::object SecretBaseEvent::buildEventJson(Project *) {
secretBaseJson["elevation"] = this->getElevation();
secretBaseJson["secret_base_id"] = this->getBaseID();
this->addCustomValuesTo(&secretBaseJson);
this->addCustomAttributesTo(&secretBaseJson);
return secretBaseJson;
}
@ -900,7 +900,7 @@ bool SecretBaseEvent::loadFromJson(QJsonObject json, Project *) {
this->setElevation(ParseUtil::jsonToInt(json["elevation"]));
this->setBaseID(ParseUtil::jsonToQString(json["secret_base_id"]));
this->readCustomValues(json);
this->readCustomAttributes(json);
return true;
}

View File

@ -10,7 +10,7 @@
#include "editcommands.h"
#include "config.h"
#include "scripting.h"
#include "customattributestable.h"
#include "customattributesframe.h"
#include "validator.h"
#include <QCheckBox>
#include <QPainter>
@ -52,6 +52,7 @@ Editor::Editor(Ui::MainWindow* ui)
connect(ui->toolButton_Open_Scripts, &QToolButton::pressed, this, &Editor::openMapScripts);
connect(ui->actionOpen_Project_in_Text_Editor, &QAction::triggered, this, &Editor::openProjectInTextEditor);
connect(ui->checkBox_ToggleGrid, &QCheckBox::toggled, this, &Editor::toggleGrid);
connect(ui->mapCustomAttributesFrame->table(), &CustomAttributesTable::edited, this, &Editor::updateCustomMapAttributes);
}
Editor::~Editor()
@ -1975,9 +1976,9 @@ void Editor::updateBorderVisibility() {
}
}
void Editor::updateCustomMapHeaderValues(QTableWidget *table)
void Editor::updateCustomMapAttributes()
{
map->setCustomAttributes(CustomAttributesTable::getAttributes(table));
map->setCustomAttributes(ui->mapCustomAttributesFrame->table()->getAttributes());
map->modify();
}

View File

@ -7,7 +7,7 @@
#include "eventframes.h"
#include "bordermetatilespixmapitem.h"
#include "currentselectedmetatilespixmapitem.h"
#include "customattributestable.h"
#include "customattributesframe.h"
#include "scripting.h"
#include "adjustingstackedwidget.h"
#include "draggablepixmapitem.h"
@ -1033,16 +1033,7 @@ void MainWindow::displayMapProperties() {
ui->comboBox_PrimaryTileset->setCurrentText(editor->map->layout()->tileset_primary_label);
ui->comboBox_SecondaryTileset->setCurrentText(editor->map->layout()->tileset_secondary_label);
// Custom fields table.
/* // TODO: Re-enable
ui->tableWidget_CustomHeaderFields->blockSignals(true);
ui->tableWidget_CustomHeaderFields->setRowCount(0);
for (auto it = map->customHeaders.begin(); it != map->customHeaders.end(); it++)
CustomAttributesTable::addAttribute(ui->tableWidget_CustomHeaderFields, it.key(), it.value());
ui->tableWidget_CustomHeaderFields->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
ui->tableWidget_CustomHeaderFields->blockSignals(false);
*/
ui->mapCustomAttributesFrame->table()->setAttributes(editor->map->customAttributes());
}
void MainWindow::on_comboBox_LayoutSelector_currentTextChanged(const QString &text) {
@ -1119,6 +1110,8 @@ bool MainWindow::setProjectUI() {
this->layoutListProxyModel->setSourceModel(this->layoutTreeModel);
ui->layoutList->setModel(layoutListProxyModel);
ui->mapCustomAttributesFrame->table()->setRestrictedKeys(project->topLevelMapFields);
return true;
}
@ -2867,27 +2860,6 @@ void MainWindow::reloadScriptEngine() {
Scripting::cb_MapOpened(editor->map->name()); // TODO: API should have equivalent for layout
}
void MainWindow::on_pushButton_AddCustomHeaderField_clicked()
{
bool ok;
QJsonValue value = CustomAttributesTable::pickType(this, &ok);
if (ok){
CustomAttributesTable::addAttribute(this->ui->tableWidget_CustomHeaderFields, "", value, true);
this->editor->updateCustomMapHeaderValues(this->ui->tableWidget_CustomHeaderFields);
}
}
void MainWindow::on_pushButton_DeleteCustomHeaderField_clicked()
{
if (CustomAttributesTable::deleteSelectedAttributes(this->ui->tableWidget_CustomHeaderFields))
this->editor->updateCustomMapHeaderValues(this->ui->tableWidget_CustomHeaderFields);
}
void MainWindow::on_tableWidget_CustomHeaderFields_cellChanged(int, int)
{
this->editor->updateCustomMapHeaderValues(this->ui->tableWidget_CustomHeaderFields);
}
void MainWindow::on_horizontalSlider_MetatileZoom_valueChanged(int value) {
porymapConfig.metatilesZoom = value;
double scale = pow(3.0, static_cast<double>(value - 30) / 30.0);

View File

@ -353,14 +353,13 @@ bool Project::loadMapData(Map* map) {
}
}
// Check for custom fields
/* // TODO: Re-enable
for (QString key : mapObj.keys()) {
if (!this->topLevelMapFields.contains(key)) {
map->customHeaders.insert(key, mapObj[key]);
QMap<QString, QJsonValue> customAttributes;
for (auto i = mapObj.constBegin(); i != mapObj.constEnd(); i++) {
if (!this->topLevelMapFields.contains(i.key())) {
customAttributes.insert(i.key(), i.value());
}
}
*/
map->setCustomAttributes(customAttributes);
return true;
}
@ -1338,11 +1337,10 @@ void Project::saveMap(Map *map) {
}
// Custom header fields.
/* // TODO: Re-enable
for (QString key : map->customHeaders.keys()) {
mapObj[key] = OrderedJson::fromQJsonValue(map->customHeaders[key]);
const auto customAttributes = map->customAttributes();
for (auto i = customAttributes.constBegin(); i != customAttributes.constEnd(); i++) {
mapObj[i.key()] = OrderedJson::fromQJsonValue(i.value());
}
*/
OrderedJson mapJson(mapObj);
OrderedJsonDoc jsonDoc(&mapJson);

View File

@ -0,0 +1,101 @@
#include "customattributesdialog.h"
#include "ui_customattributesdialog.h"
#include <QMessageBox>
static int curInputType = 0;
CustomAttributesDialog::CustomAttributesDialog(CustomAttributesTable *table) :
QDialog(table),
ui(new Ui::CustomAttributesDialog),
m_table(table)
{
setAttribute(Qt::WA_DeleteOnClose);
ui->setupUi(this);
// Type combo box
ui->comboBox_Type->addItems({"String", "Number", "Boolean"});
ui->comboBox_Type->setEditable(false);
// When the value type is changed, update the value input widget
connect(ui->comboBox_Type, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &CustomAttributesDialog::setInputType);
ui->comboBox_Type->setCurrentIndex(curInputType);
ui->spinBox_Value->setMinimum(INT_MIN);
ui->spinBox_Value->setMaximum(INT_MAX);
connect(ui->lineEdit_Name, &QLineEdit::textChanged, this, &CustomAttributesDialog::onNameChanged);
connect(ui->buttonBox, &QDialogButtonBox::clicked, this, &CustomAttributesDialog::clickedButton);
adjustSize();
}
CustomAttributesDialog::~CustomAttributesDialog() {
delete ui;
}
void CustomAttributesDialog::setInputType(int inputType) {
if (inputType < 0 || inputType >= ui->stackedWidget_Value->count())
return;
ui->stackedWidget_Value->setCurrentIndex(inputType);
// Preserve input widget for later dialogs
curInputType = inputType;
}
void CustomAttributesDialog::onNameChanged(const QString &) {
validateName(true);
}
bool CustomAttributesDialog::validateName(bool allowEmpty) {
const QString name = ui->lineEdit_Name->text();
QString errorText;
if (name.isEmpty()) {
if (!allowEmpty) errorText = QString("%1 cannot be empty.").arg(ui->label_Name->text());
} else if (m_table->restrictedKeys().contains(name)) {
errorText = QString("The name '%1' is reserved, please choose a different name.").arg(name);
}
bool isValid = errorText.isEmpty();
ui->label_NameError->setText(errorText);
ui->label_NameError->setVisible(!isValid);
ui->lineEdit_Name->setStyleSheet(!isValid ? "QLineEdit { background-color: rgba(255, 0, 0, 25%) }" : "");
return isValid;
}
QVariant CustomAttributesDialog::getValue() const {
QVariant value;
auto widget = ui->stackedWidget_Value->currentWidget();
if (widget == ui->page_String) {
value = QVariant(ui->lineEdit_Value->text());
} else if (widget == ui->page_Number) {
value = QVariant(ui->spinBox_Value->value());
} else if (widget == ui->page_Boolean) {
value = QVariant(ui->checkBox_Value->isChecked());
}
return value;
}
void CustomAttributesDialog::addNewAttribute() {
m_table->addNewAttribute(ui->lineEdit_Name->text(), QJsonValue::fromVariant(getValue()));
}
void CustomAttributesDialog::clickedButton(QAbstractButton *button) {
auto buttonRole = ui->buttonBox->buttonRole(button);
if (buttonRole == QDialogButtonBox::AcceptRole && validateName()) {
const QString key = ui->lineEdit_Name->text();
if (m_table->keys().contains(key)) {
// Warn user if key name would overwrite an existing custom attribute
const QString msg = QString("Overwrite value for existing attribute '%1'?").arg(key);
if (QMessageBox::warning(this, "Warning", msg, QMessageBox::Yes | QMessageBox::Cancel) == QMessageBox::Cancel){
return;
}
}
addNewAttribute();
done(QDialog::Accepted);
} else if (buttonRole == QDialogButtonBox::RejectRole) {
done(QDialog::Rejected);
}
}

View File

@ -0,0 +1,39 @@
#include "customattributesframe.h"
#include "ui_customattributesframe.h"
#include "customattributesdialog.h"
#include <QHBoxLayout>
#include <QPushButton>
#include <QLabel>
CustomAttributesFrame::CustomAttributesFrame(QWidget *parent) :
QFrame(parent),
ui(new Ui::CustomAttributesFrame)
{
ui->setupUi(this);
connect(ui->button_Add, &QPushButton::clicked, this, &CustomAttributesFrame::addAttribute);
connect(ui->button_Delete, &QPushButton::clicked, this, &CustomAttributesFrame::deleteAttribute);
connect(ui->tableWidget, &CustomAttributesTable::itemSelectionChanged, this, &CustomAttributesFrame::updateDeleteButton);
connect(ui->tableWidget, &CustomAttributesTable::edited, this, &CustomAttributesFrame::updateDeleteButton);
}
CustomAttributesFrame::~CustomAttributesFrame() {
delete ui;
}
CustomAttributesTable* CustomAttributesFrame::table() const {
return ui->tableWidget;
}
void CustomAttributesFrame::addAttribute() {
auto dialog = new CustomAttributesDialog(ui->tableWidget);
dialog->open();
}
void CustomAttributesFrame::deleteAttribute() {
ui->tableWidget->deleteSelectedAttributes();
}
void CustomAttributesFrame::updateDeleteButton() {
ui->button_Delete->setDisabled(ui->tableWidget->isSelectionEmpty());
}

View File

@ -1,196 +1,176 @@
#include "customattributestable.h"
#include "parseutil.h"
#include <QHBoxLayout>
#include "noscrollspinbox.h"
#include <QHeaderView>
#include <QPushButton>
#include <QTableWidget>
#include <QLabel>
#include <QScrollBar>
#include <QInputDialog>
CustomAttributesTable::CustomAttributesTable(Event *event, QWidget *parent) :
QFrame(parent)
enum Column {
Key,
Value,
Count
};
enum DataRole {
JsonType = Qt::UserRole,
OriginalValue,
};
CustomAttributesTable::CustomAttributesTable(QWidget *parent) :
QTableWidget(parent)
{
this->event = event;
this->setColumnCount(Column::Count);
this->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
this->setHorizontalHeaderLabels(QStringList({"Key", "Value"}));
this->horizontalHeader()->setStretchLastSection(true);
this->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
this->horizontalHeader()->setVisible(false);
this->verticalHeader()->setVisible(false);
QVBoxLayout *layout = new QVBoxLayout(this);
QLabel *label = new QLabel("Custom Attributes");
layout->addWidget(label);
connect(this, &QTableWidget::cellChanged, this, &CustomAttributesTable::edited);
QFrame *buttonsFrame = new QFrame(this);
buttonsFrame->setLayout(new QHBoxLayout());
QPushButton *addButton = new QPushButton(this);
QPushButton *deleteButton = new QPushButton(this);
addButton->setText("Add");
deleteButton->setText("Delete");
buttonsFrame->layout()->addWidget(addButton);
buttonsFrame->layout()->addWidget(deleteButton);
buttonsFrame->layout()->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Fixed));
buttonsFrame->layout()->setContentsMargins(0, 0, 0, 0);
layout->addWidget(buttonsFrame);
this->table = new QTableWidget(this);
this->table->setColumnCount(3);
this->table->setHorizontalHeaderLabels(QStringList({"Type", "Key", "Value"}));
this->table->horizontalHeader()->setStretchLastSection(true);
layout->addWidget(this->table);
QMap<QString, QJsonValue> customValues = this->event->getCustomValues();
for (auto it = customValues.begin(); it != customValues.end(); it++)
CustomAttributesTable::addAttribute(this->table, it.key(), it.value());
connect(addButton, &QPushButton::clicked, [=]() {
bool ok;
QJsonValue value = CustomAttributesTable::pickType(this, &ok);
if (ok){
CustomAttributesTable::addAttribute(this->table, "", value, true);
this->event->setCustomValues(CustomAttributesTable::getAttributes(this->table));
this->resizeVertically();
// Key cells are uneditable, but users should be allowed to select one and press delete to remove the row.
// Adding the "Selectable" flag to the Key cell changes its appearance to match the Value cell, which
// makes it confusing that you can't edit the Key cell. To keep the uneditable appearance and allow
// deleting rows by selecting Key cells, we select the full row when a Key cell is selected.
connect(this, &QTableWidget::cellPressed, [this](int row, int column) {
if (column == Column::Key) {
this->selectRow(row);
}
});
connect(deleteButton, &QPushButton::clicked, [=]() {
if (CustomAttributesTable::deleteSelectedAttributes(this->table)) {
this->event->setCustomValues(CustomAttributesTable::getAttributes(this->table));
this->resizeVertically();
}
});
connect(this->table, &QTableWidget::cellChanged, [=]() {
this->event->setCustomValues(CustomAttributesTable::getAttributes(this->table));
});
this->resizeVertically();
}
CustomAttributesTable::~CustomAttributesTable()
{
}
void CustomAttributesTable::resizeVertically() {
int horizontalHeaderHeight = this->table->horizontalHeader()->height();
int rowHeight = 0;
for (int i = 0; i < this->table->rowCount(); i++) {
rowHeight += this->table->rowHeight(0);
}
int totalHeight = horizontalHeaderHeight + rowHeight;
if (this->table->rowCount() == 0) {
totalHeight += 1;
} else {
totalHeight += 2;
}
this->table->setMinimumHeight(totalHeight);
this->table->setMaximumHeight(totalHeight);
}
const QMap<QString, QJsonValue> CustomAttributesTable::getAttributes(QTableWidget * table) {
QMap<QString, QJsonValue> CustomAttributesTable::getAttributes() const {
QMap<QString, QJsonValue> fields;
if (!table) return fields;
for (int row = 0; row < table->rowCount(); row++) {
QString key = "";
QTableWidgetItem *typeItem = table->item(row, 0);
QTableWidgetItem *keyItem = table->item(row, 1);
QTableWidgetItem *valueItem = table->item(row, 2);
if (keyItem) key = keyItem->text();
if (key.isEmpty() || !typeItem || !valueItem)
continue;
// Read from the table data which JSON type to save the value as
QJsonValue::Type type = static_cast<QJsonValue::Type>(typeItem->data(Qt::UserRole).toInt());
QJsonValue value;
switch (type)
{
case QJsonValue::String:
value = QJsonValue(valueItem->text());
break;
case QJsonValue::Double:
value = QJsonValue(valueItem->text().toInt());
break;
case QJsonValue::Bool:
value = QJsonValue(valueItem->checkState() == Qt::Checked);
break;
default:
// All other types will just be preserved
value = valueItem->data(Qt::UserRole).toJsonValue();
break;
}
fields[key] = value;
for (int row = 0; row < this->rowCount(); row++) {
auto keyValuePair = this->getAttribute(row);
if (!keyValuePair.first.isEmpty())
fields[keyValuePair.first] = keyValuePair.second;
}
return fields;
}
QJsonValue CustomAttributesTable::pickType(QWidget * parent, bool * ok) {
const QMap<QString, QJsonValue> valueTypes = {
{"String", QJsonValue(QString(""))},
{"Number", QJsonValue(0)},
{"Boolean", QJsonValue(false)},
};
QStringList typeNames = valueTypes.keys();
QString selection = QInputDialog::getItem(parent, "", "Choose Value Type", typeNames, typeNames.indexOf("String"), false, ok);
return valueTypes.value(selection);
QPair<QString, QJsonValue> CustomAttributesTable::getAttribute(int row) const {
auto keyItem = this->item(row, Column::Key);
if (!keyItem)
return {};
// Read from the table data which JSON type to save the value as
QJsonValue::Type type = static_cast<QJsonValue::Type>(keyItem->data(DataRole::JsonType).toInt());
QJsonValue value;
if (type == QJsonValue::String) {
value = QJsonValue(this->item(row, Column::Value)->text());
} else if (type == QJsonValue::Double) {
auto spinBox = static_cast<NoScrollSpinBox*>(this->cellWidget(row, Column::Value));
value = QJsonValue(spinBox->value());
} else if (type == QJsonValue::Bool) {
value = QJsonValue(this->item(row, Column::Value)->checkState() == Qt::Checked);
} else {
// All other types will just be preserved
value = this->item(row, Column::Value)->data(DataRole::OriginalValue).toJsonValue();
}
return {keyItem->text(), value};
}
void CustomAttributesTable::addAttribute(QTableWidget * table, QString key, QJsonValue value, bool isNew) {
if (!table) return;
QTableWidgetItem * valueItem;
int CustomAttributesTable::addAttribute(const QString &key, const QJsonValue &value) {
// Stop 'edited' signals from being emitted before we finish creating the new table data.
const QSignalBlocker blocker(this);
// Certain key names cannot be used (if they would overwrite a field used outside this table)
if (m_restrictedKeys.contains(key))
return -1;
// Overwrite existing key (if present)
if (m_keys.contains(key))
this->removeAttribute(key);
// Add new row
int rowIndex = this->rowCount();
this->insertRow(rowIndex);
QJsonValue::Type type = value.type();
switch (type)
{
case QJsonValue::String:
case QJsonValue::Double:
valueItem = new QTableWidgetItem(ParseUtil::jsonToQString(value));
// Add key name to table
auto keyItem = new QTableWidgetItem(key);
keyItem->setFlags(Qt::ItemIsEnabled);
keyItem->setData(DataRole::JsonType, type); // Record the type for writing to the file
keyItem->setTextAlignment(Qt::AlignCenter);
keyItem->setToolTip(key); // Display name as tool tip in case it's too long to see in the cell
this->setItem(rowIndex, Column::Key, keyItem);
// Add value to table
switch (type) {
case QJsonValue::String: {
// Add a regular cell item for editing text
this->setItem(rowIndex, Column::Value, new QTableWidgetItem(ParseUtil::jsonToQString(value)));
break;
case QJsonValue::Bool:
valueItem = new QTableWidgetItem("");
} case QJsonValue::Double: {
// Add a spin box for editing number values
auto spinBox = new NoScrollSpinBox(this);
spinBox->setMinimum(INT_MIN);
spinBox->setMaximum(INT_MAX);
spinBox->setValue(ParseUtil::jsonToInt(value));
// This connection will be handled by QTableWidget::cellChanged for other cell types
connect(spinBox, QOverload<int>::of(&QSpinBox::valueChanged), this, &CustomAttributesTable::edited);
this->setCellWidget(rowIndex, Column::Value, spinBox);
break;
} case QJsonValue::Bool: {
// Add a checkable cell item for editing bools
auto valueItem = new QTableWidgetItem("");
valueItem->setCheckState(value.toBool() ? Qt::Checked : Qt::Unchecked);
valueItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);
this->setItem(rowIndex, Column::Value, valueItem);
break;
default:
valueItem = new QTableWidgetItem("This value cannot be edited from this table");
valueItem->setFlags(Qt::ItemIsSelectable);
valueItem->setData(Qt::UserRole, value); // Preserve the value for writing to the file
} default: {
// Arrays, objects, or null/undefined values cannot be edited
auto valueItem = new QTableWidgetItem("This value cannot be edited from this table");
valueItem->setFlags(Qt::NoItemFlags);
valueItem->setData(DataRole::OriginalValue, value); // Preserve the value for writing to the file
this->setItem(rowIndex, Column::Value, valueItem);
break;
}
}}
m_keys.insert(key);
const QHash<QJsonValue::Type, QString> typeToName = {
{QJsonValue::Bool, "Bool"},
{QJsonValue::Double, "Number"},
{QJsonValue::String, "String"},
{QJsonValue::Array, "Array"},
{QJsonValue::Object, "Object"},
{QJsonValue::Null, "Null"},
{QJsonValue::Undefined, "Null"},
};
QTableWidgetItem * typeItem = new QTableWidgetItem(typeToName[type]);
typeItem->setFlags(Qt::ItemIsEnabled);
typeItem->setData(Qt::UserRole, type); // Record the type for writing to the file
typeItem->setTextAlignment(Qt::AlignCenter);
return rowIndex;
}
int rowIndex = table->rowCount();
table->insertRow(rowIndex);
table->setItem(rowIndex, 0, typeItem);
table->setItem(rowIndex, 1, new QTableWidgetItem(key));
table->setItem(rowIndex, 2, valueItem);
// For the user adding an attribute by interacting with the table
void CustomAttributesTable::addNewAttribute(const QString &key, const QJsonValue &value) {
int row = this->addAttribute(key, value);
if (row < 0) return;
this->resizeVertically();
this->selectRow(row);
emit this->edited();
}
if (isNew) {
valueItem->setText(""); // Erase the "0" in new numbers
table->selectRow(rowIndex);
// For programmatically populating the table
void CustomAttributesTable::setAttributes(const QMap<QString, QJsonValue> &attributes) {
m_keys.clear();
this->setRowCount(0); // Clear old values
for (auto it = attributes.cbegin(); it != attributes.cend(); it++)
this->addAttribute(it.key(), it.value());
this->resizeVertically();
}
void CustomAttributesTable::removeAttribute(const QString &key) {
for (int row = 0; row < this->rowCount(); row++) {
auto keyItem = this->item(row, Column::Key);
if (keyItem && keyItem->text() == key) {
m_keys.remove(key);
this->removeRow(row);
break;
}
}
}
bool CustomAttributesTable::deleteSelectedAttributes(QTableWidget * table) {
if (!table)
bool CustomAttributesTable::deleteSelectedAttributes() {
if (this->isEmpty())
return false;
int rowCount = table->rowCount();
if (rowCount <= 0)
return false;
QModelIndexList indexList = table->selectionModel()->selectedIndexes();
QModelIndexList indexList = this->selectionModel()->selectedIndexes();
QList<QPersistentModelIndex> persistentIndexes;
for (QModelIndex index : indexList) {
for (const auto &index : indexList) {
QPersistentModelIndex persistentIndex(index);
persistentIndexes.append(persistentIndex);
}
@ -198,12 +178,51 @@ bool CustomAttributesTable::deleteSelectedAttributes(QTableWidget * table) {
if (persistentIndexes.isEmpty())
return false;
for (QPersistentModelIndex index : persistentIndexes) {
table->removeRow(index.row());
for (const auto &index : persistentIndexes) {
auto row = index.row();
auto item = this->item(row, Column::Key);
if (item) m_keys.remove(item->text());
this->removeRow(row);
}
this->resizeVertically();
if (table->rowCount() > 0) {
table->selectRow(0);
if (this->rowCount() > 0) {
this->selectRow(0);
}
emit this->edited();
return true;
}
void CustomAttributesTable::resizeVertically() {
int height = 0;
if (this->isEmpty()) {
// Hide header when table is empty
this->horizontalHeader()->setVisible(false);
} else {
for (int i = 0; i < this->rowCount(); i++)
height += this->rowHeight(i);
// Account for header and horizontal scroll bar
this->horizontalHeader()->setVisible(true);
height += this->horizontalHeader()->height();
if (this->horizontalScrollBar()->isVisible())
height += this->horizontalScrollBar()->height();
height += 2; // Border
}
this->setMinimumHeight(height);
this->setMaximumHeight(height);
}
void CustomAttributesTable::resizeEvent(QResizeEvent *event) {
QTableWidget::resizeEvent(event);
this->resizeVertically();
}
bool CustomAttributesTable::isEmpty() const {
return this->rowCount() <= 0;
}
bool CustomAttributesTable::isSelectionEmpty() const {
return this->selectedIndexes().isEmpty();
}

View File

@ -1,5 +1,5 @@
#include "eventframes.h"
#include "customattributestable.h"
#include "customattributesframe.h"
#include "editcommands.h"
#include "draggablepixmapitem.h"
@ -97,8 +97,9 @@ void EventFrame::setup() {
}
void EventFrame::initCustomAttributesTable() {
CustomAttributesTable *customAttributes = new CustomAttributesTable(this->event, this);
this->layout_contents->addWidget(customAttributes);
this->custom_attributes = new CustomAttributesFrame(this);
this->custom_attributes->table()->setRestrictedKeys(this->event->getExpectedFields());
this->layout_contents->addWidget(this->custom_attributes);
}
void EventFrame::connectSignals(MainWindow *) {
@ -128,6 +129,12 @@ void EventFrame::connectSignals(MainWindow *) {
this->event->setZ(value);
this->event->modify();
});
this->custom_attributes->disconnect();
connect(this->custom_attributes->table(), &CustomAttributesTable::edited, [this]() {
this->event->setCustomAttributes(this->custom_attributes->table()->getAttributes());
this->event->modify();
});
}
void EventFrame::initialize() {
@ -139,6 +146,8 @@ void EventFrame::initialize() {
this->spinner_y->setValue(this->event->getY());
this->spinner_z->setValue(this->event->getZ());
this->custom_attributes->table()->setAttributes(this->event->getCustomAttributes());
this->label_icon->setPixmap(this->event->getPixmap());
}