fix(editor): properties pane not updating automatically

This commit is contained in:
maelstrom 2025-04-15 11:21:56 +02:00
parent ff5eb969d9
commit bea6f50f13
7 changed files with 117 additions and 6 deletions

View file

@ -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);
} }

View file

@ -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);

View file

@ -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 {};
} }

View file

@ -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,
}},
} }
}); });
} }

View file

@ -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) {

View file

@ -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;
}
}
} }

View file

@ -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: