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