From daf23602fe6e298b5a5dff5a65c2566e03e17eb8 Mon Sep 17 00:00:00 2001 From: Ali Kianian Date: Mon, 27 Nov 2023 16:04:23 +0200 Subject: [PATCH] QmlDesigner: Use ColorEditor as view delegate in CollectionEditor Use StudioControls.ColorEditor as a view delegate for the CollectionDetailsView Task-number: QDS-11114 Change-Id: Ic91d734c4fc62ddb51c4db7029714d409d51b732 Reviewed-by: Miikka Heikkinen Reviewed-by: Qt CI Patch Build Bot --- .../CollectionDetailsEditDelegate.qml | 229 ++++++-------- .../CollectionDetailsToolbar.qml | 1 - .../CollectionDetailsView.qml | 67 +++-- .../ColorViewDelegate.qml | 282 ++++++++++++++++++ .../imports/HelperWidgets/ColorEditor.qml | 1 + .../HelperWidgets/ColorEditorPopup.qml | 3 - .../imports/HelperWidgets/qmldir | 1 + .../collectioneditor/collectiondetails.cpp | 24 +- .../collectiondetailsmodel.cpp | 7 +- 9 files changed, 448 insertions(+), 167 deletions(-) create mode 100644 share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml index 44058419be8..2ed209eaf22 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml @@ -3,7 +3,6 @@ import QtQuick import CollectionDetails 1.0 as CollectionDetails -import HelperWidgets 2.0 as HelperWidgets import StudioControls 1.0 as StudioControls import StudioHelpers as StudioHelpers import StudioTheme 1.0 as StudioTheme @@ -11,119 +10,105 @@ import QtQuick.Templates as T Item { id: root + required property var columnType - property var __modifier : textEditor - property bool __changesAccepted: true - TableView.onCommit: { - if (root.__changesAccepted) - edit = __modifier.editor.editValue - } - - Component.onCompleted: { - __changesAccepted = true - if (edit && edit !== "") - root.__modifier.editor.editValue = edit + if (editorLoader.changesAccepted && edit !== editorLoader.acceptedValue) + edit = editorLoader.acceptedValue } onActiveFocusChanged: { - if (root.activeFocus) - root.__modifier.editor.forceActiveFocus() - } - - Connections { - id: modifierFocusConnection - - target: root.__modifier.editor - - function onActiveFocusChanged() { - if (!modifierFocusConnection.target.activeFocus) - root.TableView.commit() - } - } - - EditorPopup { - id: textEditor - - editor: textField - - StudioControls.TextField { - id: textField - - property alias editValue: textField.text - - actionIndicator.visible: false - translationIndicatorVisible: false - - onRejected: root.__changesAccepted = false - } - } - - EditorPopup { - id: numberEditor - - editor: numberField - - StudioControls.RealSpinBox { - id: numberField - - property alias editValue: numberField.realValue - - actionIndicator.visible: false - realFrom: -9e9 - realTo: 9e9 - realStepSize: 1.0 - decimals: 6 - } - } - - EditorPopup { - id: boolEditor - - editor: boolField - - StudioControls.CheckBox { - id: boolField - - property alias editValue: boolField.checked - - actionIndicatorVisible: false - } - } - - EditorPopup { - id: colorEditor - - editor: colorPicker - - implicitHeight: colorPicker.height + topPadding + bottomPadding - implicitWidth: colorPicker.width + leftPadding + rightPadding - padding: 8 - - StudioHelpers.ColorBackend { - id: colorBackend + if (root.activeFocus && !editorLoader.triggered && editorLoader.item) { + editorLoader.triggered = true + editorLoader.item.open() } - StudioControls.ColorEditorPopup { - id: colorPicker + // active focus should be checked again, because it might be affected by editorLoader.item + if (root.activeFocus && editorLoader.editor) + editorLoader.editor.forceActiveFocus() + } - property alias editValue: colorBackend.color - color: colorBackend.color + Loader { + id: editorLoader - width: 200 + active: true - Keys.onEnterPressed: colorPicker.focus = false + property var editor: editorLoader.item ? editorLoader.item.editor : null + property var editValue: editorLoader.editor ? editorLoader.editor.editValue : null + property var acceptedValue: null + property bool changesAccepted: true + property bool triggered: false - onActivateColor: function(color) { - colorBackend.activateColor(color) + Connections { + id: modifierFocusConnection + + target: editorLoader.editor + enabled: editorLoader.item !== undefined + + function onActiveFocusChanged() { + if (!modifierFocusConnection.target.activeFocus) { + editorLoader.acceptedValue = editorLoader.editValue + root.TableView.commit() + } } } - background: Rectangle { - color: StudioTheme.Values.themeControlBackgroundInteraction - border.color: StudioTheme.Values.themeInteraction - border.width: StudioTheme.Values.border + Component { + id: textEditor + + EditorPopup { + editor: textField + + StudioControls.TextField { + id: textField + + property alias editValue: textField.text + + actionIndicator.visible: false + translationIndicatorVisible: false + + onRejected: editorLoader.changesAccepted = false + } + } + } + + Component { + id: numberEditor + + EditorPopup { + + editor: numberField + + StudioControls.RealSpinBox { + id: numberField + + property alias editValue: numberField.realValue + + actionIndicator.visible: false + realFrom: -9e9 + realTo: 9e9 + realStepSize: 1.0 + decimals: 6 + } + } + } + + Component { + id: boolEditor + + EditorPopup { + + editor: boolField + + StudioControls.CheckBox { + id: boolField + + property alias editValue: boolField.checked + + actionIndicatorVisible: false + } + } } } @@ -135,7 +120,7 @@ Item { implicitHeight: contentHeight implicitWidth: contentWidth - enabled: visible + focus: true visible: false Connections { @@ -144,6 +129,8 @@ Item { function onActiveFocusChanged() { if (!editorPopup.editor.activeFocus) editorPopup.close() + else if (edit) + editorPopup.editor.editValue = edit } } @@ -151,7 +138,7 @@ Item { target: editorPopup.editor.Keys function onEscapePressed() { - root.__changesAccepted = false + editorLoader.changesAccepted = false editorPopup.close() } } @@ -165,14 +152,8 @@ Item { && columnType !== CollectionDetails.DataType.Number PropertyChanges { - target: root - __modifier: textEditor - } - - PropertyChanges { - target: textEditor - visible: true - focus: true + target: editorLoader + sourceComponent: textEditor } }, State { @@ -180,14 +161,8 @@ Item { when: columnType === CollectionDetails.DataType.Number PropertyChanges { - target: root - __modifier: numberEditor - } - - PropertyChanges { - target: numberEditor - visible: true - focus: true + target: editorLoader + sourceComponent: numberEditor } }, State { @@ -195,14 +170,8 @@ Item { when: columnType === CollectionDetails.DataType.Boolean PropertyChanges { - target: root - __modifier: boolEditor - } - - PropertyChanges { - target: boolEditor - visible: true - focus: true + target: editorLoader + sourceComponent: boolEditor } }, State { @@ -210,14 +179,8 @@ Item { when: columnType === CollectionDetails.DataType.Color PropertyChanges { - target: root - __modifier: colorEditor - } - - PropertyChanges { - target: colorEditor - visible: true - focus: true + target: editorLoader + sourceComponent: null } } ] diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml index 3e268d10f48..a79a2d5c5d7 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml @@ -114,7 +114,6 @@ Item { } } - PlatformWidgets.FileDialog { id: fileDialog diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml index 42bb528c917..f3477b8b487 100644 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml @@ -217,8 +217,10 @@ Rectangle { delegate: Rectangle { id: itemCell + + clip: true implicitWidth: 100 - implicitHeight: itemText.height + implicitHeight: StudioTheme.Values.baseHeight border.color: dataTypeWarning !== CollectionDetails.Warning.None ? StudioTheme.Values.themeWarning : StudioTheme.Values.themeControlBackgroundInteraction border.width: 1 @@ -231,25 +233,50 @@ Rectangle { acceptedButtons: Qt.NoButton } - Text { - id: itemText + Loader { + id: cellContentLoader - text: display - color: StudioTheme.Values.themePlaceholderTextColorInteraction - width: parent.width - leftPadding: 5 - topPadding: 3 - bottomPadding: 3 - font.pixelSize: StudioTheme.Values.baseFontSize - horizontalAlignment: Text.AlignLeft - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight + property int cellColumnType: columnType ? columnType : 0 + + Component { + id: cellText + + Text { + text: display + color: itemSelected + ? StudioTheme.Values.themeInteraction + : StudioTheme.Values.themePlaceholderTextColorInteraction + leftPadding: 5 + topPadding: 3 + bottomPadding: 3 + font.pixelSize: StudioTheme.Values.baseFontSize + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + } + + Component { + id: colorEditorComponent + + ColorViewDelegate {} + } + + function resetSource() { + if (columnType == CollectionDetails.DataType.Color) + cellContentLoader.sourceComponent = colorEditorComponent + else + cellContentLoader.sourceComponent = cellText + } + + Component.onCompleted: resetSource() + onCellColumnTypeChanged: resetSource() } TableView.editDelegate: CollectionDetailsEditDelegate { anchors { - top: itemText.top - left: itemText.left + top: itemCell.top + left: itemCell.left } } @@ -262,11 +289,6 @@ Rectangle { target: itemCell color: StudioTheme.Values.themeControlBackground } - - PropertyChanges { - target: itemText - color: StudioTheme.Values.themePlaceholderTextColorInteraction - } }, State { name: "selected" @@ -277,11 +299,6 @@ Rectangle { color: StudioTheme.Values.themeControlBackgroundInteraction border.color: StudioTheme.Values.themeControlBackground } - - PropertyChanges { - target: itemText - color: StudioTheme.Values.themeInteraction - } } ] } diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml new file mode 100644 index 00000000000..1414a2dd3a2 --- /dev/null +++ b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml @@ -0,0 +1,282 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtQuick.Layouts +import QtQuick.Shapes +import QtQuick.Templates as T +import HelperWidgets 2.0 as HelperWidgets +import StudioTheme as StudioTheme +import StudioControls as StudioControls +import QtQuickDesignerTheme +import QtQuickDesignerColorPalette + +Row { + id: colorEditor + + property color color + property bool supportGradient: false + readonly property color __editColor: edit + + property variant value: { + if (!edit) + return "white" // default color for Rectangle + + if (colorEditor.isVector3D) { + return Qt.rgba(__editColor.x, + __editColor.y, + __editColor.z, 1) + } + + return __editColor + } + + property alias gradientPropertyName: popupDialog.gradientPropertyName + + property alias gradientThumbnail: gradientThumbnail + property alias shapeGradientThumbnail: shapeGradientThumbnail + + property bool shapeGradients: false + property bool isVector3D: false + property color originalColor + + property bool __block: false + + function resetShapeColor() { + if (edit) + edit = "" + } + + function writeColor() { + if (colorEditor.isVector3D) { + edit = Qt.vector3d(colorEditor.color.r, + colorEditor.color.g, + colorEditor.color.b) + } else { + edit = colorEditor.color + } + } + + function initEditor() { + colorEditor.syncColor() + } + + // Syncing color from backend to frontend and block reflection + function syncColor() { + colorEditor.__block = true + colorEditor.color = colorEditor.value + hexTextField.syncColor() + colorEditor.__block = false + } + + Connections { + id: backendConnection + + target: colorEditor + + function onValueChanged() { + if (popupDialog.isSolid()) + colorEditor.syncColor() + } + + function on__EditColorChanged() { + if (popupDialog.isSolid()) + colorEditor.syncColor() + } + } + + Timer { + id: colorEditorTimer + + repeat: false + interval: 100 + running: false + onTriggered: { + backendConnection.enabled = false + colorEditor.writeColor() + hexTextField.syncColor() + backendConnection.enabled = true + } + } + + onColorChanged: { + if (colorEditor.__block) + return + + if (!popupDialog.isInValidState) + return + + popupDialog.commitToGradient() + + // Delay setting the color to keep ui responsive + if (popupDialog.isSolid()) + colorEditorTimer.restart() + } + + Rectangle { + id: preview + + implicitWidth: StudioTheme.Values.twoControlColumnWidth + implicitHeight: StudioTheme.Values.height + color: colorEditor.color + border.color: StudioTheme.Values.themeControlOutline + border.width: StudioTheme.Values.border + + Rectangle { + id: gradientThumbnail + + anchors.fill: parent + anchors.margins: StudioTheme.Values.border + visible: !popupDialog.isSolid() + && !colorEditor.shapeGradients + && popupDialog.isLinearGradient() + } + + Shape { + id: shape + + anchors.fill: parent + anchors.margins: StudioTheme.Values.border + visible: !popupDialog.isSolid() && colorEditor.shapeGradients + + ShapePath { + id: shapeGradientThumbnail + + startX: shape.x - 1 + startY: shape.y - 1 + strokeWidth: -1 + strokeColor: "green" + + PathLine { + x: shape.x - 1 + y: shape.height + } + PathLine { + x: shape.width + y: shape.height + } + PathLine { + x: shape.width + y: shape.y - 1 + } + } + } + + Image { + anchors.fill: parent + source: "qrc:/navigator/icon/checkers.png" + fillMode: Image.Tile + z: -1 + } + + MouseArea { + anchors.fill: parent + onClicked: { + popupDialog.visibility ? popupDialog.close() : popupDialog.open() + forceActiveFocus() + } + } + + StudioControls.PopupDialog { + id: popupDialog + + property bool isInValidState: loader.active ? popupDialog.loaderItem.isInValidState : true + property QtObject loaderItem: loader.item + property string gradientPropertyName + + keepOpen: loader.item?.eyeDropperActive ?? false + + width: 260 + + function commitToGradient() { + if (!loader.active) + return + + if (colorEditor.supportGradient && popupDialog.loaderItem.gradientModel.hasGradient) { + var hexColor = convertColorToString(colorEditor.color) + hexTextField.text = hexColor + edit = hexColor + popupDialog.loaderItem.commitGradientColor() + } + } + + function isSolid() { + if (!loader.active) + return true + + return popupDialog.loaderItem.isSolid() + } + + function isLinearGradient(){ + if (!loader.active) + return false + + return popupDialog.loaderItem.isLinearGradient() + } + + function ensureLoader() { + if (!loader.active) + loader.active = true + } + + function open() { + popupDialog.ensureLoader() + popupDialog.show(preview) + } + + function determineActiveColorMode() { + if (loader.active && popupDialog.loaderItem) + popupDialog.loaderItem.determineActiveColorMode() + else + colorEditor.syncColor() + } + + Loader { + id: loader + + active: colorEditor.supportGradient + + sourceComponent: HelperWidgets.ColorEditorPopup { + shapeGradients: colorEditor.shapeGradients + supportGradient: colorEditor.supportGradient + width: popupDialog.contentWidth + } + + onLoaded: { + popupDialog.loaderItem.initEditor() + popupDialog.titleBar = loader.item.titleBarContent + } + } + } + } + + HelperWidgets.LineEdit { + id: hexTextField + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + width: hexTextField.implicitWidth + enabled: popupDialog.isSolid() + writeValueManually: true + validator: RegularExpressionValidator { + regularExpression: /#[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?/g + } + showTranslateCheckBox: false + showExtendedFunctionButton: false + indicatorVisible: false + + onAccepted: colorEditor.color = hexTextField.text + onCommitData: { + colorEditor.color = hexTextField.text + if (popupDialog.isSolid()) + colorEditor.writeColor() + } + + function syncColor() { + hexTextField.text = colorEditor.color + } + } + + Component.onCompleted: popupDialog.determineActiveColorMode() + + on__EditColorChanged: popupDialog.determineActiveColorMode() +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml index 3262b03065d..88624471627 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditor.qml @@ -36,6 +36,7 @@ SecondColumnLayout { property alias shapeGradientThumbnail: shapeGradientThumbnail property alias showExtendedFunctionButton: hexTextField.showExtendedFunctionButton + property alias showHexTextField: hexTextField.visible property bool shapeGradients: false property color originalColor diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml index fcf2d341b89..9e2fecc6771 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ColorEditorPopup.qml @@ -393,10 +393,7 @@ Column { } } } - } - Connections { - target: modelNodeBackend function onSelectionChanged() { root.initEditor() } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir index 6fc885ac57b..94c3da27186 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir @@ -10,6 +10,7 @@ ButtonRowButton 2.0 ButtonRowButton.qml CharacterSection 2.0 CharacterSection.qml CheckBox 2.0 CheckBox.qml ColorEditor 2.0 ColorEditor.qml +ColorEditorPopup 2.0 ColorEditorPopup.qml ColorLogic 2.0 ColorLogic.qml ComboBox 2.0 ComboBox.qml ComponentButton 2.0 ComponentButton.qml diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp index 7245f70ae8f..ec6017ffa35 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp @@ -85,6 +85,24 @@ static QVariant valueToVariant(const QJsonValue &value, CollectionDetails::DataT } } +static QJsonValue variantToJsonValue(const QVariant &variant) +{ + using VariantType = QVariant::Type; + + switch (variant.type()) { + case VariantType::Bool: + return variant.toBool(); + case VariantType::Double: + case VariantType::Int: + return variant.toDouble(); + case VariantType::String: + case VariantType::Color: + case VariantType::Url: + default: + return variant.toString(); + } +} + CollectionDetails::CollectionDetails() : d(new Private()) {} @@ -278,8 +296,10 @@ bool CollectionDetails::setPropertyType(int column, DataType type) for (QJsonObject &element : d->elements) { if (element.contains(property.name)) { - QJsonValue value = element.value(property.name); - element.insert(property.name, valueToVariant(value, type).toJsonValue()); + const QJsonValue value = element.value(property.name); + const QVariant properTypedValue = valueToVariant(value, type); + const QJsonValue properTypedJsonValue = variantToJsonValue(properTypedValue); + element.insert(property.name, properTypedJsonValue); changed = true; } } diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp index 559a556d6ef..fc5f93670f2 100644 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp +++ b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp @@ -340,9 +340,10 @@ bool CollectionDetailsModel::setPropertyType(int column, const QString &newValue newValue)); if (changed) { emit headerDataChanged(Qt::Horizontal, column, column); - emit dataChanged(index(0, column), - index(rowCount() - 1, column), - {Qt::DisplayRole, DataTypeRole, DataTypeWarningRole, ColumnDataTypeRole}); + emit dataChanged( + index(0, column), + index(rowCount() - 1, column), + {Qt::DisplayRole, Qt::EditRole, DataTypeRole, DataTypeWarningRole, ColumnDataTypeRole}); } return changed;