diff --git a/PropertyEditor/resources.qrc b/PropertyEditor/resources.qrc
index 55b790d..5f86164 100644
--- a/PropertyEditor/resources.qrc
+++ b/PropertyEditor/resources.qrc
@@ -23,5 +23,6 @@
resources/Qml/ValueEditor/FileSelector.qml
resources/Qml/ValueEditor/LineTextBox.qml
resources/Qml/ValueEditor/MultiLineTextBox.qml
+ resources/Qml/ValueEditor/FontBox.qml
diff --git a/PropertyEditor/resources/Qml/ValueEditor/FontBox.qml b/PropertyEditor/resources/Qml/ValueEditor/FontBox.qml
new file mode 100644
index 0000000..2218474
--- /dev/null
+++ b/PropertyEditor/resources/Qml/ValueEditor/FontBox.qml
@@ -0,0 +1,355 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import ColorPalette
+
+Item {
+ id: control
+ property font value
+ property var fontFamilies: []
+ property bool expanded: false
+ property bool internalUpdate: false
+ property int sizeValue: control.value.pointSize
+
+ readonly property int kRowHeight: 25
+ readonly property int kEditRowCount: 5
+ readonly property int kExpandedHeight: kRowHeight + kEditRowCount * kRowHeight + (kEditRowCount - 1) * 2
+
+ implicitHeight: height
+ implicitWidth: 100
+ height: expanded ? kExpandedHeight : kRowHeight
+
+ signal asValueChanged(value: var)
+
+ function setValue(newValue: var) {
+ if (internalUpdate) return
+ if (newValue !== value) {
+ internalUpdate = true
+ value = newValue
+ sizeValue = value.pointSize
+ internalUpdate = false
+ asValueChanged(value)
+ }
+ }
+
+ function applySizeEdit() {
+ if (control.internalUpdate) return
+ control.internalUpdate = true
+ control.value.pointSize = sizeValue
+ control.internalUpdate = false
+ control.asValueChanged(control.value)
+ }
+
+ // Header row
+ Rectangle {
+ id: headerRow
+ height: kRowHeight
+ width: parent.width
+ color: ColorPalette.theme.textBoxBackground
+ border.color: expanded ? ColorPalette.theme.comboBoxItemBackgroundHover : ColorPalette.theme.textBoxBackground
+ border.width: 1
+
+ Text {
+ anchors.fill: parent
+ anchors.leftMargin: 5
+ anchors.rightMargin: 20
+ verticalAlignment: Text.AlignVCenter
+ text: value.family + ", " + value.pointSize + "pt"
+ color: ColorPalette.theme.textPrimary
+ elide: Text.ElideRight
+ }
+
+ Text {
+ anchors.right: parent.right
+ anchors.rightMargin: 5
+ anchors.verticalCenter: parent.verticalCenter
+ text: expanded ? "▲" : "▼"
+ color: ColorPalette.theme.labelPrimary
+ font.pixelSize: 10
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: control.expanded = !control.expanded
+ }
+ }
+
+ // Edit pane — 5 rows
+ Column {
+ id: editPane
+ visible: control.expanded
+ anchors.top: headerRow.bottom
+ anchors.left: parent.left
+ anchors.right: parent.right
+ spacing: 2
+
+ // Row 1: Font Family
+ ComboBox {
+ id: familyCombo
+ width: parent.width
+ height: kRowHeight
+ model: control.fontFamilies.length > 0 ? control.fontFamilies : []
+ currentIndex: -1
+ clip: true
+
+ background: Rectangle {
+ color: ColorPalette.theme.comboBoxBackground
+ border.color: familyCombo.activeFocus ? ColorPalette.theme.comboBoxItemBackgroundHover : ColorPalette.theme.rowBorder
+ border.width: 1
+ }
+
+ contentItem: Text {
+ text: familyCombo.currentIndex >= 0 ? familyCombo.currentText : value.family
+ color: ColorPalette.theme.textPrimary
+ verticalAlignment: Text.AlignVCenter
+ leftPadding: 6
+ elide: Text.ElideRight
+ }
+
+ popup: Popup {
+ y: familyCombo.height
+ width: familyCombo.width
+ implicitHeight: Math.min(contentItem.contentHeight, 200)
+ padding: 1
+
+ contentItem: ListView {
+ clip: true
+ implicitHeight: Math.min(contentHeight, 200)
+ model: familyCombo.delegateModel
+ currentIndex: familyCombo.highlightedIndex
+ ScrollIndicator.vertical: ScrollIndicator {}
+ }
+
+ background: Rectangle {
+ color: ColorPalette.theme.comboBoxItemBackground
+ border.color: ColorPalette.theme.rowBorder
+ }
+ }
+
+ // Sync ComboBox selection when fontFamilies arrives (C++ sets it after value)
+ function syncComboIndex() {
+ if (control.fontFamilies.length > 0 && value.family) {
+ var idx = find(value.family)
+ if (idx >= 0) currentIndex = idx
+ }
+ }
+
+ Component.onCompleted: syncComboIndex()
+ onModelChanged: syncComboIndex()
+
+ onActivated: control.applyEdit()
+ }
+
+ // Row 2: Font Size — left spacer aligns "Size" with checkbox text below
+ RowLayout {
+ width: parent.width
+ height: kRowHeight
+ spacing: 0
+
+ // Spacer to align "Size" with checkbox text (indicator 14 + spacing 6 = 20)
+ Item { Layout.preferredWidth: 20 }
+
+ Text {
+ text: "Size"
+ color: ColorPalette.theme.labelPrimary
+ verticalAlignment: Text.AlignVCenter
+ Layout.preferredWidth: 28
+ Layout.alignment: Qt.AlignVCenter
+ }
+
+ // Decrease button
+ Rectangle {
+ implicitWidth: 22; implicitHeight: kRowHeight
+ Layout.alignment: Qt.AlignVCenter
+ color: sizeDownMa.pressed ? ColorPalette.theme.comboBoxItemBackgroundHover : "transparent"
+ border.color: ColorPalette.theme.rowBorder
+ border.width: 0.5
+ Text {
+ anchors.centerIn: parent
+ text: "−"
+ font.pixelSize: 12
+ color: ColorPalette.theme.labelPrimary
+ }
+ MouseArea {
+ id: sizeDownMa
+ anchors.fill: parent
+ onClicked: { if (sizeValue > 1) sizeValue--; applySizeEdit() }
+ }
+ }
+
+ // Number input
+ Rectangle {
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignVCenter
+ implicitHeight: kRowHeight
+ color: ColorPalette.theme.comboBoxBackground
+ border.color: sizeInput.activeFocus ? ColorPalette.theme.comboBoxItemBackgroundHover : ColorPalette.theme.rowBorder
+ border.width: 1
+ TextInput {
+ id: sizeInput
+ anchors.fill: parent
+ anchors.leftMargin: 4
+ anchors.rightMargin: 4
+ color: ColorPalette.theme.textPrimary
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+ validator: IntValidator { bottom: 1; top: 512 }
+ inputMethodHints: Qt.ImhDigitsOnly
+ text: sizeValue
+ onEditingFinished: { sizeValue = parseInt(text) || 1; applySizeEdit() }
+ }
+ }
+
+ // Increase button
+ Rectangle {
+ implicitWidth: 22; implicitHeight: kRowHeight
+ Layout.alignment: Qt.AlignVCenter
+ color: sizeUpMa.pressed ? ColorPalette.theme.comboBoxItemBackgroundHover : "transparent"
+ border.color: ColorPalette.theme.rowBorder
+ border.width: 0.5
+ Text {
+ anchors.centerIn: parent
+ text: "+"
+ font.pixelSize: 12
+ color: ColorPalette.theme.labelPrimary
+ }
+ MouseArea {
+ id: sizeUpMa
+ anchors.fill: parent
+ onClicked: { if (sizeValue < 512) sizeValue++; applySizeEdit() }
+ }
+ }
+
+ Text {
+ text: "pt"
+ color: ColorPalette.theme.labelPrimary
+ verticalAlignment: Text.AlignVCenter
+ Layout.preferredWidth: 20
+ Layout.alignment: Qt.AlignVCenter
+ Layout.leftMargin: 2
+ }
+ }
+
+ // Row 3: Bold
+ CheckBox {
+ id: boldCheck
+ text: "Bold"
+ height: kRowHeight
+ checked: control.value.bold
+ leftPadding: 0
+ spacing: 6
+
+ indicator: Rectangle {
+ implicitWidth: 14; implicitHeight: 14
+ x: boldCheck.leftPadding
+ y: (boldCheck.height - height) / 2
+ color: boldCheck.checked ? ColorPalette.theme.comboBoxItemBackgroundHover : "transparent"
+ border.color: boldCheck.checked ? ColorPalette.theme.comboBoxItemBackgroundHover : ColorPalette.theme.rowBorder
+ border.width: 1.5
+ radius: 2
+ Rectangle {
+ visible: boldCheck.checked
+ anchors.centerIn: parent
+ width: 8; height: 8
+ color: ColorPalette.theme.labelPrimary
+ radius: 1
+ }
+ }
+
+ contentItem: Text {
+ text: boldCheck.text
+ color: ColorPalette.theme.textPrimary
+ verticalAlignment: Text.AlignVCenter
+ leftPadding: boldCheck.indicator.width + boldCheck.spacing
+ }
+
+ onToggled: control.applyEdit()
+ }
+
+ // Row 4: Italic
+ CheckBox {
+ id: italicCheck
+ text: "Italic"
+ height: kRowHeight
+ checked: control.value.italic
+ leftPadding: 0
+ spacing: 6
+
+ indicator: Rectangle {
+ implicitWidth: 14; implicitHeight: 14
+ x: italicCheck.leftPadding
+ y: (italicCheck.height - height) / 2
+ color: italicCheck.checked ? ColorPalette.theme.comboBoxItemBackgroundHover : "transparent"
+ border.color: italicCheck.checked ? ColorPalette.theme.comboBoxItemBackgroundHover : ColorPalette.theme.rowBorder
+ border.width: 1.5
+ radius: 2
+ Rectangle {
+ visible: italicCheck.checked
+ anchors.centerIn: parent
+ width: 8; height: 8
+ color: ColorPalette.theme.labelPrimary
+ radius: 1
+ }
+ }
+
+ contentItem: Text {
+ text: italicCheck.text
+ color: ColorPalette.theme.textPrimary
+ verticalAlignment: Text.AlignVCenter
+ leftPadding: italicCheck.indicator.width + italicCheck.spacing
+ }
+
+ onToggled: control.applyEdit()
+ }
+
+ // Row 5: Underline
+ CheckBox {
+ id: underlineCheck
+ text: "Underline"
+ height: kRowHeight
+ checked: control.value.underline
+ leftPadding: 0
+ spacing: 6
+
+ indicator: Rectangle {
+ implicitWidth: 14; implicitHeight: 14
+ x: underlineCheck.leftPadding
+ y: (underlineCheck.height - height) / 2
+ color: underlineCheck.checked ? ColorPalette.theme.comboBoxItemBackgroundHover : "transparent"
+ border.color: underlineCheck.checked ? ColorPalette.theme.comboBoxItemBackgroundHover : ColorPalette.theme.rowBorder
+ border.width: 1.5
+ radius: 2
+ Rectangle {
+ visible: underlineCheck.checked
+ anchors.centerIn: parent
+ width: 8; height: 8
+ color: ColorPalette.theme.labelPrimary
+ radius: 1
+ }
+ }
+
+ contentItem: Text {
+ text: underlineCheck.text
+ color: ColorPalette.theme.textPrimary
+ verticalAlignment: Text.AlignVCenter
+ leftPadding: underlineCheck.indicator.width + underlineCheck.spacing
+ }
+
+ onToggled: control.applyEdit()
+ }
+ }
+
+ function applyEdit() {
+ if (control.internalUpdate) return
+ control.internalUpdate = true
+ control.value = Qt.font({
+ family: familyCombo.currentText || value.family,
+ pointSize: sizeValue,
+ bold: boldCheck.checked,
+ italic: italicCheck.checked,
+ underline: underlineCheck.checked
+ })
+ control.internalUpdate = false
+ control.asValueChanged(control.value)
+ }
+}
diff --git a/PropertyEditor/source/src/Customization/PropertyTypeCustomization_Font.cpp b/PropertyEditor/source/src/Customization/PropertyTypeCustomization_Font.cpp
new file mode 100644
index 0000000..02cb1db
--- /dev/null
+++ b/PropertyEditor/source/src/Customization/PropertyTypeCustomization_Font.cpp
@@ -0,0 +1,23 @@
+#include "PropertyTypeCustomization_Font.h"
+#include "QQuickDetailsViewLayoutBuilder.h"
+#include "QQuickDetailsViewMananger.h"
+#include "QPropertyHandle.h"
+
+void PropertyTypeCustomization_Font::customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder)
+{
+ // Same as makePropertyRow but uses the manager's registered type editor
+ // instead of the Object impl's createValueEditor (which returns nullptr).
+ auto slotPair = inBuilder->makeNameValueSlot();
+
+ QQuickItem* nameEditor = inPropertyHandle->setupNameEditor(slotPair.first);
+ QQuickItem* valueEditor = QQuickDetailsViewManager::Get()->createValueEditor(inPropertyHandle, slotPair.second);
+
+ QQmlEngine* engine = qmlEngine(inBuilder->rootItem());
+ QQmlContext* context = qmlContext(inBuilder->rootItem());
+ context->parentContext()->setContextProperty("heightProxy", valueEditor ? valueEditor : nameEditor);
+}
+
+void PropertyTypeCustomization_Font::customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder)
+{
+ // QFont has no children — don't expand as tree
+}
diff --git a/PropertyEditor/source/src/Customization/PropertyTypeCustomization_Font.h b/PropertyEditor/source/src/Customization/PropertyTypeCustomization_Font.h
new file mode 100644
index 0000000..bbe86db
--- /dev/null
+++ b/PropertyEditor/source/src/Customization/PropertyTypeCustomization_Font.h
@@ -0,0 +1,12 @@
+#ifndef PropertyTypeCustomization_Font_h__
+#define PropertyTypeCustomization_Font_h__
+
+#include "IPropertyTypeCustomization.h"
+
+class PropertyTypeCustomization_Font : public IPropertyTypeCustomization {
+protected:
+ virtual void customizeHeaderRow(QPropertyHandle* inPropertyHandle, QQuickDetailsViewRowBuilder* inBuilder) override;
+ virtual void customizeChildren(QPropertyHandle* inPropertyHandle, QQuickDetailsViewLayoutBuilder* inBuilder) override;
+};
+
+#endif // PropertyTypeCustomization_Font_h__
diff --git a/PropertyEditor/source/src/PropertyHandleImpl/IPropertyHandleImpl.cpp b/PropertyEditor/source/src/PropertyHandleImpl/IPropertyHandleImpl.cpp
index c95c6ff..0d4acec 100644
--- a/PropertyEditor/source/src/PropertyHandleImpl/IPropertyHandleImpl.cpp
+++ b/PropertyEditor/source/src/PropertyHandleImpl/IPropertyHandleImpl.cpp
@@ -18,7 +18,7 @@ QQuickItem* IPropertyHandleImpl::createNameEditor(QQuickItem* inParent)
Item{
implicitHeight: 25
width: parent.width
- anchors.verticalCenter: parent.verticalCenter
+ anchors.top: parent.top
Text {
anchors.fill: parent
verticalAlignment: Text.AlignVCenter
diff --git a/PropertyEditor/source/src/QQuickDetailsViewBasicTypeEditor.cpp b/PropertyEditor/source/src/QQuickDetailsViewBasicTypeEditor.cpp
index 4933e8a..ef36aaf 100644
--- a/PropertyEditor/source/src/QQuickDetailsViewBasicTypeEditor.cpp
+++ b/PropertyEditor/source/src/QQuickDetailsViewBasicTypeEditor.cpp
@@ -3,6 +3,8 @@
#include "QQuickFunctionLibrary.h"
#include
#include
+#include
+#include
#include
#define REGINTER_NUMBER_EDITOR_CREATOR(TypeName, DefaultPrecision) \
@@ -235,4 +237,33 @@ void QQuickDetailsViewManager::RegisterBasicTypeEditor() {
connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant)));
return valueEditor;
});
+
+ registerTypeEditor(QMetaType::fromType(), [](QPropertyHandle* handle, QQuickItem* parent)->QQuickItem* {
+ QQmlEngine* engine = qmlEngine(parent);
+ QQmlContext* context = qmlContext(parent);
+ QQmlComponent comp(engine);
+ comp.setData(R"(
+ import QtQuick;
+ import QtQuick.Controls;
+ import "qrc:/resources/Qml/ValueEditor"
+ FontBox{
+ anchors.verticalCenter: parent.verticalCenter
+ width: parent.width
+ }
+ )", QUrl());
+ QVariantMap initialProperties;
+ initialProperties["parent"] = QVariant::fromValue(parent);
+ auto valueEditor = qobject_cast(comp.createWithInitialProperties(initialProperties, context));
+ if (!comp.errors().isEmpty()) {
+ qDebug() << comp.errorString();
+ }
+ valueEditor->setParentItem(parent);
+ valueEditor->setProperty("value", handle->getVar());
+
+ valueEditor->setProperty("fontFamilies", QFontDatabase::families());
+
+ connect(valueEditor, SIGNAL(asValueChanged(QVariant)), handle, SLOT(setVar(QVariant)));
+ connect(handle, SIGNAL(asRequestRollback(QVariant)), valueEditor, SLOT(setValue(QVariant)));
+ return valueEditor;
+ });
}
diff --git a/PropertyEditor/source/src/QQuickDetailsViewLayoutBuilder.cpp b/PropertyEditor/source/src/QQuickDetailsViewLayoutBuilder.cpp
index 2bf2561..59ac8e6 100644
--- a/PropertyEditor/source/src/QQuickDetailsViewLayoutBuilder.cpp
+++ b/PropertyEditor/source/src/QQuickDetailsViewLayoutBuilder.cpp
@@ -50,16 +50,16 @@ QPair QQuickDetailsViewRowBuilder::makeNameValueSlot()
}
Item{
id: nameEditorContent
- height: parent.height
+ implicitHeight: 25
+ anchors.top: parent.top
anchors.left: parent.left
anchors.leftMargin: padding + (detailsDelegate.isTreeNode ? (detailsDelegate.depth + 1) * detailsDelegate.indent : 0)
anchors.right: splitter.left
- anchors.verticalCenter: parent.verticalCenter
}
Item{
id: valueEditorContent
implicitHeight: 25
- anchors.left: splitter.left
+ anchors.left: splitter.left
anchors.leftMargin: 10
anchors.rightMargin: 10
anchors.right: parent.right
diff --git a/PropertyEditor/source/src/QQuickDetailsViewMananger.cpp b/PropertyEditor/source/src/QQuickDetailsViewMananger.cpp
index 91e7779..91cb3c7 100644
--- a/PropertyEditor/source/src/QQuickDetailsViewMananger.cpp
+++ b/PropertyEditor/source/src/QQuickDetailsViewMananger.cpp
@@ -7,8 +7,10 @@
#include "Customization/PropertyTypeCustomization_Associative.h"
#include "Customization/PropertyTypeCustomization_ObjectDefault.h"
#include "Customization/PropertyTypeCustomization_Matrix4x4.h"
+#include "Customization/PropertyTypeCustomization_Font.h"
#include
#include
+#include
QQuickDetailsViewManager* QQuickDetailsViewManager::Get()
{
@@ -87,6 +89,10 @@ QSharedPointer QQuickDetailsViewManager::getCustomPr
return QSharedPointer::create();
}
else if (inHandle->getPropertyType() == QPropertyHandle::Object) {
+ // Check metaType-level customizations first (for Q_GADGET types like QFont)
+ if (auto it = mMetaTypeCustomizationMap.find(inHandle->getType()); it != mMetaTypeCustomizationMap.end()) {
+ return it.value()();
+ }
const QMetaObject* metaObject = inHandle->metaObject();
for (const auto& It : mClassCustomizationMap.asKeyValueRange()) {
if (It.first == metaObject) {
@@ -134,5 +140,9 @@ QQuickDetailsViewManager::QQuickDetailsViewManager()
RegisterBasicTypeEditor();
registerPropertyTypeCustomization();
+
+ mMetaTypeCustomizationMap.insert(QMetaType::fromType(), []() {
+ return QSharedPointer::create();
+ });
}