fix(editor): properties pane not updating automatically
This commit is contained in:
parent
ff5eb969d9
commit
bea6f50f13
7 changed files with 117 additions and 6 deletions
|
@ -12,10 +12,11 @@ std::shared_ptr<Handles> editorToolHandles = Handles::New();
|
||||||
|
|
||||||
|
|
||||||
std::vector<InstanceRefWeak> currentSelection;
|
std::vector<InstanceRefWeak> currentSelection;
|
||||||
std::vector<SelectionUpdateHandler> selectionUpdateHandlers;
|
std::vector<SelectionUpdateHandler> selectionUpdateListeners;
|
||||||
|
std::vector<PropertyUpdateHandler> propertyUpdatelisteners;
|
||||||
|
|
||||||
void setSelection(std::vector<InstanceRefWeak> newSelection, bool fromExplorer) {
|
void setSelection(std::vector<InstanceRefWeak> newSelection, bool fromExplorer) {
|
||||||
for (SelectionUpdateHandler handler : selectionUpdateHandlers) {
|
for (SelectionUpdateHandler handler : selectionUpdateListeners) {
|
||||||
handler(currentSelection, newSelection, fromExplorer);
|
handler(currentSelection, newSelection, fromExplorer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,5 +28,15 @@ const std::vector<InstanceRefWeak> getSelection() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void addSelectionListener(SelectionUpdateHandler handler) {
|
void addSelectionListener(SelectionUpdateHandler handler) {
|
||||||
selectionUpdateHandlers.push_back(handler);
|
selectionUpdateListeners.push_back(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendPropertyUpdatedSignal(InstanceRef instance, std::string property, Data::Variant newValue) {
|
||||||
|
for (PropertyUpdateHandler handler : propertyUpdatelisteners) {
|
||||||
|
handler(instance, property, newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void addPropertyUpdateListener(PropertyUpdateHandler handler) {
|
||||||
|
propertyUpdatelisteners.push_back(handler);
|
||||||
}
|
}
|
|
@ -11,6 +11,7 @@ class Instance;
|
||||||
typedef std::function<void(InstanceRef object, std::optional<InstanceRef> oldParent, std::optional<InstanceRef> newParent)> HierarchyPreUpdateHandler;
|
typedef std::function<void(InstanceRef object, std::optional<InstanceRef> oldParent, std::optional<InstanceRef> newParent)> HierarchyPreUpdateHandler;
|
||||||
typedef std::function<void(InstanceRef object, std::optional<InstanceRef> oldParent, std::optional<InstanceRef> newParent)> HierarchyPostUpdateHandler;
|
typedef std::function<void(InstanceRef object, std::optional<InstanceRef> oldParent, std::optional<InstanceRef> newParent)> HierarchyPostUpdateHandler;
|
||||||
typedef std::function<void(std::vector<InstanceRefWeak> oldSelection, std::vector<InstanceRefWeak> newSelection, bool fromExplorer)> SelectionUpdateHandler;
|
typedef std::function<void(std::vector<InstanceRefWeak> oldSelection, std::vector<InstanceRefWeak> newSelection, bool fromExplorer)> SelectionUpdateHandler;
|
||||||
|
typedef std::function<void(InstanceRef instance, std::string property, Data::Variant newValue)> PropertyUpdateHandler;
|
||||||
|
|
||||||
// TEMPORARY COMMON DATA FOR VARIOUS INTERNAL COMPONENTS
|
// TEMPORARY COMMON DATA FOR VARIOUS INTERNAL COMPONENTS
|
||||||
|
|
||||||
|
@ -23,4 +24,7 @@ extern std::shared_ptr<Handles> editorToolHandles;
|
||||||
|
|
||||||
void setSelection(std::vector<InstanceRefWeak> newSelection, bool fromExplorer = false);
|
void setSelection(std::vector<InstanceRefWeak> newSelection, bool fromExplorer = false);
|
||||||
const std::vector<InstanceRefWeak> getSelection();
|
const std::vector<InstanceRefWeak> getSelection();
|
||||||
void addSelectionListener(SelectionUpdateHandler handler);
|
void addSelectionListener(SelectionUpdateHandler handler);
|
||||||
|
|
||||||
|
void sendPropertyUpdatedSignal(InstanceRef instance, std::string property, Data::Variant newValue);
|
||||||
|
void addPropertyUpdateListener(PropertyUpdateHandler handler);
|
|
@ -174,6 +174,7 @@ fallible<MemberNotFound, AssignToReadOnlyMember> Instance::SetPropertyValue(std:
|
||||||
|
|
||||||
meta.codec.write(value, meta.backingField);
|
meta.codec.write(value, meta.backingField);
|
||||||
if (meta.updateCallback) meta.updateCallback.value()(name);
|
if (meta.updateCallback) meta.updateCallback.value()(name);
|
||||||
|
sendPropertyUpdatedSignal(shared_from_this(), name, value);
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,7 +111,47 @@ Part::Part(PartConstructParams params): Instance(&TYPE), cframe(Data::CFrame(par
|
||||||
.codec = fieldCodecOf<Data::Float, float>(),
|
.codec = fieldCodecOf<Data::Float, float>(),
|
||||||
.flags = PROP_UNIT_FLOAT,
|
.flags = PROP_UNIT_FLOAT,
|
||||||
.category = PROP_CATEGORY_APPEARENCE,
|
.category = PROP_CATEGORY_APPEARENCE,
|
||||||
}}
|
}},
|
||||||
|
|
||||||
|
// Surfaces
|
||||||
|
|
||||||
|
{ "TopSurface", {
|
||||||
|
.backingField = &topSurface,
|
||||||
|
.type = &Data::Int::TYPE, // Replace with enum
|
||||||
|
.codec = fieldCodecOf<Data::Int, int>(),
|
||||||
|
.flags = PROP_HIDDEN,
|
||||||
|
.category = PROP_CATEGORY_SURFACE,
|
||||||
|
}}, { "BottomSurface", {
|
||||||
|
.backingField = &bottomSurface,
|
||||||
|
.type = &Data::Int::TYPE, // Replace with enum
|
||||||
|
.codec = fieldCodecOf<Data::Int, int>(),
|
||||||
|
.flags = PROP_HIDDEN,
|
||||||
|
.category = PROP_CATEGORY_SURFACE,
|
||||||
|
}}, { "FrontSurface", {
|
||||||
|
.backingField = &frontSurface,
|
||||||
|
.type = &Data::Int::TYPE, // Replace with enum
|
||||||
|
.codec = fieldCodecOf<Data::Int, int>(),
|
||||||
|
.flags = PROP_HIDDEN,
|
||||||
|
.category = PROP_CATEGORY_SURFACE,
|
||||||
|
}}, { "BackSurface", {
|
||||||
|
.backingField = &backSurface,
|
||||||
|
.type = &Data::Int::TYPE, // Replace with enum
|
||||||
|
.codec = fieldCodecOf<Data::Int, int>(),
|
||||||
|
.flags = PROP_HIDDEN,
|
||||||
|
.category = PROP_CATEGORY_SURFACE,
|
||||||
|
}}, { "RightSurface", {
|
||||||
|
.backingField = &rightSurface,
|
||||||
|
.type = &Data::Int::TYPE, // Replace with enum
|
||||||
|
.codec = fieldCodecOf<Data::Int, int>(),
|
||||||
|
.flags = PROP_HIDDEN,
|
||||||
|
.category = PROP_CATEGORY_SURFACE,
|
||||||
|
}}, { "LeftSurface", {
|
||||||
|
.backingField = &leftSurface,
|
||||||
|
.type = &Data::Int::TYPE, // Replace with enum
|
||||||
|
.codec = fieldCodecOf<Data::Int, int>(),
|
||||||
|
.flags = PROP_HIDDEN,
|
||||||
|
.category = PROP_CATEGORY_SURFACE,
|
||||||
|
}},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,6 +147,7 @@ void MainGLWidget::handleObjectDrag(QMouseEvent* evt) {
|
||||||
draggingObject->lock()->cframe = newFrame + unsinkOffset;
|
draggingObject->lock()->cframe = newFrame + unsinkOffset;
|
||||||
|
|
||||||
gWorkspace()->SyncPartPhysics(draggingObject->lock());
|
gWorkspace()->SyncPartPhysics(draggingObject->lock());
|
||||||
|
sendPropertyUpdatedSignal(draggingObject->lock(), "Position", draggingObject->lock()->position());
|
||||||
}
|
}
|
||||||
|
|
||||||
inline glm::vec3 vec3fy(glm::vec4 vec) {
|
inline glm::vec3 vec3fy(glm::vec4 vec) {
|
||||||
|
@ -224,6 +225,8 @@ void MainGLWidget::handleLinearTransform(QMouseEvent* evt) {
|
||||||
playSound("./assets/excluded/switch.wav");
|
playSound("./assets/excluded/switch.wav");
|
||||||
|
|
||||||
gWorkspace()->SyncPartPhysics(std::dynamic_pointer_cast<Part>(editorToolHandles->adornee->lock()));
|
gWorkspace()->SyncPartPhysics(std::dynamic_pointer_cast<Part>(editorToolHandles->adornee->lock()));
|
||||||
|
sendPropertyUpdatedSignal(draggingObject->lock(), "Position", draggingObject->lock()->position());
|
||||||
|
sendPropertyUpdatedSignal(draggingObject->lock(), "Size", Data::Vector3(draggingObject->lock()->size));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also implemented based on Godot: [c7ea8614](godot/editor/plugins/canvas_item_editor_plugin.cpp#L1490)
|
// Also implemented based on Godot: [c7ea8614](godot/editor/plugins/canvas_item_editor_plugin.cpp#L1490)
|
||||||
|
@ -266,6 +269,7 @@ void MainGLWidget::handleRotationalTransform(QMouseEvent* evt) {
|
||||||
part->cframe = initialFrame * Data::CFrame::FromEulerAnglesXYZ(-angles);
|
part->cframe = initialFrame * Data::CFrame::FromEulerAnglesXYZ(-angles);
|
||||||
|
|
||||||
gWorkspace()->SyncPartPhysics(std::dynamic_pointer_cast<Part>(editorToolHandles->adornee->lock()));
|
gWorkspace()->SyncPartPhysics(std::dynamic_pointer_cast<Part>(editorToolHandles->adornee->lock()));
|
||||||
|
sendPropertyUpdatedSignal(draggingObject->lock(), "Rotation", draggingObject->lock()->cframe.ToEulerAnglesXYZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<HandleFace> MainGLWidget::raycastHandle(glm::vec3 pointDir) {
|
std::optional<HandleFace> MainGLWidget::raycastHandle(glm::vec3 pointDir) {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "panes/propertiesview.h"
|
#include "panes/propertiesview.h"
|
||||||
|
#include "common.h"
|
||||||
#include "datatypes/base.h"
|
#include "datatypes/base.h"
|
||||||
|
|
||||||
#include <QColorDialog>
|
#include <QColorDialog>
|
||||||
|
@ -6,6 +7,11 @@
|
||||||
#include <QSpinBox>
|
#include <QSpinBox>
|
||||||
#include <QStyledItemDelegate>
|
#include <QStyledItemDelegate>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
#include <QTime>
|
||||||
|
#include <chrono>
|
||||||
|
#include <functional>
|
||||||
|
#include <qnamespace.h>
|
||||||
|
#include <qtreewidget.h>
|
||||||
|
|
||||||
class PropertiesItemDelegate : public QStyledItemDelegate {
|
class PropertiesItemDelegate : public QStyledItemDelegate {
|
||||||
PropertiesView* view;
|
PropertiesView* view;
|
||||||
|
@ -231,6 +237,8 @@ PropertiesView::PropertiesView(QWidget* parent):
|
||||||
else if (item->parent())
|
else if (item->parent())
|
||||||
item->setFlags(item->flags() | Qt::ItemIsEditable);
|
item->setFlags(item->flags() | Qt::ItemIsEditable);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addPropertyUpdateListener(std::bind(&PropertiesView::onPropertyUpdated, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertiesView::~PropertiesView() {
|
PropertiesView::~PropertiesView() {
|
||||||
|
@ -361,4 +369,46 @@ void PropertiesView::rebuildCompositeProperty(QTreeWidgetItem *item, const Data:
|
||||||
item->addChild(yItem);
|
item->addChild(yItem);
|
||||||
item->addChild(zItem);
|
item->addChild(zItem);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// static auto lastUpdateTime = std::chrono::steady_clock::now();
|
||||||
|
void PropertiesView::onPropertyUpdated(InstanceRef inst, std::string property, Data::Variant newValue) {
|
||||||
|
// if (!currentInstance || currentInstance->expired() || instance != currentInstance->lock()) return;
|
||||||
|
// if (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - lastUpdateTime).count() < 1000) return;
|
||||||
|
// lastUpdateTime = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
PropertyMeta meta = inst->GetPropertyMeta(property).expect();
|
||||||
|
Data::Variant currentValue = inst->GetPropertyValue(property).expect();
|
||||||
|
|
||||||
|
if (meta.type == &Data::CFrame::TYPE) return;
|
||||||
|
|
||||||
|
for (int categoryItemIdx = 0; categoryItemIdx < topLevelItemCount(); categoryItemIdx++) {
|
||||||
|
QTreeWidgetItem* categoryItem = topLevelItem(categoryItemIdx);
|
||||||
|
for (int itemIdx = 0; itemIdx < categoryItem->childCount(); itemIdx++) {
|
||||||
|
QTreeWidgetItem* item = categoryItem->child(itemIdx);
|
||||||
|
|
||||||
|
if (item->data(0, Qt::DisplayRole).toString().toStdString() != property) continue;
|
||||||
|
|
||||||
|
if (meta.type == &Data::Bool::TYPE) {
|
||||||
|
item->setCheckState(1, (bool)currentValue.get<Data::Bool>() ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
|
||||||
|
} else if (meta.type == &Data::Color3::TYPE) {
|
||||||
|
Data::Color3 color = currentValue.get<Data::Color3>();
|
||||||
|
item->setData(1, Qt::DecorationRole, QColor::fromRgbF(color.R(), color.G(), color.B()));
|
||||||
|
item->setData(1, Qt::DisplayRole, QString::fromStdString(currentValue.ToString()));
|
||||||
|
} else if (meta.type == &Data::Vector3::TYPE) {
|
||||||
|
Data::Vector3 vector = currentValue.get<Data::Vector3>();
|
||||||
|
item->setData(1, Qt::DisplayRole, QString::fromStdString(currentValue.ToString()));
|
||||||
|
} else {
|
||||||
|
item->setData(1, Qt::DisplayRole, QString::fromStdString(currentValue.ToString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (meta.type != &Data::Color3::TYPE && (!meta.type->fromString || meta.flags & PROP_READONLY)) {
|
||||||
|
item->setDisabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
rebuildCompositeProperty(item, meta.type, currentValue);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -5,8 +5,8 @@
|
||||||
#include "objects/base/instance.h"
|
#include "objects/base/instance.h"
|
||||||
|
|
||||||
class Ui_MainWindow;
|
class Ui_MainWindow;
|
||||||
|
|
||||||
class PropertiesItemDelegate;
|
class PropertiesItemDelegate;
|
||||||
|
namespace Data { class Variant; };
|
||||||
|
|
||||||
class PropertiesView : public QTreeWidget {
|
class PropertiesView : public QTreeWidget {
|
||||||
Q_DECLARE_PRIVATE(QTreeView)
|
Q_DECLARE_PRIVATE(QTreeView)
|
||||||
|
@ -15,6 +15,7 @@ class PropertiesView : public QTreeWidget {
|
||||||
void propertyChanged(QTreeWidgetItem *item, int column);
|
void propertyChanged(QTreeWidgetItem *item, int column);
|
||||||
void activateProperty(QTreeWidgetItem *item, int column);
|
void activateProperty(QTreeWidgetItem *item, int column);
|
||||||
void rebuildCompositeProperty(QTreeWidgetItem *item, const Data::TypeInfo*, Data::Variant);
|
void rebuildCompositeProperty(QTreeWidgetItem *item, const Data::TypeInfo*, Data::Variant);
|
||||||
|
void onPropertyUpdated(InstanceRef instance, std::string property, Data::Variant newValue);
|
||||||
|
|
||||||
friend PropertiesItemDelegate;
|
friend PropertiesItemDelegate;
|
||||||
protected:
|
protected:
|
||||||
|
|
Loading…
Add table
Reference in a new issue