diff --git a/core/src/common.cpp b/core/src/common.cpp index f455225..ca4c815 100644 --- a/core/src/common.cpp +++ b/core/src/common.cpp @@ -12,10 +12,11 @@ std::shared_ptr editorToolHandles = Handles::New(); std::vector currentSelection; -std::vector selectionUpdateHandlers; +std::vector selectionUpdateListeners; +std::vector propertyUpdatelisteners; void setSelection(std::vector newSelection, bool fromExplorer) { - for (SelectionUpdateHandler handler : selectionUpdateHandlers) { + for (SelectionUpdateHandler handler : selectionUpdateListeners) { handler(currentSelection, newSelection, fromExplorer); } @@ -27,5 +28,15 @@ const std::vector getSelection() { } 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); } \ No newline at end of file diff --git a/core/src/common.h b/core/src/common.h index 4a5f052..b592786 100644 --- a/core/src/common.h +++ b/core/src/common.h @@ -11,6 +11,7 @@ class Instance; typedef std::function oldParent, std::optional newParent)> HierarchyPreUpdateHandler; typedef std::function oldParent, std::optional newParent)> HierarchyPostUpdateHandler; typedef std::function oldSelection, std::vector newSelection, bool fromExplorer)> SelectionUpdateHandler; +typedef std::function PropertyUpdateHandler; // TEMPORARY COMMON DATA FOR VARIOUS INTERNAL COMPONENTS @@ -23,4 +24,7 @@ extern std::shared_ptr editorToolHandles; void setSelection(std::vector newSelection, bool fromExplorer = false); const std::vector getSelection(); -void addSelectionListener(SelectionUpdateHandler handler); \ No newline at end of file +void addSelectionListener(SelectionUpdateHandler handler); + +void sendPropertyUpdatedSignal(InstanceRef instance, std::string property, Data::Variant newValue); +void addPropertyUpdateListener(PropertyUpdateHandler handler); \ No newline at end of file diff --git a/core/src/objects/base/instance.cpp b/core/src/objects/base/instance.cpp index 8f9272d..5febc99 100644 --- a/core/src/objects/base/instance.cpp +++ b/core/src/objects/base/instance.cpp @@ -174,6 +174,7 @@ fallible Instance::SetPropertyValue(std: meta.codec.write(value, meta.backingField); if (meta.updateCallback) meta.updateCallback.value()(name); + sendPropertyUpdatedSignal(shared_from_this(), name, value); return {}; } diff --git a/core/src/objects/part.cpp b/core/src/objects/part.cpp index bb74e87..d47aa39 100644 --- a/core/src/objects/part.cpp +++ b/core/src/objects/part.cpp @@ -111,7 +111,47 @@ Part::Part(PartConstructParams params): Instance(&TYPE), cframe(Data::CFrame(par .codec = fieldCodecOf(), .flags = PROP_UNIT_FLOAT, .category = PROP_CATEGORY_APPEARENCE, - }} + }}, + + // Surfaces + + { "TopSurface", { + .backingField = &topSurface, + .type = &Data::Int::TYPE, // Replace with enum + .codec = fieldCodecOf(), + .flags = PROP_HIDDEN, + .category = PROP_CATEGORY_SURFACE, + }}, { "BottomSurface", { + .backingField = &bottomSurface, + .type = &Data::Int::TYPE, // Replace with enum + .codec = fieldCodecOf(), + .flags = PROP_HIDDEN, + .category = PROP_CATEGORY_SURFACE, + }}, { "FrontSurface", { + .backingField = &frontSurface, + .type = &Data::Int::TYPE, // Replace with enum + .codec = fieldCodecOf(), + .flags = PROP_HIDDEN, + .category = PROP_CATEGORY_SURFACE, + }}, { "BackSurface", { + .backingField = &backSurface, + .type = &Data::Int::TYPE, // Replace with enum + .codec = fieldCodecOf(), + .flags = PROP_HIDDEN, + .category = PROP_CATEGORY_SURFACE, + }}, { "RightSurface", { + .backingField = &rightSurface, + .type = &Data::Int::TYPE, // Replace with enum + .codec = fieldCodecOf(), + .flags = PROP_HIDDEN, + .category = PROP_CATEGORY_SURFACE, + }}, { "LeftSurface", { + .backingField = &leftSurface, + .type = &Data::Int::TYPE, // Replace with enum + .codec = fieldCodecOf(), + .flags = PROP_HIDDEN, + .category = PROP_CATEGORY_SURFACE, + }}, } }); } diff --git a/editor/mainglwidget.cpp b/editor/mainglwidget.cpp index 8bdf30a..ddb0c73 100644 --- a/editor/mainglwidget.cpp +++ b/editor/mainglwidget.cpp @@ -147,6 +147,7 @@ void MainGLWidget::handleObjectDrag(QMouseEvent* evt) { draggingObject->lock()->cframe = newFrame + unsinkOffset; gWorkspace()->SyncPartPhysics(draggingObject->lock()); + sendPropertyUpdatedSignal(draggingObject->lock(), "Position", draggingObject->lock()->position()); } inline glm::vec3 vec3fy(glm::vec4 vec) { @@ -224,6 +225,8 @@ void MainGLWidget::handleLinearTransform(QMouseEvent* evt) { playSound("./assets/excluded/switch.wav"); gWorkspace()->SyncPartPhysics(std::dynamic_pointer_cast(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) @@ -266,6 +269,7 @@ void MainGLWidget::handleRotationalTransform(QMouseEvent* evt) { part->cframe = initialFrame * Data::CFrame::FromEulerAnglesXYZ(-angles); gWorkspace()->SyncPartPhysics(std::dynamic_pointer_cast(editorToolHandles->adornee->lock())); + sendPropertyUpdatedSignal(draggingObject->lock(), "Rotation", draggingObject->lock()->cframe.ToEulerAnglesXYZ()); } std::optional MainGLWidget::raycastHandle(glm::vec3 pointDir) { diff --git a/editor/panes/propertiesview.cpp b/editor/panes/propertiesview.cpp index c4dc095..57fa3e1 100644 --- a/editor/panes/propertiesview.cpp +++ b/editor/panes/propertiesview.cpp @@ -1,4 +1,5 @@ #include "panes/propertiesview.h" +#include "common.h" #include "datatypes/base.h" #include @@ -6,6 +7,11 @@ #include #include #include +#include +#include +#include +#include +#include class PropertiesItemDelegate : public QStyledItemDelegate { PropertiesView* view; @@ -231,6 +237,8 @@ PropertiesView::PropertiesView(QWidget* parent): else if (item->parent()) item->setFlags(item->flags() | Qt::ItemIsEditable); }); + + addPropertyUpdateListener(std::bind(&PropertiesView::onPropertyUpdated, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); } PropertiesView::~PropertiesView() { @@ -361,4 +369,46 @@ void PropertiesView::rebuildCompositeProperty(QTreeWidgetItem *item, const Data: item->addChild(yItem); 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::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() ? Qt::CheckState::Checked : Qt::CheckState::Unchecked); + } else if (meta.type == &Data::Color3::TYPE) { + Data::Color3 color = currentValue.get(); + 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(); + 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; + } + } } \ No newline at end of file diff --git a/editor/panes/propertiesview.h b/editor/panes/propertiesview.h index 1d5a509..6460b1d 100644 --- a/editor/panes/propertiesview.h +++ b/editor/panes/propertiesview.h @@ -5,8 +5,8 @@ #include "objects/base/instance.h" class Ui_MainWindow; - class PropertiesItemDelegate; +namespace Data { class Variant; }; class PropertiesView : public QTreeWidget { Q_DECLARE_PRIVATE(QTreeView) @@ -15,6 +15,7 @@ class PropertiesView : public QTreeWidget { void propertyChanged(QTreeWidgetItem *item, int column); void activateProperty(QTreeWidgetItem *item, int column); void rebuildCompositeProperty(QTreeWidgetItem *item, const Data::TypeInfo*, Data::Variant); + void onPropertyUpdated(InstanceRef instance, std::string property, Data::Variant newValue); friend PropertiesItemDelegate; protected: