diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml deleted file mode 100644 index 71499cc47e8..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsEditDelegate.qml +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -import QtQuick -import CollectionDetails 1.0 as CollectionDetails -import StudioControls 1.0 as StudioControls -import StudioHelpers as StudioHelpers -import StudioTheme 1.0 as StudioTheme -import QtQuick.Templates as T - -Item { - id: root - - required property var columnType - - TableView.onCommit: { - if (editorLoader.changesAccepted && edit !== editorLoader.acceptedValue) - edit = editorLoader.acceptedValue - } - - onActiveFocusChanged: { - if (root.activeFocus && !editorLoader.triggered && editorLoader.item) { - editorLoader.triggered = true - editorLoader.item.open() - } - - // active focus should be checked again, because it might be affected by editorLoader.item - if (root.activeFocus && editorLoader.editor) - editorLoader.editor.forceActiveFocus() - } - - Loader { - id: editorLoader - - active: true - - 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 - - Connections { - id: modifierFocusConnection - - target: editorLoader.editor - enabled: editorLoader.item !== undefined - - function onActiveFocusChanged() { - if (!modifierFocusConnection.target.activeFocus) { - editorLoader.acceptedValue = editorLoader.editValue - root.TableView.commit() - } - } - } - - 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: realEditor - - EditorPopup { - - editor: realField - - StudioControls.RealSpinBox { - id: realField - - property alias editValue: realField.realValue - - actionIndicator.visible: false - realFrom: -9e9 - realTo: 9e9 - realStepSize: 1.0 - decimals: 6 - trailingZeroes: false - - onActiveFocusChanged: { - if (realField.activeFocus) - realField.contentItem.focus = true - } - - textFromValue: function (value, locale) { - locale.numberOptions = Locale.OmitGroupSeparator - var decimals = realField.trailingZeroes ? realField.decimals : decimalCounter(realField.realValue) - if (decimals > 0) { - var text = Number(realField.realValue).toLocaleString(locale, 'f', decimals + 1) - return text.substring(0, text.length - 1) - } - return Number(realField.realValue).toLocaleString(locale, 'f', decimals) - } - } - } - } - - Component { - id: integerEditor - - EditorPopup { - - editor: integerField - - StudioControls.SpinBox { - id: integerField - - property alias editValue: integerField.value - - actionIndicatorVisible: false - spinBoxIndicatorVisible: true - from: -2147483647 - to: 2147483647 - decimals: 0 - - onActiveFocusChanged: { - if (integerField.activeFocus) - integerField.contentItem.focus = true - } - } - } - } - } - - component EditorPopup: T.Popup { - id: editorPopup - - required property Item editor - - implicitHeight: contentHeight - implicitWidth: contentWidth - - focus: true - visible: false - - Connections { - target: editorPopup.editor - - function onActiveFocusChanged() { - if (!editorPopup.editor.activeFocus) - editorPopup.close() - else if (edit) - editorPopup.editor.editValue = edit - } - } - - Connections { - target: editorPopup.editor.Keys - - function onEscapePressed() { - editorLoader.changesAccepted = false - editorPopup.close() - } - - function onReturnPressed() { - editorPopup.close() - } - - function onEnterPressed() { - editorPopup.close() - } - } - } - - states: [ - State { - name: "default" - when: columnType !== CollectionDetails.DataType.Boolean - && columnType !== CollectionDetails.DataType.Color - && columnType !== CollectionDetails.DataType.Integer - && columnType !== CollectionDetails.DataType.Real - - PropertyChanges { - target: editorLoader - sourceComponent: textEditor - } - }, - State { - name: "integer" - when: columnType === CollectionDetails.DataType.Integer - - PropertyChanges { - target: editorLoader - sourceComponent: integerEditor - } - }, - State { - name: "real" - when: columnType === CollectionDetails.DataType.Real - - PropertyChanges { - target: editorLoader - sourceComponent: realEditor - } - }, - State { - name: "color" - when: columnType === CollectionDetails.DataType.Color - - PropertyChanges { - target: editorLoader - sourceComponent: null - } - } - ] -} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml deleted file mode 100644 index e7997f7eae6..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsToolbar.qml +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright (C) 2023 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.Controls -import QtQuick.Layouts -import Qt.labs.platform as PlatformWidgets -import HelperWidgets 2.0 as HelperWidgets -import StudioControls 1.0 as StudioControls -import StudioTheme 1.0 as StudioTheme -import CollectionDetails -import CollectionEditorBackend - -Rectangle { - id: root - - required property var model - required property var backend - property int selectedRow: -1 - - implicitHeight: StudioTheme.Values.toolbarHeight - color: StudioTheme.Values.themeToolbarBackground - - function addNewColumn() { - addColumnDialog.popUp(root.model.columnCount()) - } - - function addNewRow() { - root.model.insertRow(root.model.rowCount()) - } - - function closeDialogs() { - addColumnDialog.reject() - fileDialog.reject() - } - - RowLayout { - id: container - - anchors.fill: parent - anchors.topMargin: StudioTheme.Values.toolbarVerticalMargin - anchors.bottomMargin: StudioTheme.Values.toolbarVerticalMargin - - spacing: StudioTheme.Values.sectionRowSpacing - - RowLayout { - id: leftSideToolbar - - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - Layout.leftMargin: StudioTheme.Values.toolbarHorizontalMargin - spacing: StudioTheme.Values.sectionRowSpacing - - IconButton { - id: addColumnLeftButton - - buttonIcon: StudioTheme.Constants.addcolumnleft_medium - tooltip: qsTr("Add column left") - enabled: root.model.selectedColumn > -1 - onClicked: addColumnDialog.popUp(root.model.selectedColumn) - } - - IconButton { - id: addColumnRightButton - - buttonIcon: StudioTheme.Constants.addcolumnright_medium - tooltip: qsTr("Add column right") - enabled: root.model.selectedColumn > -1 - onClicked: addColumnDialog.popUp(root.model.selectedColumn + 1) - } - - IconButton { - id: deleteColumnButton - - buttonIcon: StudioTheme.Constants.deletecolumn_medium - tooltip: qsTr("Delete selected column") - enabled: root.model.selectedColumn > -1 - onClicked: root.model.removeColumn(root.model.selectedColumn) - } - - Item { // spacer - implicitWidth: StudioTheme.Values.toolbarSpacing - implicitHeight: 1 - } - - IconButton { - id: addRowBelowButton - - buttonIcon: StudioTheme.Constants.addrowbelow_medium - tooltip: qsTr("Add row below") - enabled: root.model.selectedRow > -1 - onClicked: root.model.insertRow(root.model.selectedRow + 1) - } - - IconButton { - id: addRowAboveButton - - buttonIcon: StudioTheme.Constants.addrowabove_medium - tooltip: qsTr("Add row above") - enabled: root.model.selectedRow > -1 - onClicked: root.model.insertRow(root.model.selectedRow) - } - - IconButton { - id: deleteSelectedRowButton - - buttonIcon: StudioTheme.Constants.deleterow_medium - tooltip: qsTr("Delete selected row") - enabled: root.model.selectedRow > -1 - onClicked: root.model.removeRow(root.model.selectedRow) - } - } - - RowLayout { - id: rightSideToolbar - - spacing: StudioTheme.Values.sectionRowSpacing - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - Layout.rightMargin: StudioTheme.Values.toolbarHorizontalMargin - - IconButton { - id: saveCollectionButton - - buttonIcon: StudioTheme.Constants.save_medium - tooltip: qsTr("Save changes") - enabled: root.model.collectionName !== "" && root.model.hasUnsavedChanges - onClicked: root.model.saveDataStoreCollections() - - Rectangle { - width: StudioTheme.Values.smallStatusIndicatorDiameter - height: StudioTheme.Values.smallStatusIndicatorDiameter - radius: StudioTheme.Values.smallStatusIndicatorDiameter / 2 - anchors.right: parent.right - anchors.top: parent.top - visible: root.model.hasUnsavedChanges - color: StudioTheme.Values.themeIconColorSelected - } - } - - IconButton { - id: exportCollectionButton - - buttonIcon: StudioTheme.Constants.export_medium - tooltip: qsTr("Export model") - enabled: root.model.collectionName !== "" - onClicked: fileDialog.open() - } - } - } - - PlatformWidgets.FileDialog { - id: fileDialog - - fileMode: PlatformWidgets.FileDialog.SaveFile - - nameFilters: ["JSON Files (*.json)", - "Comma-Separated Values (*.csv)" - ] - - selectedNameFilter.index: 0 - - onAccepted: { - let filePath = fileDialog.file.toString() - root.model.exportCollection(filePath) - } - } - - component IconButton: HelperWidgets.AbstractButton { - style: StudioTheme.Values.viewBarButtonStyle - } - - component Spacer: Item { - implicitWidth: 1 - implicitHeight: StudioTheme.Values.columnGap - } - - RegularExpressionValidator { - id: nameValidator - regularExpression: /^\w+$/ - } - - StudioControls.Dialog { - id: addColumnDialog - - property int clickedIndex: -1 - property bool nameIsValid - - title: qsTr("Add Column") - - function popUp(index) - { - addColumnDialog.clickedIndex = index - columnName.text = "" - columnName.forceActiveFocus() - addedPropertyType.currentIndex = addedPropertyType.find("String") - - addColumnDialog.open() - } - - function addColumnName() { - if (addColumnDialog.nameIsValid) { - root.model.addColumn(addColumnDialog.clickedIndex, columnName.text, addedPropertyType.currentText) - addColumnDialog.accept() - } else { - addColumnDialog.reject() - } - } - - contentItem: ColumnLayout { - spacing: 2 - - Text { - text: qsTr("Column name:") - color: StudioTheme.Values.themeTextColor - } - - StudioControls.TextField { - id: columnName - - Layout.fillWidth: true - - actionIndicator.visible: false - translationIndicator.visible: false - validator: nameValidator - - Keys.onEnterPressed: addColumnDialog.addColumnName() - Keys.onReturnPressed: addColumnDialog.addColumnName() - Keys.onEscapePressed: addColumnDialog.reject() - - onTextChanged: { - addColumnDialog.nameIsValid = (columnName.text !== "" - && !root.model.isPropertyAvailable(columnName.text)) - } - } - - Spacer { implicitHeight: StudioTheme.Values.controlLabelGap } - - Label { - Layout.fillWidth: true - - text: qsTr("The model already contains \"%1\"!").arg(columnName.text) - visible: columnName.text !== "" && !addColumnDialog.nameIsValid - - color: StudioTheme.Values.themeTextColor - wrapMode: Label.WordWrap - padding: 5 - - background: Rectangle { - color: "transparent" - border.width: StudioTheme.Values.border - border.color: StudioTheme.Values.themeWarning - } - } - - Spacer {} - - Text { - text: qsTr("Type:") - color: StudioTheme.Values.themeTextColor - } - - StudioControls.ComboBox { - id: addedPropertyType - - Layout.fillWidth: true - - model: CollectionDataTypeModel{} - textRole: "display" - tooltipRole: "toolTip" - actionIndicatorVisible: false - } - - Spacer {} - - RowLayout { - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - spacing: StudioTheme.Values.sectionRowSpacing - - HelperWidgets.Button { - enabled: addColumnDialog.nameIsValid - text: qsTr("Add") - onClicked: addColumnDialog.addColumnName() - } - - HelperWidgets.Button { - text: qsTr("Cancel") - onClicked: addColumnDialog.reject() - } - } - } - } -} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml deleted file mode 100644 index ad721472ed7..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionDetailsView.qml +++ /dev/null @@ -1,813 +0,0 @@ -// Copyright (C) 2023 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.Controls -import QtQuick.Layouts -import CollectionDetails 1.0 as CollectionDetails -import HelperWidgets 2.0 as HelperWidgets -import StudioControls 1.0 as StudioControls -import StudioTheme 1.0 as StudioTheme - -Rectangle { - id: root - - required property var model - required property var backend - required property var sortedModel - - implicitWidth: 300 - implicitHeight: 400 - color: StudioTheme.Values.themeControlBackground - - function closeDialogs() { - editPropertyDialog.reject() - deleteColumnDialog.reject() - toolbar.closeDialogs() - } - - MouseArea { - anchors.fill: parent - onClicked: tableView.model.deselectAll() - } - - Column { - id: topRow - readonly property real maxAvailableHeight: root.height - - visible: root.model.collectionName !== "" - width: parent.width - spacing: 10 - - CollectionDetailsToolbar { - id: toolbar - model: root.model - backend: root.backend - width: parent.width - } - - GridLayout { - id: gridLayout - readonly property real maxAvailableHeight: topRow.maxAvailableHeight - - topRow.spacing - - toolbar.height - - columns: 3 - rowSpacing: 1 - columnSpacing: 1 - width: parent.width - - anchors { - left: parent.left - leftMargin: StudioTheme.Values.collectionTableHorizontalMargin - } - - Rectangle { - id: tableTopLeftCorner - - clip: true - visible: !tableView.model.isEmpty - color: StudioTheme.Values.themeControlBackgroundInteraction - border.color: StudioTheme.Values.themeControlBackgroundInteraction - border.width: 2 - - Layout.preferredWidth: rowIdView.width - Layout.preferredHeight: headerView.height - Layout.minimumWidth: rowIdView.width - Layout.minimumHeight: headerView.height - - Text { - anchors.fill: parent - font: headerTextMetrics.font - text: "#" - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - color: StudioTheme.Values.themeTextColor - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.LeftButton - property int order: Qt.AscendingOrder - onClicked: { - order = (order == Qt.AscendingOrder) ? Qt.DescendingOrder : Qt.AscendingOrder; - tableView.closeEditors() - tableView.model.sort(-1, order) - } - } - } - } - - HorizontalHeaderView { - id: headerView - - property real topPadding: 5 - property real bottomPadding: 5 - - Layout.preferredHeight: headerTextMetrics.height + topPadding + bottomPadding - Layout.columnSpan: 2 - syncView: tableView - clip: true - - delegate: HeaderDelegate { - selectedItem: tableView.model.selectedColumn - color: StudioTheme.Values.themeControlBackgroundInteraction - - MouseArea { - id: topHeaderMouseArea - - anchors.fill: parent - anchors.leftMargin: StudioTheme.Values.borderHover - anchors.rightMargin: StudioTheme.Values.borderHover - acceptedButtons: Qt.LeftButton | Qt.RightButton - hoverEnabled: true - onClicked: (mouse) => { - tableView.model.selectColumn(index) - - if (mouse.button === Qt.RightButton) { - let posX = index === root.model.columnCount() - 1 ? parent.width - editPropertyDialog.width : 0 - - headerMenu.clickedHeaderIndex = index - headerMenu.dialogPos = parent.mapToGlobal(posX, parent.height) - headerMenu.popup() - } else { - headerMenu.close() - } - } - } - - ToolTip { - id: topHeaderToolTip - - property bool expectedToBeShown: topHeaderMouseArea.containsMouse - visible: expectedToBeShown && text !== "" - delay: 1000 - - onExpectedToBeShownChanged: { - if (expectedToBeShown) - text = root.model.propertyType(index) - } - } - } - - StudioControls.Menu { - id: headerMenu - - property int clickedHeaderIndex: -1 - property point dialogPos - - onClosed: { - headerMenu.clickedHeaderIndex = -1 - } - - StudioControls.MenuItem { - text: qsTr("Edit") - onTriggered: editPropertyDialog.openDialog(headerMenu.clickedHeaderIndex, - headerMenu.dialogPos) - } - - StudioControls.MenuItem { - text: qsTr("Delete") - onTriggered: deleteColumnDialog.popUp(headerMenu.clickedHeaderIndex) - } - - StudioControls.MenuItem { - text: qsTr("Sort Ascending") - onTriggered: { - tableView.closeEditors() - tableView.model.sort(headerMenu.clickedHeaderIndex, Qt.AscendingOrder) - } - } - - StudioControls.MenuItem { - text: qsTr("Sort Descending") - onTriggered: { - tableView.closeEditors() - tableView.model.sort(headerMenu.clickedHeaderIndex, Qt.DescendingOrder) - } - } - } - } - - VerticalHeaderView { - id: rowIdView - - syncView: tableView - clip: true - - Layout.preferredHeight: tableView.height - Layout.rowSpan: 2 - Layout.alignment: Qt.AlignTop + Qt.AlignLeft - width: implicitWidth // suppresses GridLayout warnings when resizing - - delegate: HeaderDelegate { - selectedItem: tableView.model.selectedRow - color: StudioTheme.Values.themeControlBackgroundHover - - MouseArea { - anchors.fill: parent - anchors.topMargin: StudioTheme.Values.borderHover - anchors.bottomMargin: StudioTheme.Values.borderHover - acceptedButtons: Qt.LeftButton - onClicked: tableView.model.selectRow(index) - } - } - } - - TableView { - id: tableView - - model: root.sortedModel - clip: true - - readonly property real maxAvailableHeight: gridLayout.maxAvailableHeight - - addRowButton.height - - headerView.height - - (2 * gridLayout.rowSpacing) - readonly property real maxAvailableWidth: gridLayout.width - - StudioTheme.Values.collectionTableHorizontalMargin - - rowIdView.width - - addColumnButton.width - - gridLayout.columnSpacing - - property real childrenWidth: tableView.contentItem.childrenRect.width - property real childrenHeight: tableView.contentItem.childrenRect.height - - property int targetRow - property int targetColumn - - property Item popupEditingItem - - Layout.alignment: Qt.AlignTop + Qt.AlignLeft - Layout.preferredWidth: tableView.contentWidth - Layout.preferredHeight: tableView.contentHeight - Layout.minimumWidth: 100 - Layout.minimumHeight: 20 - Layout.maximumWidth: maxAvailableWidth - Layout.maximumHeight: maxAvailableHeight - - columnWidthProvider: function(column) { - if (!isColumnLoaded(column)) - return -1 - let w = explicitColumnWidth(column) - if (w < 0) - w = implicitColumnWidth(column) - return Math.max(w, StudioTheme.Values.collectionCellMinimumWidth) - } - - rowHeightProvider: function(row) { - if (!isRowLoaded(row)) - return -1 - let h = explicitRowHeight(row) - if (h < 0) - h = implicitRowHeight(row) - return Math.max(h, StudioTheme.Values.collectionCellMinimumHeight) - } - - function closePopupEditor() { - if (tableView.popupEditingItem) - tableView.popupEditingItem.closeEditor() - tableView.popupEditingItem = null - } - - function openNewPopupEditor(item, editor) { - if (tableView.popupEditingItem !== item) { - closePopupEditor() - tableView.popupEditingItem = item - } - } - - function closeEditors() { - closeEditor() - closePopupEditor() - } - - function ensureRowIsVisible(row) { - let rows = tableView.model.rowCount() - let rowIsLoaded = tableView.isRowLoaded(row) - - if (row < 0 || row >= rows || rowIsLoaded) { - if (rowIsLoaded) - tableView.positionViewAtRow(row, Qt.AlignLeft | Qt.AlignTop) - - tableView.targetRow = -1 - return - } - - tableView.targetRow = row - tableView.positionViewAtRow(row, Qt.AlignLeft | Qt.AlignTop) - ensureTimer.start() - } - - function ensureColumnIsVisible(column) { - let columns = tableView.model.columnCount() - let columnIsLoaded = tableView.isColumnLoaded(column) - - if (column < 0 || column >= columns || columnIsLoaded) { - if (columnIsLoaded) - tableView.positionViewAtColumn(column, Qt.AlignLeft | Qt.AlignTop) - - tableView.targetColumn = -1 - return - } - - tableView.targetColumn = column - tableView.positionViewAtColumn(column, Qt.AlignLeft | Qt.AlignTop) - ensureTimer.start() - } - - onMaxAvailableHeightChanged: resetSizeTimer.start() - onMaxAvailableWidthChanged: resetSizeTimer.start() - onChildrenWidthChanged: resetSizeTimer.start() - onChildrenHeightChanged: resetSizeTimer.start() - - delegate: Rectangle { - id: itemCell - - clip: true - implicitWidth: 100 - implicitHeight: StudioTheme.Values.baseHeight - color: itemSelected ? StudioTheme.Values.themeControlBackgroundInteraction - : StudioTheme.Values.themeControlBackground - border.width: 1 - border.color: { - if (dataTypeWarning !== CollectionDetails.Warning.None) - return StudioTheme.Values.themeWarning - - if (itemSelected) - return StudioTheme.Values.themeControlOutlineInteraction - - return StudioTheme.Values.themeControlBackgroundInteraction - } - - HelperWidgets.ToolTipArea { - anchors.fill: parent - text: root.model.warningToString(dataTypeWarning) - enabled: dataTypeWarning !== CollectionDetails.Warning.None && text !== "" - hoverEnabled: true - acceptedButtons: Qt.NoButton - } - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.RightButton - onClicked: (mouse) => { - let row = index % tableView.model.rowCount() - - tableView.model.selectRow(row) - cellContextMenu.showMenu(row) - } - } - - Loader { - id: cellContentLoader - - property int cellColumnType: columnType ? columnType : 0 - - Component { - id: cellText - - Text { - text: display ?? "" - color: itemSelected ? StudioTheme.Values.themeInteraction - : StudioTheme.Values.themeTextColor - leftPadding: 5 - topPadding: 3 - bottomPadding: 3 - font.pixelSize: StudioTheme.Values.baseFontSize - horizontalAlignment: Text.AlignLeft - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - } - - Component { - id: checkBoxComponent - - StudioControls.CheckBox { - id: checkBoxDelegate - - readonly property bool editValue: edit - - text: "" - actionIndicatorVisible: false - checked: checkBoxDelegate.editValue - onCheckedChanged: { - if (checkBoxDelegate.editValue !== checkBoxDelegate.checked) - edit = checkBoxDelegate.checked - } - } - } - - Component { - id: colorEditorComponent - - ColorViewDelegate { - id: colorEditorItem - - readonly property color editValue: edit - readonly property color displayValue: display - property string _frontColorStr - property string _backendColorStr - - actionIndicatorVisible: false - - onColorChanged: { - _frontColorStr = colorEditorItem.color.toString() - if (_frontColorStr != _backendColorStr) - edit = colorEditorItem.color - } - - Component.onCompleted: { - colorEditorItem.color = display - } - - onDisplayValueChanged: { - _backendColorStr = colorEditorItem.displayValue.toString() - if (_frontColorStr != _backendColorStr) - colorEditorItem.color = colorEditorItem.displayValue - } - - onEditorOpened: (item, editor) => { - tableView.openNewPopupEditor(item, editor) - } - } - } - - function resetSource() { - if (columnType === CollectionDetails.DataType.Color) - cellContentLoader.sourceComponent = colorEditorComponent - else if (columnType === CollectionDetails.DataType.Boolean) - cellContentLoader.sourceComponent = checkBoxComponent - else - cellContentLoader.sourceComponent = cellText - } - - Component.onCompleted: resetSource() - onCellColumnTypeChanged: resetSource() - } - - TableView.editDelegate: CollectionDetailsEditDelegate { - anchors { - top: itemCell.top - left: itemCell.left - } - Component.onCompleted: tableView.model.deselectAll() - } - } - - Timer { - id: resetSizeTimer - - interval: 100 - repeat: false - onTriggered: { - let cWidth = Math.min(tableView.maxAvailableWidth, tableView.childrenWidth) - let cHeight = Math.min(tableView.maxAvailableHeight, tableView.childrenHeight) - - if (tableView.contentWidth !== cWidth || tableView.contentHeight !== cHeight) - tableView.returnToBounds() - } - } - - Timer { - id: ensureTimer - - interval: 100 - repeat: false - onTriggered: { - tableView.ensureRowIsVisible(tableView.targetRow) - tableView.ensureColumnIsVisible(tableView.targetColumn) - } - } - - Connections { - target: tableView.model - - function onModelReset() { - root.closeDialogs() - tableView.clearColumnWidths() - tableView.clearRowHeights() - } - - function onRowsInserted(parent, first, last) { - tableView.closeEditors() - tableView.model.selectRow(first) - tableView.ensureRowIsVisible(first) - } - - function onColumnsInserted(parent, first, last) { - tableView.closeEditors() - tableView.model.selectColumn(first) - tableView.ensureColumnIsVisible(first) - } - - function onRowsRemoved(parent, first, last) { - let nextRow = first - 1 - if (nextRow < 0 && tableView.model.rowCount(parent) > 0) - nextRow = 0 - - tableView.model.selectRow(nextRow) - } - - function onColumnsRemoved(parent, first, last) { - let nextColumn = first - 1 - if (nextColumn < 0 && tableView.model.columnCount(parent) > 0) - nextColumn = 0 - - tableView.model.selectColumn(nextColumn) - } - } - - HoverHandler { id: hoverHandler } - - ScrollBar.horizontal: StudioControls.TransientScrollBar { - id: horizontalScrollBar - style: StudioTheme.Values.viewStyle - orientation: Qt.Horizontal - - show: (hoverHandler.hovered || tableView.focus || horizontalScrollBar.inUse) - && horizontalScrollBar.isNeeded - } - - ScrollBar.vertical: StudioControls.TransientScrollBar { - id: verticalScrollBar - style: StudioTheme.Values.viewStyle - orientation: Qt.Vertical - - show: (hoverHandler.hovered || tableView.focus || verticalScrollBar.inUse) - && verticalScrollBar.isNeeded - } - } - - HelperWidgets.IconButton { - id: addColumnButton - - iconSize:16 - Layout.preferredWidth: 24 - Layout.preferredHeight: tableView.height - Layout.minimumHeight: 24 - Layout.alignment: Qt.AlignLeft + Qt.AlignVCenter - - icon: StudioTheme.Constants.create_medium - tooltip: "Add Column" - - onClicked: { - tableView.closeEditors() - toolbar.addNewColumn() - } - } - - HelperWidgets.IconButton { - id: addRowButton - - iconSize:16 - Layout.preferredWidth: tableView.width - Layout.preferredHeight: 24 - Layout.minimumWidth: 24 - Layout.alignment: Qt.AlignTop + Qt.AlignHCenter - - icon: StudioTheme.Constants.create_medium - tooltip: "Add Row" - - onClicked: { - tableView.closeEditors() - toolbar.addNewRow() - } - } - - Item { - Layout.fillWidth: true - Layout.fillHeight: true - } - } - } - - ColumnLayout { - id: importsProblem - - visible: !topRow.visible && rootView.dataStoreExists && !rootView.projectImportExists - width: parent.width - anchors.verticalCenter: parent.verticalCenter - clip: true - - Text { - text: qsTr("Import the project to your design document to make the Model Editor enabled.") - Layout.alignment: Qt.AlignCenter - Layout.maximumWidth: parent.width - leftPadding: StudioTheme.Values.collectionItemTextPadding - rightPadding: StudioTheme.Values.collectionItemTextPadding - color: StudioTheme.Values.themeTextColor - font.pixelSize: StudioTheme.Values.mediumFontSize - wrapMode: Text.Wrap - } - - HelperWidgets.Button { - text: qsTr("Enable DataStore (This will add the required import)") - Layout.alignment: Qt.AlignCenter - onClicked: rootView.addProjectImport() - leftPadding: StudioTheme.Values.collectionItemTextPadding - rightPadding: StudioTheme.Values.collectionItemTextPadding - } - } - - Text { - anchors.centerIn: parent - text: qsTr("There are no models in this project.\nAdd or import a model.") - visible: !topRow.visible && !importsProblem.visible - color: StudioTheme.Values.themeTextColor - font.pixelSize: StudioTheme.Values.mediumFontSize - } - - TextMetrics { - id: headerTextMetrics - - font.pixelSize: StudioTheme.Values.baseFontSize - text: "Xq" - } - - StudioControls.Menu { - id: cellContextMenu - - property int rowIndex: -1 - - closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside - - function showMenu(rowIndex) { - cellContextMenu.rowIndex = rowIndex - cellContextMenu.popup() - } - - CellContextMenuItem { - id: addRowAboveCellMenuItem - - itemText: qsTr("Add row above") - itemIcon: StudioTheme.Constants.addrowabove_medium - onTriggered: root.model.insertRow(cellContextMenu.rowIndex) - } - CellContextMenuItem { - id: addRowBelowCellMenuItem - - itemText: qsTr("Add row below") - itemIcon: StudioTheme.Constants.addrowbelow_medium - onTriggered: root.model.insertRow(cellContextMenu.rowIndex + 1) - } - CellContextMenuItem { - id: deleteRowCellMenuItem - - itemText: qsTr("Delete row") - itemIcon: StudioTheme.Constants.deleterow_medium - onTriggered: root.model.removeRows(cellContextMenu.rowIndex, 1) - } - } - - component HeaderDelegate: Rectangle { - id: headerItem - - required property int selectedItem - property alias horizontalAlignment: headerText.horizontalAlignment - property alias verticalAlignment: headerText.verticalAlignment - - implicitWidth: headerText.implicitWidth - implicitHeight: headerText.implicitHeight - border.width: 1 - clip: true - - Text { - id: headerText - - topPadding: headerView.topPadding - bottomPadding: headerView.bottomPadding - leftPadding: 5 - rightPadding: 5 - text: display - font: headerTextMetrics.font - color: StudioTheme.Values.themeTextColor - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - states: [ - State { - name: "default" - when: index !== selectedItem - PropertyChanges { - target: headerItem - border.color: StudioTheme.Values.themeControlBackgroundInteraction - } - - PropertyChanges { - target: headerText - font.bold: false - } - }, - State { - name: "selected" - when: index === selectedItem - - PropertyChanges { - target: headerItem - border.color: StudioTheme.Values.themeControlBackground - } - - PropertyChanges { - target: headerText - font.bold: true - } - } - ] - } - - component CellContextMenuItem: StudioControls.MenuItem { - id: cellContextMenuItemComponent - - property alias itemText: cellContextMenuText.text - property alias itemIcon: cellContextMenuIcon.text - text: "" - - implicitWidth: cellContextMenuRow.width - implicitHeight: cellContextMenuRow.height - - Row { - id: cellContextMenuRow - - property color textColor : cellContextMenuItemComponent.enabled - ? cellContextMenuItemComponent.highlighted - ? cellContextMenuItemComponent.style.text.selectedText - : cellContextMenuItemComponent.style.text.idle - : cellContextMenuItemComponent.style.text.disabled - - spacing: 2 * StudioTheme.Values.contextMenuHorizontalPadding - height: StudioTheme.Values.defaultControlHeight - leftPadding: StudioTheme.Values.contextMenuHorizontalPadding - rightPadding: StudioTheme.Values.contextMenuHorizontalPadding - - Text { - id: cellContextMenuIcon - - color: cellContextMenuRow.textColor - text: StudioTheme.Constants.addrowabove_medium - font.family: StudioTheme.Constants.iconFont.family - font.pixelSize: StudioTheme.Values.myIconFontSize - anchors.verticalCenter: parent.verticalCenter - } - - Text { - id: cellContextMenuText - - color: cellContextMenuRow.textColor - anchors.verticalCenter: parent.verticalCenter - } - } - } - - EditPropertyDialog { - id: editPropertyDialog - model: root.model - } - - StudioControls.Dialog { - id: deleteColumnDialog - - property int clickedIndex: -1 - - title: qsTr("Delete Column") - width: 400 - - onAccepted: { - root.model.removeColumn(clickedIndex) - } - - function popUp(index) - { - deleteColumnDialog.clickedIndex = index - deleteColumnDialog.open() - } - - contentItem: ColumnLayout { - spacing: StudioTheme.Values.sectionColumnSpacing - - Text { - text: qsTr("Are you sure that you want to delete column \"%1\"?").arg( - root.model.headerData( - deleteColumnDialog.clickedIndex, Qt.Horizontal)) - color: StudioTheme.Values.themeTextColor - } - - RowLayout { - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - spacing: StudioTheme.Values.sectionRowSpacing - - HelperWidgets.Button { - text: qsTr("Delete") - onClicked: deleteColumnDialog.accept() - } - - HelperWidgets.Button { - text: qsTr("Cancel") - onClicked: deleteColumnDialog.reject() - } - } - } - } -} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml deleted file mode 100644 index e110a8a62a6..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionItem.qml +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright (C) 2023 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.Controls -import QtQuick.Layouts -import HelperWidgets 2.0 as HelperWidgets -import StudioControls 1.0 as StudioControls -import StudioTheme as StudioTheme -import CollectionEditorBackend - -Item { - id: root - - implicitWidth: 300 - implicitHeight: boundingRect.height + 3 - - property color textColor - readonly property string name: collectionName ?? "" - readonly property bool isSelected: collectionIsSelected - readonly property int id: index - - function rename(newName) { - collectionName = newName - } - - signal selectItem(int itemIndex) - signal deleteItem() - signal contextMenuRequested() - - Item { - id: boundingRect - - width: parent.width - height: itemLayout.height - clip: true - - MouseArea { - id: itemMouse - - anchors.fill: parent - acceptedButtons: Qt.LeftButton - propagateComposedEvents: true - hoverEnabled: true - onClicked: (event) => { - if (!collectionIsSelected) { - collectionIsSelected = true - event.accepted = true - } - } - } - - Rectangle { - id: innerRect - anchors.fill: parent - } - - RowLayout { - id: itemLayout - - width: parent.width - - Text { - id: nameHolder - - Layout.fillWidth: true - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - Layout.leftMargin: StudioTheme.Values.collectionItemTextSideMargin - Layout.topMargin: StudioTheme.Values.collectionItemTextMargin - Layout.bottomMargin: StudioTheme.Values.collectionItemTextMargin - - text: collectionName - font.pixelSize: StudioTheme.Values.baseFontSize - color: root.textColor - topPadding: StudioTheme.Values.collectionItemTextPadding - bottomPadding: StudioTheme.Values.collectionItemTextPadding - elide: Text.ElideMiddle - verticalAlignment: Text.AlignVCenter - } - - Text { - id: threeDots - - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - Layout.topMargin: StudioTheme.Values.collectionItemTextMargin - Layout.bottomMargin: StudioTheme.Values.collectionItemTextMargin - Layout.rightMargin: StudioTheme.Values.collectionItemTextSideMargin - - text: StudioTheme.Constants.more_medium - font.family: StudioTheme.Constants.iconFont.family - font.pixelSize: StudioTheme.Values.baseIconFontSize - color: root.textColor - padding: StudioTheme.Values.collectionItemTextPadding - - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.RightButton | Qt.LeftButton - onClicked: root.contextMenuRequested() - } - } - } - } - - states: [ - State { - name: "default" - when: !collectionIsSelected && !itemMouse.containsMouse - - PropertyChanges { - target: innerRect - opacity: 0.6 - color: StudioTheme.Values.themeControlBackground - } - - PropertyChanges { - target: root - textColor: StudioTheme.Values.themeTextColor - } - }, - State { - name: "hovered" - when: !collectionIsSelected && itemMouse.containsMouse - - PropertyChanges { - target: innerRect - opacity: 0.8 - color: StudioTheme.Values.themeControlBackgroundHover - } - - PropertyChanges { - target: root - textColor: StudioTheme.Values.themeTextColor - } - }, - State { - name: "selected" - when: collectionIsSelected - - PropertyChanges { - target: innerRect - opacity: 1 - color: StudioTheme.Values.themeIconColorSelected - } - - PropertyChanges { - target: root - textColor: StudioTheme.Values.themeTextSelectedTextColor - } - } - ] -} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml deleted file mode 100644 index cbfab526152..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionListView.qml +++ /dev/null @@ -1,124 +0,0 @@ -// 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.Controls -import QtQuick.Layouts -import HelperWidgets 2.0 as HelperWidgets -import StudioControls 1.0 as StudioControls -import StudioTheme as StudioTheme -import CollectionEditorBackend - -ListView { - id: root - - model: CollectionEditorBackend.model - clip: true - - function closeDialogs() { - currentCollection.dereference() - collectionMenu.close() - deleteDialog.reject() - renameDialog.reject() - } - - function deleteCurrentCollection() { - deleteDialog.open() - } - - delegate: CollectionItem { - implicitWidth: root.width - onDeleteItem: root.model.removeRow(index) - onContextMenuRequested: collectionMenu.openMenu(this) - } - - QtObject { - id: currentCollection - - property CollectionItem item - readonly property string name: item ? item.name : "" - readonly property bool selected: item ? item.isSelected : false - readonly property int index: item ? item.id : -1 - - function updateItem() { - currentCollection.item = collectionMenu.clickedItem ?? root.itemAtIndex(root.model.selectedIndex) - } - - function rename(newName) { - if (item) - item.rename(newName) - } - - function deleteItem() { - if (item) - item.deleteItem() - } - - function dereference() { - item = null - } - } - - StudioControls.Menu { - id: collectionMenu - - property CollectionItem clickedItem - - closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside - enabled: root.count - - function openMenu(item) { - collectionMenu.clickedItem = item - currentCollection.updateItem() - collectionMenu.popup() - } - - onClosed: collectionMenu.clickedItem = null - - Action { - id: menuDeleteAction - - text: qsTr("Delete") - onTriggered: deleteDialog.open() - } - - Action { - id: menuRenameAction - - text: qsTr("Rename") - onTriggered: renameDialog.open() - } - - Action { - id: menuAssignAction - - text: qsTr("Assign to the selected node") - enabled: CollectionEditorBackend.rootView.targetNodeSelected - onTriggered: rootView.assignCollectionToSelectedNode(currentCollection.name) - } - } - - ConfirmDeleteCollectionDialog { - id: deleteDialog - - collectionName: currentCollection.name - onAboutToShow: currentCollection.updateItem() - onAccepted: currentCollection.deleteItem() - } - - RenameCollectionDialog { - id: renameDialog - - collectionName: currentCollection.name - onAboutToShow: currentCollection.updateItem() - onAccepted: currentCollection.rename(renameDialog.newCollectionName) - } - - Connections { - target: root.model - - function onModelReset() { - root.closeDialogs() - } - } -} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml deleted file mode 100644 index b57a4f75fe2..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/CollectionView.qml +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright (C) 2023 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.Controls -import QtQuick.Layouts -import HelperWidgets as HelperWidgets -import StudioTheme as StudioTheme -import CollectionEditorBackend - -Item { - id: root - focus: true - - property var rootView: CollectionEditorBackend.rootView - property var model: CollectionEditorBackend.model - property var collectionDetailsModel: CollectionEditorBackend.collectionDetailsModel - property var collectionDetailsSortFilterModel: CollectionEditorBackend.collectionDetailsSortFilterModel - - function showWarning(title, message) { - warningDialog.title = title - warningDialog.message = message - warningDialog.open() - } - - // called from C++ when using the delete key - function deleteSelectedCollection() { - collectionListView.deleteCurrentCollection() - } - - function closeDialogs() { - importDialog.reject() - newCollection.reject() - warningDialog.reject() - } - - ImportDialog { - id: importDialog - - backendValue: root.rootView - anchors.centerIn: parent - } - - NewCollectionDialog { - id: newCollection - - anchors.centerIn: parent - } - - Message { - id: warningDialog - - title: "" - message: "" - } - - Rectangle { - // Covers the toolbar color on top to prevent the background - // color for the margin of splitter - - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - height: topToolbar.height - color: topToolbar.color - } - - SplitView { - id: splitView - - readonly property bool isHorizontal: splitView.orientation === Qt.Horizontal - - orientation: width >= 500 ? Qt.Horizontal : Qt.Vertical - anchors.fill: parent - - onOrientationChanged: detailsView.closeDialogs() - - handle: Item { - id: handleDelegate - - property color color: SplitHandle.pressed ? StudioTheme.Values.themeControlOutlineInteraction - : SplitHandle.hovered ? StudioTheme.Values.themeControlOutlineHover - : StudioTheme.Values.themeControlOutline - - implicitWidth: 1 - implicitHeight: 1 - - Rectangle { - id: handleRect - - property real verticalMargin: splitView.isHorizontal ? StudioTheme.Values.splitterMargin : 0 - property real horizontalMargin: splitView.isHorizontal ? 0 : StudioTheme.Values.splitterMargin - - anchors.fill: parent - anchors.topMargin: handleRect.verticalMargin - anchors.bottomMargin: handleRect.verticalMargin - anchors.leftMargin: handleRect.horizontalMargin - anchors.rightMargin: handleRect.horizontalMargin - - color: handleDelegate.color - } - - containmentMask: Item { - x: splitView.isHorizontal ? ((handleDelegate.width - width) / 2) : 0 - y: splitView.isHorizontal ? 0 : ((handleDelegate.height - height) / 2) - height: splitView.isHorizontal ? handleDelegate.height : StudioTheme.Values.borderHover - width: splitView.isHorizontal ? StudioTheme.Values.borderHover : handleDelegate.width - } - } - - ColumnLayout { - id: collectionsSideBar - spacing: 0 - - SplitView.minimumWidth: 200 - SplitView.maximumWidth: 450 - SplitView.minimumHeight: 200 - SplitView.maximumHeight: 400 - SplitView.fillWidth: !splitView.isHorizontal - SplitView.fillHeight: splitView.isHorizontal - - Rectangle { - id: topToolbar - color: StudioTheme.Values.themeToolbarBackground - - Layout.preferredHeight: StudioTheme.Values.toolbarHeight - Layout.fillWidth: true - Layout.alignment: Qt.AlignTop | Qt.AlignHCenter - - Text { - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: StudioTheme.Values.toolbarHorizontalMargin - - text: qsTr("Data Models") - font.pixelSize: StudioTheme.Values.baseFontSize - color: StudioTheme.Values.themeTextColor - } - - HelperWidgets.AbstractButton { - id: importCollectionButton - - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: StudioTheme.Values.toolbarHorizontalMargin - - style: StudioTheme.Values.viewBarButtonStyle - buttonIcon: StudioTheme.Constants.import_medium - tooltip: qsTr("Import a model") - - onClicked: importDialog.open() - } - } - - CollectionListView { // Model Groups - id: collectionListView - - Layout.fillWidth: true - Layout.minimumHeight: bottomSpacer.isExpanded ? 150 : 0 - Layout.fillHeight: !bottomSpacer.isExpanded - Layout.preferredHeight: contentHeight - Layout.maximumHeight: contentHeight - Layout.alignment: Qt.AlignTop | Qt.AlignHCenter - } - - HelperWidgets.IconButton { - id: addCollectionButton - - iconSize: 16 - Layout.fillWidth: true - Layout.minimumWidth: 24 - Layout.alignment: Qt.AlignTop | Qt.AlignHCenter - - tooltip: qsTr("Add a new model") - icon: StudioTheme.Constants.create_medium - onClicked: newCollection.open() - } - - Item { - id: bottomSpacer - - readonly property bool isExpanded: height > 0 - Layout.minimumWidth: 1 - Layout.fillHeight: true - } - } - - CollectionDetailsView { - id: detailsView - - model: root.collectionDetailsModel - backend: root.model - sortedModel: root.collectionDetailsSortFilterModel - SplitView.fillHeight: true - SplitView.fillWidth: true - } - } - - Connections { - target: root.model - - function onModelReset() { - root.closeDialogs() - } - } -} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml deleted file mode 100644 index b563b6734af..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ColorViewDelegate.qml +++ /dev/null @@ -1,182 +0,0 @@ -// 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 -import StudioHelpers - -Item { - id: root - - property StudioTheme.ControlStyle style: StudioTheme.Values.controlStyle - - property alias color: colorBackend.color - - property alias actionIndicatorVisible: actionIndicator.visible - property real __actionIndicatorWidth: root.style.actionIndicatorSize.width - property real __actionIndicatorHeight: root.style.actionIndicatorSize.height - - property alias showHexTextField: hexTextField.visible - - readonly property real padding: 2 - readonly property real innerItemsHeight: root.height - 2 * root.padding - - width: root.style.controlSize.width - height: root.style.controlSize.height - - clip: true - - signal editorOpened(var item, var editorPopup) - - function closeEditor() { - loader.close() - } - - ColorBackend { - id: colorBackend - } - - StudioControls.ActionIndicator { - id: actionIndicator - style: root.style - __parentControl: root - x: root.padding - y: root.padding - width: actionIndicator.visible ? root.__actionIndicatorWidth : 0 - height: actionIndicator.visible ? root.__actionIndicatorHeight : 0 - } - - Rectangle { - id: preview - x: root.padding + actionIndicator.width - y: root.padding - z: previewMouseArea.containsMouse ? 10 : 0 - implicitWidth: root.innerItemsHeight - implicitHeight: root.innerItemsHeight - color: root.color - border.color: previewMouseArea.containsMouse ? root.style.border.hover : root.style.border.idle - border.width: root.style.borderWidth - - Image { - anchors.fill: parent - source: "qrc:/navigator/icon/checkers.png" - fillMode: Image.Tile - z: -1 - } - - MouseArea { - id: previewMouseArea - anchors.fill: parent - hoverEnabled: true - onClicked: { - loader.toggle() - previewMouseArea.forceActiveFocus() - } - } - - Loader { - id: loader - - function ensureLoader() { - if (!loader.active) - loader.active = true - if (loader.sourceComponent === null) - loader.sourceComponent = popupDialogComponent - } - - function open() { - loader.ensureLoader() - loader.item.show(preview) - - if (loader.status === Loader.Ready) - loader.item.originalColor = root.color - } - - function close() { - if (loader.item) - loader.item.close() - } - - function toggle() { - if (loader.item) - loader.close() - else - loader.open() - } - - Component { - id: popupDialogComponent - - - StudioControls.PopupDialog { - id: popupDialog - - property alias color: popup.color - property alias originalColor: popup.originalColor - titleBar: popup.titleBarContent - - width: 260 - - StudioControls.ColorEditorPopup { - id: popup - width: popupDialog.contentWidth - - onActivateColor: function(color) { - colorBackend.activateColor(color) - } - } - - onClosing: { - loader.sourceComponent = null - } - } - } - - sourceComponent: null - - Binding { - target: loader.item - property: "color" - value: root.color - when: loader.status === Loader.Ready - } - - onLoaded: { - loader.item.originalColor = root.color - root.editorOpened(root, loader.item) - } - } - } - - StudioControls.TextField { - id: hexTextField - style: root.style - x: root.padding + actionIndicator.width + preview.width - preview.border.width - y: root.padding - width: root.width - hexTextField.x - 2 * root.padding - height: root.innerItemsHeight - text: root.color - actionIndicatorVisible: false - translationIndicatorVisible: false - indicatorVisible: true - indicator.icon.text: StudioTheme.Constants.copy_small - indicator.onClicked: { - hexTextField.selectAll() - hexTextField.copy() - hexTextField.deselect() - } - - validator: RegularExpressionValidator { - regularExpression: /#[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?/g - } - - onAccepted: colorBackend.activateColor(colorFromString(hexTextField.text)) - } -} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ConfirmDeleteCollectionDialog.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ConfirmDeleteCollectionDialog.qml deleted file mode 100644 index 60f0f5e7b31..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ConfirmDeleteCollectionDialog.qml +++ /dev/null @@ -1,57 +0,0 @@ -// 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.Controls -import QtQuick.Layouts -import HelperWidgets 2.0 as HelperWidgets -import StudioControls 1.0 as StudioControls -import StudioTheme as StudioTheme - -StudioControls.Dialog { - id: root - - required property string collectionName - - title: qsTr("Deleting the model") - clip: true - - contentItem: ColumnLayout { - id: deleteDialogContent // Keep the id here even if it's not used, because the dialog might lose implicitSize - - width: 300 - spacing: 2 - - Text { - Layout.fillWidth: true - - wrapMode: Text.WordWrap - color: StudioTheme.Values.themeTextColor - text: qsTr("Are you sure that you want to delete model \"%1\"?" - + "\nThe model will be deleted permanently.").arg(root.collectionName) - - } - - Item { // spacer - implicitWidth: 1 - implicitHeight: StudioTheme.Values.columnGap - } - - RowLayout { - spacing: StudioTheme.Values.sectionRowSpacing - Layout.alignment: Qt.AlignRight | Qt.AlignVCenter - Layout.fillWidth: true - Layout.preferredHeight: 40 - - HelperWidgets.Button { - text: qsTr("Delete") - onClicked: root.accept() - } - - HelperWidgets.Button { - text: qsTr("Cancel") - onClicked: root.reject() - } - } - } -} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml deleted file mode 100644 index 546dc5d770c..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/EditPropertyDialog.qml +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright (C) 2023 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 StudioTheme 1.0 as StudioTheme -import StudioControls 1.0 as StudioControls -import HelperWidgets 2.0 as HelperWidgets -import CollectionDetails - -StudioControls.Dialog { - id: root - - required property var model - property int __propertyIndex: -1 - property string __currentName - - title: qsTr("Edit Column") - - function openDialog(index, initialPosition) { - root.__propertyIndex = index - - if (root.__propertyIndex < 0) - return - - root.__currentName = root.model.propertyName(root.__propertyIndex) - nameTextField.text = root.__currentName - nameTextField.selectAll() - nameTextField.forceActiveFocus() - - typeComboBox.initialType = root.model.propertyType(root.__propertyIndex) - typeComboBox.currentIndex = typeComboBox.find(typeComboBox.initialType) - - let newPoint = mapFromGlobal(initialPosition.x, initialPosition.y) - x = newPoint.x - y = newPoint.y - - root.open() - } - - onWidthChanged: { - if (visible && x > parent.width) - root.close() - } - - onAccepted: { - if (nameTextField.text !== "" && nameTextField.text !== root.__currentName) - root.model.renameColumn(root.__propertyIndex, nameTextField.text) - - if (typeComboBox.initialType !== typeComboBox.currentText) - root.model.setPropertyType(root.__propertyIndex, typeComboBox.currentText) - } - - contentItem: Column { - spacing: 5 - - Grid { - columns: 2 - rows: 2 - rowSpacing: 2 - columnSpacing: 25 - verticalItemAlignment: Grid.AlignVCenter - - Text { - text: qsTr("Name") - color: StudioTheme.Values.themeTextColor - verticalAlignment: Text.AlignVCenter - } - - StudioControls.TextField { - id: nameTextField - - actionIndicator.visible: false - translationIndicator.visible: false - - Keys.onEnterPressed: root.accept() - Keys.onReturnPressed: root.accept() - Keys.onEscapePressed: root.reject() - - validator: RegularExpressionValidator { - regularExpression: /^\w+$/ - } - } - - Text { - text: qsTr("Type") - color: StudioTheme.Values.themeTextColor - } - - StudioControls.ComboBox { - id: typeComboBox - - property string initialType - - model: CollectionDataTypeModel{} - textRole: "display" - tooltipRole: "toolTip" - actionIndicatorVisible: false - } - } - - Rectangle { - id: warningBox - - visible: typeComboBox.initialType !== typeComboBox.currentText - color: "transparent" - clip: true - border.color: StudioTheme.Values.themeWarning - width: parent.width - height: warning.height - - Row { - id: warning - - padding: 5 - spacing: 5 - - HelperWidgets.IconLabel { - icon: StudioTheme.Constants.warning_medium - anchors.verticalCenter: parent.verticalCenter - } - - Text { - text: qsTr("Conversion from %1 to %2 may lead to data loss") - .arg(typeComboBox.initialType) - .arg(typeComboBox.currentText) - - width: warningBox.width - 20 - - color: StudioTheme.Values.themeTextColor - wrapMode: Text.WordWrap - } - } - } - - Row { - height: 40 - spacing: 5 - - HelperWidgets.Button { - id: editButton - - text: qsTr("Apply") - enabled: nameTextField.text !== "" - anchors.bottom: parent.bottom - - onClicked: root.accept() - } - - HelperWidgets.Button { - text: qsTr("Cancel") - anchors.bottom: parent.bottom - - onClicked: root.reject() - } - } - } -} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/IconTextButton.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/IconTextButton.qml deleted file mode 100644 index fe9d094349c..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/IconTextButton.qml +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (C) 2023 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.Controls -import QtQuick.Layouts -import StudioTheme as StudioTheme - -Rectangle { - id: root - - required property string text - required property string icon - - property alias tooltip: toolTip.text - property StudioTheme.ControlStyle style: StudioTheme.Values.viewBarButtonStyle - property int fontSize: StudioTheme.Values.baseFontSize - - implicitHeight: style.squareControlSize.height - implicitWidth: rowAlign.width - - signal clicked() - - RowLayout { - id: rowAlign - - anchors.verticalCenter: parent.verticalCenter - spacing: StudioTheme.Values.inputHorizontalPadding - - Text { - id: iconItem - - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - text: root.icon - color: StudioTheme.Values.themeTextColor - font.family: StudioTheme.Constants.iconFont.family - font.pixelSize: StudioTheme.Values.bigFont - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - leftPadding: StudioTheme.Values.inputHorizontalPadding - } - - Text { - id: textItem - - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - text: root.text - color: StudioTheme.Values.themeTextColor - font.family: StudioTheme.Constants.font.family - font.pixelSize: StudioTheme.Values.baseFontSize - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - rightPadding: StudioTheme.Values.inputHorizontalPadding - } - } - - MouseArea { - id: mouseArea - anchors.fill: parent - hoverEnabled: true - onClicked: root.clicked() - } - - ToolTip { - id: toolTip - - visible: mouseArea.containsMouse && text !== "" - delay: 1000 - } - - states: [ - State { - name: "default" - when: !mouseArea.pressed && !mouseArea.containsMouse - PropertyChanges { - target: root - color: StudioTheme.Values.themeControlBackground - } - }, - State { - name: "Pressed" - when: mouseArea.pressed - PropertyChanges { - target: root - color: StudioTheme.Values.themeControlBackgroundInteraction - } - }, - State { - name: "Hovered" - when: !mouseArea.pressed && mouseArea.containsMouse - PropertyChanges { - target: root - color: StudioTheme.Values.themeControlBackgroundHover - } - } - ] -} diff --git a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ImportDialog.qml b/share/qtcreator/qmldesigner/collectionEditorQmlSource/ImportDialog.qml deleted file mode 100644 index 21a5e4e8e04..00000000000 --- a/share/qtcreator/qmldesigner/collectionEditorQmlSource/ImportDialog.qml +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright (C) 2023 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.Controls -import QtQuick.Layouts -import Qt.labs.platform as PlatformWidgets -import HelperWidgets as HelperWidgets -import StudioControls as StudioControls -import StudioTheme as StudioTheme - -StudioControls.Dialog { - id: root - - title: qsTr("Import a model") - anchors.centerIn: parent - closePolicy: Popup.CloseOnEscape - modal: true - - required property var backendValue - - property bool fileExists: false - - onOpened: { - collectionName.text = "Model" - fileName.text = qsTr("Model path") - fileName.selectAll() - fileName.forceActiveFocus() - } - - onRejected: { - fileName.text = "" - } - - function acceptIfIsValid() { - if (btnImport.enabled) - btnImport.onClicked() - } - - RegularExpressionValidator { - id: fileNameValidator - regularExpression: /^(\w[^*> -#include - -namespace QmlDesigner { - -struct CollectionDataTypeModel::Details -{ - CollectionDetails::DataType type; - QString name; - QString description; -}; - -const QList CollectionDataTypeModel::m_orderedDetails{ - {DataType::String, "String", "Text"}, - {DataType::Integer, "Integer", "Whole number that can be positive, negative, or zero"}, - {DataType::Real, "Real", "Number with a decimal"}, - {DataType::Image, "Image", "Image resource"}, - {DataType::Color, "Color", "HEX value"}, - {DataType::Url, "Url", "Resource locator"}, - {DataType::Boolean, "Boolean", "True/false"}, -}; - -CollectionDataTypeModel::CollectionDataTypeModel(QObject *parent) - : QAbstractListModel(parent) -{ -} - -int CollectionDataTypeModel::rowCount([[maybe_unused]] const QModelIndex &parent) const -{ - return m_orderedDetails.size(); -} - -QVariant CollectionDataTypeModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return {}; - - if (role == Qt::DisplayRole) - return m_orderedDetails.at(index.row()).name; - if (role == Qt::ToolTipRole) - return m_orderedDetails.at(index.row()).description; - - return {}; -} - -QString CollectionDataTypeModel::dataTypeToString(DataType dataType) -{ - static const QHash dataTypeHash = []() -> QHash { - QHash result; - for (const Details &details : m_orderedDetails) - result.insert(details.type, details.name); - return result; - }(); - - if (dataTypeHash.contains(dataType)) - return dataTypeHash.value(dataType); - - return "Unknown"; -} - -CollectionDetails::DataType CollectionDataTypeModel::dataTypeFromString(const QString &dataType) -{ - static const QHash stringTypeHash = []() -> QHash { - QHash result; - for (const Details &details : m_orderedDetails) - result.insert(details.name, details.type); - return result; - }(); - - if (stringTypeHash.contains(dataType)) - return stringTypeHash.value(dataType); - - return DataType::String; -} - -void CollectionDataTypeModel::registerDeclarativeType() -{ - qmlRegisterType("CollectionDetails", 1, 0, "CollectionDataTypeModel"); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.h deleted file mode 100644 index 1f91aecbff7..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (C) 2024 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "collectiondetails.h" - -#include -#include - -namespace QmlDesigner { - -class CollectionDataTypeModel : public QAbstractListModel -{ - Q_OBJECT - -public: - using DataType = CollectionDetails::DataType; - CollectionDataTypeModel(QObject *parent = nullptr); - - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - - static Q_INVOKABLE QString dataTypeToString(DataType dataType); - static Q_INVOKABLE DataType dataTypeFromString(const QString &dataType); - - static void registerDeclarativeType(); - -private: - struct Details; - static const QList
m_orderedDetails; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp deleted file mode 100644 index eedfbd3178a..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp +++ /dev/null @@ -1,882 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "collectiondetails.h" - -#include "collectiondatatypemodel.h" -#include "collectioneditorutils.h" -#include "collectionjsonparser.h" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace QmlDesigner { -#define COLLERR_OK QT_TRANSLATE_NOOP("CollectioParseError", "no error occurred") -#define COLLERR_MAINOBJECT QT_TRANSLATE_NOOP("CollectioParseError", "Document object not found") -#define COLLERR_COLLECTIONNAME QT_TRANSLATE_NOOP("CollectioParseError", "Model name not found") -#define COLLERR_COLLECTIONOBJ QT_TRANSLATE_NOOP("CollectioParseError", "Model is not an object") -#define COLLERR_COLUMNARRAY QT_TRANSLATE_NOOP("CollectioParseError", "Column is not an array") -#define COLLERR_UNKNOWN QT_TRANSLATE_NOOP("CollectioParseError", "Unknown error") - -struct CollectionProperty -{ - using DataType = CollectionDetails::DataType; - - QString name; - DataType type; -}; - -const QMap DataTypeWarning::dataTypeWarnings = { - {DataTypeWarning::CellDataTypeMismatch, "Cell and column data types do not match."} -}; - -class CollectionDetails::Private -{ -public: - QList properties; - QList dataRecords; - CollectionReference reference; - bool isChanged = false; - - bool isValidColumnId(int column) const { return column > -1 && column < properties.size(); } - - bool isValidRowId(int row) const { return row > -1 && row < dataRecords.size(); } -}; - -/** - * @brief getCustomUrl - * Address = - * - * @param value The input value to be evaluated - * @param dataType if the value is a valid url, the data type - * will be stored to this parameter, otherwise, it will be String - * @param urlResult if the value is a valid url, the address - * will be stored in this parameter, otherwise it will be empty. - * @return true if the result is url - */ -static bool getCustomUrl(const QString &value, - CollectionDetails::DataType &dataType, - QUrl *urlResult = nullptr) -{ - static const QRegularExpression urlRegex{ - "^(?
" - "(?https?:\\/\\/" - "(?:www\\.|(?!www))[A-z0-9][A-z0-9-]+[A-z0-9]\\.[^\\s]{2,}|www\\.[A-z0-9][A-z0-9-]+" - "[A-z0-9]\\.[^\\s]{2,}|https?:\\/\\/" - "(?:www\\.|(?!www))[A-z0-9]+\\.[^\\s]{2,}|www\\.[A-z0-9]+\\.[^\\s]{2,})|" // end of Url - "(?(" - "?:(?:[A-z]:)|(?:(?:\\\\|\\/){1,2}\\w+)\\$?)(?:(?:\\\\|\\/)(?:\\w[\\w ]*.*))+)" // end of LocalFile - "){1}$" // end of Address - }; - - const QRegularExpressionMatch match = urlRegex.match(value.trimmed()); - if (match.hasCaptured("Address")) { - dataType = CollectionDetails::DataType::Url; - - if (urlResult) - urlResult->setUrl(match.captured("Address")); - - return true; - } - - if (urlResult) - urlResult->clear(); - - dataType = CollectionDetails::DataType::String; - return false; -} - -/** - * @brief dataTypeFromString - * @param value The string value to be evaluated - * @return Bool, Color, Integer, Real, Url, - * Image if these types are detected within the non-empty string, - * Otherwise it returns String. - * If the value is integer, but it's out of the int range, it will be - * considered as a Real. - */ -static CollectionDetails::DataType dataTypeFromString(const QString &value) -{ - using DataType = CollectionDetails::DataType; - static const QRegularExpression validator{ - "(?^(?:true|false)$)|" - "(?^(?:#(?:(?:[0-9a-fA-F]{2}){3,4}|(?:[0-9a-fA-F]){3,4}))$)|" - "(?^\\d+$)|" - "(?^(?:-?(?:0|[1-9]\\d*)?(?:\\.\\d*)?(?<=\\d|\\.)" - "(?:e-?(?:0|[1-9]\\d*))?|0x[0-9a-f]+)$)"}; - static const int boolIndex = validator.namedCaptureGroups().indexOf("boolean"); - static const int colorIndex = validator.namedCaptureGroups().indexOf("color"); - static const int integerIndex = validator.namedCaptureGroups().indexOf("integer"); - static const int realIndex = validator.namedCaptureGroups().indexOf("real"); - - [[maybe_unused]] static const bool allIndexesFound = - [](const std::initializer_list &captureIndexes) { - QTC_ASSERT(Utils::allOf(captureIndexes, [](int val) { return val > -1; }), return false); - return true; - }({boolIndex, colorIndex, integerIndex, realIndex}); - - if (value.isEmpty()) - return DataType::String; - - const QString trimmedValue = value.trimmed(); - QRegularExpressionMatch match = validator.match(trimmedValue); - - if (match.hasCaptured(boolIndex)) - return DataType::Boolean; - if (match.hasCaptured(colorIndex)) - return DataType::Color; - if (match.hasCaptured(integerIndex)) { - bool isInt = false; - trimmedValue.toInt(&isInt); - return isInt ? DataType::Integer : DataType::Real; - } - if (match.hasCaptured(realIndex)) - return DataType::Real; - - DataType urlType; - if (getCustomUrl(trimmedValue, urlType)) - return urlType; - - return DataType::String; -} - -static CollectionProperty::DataType dataTypeFromJsonValue(const QJsonValue &value) -{ - using DataType = CollectionDetails::DataType; - using JsonType = QJsonValue::Type; - - switch (value.type()) { - case JsonType::Null: - case JsonType::Undefined: - return DataType::String; - case JsonType::Bool: - return DataType::Boolean; - case JsonType::Double: { - if (qFuzzyIsNull(std::remainder(value.toDouble(), 1))) - return DataType::Integer; - return DataType::Real; - } - case JsonType::String: - return dataTypeFromString(value.toString()); - default: - return DataType::String; - } -} - -static QList getColumnsFromImportedJsonArray(const QJsonArray &importedArray) -{ - using DataType = CollectionDetails::DataType; - - QHash resultSet; - QList result; - - for (const QJsonValue &value : importedArray) { - if (value.isObject()) { - const QJsonObject object = value.toObject(); - QJsonObject::ConstIterator element = object.constBegin(); - const QJsonObject::ConstIterator stopItem = object.constEnd(); - - while (element != stopItem) { - const QString propertyName = element.key(); - if (resultSet.contains(propertyName)) { - CollectionProperty &property = result[resultSet.value(propertyName)]; - if (property.type == DataType::Integer) { - const DataType currentCellDataType = dataTypeFromJsonValue(element.value()); - if (currentCellDataType == DataType::Real) - property.type = currentCellDataType; - } - } else { - result.append({propertyName, dataTypeFromJsonValue(element.value())}); - resultSet.insert(propertyName, resultSet.size()); - } - ++element; - } - } - } - - return result; -} - -static QVariant valueToVariant(const QJsonValue &value, CollectionDetails::DataType type) -{ - using DataType = CollectionDetails::DataType; - QVariant variantValue = value.toVariant(); - - switch (type) { - case DataType::String: - return variantValue.toString(); - case DataType::Integer: - return variantValue.toInt(); - case DataType::Real: - return variantValue.toDouble(); - case DataType::Boolean: - return variantValue.toBool(); - case DataType::Color: - return variantValue.value(); - case DataType::Url: - case DataType::Image: - return variantValue.value(); - default: - return variantValue; - } -} - -static QJsonValue variantToJsonValue( - const QVariant &variant, CollectionDetails::DataType type = CollectionDetails::DataType::String) -{ - using DataType = CollectionDetails::DataType; - - switch (type) { - case DataType::Boolean: - return variant.toBool(); - case DataType::Real: - return variant.toDouble(); - case DataType::Integer: - return variant.toInt(); - case DataType::Image: - case DataType::String: - case DataType::Color: - case DataType::Url: - default: - return variant.toString(); - } -} - -inline static bool isEmptyJsonValue(const QJsonValue &value) -{ - return value.isNull() || value.isUndefined() || (value.isString() && value.toString().isEmpty()); -} - -QStringList csvReadLine(const QString &line) -{ - static const QRegularExpression lineRegex{ - "(?:,\\\"|^\\\")(?\\\"\\\"|[\\w\\W]*?)(?=\\\",|\\\"$)" - "|(?:,(?!\\\")|^(?!\\\"))(?[^,]*?)(?=$|,)|(\\\\r\\\\n|\\\\n)"}; - static const int valueIndex = lineRegex.namedCaptureGroups().indexOf("value"); - static const int quoteIndex = lineRegex.namedCaptureGroups().indexOf("quote"); - Q_ASSERT(valueIndex > 0 && quoteIndex > 0); - - QStringList result; - QRegularExpressionMatchIterator iterator = lineRegex.globalMatch(line, 0); - while (iterator.hasNext()) { - const QRegularExpressionMatch match = iterator.next(); - - if (match.hasCaptured(valueIndex)) - result.append(match.captured(valueIndex)); - else if (match.hasCaptured(quoteIndex)) - result.append(match.captured(quoteIndex)); - } - return result; -} - -QString CollectionParseError::errorString() const -{ - switch (errorNo) { - case NoError: - return COLLERR_OK; - case MainObjectMissing: - return COLLERR_MAINOBJECT; - case CollectionNameNotFound: - return COLLERR_COLLECTIONNAME; - case CollectionIsNotObject: - return COLLERR_COLLECTIONOBJ; - case ColumnsBlockIsNotArray: - return COLLERR_COLUMNARRAY; - case UnknownError: - default: - return COLLERR_UNKNOWN; - } -} - -CollectionDetails::CollectionDetails() - : d(new Private()) -{} - -CollectionDetails::CollectionDetails(const CollectionReference &reference) - : CollectionDetails() -{ - d->reference = reference; -} - -void CollectionDetails::resetData(const QJsonDocument &localDocument, - const QString &collectionToImport, - CollectionParseError *error) -{ - CollectionDetails importedCollection = fromLocalJson(localDocument, collectionToImport, error); - d->properties.swap(importedCollection.d->properties); - d->dataRecords.swap(importedCollection.d->dataRecords); -} - -CollectionDetails::CollectionDetails(const CollectionDetails &other) = default; - -CollectionDetails::~CollectionDetails() = default; - -void CollectionDetails::insertColumn(const QString &propertyName, - int colIdx, - const QVariant &defaultValue, - DataType type) -{ - if (containsPropertyName(propertyName)) - return; - - CollectionProperty property = {propertyName, type}; - if (d->isValidColumnId(colIdx)) { - d->properties.insert(colIdx, property); - } else { - colIdx = d->properties.size(); - d->properties.append(property); - } - - const QJsonValue defaultJsonValue = QJsonValue::fromVariant(defaultValue); - for (QJsonArray &record : d->dataRecords) - record.insert(colIdx, defaultJsonValue); - - markChanged(); -} - -bool CollectionDetails::removeColumns(int colIdx, int count) -{ - if (!d->isValidColumnId(colIdx)) - return false; - - int maxCount = d->properties.count() - colIdx; - count = std::min(maxCount, count); - - if (count < 1) - return false; - - d->properties.remove(colIdx, count); - - for (QJsonArray &record : d->dataRecords) { - QJsonArray newElement; - - auto elementItr = record.constBegin(); - auto elementEnd = elementItr + colIdx; - while (elementItr != elementEnd) - newElement.append(*(elementItr++)); - - elementItr += count; - elementEnd = record.constEnd(); - - while (elementItr != elementEnd) - newElement.append(*(elementItr++)); - - record = newElement; - } - - markChanged(); - - return true; -} - -void CollectionDetails::insertEmptyRows(int row, int count) -{ - if (count < 1) - return; - - row = qBound(0, row, rows()); - - insertRecords({}, row, count); - - markChanged(); -} - -bool CollectionDetails::removeRows(int row, int count) -{ - if (!d->isValidRowId(row)) - return false; - - int maxCount = d->dataRecords.count() - row; - count = std::min(maxCount, count); - - if (count < 1) - return false; - - d->dataRecords.remove(row, count); - markChanged(); - return true; -} - -bool CollectionDetails::setPropertyValue(int row, int column, const QVariant &value) -{ - if (!d->isValidRowId(row) || !d->isValidColumnId(column)) - return false; - - QVariant currentValue = data(row, column); - if (value == currentValue) - return false; - - QJsonArray &record = d->dataRecords[row]; - record.replace(column, variantToJsonValue(value, typeAt(column))); - markChanged(); - return true; -} - -bool CollectionDetails::setPropertyName(int column, const QString &value) -{ - if (!d->isValidColumnId(column)) - return false; - - const CollectionProperty &oldProperty = d->properties.at(column); - if (oldProperty.name == value) - return false; - - d->properties.replace(column, {value, oldProperty.type}); - - markChanged(); - return true; -} - -bool CollectionDetails::setPropertyType(int column, DataType type) -{ - if (!d->isValidColumnId(column)) - return false; - - bool changed = false; - CollectionProperty &property = d->properties[column]; - if (property.type != type) - changed = true; - - const DataType formerType = property.type; - property.type = type; - - for (QJsonArray &rowData : d->dataRecords) { - if (column < rowData.size()) { - const QJsonValue value = rowData.at(column); - const QVariant properTypedValue = valueToVariant(value, formerType); - const QJsonValue properTypedJsonValue = variantToJsonValue(properTypedValue, type); - rowData.replace(column, properTypedJsonValue); - changed = true; - } - } - - if (changed) - markChanged(); - - return changed; -} - -CollectionReference CollectionDetails::reference() const -{ - return d->reference; -} - -QVariant CollectionDetails::data(int row, int column) const -{ - if (!d->isValidRowId(row)) - return {}; - - if (!d->isValidColumnId(column)) - return {}; - - const QJsonValue cellValue = d->dataRecords.at(row).at(column); - - return cellValue.toVariant(); -} - -QString CollectionDetails::propertyAt(int column) const -{ - if (!d->isValidColumnId(column)) - return {}; - - return d->properties.at(column).name; -} - -CollectionDetails::DataType CollectionDetails::typeAt(int column) const -{ - if (!d->isValidColumnId(column)) - return {}; - - return d->properties.at(column).type; -} - -CollectionDetails::DataType CollectionDetails::typeAt(int row, int column) const -{ - if (!d->isValidRowId(row) || !d->isValidColumnId(column)) - return {}; - - const QJsonValue cellData = d->dataRecords.at(row).at(column); - return dataTypeFromJsonValue(cellData); -} - -DataTypeWarning::Warning CollectionDetails::cellWarningCheck(int row, int column) const -{ - const QJsonValue cellValue = d->dataRecords.at(row).at(column); - - const DataType columnType = typeAt(column); - const DataType cellType = typeAt(row, column); - - if (isEmptyJsonValue(cellValue)) - return DataTypeWarning::Warning::None; - - if ((columnType == DataType::String || columnType == DataType::Real) && cellType == DataType::Integer) - return DataTypeWarning::Warning::None; - - if (columnType == DataType::Url && cellType == DataType::String) - return DataTypeWarning::Warning::None; - - if (columnType == DataType::Image && (cellType == DataType::Url || cellType == DataType::String)) - return DataTypeWarning::Warning::None; - - if (columnType != cellType) - return DataTypeWarning::Warning::CellDataTypeMismatch; - - return DataTypeWarning::Warning::None; -} - -bool CollectionDetails::containsPropertyName(const QString &propertyName) const -{ - return Utils::anyOf(d->properties, [&propertyName](const CollectionProperty &property) { - return property.name == propertyName; - }); -} - -bool CollectionDetails::hasValidReference() const -{ - return d->reference.node.isValid() && d->reference.name.size(); -} - -bool CollectionDetails::isChanged() const -{ - return d->isChanged; -} - -int CollectionDetails::columns() const -{ - return d->properties.size(); -} - -int CollectionDetails::rows() const -{ - return d->dataRecords.size(); -} - -bool CollectionDetails::markSaved() -{ - if (d->isChanged) { - d->isChanged = false; - return true; - } - return false; -} - -void CollectionDetails::swap(CollectionDetails &other) -{ - d.swap(other.d); -} - -void CollectionDetails::resetReference(const CollectionReference &reference) -{ - if (d->reference != reference) { - d->reference = reference; - markChanged(); - } -} - -QString CollectionDetails::toJson() const -{ - QJsonArray exportedArray; - const int propertyCount = d->properties.count(); - - for (const QJsonArray &record : std::as_const(d->dataRecords)) { - const int valueCount = std::min(int(record.count()), propertyCount); - - QJsonObject exportedElement; - for (int i = 0; i < valueCount; ++i) { - const QJsonValue &value = record.at(i); - if (isEmptyJsonValue(value)) - exportedElement.insert(d->properties.at(i).name, QJsonValue::Null); - else - exportedElement.insert(d->properties.at(i).name, value); - } - - exportedArray.append(exportedElement); - } - - return QString::fromUtf8(QJsonDocument(exportedArray).toJson()); -} - -QString CollectionDetails::toCsv() const -{ - QString content; - - auto gotoNextLine = [&content]() { - if (content.size() && content.back() == ',') - content.back() = '\n'; - else - content += "\n"; - }; - - const int propertyCount = d->properties.count(); - if (propertyCount <= 0) - return ""; - - for (const CollectionProperty &property : std::as_const(d->properties)) - content += property.name + ','; - - gotoNextLine(); - - for (const QJsonArray &record : std::as_const(d->dataRecords)) { - const int valueCount = std::min(int(record.count()), propertyCount); - int i = 0; - for (; i < valueCount; ++i) { - const QJsonValue &value = record.at(i); - - if (value.isDouble()) - content += QString::number(value.toDouble()) + ','; - else if (value.isBool()) - content += value.toBool() ? "true," : "false,"; - else - content += value.toString() + ','; - } - - for (; i < propertyCount; ++i) - content += ','; - - gotoNextLine(); - } - - return content; -} - -QJsonObject CollectionDetails::toLocalJson() const -{ - QJsonObject collectionObject; - QJsonArray columnsArray; - QJsonArray dataArray; - - for (const CollectionProperty &property : std::as_const(d->properties)) { - QJsonObject columnObject; - columnObject.insert("name", property.name); - columnObject.insert("type", CollectionDataTypeModel::dataTypeToString(property.type)); - columnsArray.append(columnObject); - } - - for (const QJsonArray &record : std::as_const(d->dataRecords)) - dataArray.append(record); - - collectionObject.insert("columns", columnsArray); - collectionObject.insert("data", dataArray); - - return collectionObject; -} - -void CollectionDetails::registerDeclarativeType() -{ - typedef CollectionDetails::DataType DataType; - qRegisterMetaType("DataType"); - qmlRegisterUncreatableType("CollectionDetails", 1, 0, "DataType", "Enum type"); - - qRegisterMetaType("Warning"); - qmlRegisterUncreatableType("CollectionDetails", 1, 0, "Warning", "Enum type"); -} - -CollectionDetails CollectionDetails::fromImportedCsv(const QByteArray &document, - const bool &firstRowIsHeader) -{ - QStringList headers; - QJsonArray importedArray; - - QTextStream stream(document); - stream.setEncoding(QStringConverter::Latin1); - - if (firstRowIsHeader && !stream.atEnd()) { - headers = Utils::transform(csvReadLine(stream.readLine()), - [](const QString &value) -> QString { return value.trimmed(); }); - } - - while (!stream.atEnd()) { - const QStringList recordDataList = csvReadLine(stream.readLine()); - int column = -1; - QJsonObject recordData; - for (const QString &cellData : recordDataList) { - if (++column == headers.size()) { - QString proposalName; - int proposalId = column; - do - proposalName = QString("Column %1").arg(++proposalId); - while (headers.contains(proposalName)); - headers.append(proposalName); - } - recordData.insert(headers.at(column), cellData); - } - importedArray.append(recordData); - } - - return fromImportedJson(importedArray, headers); -} - -QList CollectionDetails::fromImportedJson(const QByteArray &jsonContent, - QJsonParseError *error) -{ - QJsonParseError parseError; - - QList collectionObjects = JsonCollectionParser::parseCollectionObjects(jsonContent, - error); - - if (error) - *error = parseError; - - if (parseError.error != QJsonParseError::NoError) - return {}; - return Utils::transform(collectionObjects, [](const CollectionObject &object) { - CollectionDetails result = fromImportedJson(object.array, object.propertyOrder); - result.d->reference.name = object.name; - return result; - }); -} - -CollectionDetails CollectionDetails::fromLocalJson(const QJsonDocument &document, - const QString &collectionName, - CollectionParseError *error) -{ - auto setError = [&error](CollectionParseError::ParseError parseError) { - if (error) - error->errorNo = parseError; - }; - - setError(CollectionParseError::NoError); - - if (document.isObject()) { - QJsonObject collectionMap = document.object(); - if (collectionMap.contains(collectionName)) { - QJsonValue collectionValue = collectionMap.value(collectionName); - if (collectionValue.isObject()) - return fromLocalCollection(collectionValue.toObject()); - else - setError(CollectionParseError::CollectionIsNotObject); - } else { - setError(CollectionParseError::CollectionNameNotFound); - } - } else { - setError(CollectionParseError::MainObjectMissing); - } - - return CollectionDetails{}; -} - -CollectionDetails &CollectionDetails::operator=(const CollectionDetails &other) -{ - CollectionDetails value(other); - swap(value); - return *this; -} - -void CollectionDetails::markChanged() -{ - d->isChanged = true; -} - -void CollectionDetails::insertRecords(const QJsonArray &record, int idx, int count) -{ - if (count < 1) - return; - - QJsonArray localRecord; - const int columnsCount = columns(); - for (int i = 0; i < columnsCount; i++) { - const QJsonValue originalCellData = record.at(i); - if (originalCellData.isArray()) - localRecord.append({}); - else - localRecord.append(originalCellData); - } - - if (idx > d->dataRecords.size() || idx < 0) - idx = d->dataRecords.size(); - - d->dataRecords.insert(idx, count, localRecord); -} - -CollectionDetails CollectionDetails::fromImportedJson(const QJsonArray &importedArray, - const QStringList &propertyPriority) -{ - QList columnData = getColumnsFromImportedJsonArray(importedArray); - if (!propertyPriority.isEmpty()) { - QMap priorityMap; - for (const QString &propertyName : propertyPriority) { - if (!priorityMap.contains(propertyName)) - priorityMap.insert(propertyName, priorityMap.size()); - } - const int lowestPriority = priorityMap.size(); - - Utils::sort(columnData, [&](const CollectionProperty &a, const CollectionProperty &b) { - return priorityMap.value(a.name, lowestPriority) - < priorityMap.value(b.name, lowestPriority); - }); - } - - QList localJsonArray; - for (const QJsonValue &importedRowValue : importedArray) { - QJsonObject importedRowObject = importedRowValue.toObject(); - QJsonArray localRow; - for (const CollectionProperty &property : columnData) - localRow.append(importedRowObject.value(property.name)); - localJsonArray.append(localRow); - } - CollectionDetails result; - result.d->properties = columnData; - result.d->dataRecords = localJsonArray; - result.markSaved(); - - return result; -} - -CollectionDetails CollectionDetails::fromLocalCollection(const QJsonObject &localCollection, - CollectionParseError *error) -{ - auto setError = [&error](CollectionParseError::ParseError parseError) { - if (error) - error->errorNo = parseError; - }; - - CollectionDetails result; - setError(CollectionParseError::NoError); - - if (localCollection.contains("columns")) { - const QJsonValue columnsValue = localCollection.value("columns"); - if (columnsValue.isArray()) { - const QJsonArray columns = columnsValue.toArray(); - for (const QJsonValue &columnValue : columns) { - if (columnValue.isObject()) { - const QJsonObject column = columnValue.toObject(); - const QString columnName = column.value("name").toString(); - if (!columnName.isEmpty()) { - result.insertColumn(columnName, - -1, - {}, - CollectionDataTypeModel::dataTypeFromString( - column.value("type").toString())); - } - } - } - - if (int columnsCount = result.columns()) { - const QJsonArray dataRecords = localCollection.value("data").toArray(); - for (const QJsonValue &dataRecordValue : dataRecords) { - QJsonArray dataRecord = dataRecordValue.toArray(); - while (dataRecord.count() > columnsCount) - dataRecord.removeLast(); - - result.insertRecords(dataRecord); - } - } - } else { - setError(CollectionParseError::ColumnsBlockIsNotArray); - return result; - } - } - - return result; -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h deleted file mode 100644 index 984feabc0a3..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "modelnode.h" - -#include - -QT_BEGIN_NAMESPACE -class QJsonObject; -struct QJsonParseError; -class QVariant; -QT_END_NAMESPACE - -namespace QmlDesigner { - -struct CollectionReference -{ - ModelNode node; - QString name; - - friend auto qHash(const CollectionReference &collection) - { - return qHash(collection.node) ^ ::qHash(collection.name); - } - - bool operator==(const CollectionReference &other) const - { - return node == other.node && name == other.name; - } - - bool operator!=(const CollectionReference &other) const { return !(*this == other); } -}; - -struct CollectionProperty; - -struct DataTypeWarning { -public: - enum Warning { None, CellDataTypeMismatch }; - Q_ENUM(Warning) - - Warning warning = None; - DataTypeWarning(Warning warning) - : warning(warning) - {} - - static QString getDataTypeWarningString(Warning warning) - { - return dataTypeWarnings.value(warning); - } - -private: - Q_GADGET - static const QMap dataTypeWarnings; -}; - -struct CollectionParseError -{ - enum ParseError { - NoError, - MainObjectMissing, - CollectionNameNotFound, - CollectionIsNotObject, - ColumnsBlockIsNotArray, - UnknownError - }; - - ParseError errorNo = ParseError::NoError; - QString errorString() const; -}; - -class CollectionDetails -{ - Q_GADGET - -public: - enum class DataType { Unknown, String, Url, Integer, Real, Boolean, Image, Color }; - Q_ENUM(DataType) - - explicit CollectionDetails(); - CollectionDetails(const CollectionReference &reference); - CollectionDetails(const CollectionDetails &other); - ~CollectionDetails(); - - void resetData(const QJsonDocument &localDocument, - const QString &collectionToImport, - CollectionParseError *error = nullptr); - - void insertColumn(const QString &propertyName, - int colIdx = -1, - const QVariant &defaultValue = {}, - DataType type = DataType::String); - bool removeColumns(int colIdx, int count = 1); - - void insertEmptyRows(int row = 0, int count = 1); - bool removeRows(int row, int count = 1); - bool setPropertyValue(int row, int column, const QVariant &value); - - bool setPropertyName(int column, const QString &value); - bool setPropertyType(int column, DataType type); - - CollectionReference reference() const; - QVariant data(int row, int column) const; - QString propertyAt(int column) const; - DataType typeAt(int column) const; - DataType typeAt(int row, int column) const; - DataTypeWarning::Warning cellWarningCheck(int row, int column) const; - bool containsPropertyName(const QString &propertyName) const; - - bool hasValidReference() const; - bool isChanged() const; - - int columns() const; - int rows() const; - - bool markSaved(); - - void swap(CollectionDetails &other); - void resetReference(const CollectionReference &reference); - - QString toJson() const; - QString toCsv() const; - QJsonObject toLocalJson() const; - - static void registerDeclarativeType(); - - static CollectionDetails fromImportedCsv(const QByteArray &document, - const bool &firstRowIsHeader = true); - static QList fromImportedJson(const QByteArray &jsonContent, - QJsonParseError *error = nullptr); - static CollectionDetails fromLocalJson(const QJsonDocument &document, - const QString &collectionName, - CollectionParseError *error = nullptr); - - CollectionDetails &operator=(const CollectionDetails &other); - -private: - void markChanged(); - void insertRecords(const QJsonArray &record, int idx = -1, int count = 1); - - static CollectionDetails fromImportedJson(const QJsonArray &importedArray, - const QStringList &propertyPriority = {}); - static CollectionDetails fromLocalCollection(const QJsonObject &localCollection, - CollectionParseError *error = nullptr); - - // The private data is supposed to be shared between the copies - class Private; - QSharedPointer d; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp deleted file mode 100644 index ab2278fb726..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp +++ /dev/null @@ -1,632 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "collectiondetailsmodel.h" - -#include "collectiondatatypemodel.h" -#include "collectioneditorutils.h" -#include "modelnode.h" - -#include - -#include -#include -#include - -#include -#include -#include -#include - -namespace QmlDesigner { - -CollectionDetailsModel::CollectionDetailsModel(QObject *parent) - : QAbstractTableModel(parent) -{ - connect(this, &CollectionDetailsModel::modelReset, this, &CollectionDetailsModel::updateEmpty); - connect(this, &CollectionDetailsModel::rowsInserted, this, &CollectionDetailsModel::updateEmpty); - connect(this, &CollectionDetailsModel::rowsRemoved, this, &CollectionDetailsModel::updateEmpty); -} - -QHash CollectionDetailsModel::roleNames() const -{ - static QHash roles; - if (roles.isEmpty()) { - roles.insert(QAbstractTableModel::roleNames()); - roles.insert(SelectedRole, "itemSelected"); - roles.insert(DataTypeRole, "dataType"); - roles.insert(ColumnDataTypeRole, "columnType"); - roles.insert(DataTypeWarningRole, "dataTypeWarning"); - } - return roles; -} - -int CollectionDetailsModel::rowCount([[maybe_unused]] const QModelIndex &parent) const -{ - return m_currentCollection.rows(); -} - -int CollectionDetailsModel::columnCount([[maybe_unused]] const QModelIndex &parent) const -{ - return m_currentCollection.columns(); -} - -QVariant CollectionDetailsModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return {}; - - QTC_ASSERT(m_currentCollection.hasValidReference(), return {}); - - if (role == SelectedRole) - return (index.column() == m_selectedColumn || index.row() == m_selectedRow); - - if (role == DataTypeRole) - return QVariant::fromValue(m_currentCollection.typeAt(index.row(), index.column())); - - if (role == ColumnDataTypeRole) - return QVariant::fromValue(m_currentCollection.typeAt(index.column())); - - if (role == Qt::EditRole) - return m_currentCollection.data(index.row(), index.column()); - - if (role == DataTypeWarningRole ) - return QVariant::fromValue(m_currentCollection.cellWarningCheck(index.row(), index.column())); - - return m_currentCollection.data(index.row(), index.column()).toString(); -} - -bool CollectionDetailsModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - if (!index.isValid()) - return {}; - - if (role == Qt::EditRole) { - DataTypeWarning::Warning prevWarning = m_currentCollection.cellWarningCheck(index.row(), index.column()); - bool changed = m_currentCollection.setPropertyValue(index.row(), index.column(), value); - - if (changed) { - QList roles = {Qt::DisplayRole, Qt::EditRole}; - - if (prevWarning != m_currentCollection.cellWarningCheck(index.row(), index.column())) - roles << DataTypeWarningRole; - - setHasUnsavedChanges(true); - emit dataChanged(index, index, roles); - } - - return true; - } - - return false; -} - -bool CollectionDetailsModel::setHeaderData(int section, - Qt::Orientation orientation, - const QVariant &value, - [[maybe_unused]] int role) -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - if (orientation == Qt::Vertical) - return false; - - bool headerChanged = m_currentCollection.setPropertyName(section, value.toString()); - if (headerChanged) - emit this->headerDataChanged(orientation, section, section); - - return headerChanged; -} - -bool CollectionDetailsModel::insertRows(int row, int count, [[maybe_unused]] const QModelIndex &parent) -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - if (count < 1) - return false; - - row = qBound(0, row, rowCount()); - - beginInsertRows({}, row, row + count - 1); - m_currentCollection.insertEmptyRows(row, count); - endInsertRows(); - setHasUnsavedChanges(true); - - return true; -} - -bool CollectionDetailsModel::removeColumns(int column, int count, const QModelIndex &parent) -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - if (column < 0 || column >= columnCount(parent) || count < 1) - return false; - - count = std::min(count, columnCount(parent) - column); - beginRemoveColumns(parent, column, column + count - 1); - bool columnsRemoved = m_currentCollection.removeColumns(column, count); - endRemoveColumns(); - - if (!columnCount(parent)) - removeRows(0, rowCount(parent), parent); - - ensureSingleCell(); - setHasUnsavedChanges(true); - return columnsRemoved; -} - -bool CollectionDetailsModel::removeRows(int row, int count, const QModelIndex &parent) -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - if (row < 0 || row >= rowCount(parent) || count < 1) - return false; - - count = std::min(count, rowCount(parent) - row); - beginRemoveRows(parent, row, row + count - 1); - bool rowsRemoved = m_currentCollection.removeRows(row, count); - endRemoveRows(); - - ensureSingleCell(); - setHasUnsavedChanges(true); - return rowsRemoved; -} - -Qt::ItemFlags CollectionDetailsModel::flags(const QModelIndex &index) const -{ - if (!index.isValid()) - return {}; - - return {Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable}; -} - -QVariant CollectionDetailsModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (orientation == Qt::Horizontal) { - if (role == DataTypeRole) - return CollectionDataTypeModel::dataTypeToString(m_currentCollection.typeAt(section)); - else - return m_currentCollection.propertyAt(section); - } - - if (orientation == Qt::Vertical) - return section + 1; - - return {}; -} - -CollectionDetails::DataType CollectionDetailsModel::propertyDataType(int column) const -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return CollectionDetails::DataType::String); - - return m_currentCollection.typeAt(column); -} - -int CollectionDetailsModel::selectedColumn() const -{ - return m_selectedColumn; -} - -int CollectionDetailsModel::selectedRow() const -{ - return m_selectedRow; -} - -QString CollectionDetailsModel::propertyName(int column) const -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return {}); - - return m_currentCollection.propertyAt(column); -} - -QString CollectionDetailsModel::propertyType(int column) const -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return {}); - - return CollectionDataTypeModel::dataTypeToString(m_currentCollection.typeAt(column)); -} - -bool CollectionDetailsModel::isPropertyAvailable(const QString &name) -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - return m_currentCollection.containsPropertyName(name); -} - -bool CollectionDetailsModel::addColumn(int column, const QString &name, const QString &propertyType) -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - if (m_currentCollection.containsPropertyName(name)) - return false; - - if (column < 0 || column > columnCount()) - column = columnCount(); - - beginInsertColumns({}, column, column); - m_currentCollection.insertColumn(name, - column, - {}, - CollectionDataTypeModel::dataTypeFromString(propertyType)); - endInsertColumns(); - setHasUnsavedChanges(true); - return m_currentCollection.containsPropertyName(name); -} - -bool CollectionDetailsModel::selectColumn(int section) -{ - if (m_selectedColumn == section) - return false; - - const int columns = columnCount(); - - if (section >= columns) - section = columns - 1; - - selectRow(-1); - - const int rows = rowCount(); - const int previousColumn = m_selectedColumn; - - m_selectedColumn = section; - emit this->selectedColumnChanged(m_selectedColumn); - - auto notifySelectedDataChanged = [this, columns, rows](int notifyingColumn) { - if (notifyingColumn > -1 && notifyingColumn < columns && rows) { - emit dataChanged(index(0, notifyingColumn), - index(rows - 1, notifyingColumn), - {SelectedRole}); - } - }; - - notifySelectedDataChanged(previousColumn); - notifySelectedDataChanged(m_selectedColumn); - - return true; -} - -bool CollectionDetailsModel::renameColumn(int section, const QString &newValue) -{ - return setHeaderData(section, Qt::Horizontal, newValue); -} - -bool CollectionDetailsModel::setPropertyType(int column, const QString &newValue) -{ - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - bool changed = m_currentCollection.setPropertyType(column, - CollectionDataTypeModel::dataTypeFromString( - newValue)); - if (changed) { - emit headerDataChanged(Qt::Horizontal, column, column); - emit dataChanged( - index(0, column), - index(rowCount() - 1, column), - {Qt::DisplayRole, Qt::EditRole, DataTypeRole, DataTypeWarningRole, ColumnDataTypeRole}); - } - - setHasUnsavedChanges(true); - return changed; -} - -bool CollectionDetailsModel::selectRow(int row) -{ - if (m_selectedRow == row) - return false; - - const int rows = rowCount(); - - if (row >= rows) - row = rows - 1; - - selectColumn(-1); - - const int columns = columnCount(); - const int previousRow = m_selectedRow; - - m_selectedRow = row; - emit this->selectedRowChanged(m_selectedRow); - - auto notifySelectedDataChanged = [this, rows, columns](int notifyingRow) { - if (notifyingRow > -1 && notifyingRow < rows && columns) - emit dataChanged(index(notifyingRow, 0), index(notifyingRow, columns - 1), {SelectedRole}); - }; - - notifySelectedDataChanged(previousRow); - notifySelectedDataChanged(m_selectedRow); - - return true; -} - -void CollectionDetailsModel::deselectAll() -{ - selectColumn(-1); - selectRow(-1); -} - -void CollectionDetailsModel::loadCollection(const ModelNode &sourceNode, const QString &collection) -{ - QString fileName = CollectionEditorUtils::getSourceCollectionPath(sourceNode); - - CollectionReference newReference{sourceNode, collection}; - bool alreadyOpen = m_openedCollections.contains(newReference); - - if (alreadyOpen) { - if (m_currentCollection.reference() != newReference) { - deselectAll(); - beginResetModel(); - switchToCollection(newReference); - ensureSingleCell(); - endResetModel(); - } - } else { - deselectAll(); - switchToCollection(newReference); - loadJsonCollection(fileName, collection); - } -} - -void CollectionDetailsModel::removeCollection(const ModelNode &sourceNode, const QString &collection) -{ - CollectionReference collectionRef{sourceNode, collection}; - if (!m_openedCollections.contains(collectionRef)) - return; - - if (m_currentCollection.reference() == collectionRef) - loadCollection({}, {}); - - m_openedCollections.remove(collectionRef); -} - -void CollectionDetailsModel::removeAllCollections() -{ - loadCollection({}, {}); - m_openedCollections.clear(); -} - -void CollectionDetailsModel::renameCollection(const ModelNode &sourceNode, - const QString &oldName, - const QString &newName) -{ - CollectionReference oldRef{sourceNode, oldName}; - if (!m_openedCollections.contains(oldRef)) - return; - - CollectionReference newReference{sourceNode, newName}; - bool collectionIsSelected = m_currentCollection.reference() == oldRef; - CollectionDetails collection = m_openedCollections.take(oldRef); - collection.resetReference(newReference); - m_openedCollections.insert(newReference, collection); - - if (collectionIsSelected) - setCollectionName(newName); -} - -bool CollectionDetailsModel::saveDataStoreCollections() -{ - const ModelNode node = m_currentCollection.reference().node; - Utils::expected_str jsonContents = m_jsonFilePath.fileContents(); - if (!jsonContents.has_value()) { - qWarning() << __FUNCTION__ << jsonContents.error(); - return false; - } - - QJsonParseError jpe; - QJsonDocument document = QJsonDocument::fromJson(jsonContents.value(), &jpe); - - if (jpe.error == QJsonParseError::NoError) { - QJsonObject obj = document.object(); - - QList collectionsToBeSaved; - for (CollectionDetails &openedCollection : m_openedCollections) { - const CollectionReference reference = openedCollection.reference(); - if (reference.node == node) { - obj.insert(reference.name, openedCollection.toLocalJson()); - collectionsToBeSaved << openedCollection; - } - } - - document.setObject(obj); - - if (CollectionEditorUtils::writeToJsonDocument(m_jsonFilePath, document)) { - const CollectionReference currentReference = m_currentCollection.reference(); - for (CollectionDetails &collection : collectionsToBeSaved) { - collection.markSaved(); - const CollectionReference reference = collection.reference(); - if (reference != currentReference) - closeCollectionIfSaved(reference); - } - setHasUnsavedChanges(false); - return true; - } - } - return false; -} - -bool CollectionDetailsModel::exportCollection(const QUrl &url) -{ - using Core::EditorManager; - using Utils::FilePath; - using Utils::TextFileFormat; - - QTC_ASSERT(m_currentCollection.hasValidReference(), return false); - - bool saved = false; - const FilePath filePath = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() - : url.toString()); - const QString saveFormat = filePath.toFileInfo().suffix().toLower(); - const QString content = saveFormat == "csv" ? m_currentCollection.toCsv() - : m_currentCollection.toJson(); - - TextFileFormat textFileFormat; - textFileFormat.codec = EditorManager::defaultTextCodec(); - textFileFormat.lineTerminationMode = EditorManager::defaultLineEnding(); - QString errorMessage; - saved = textFileFormat.writeFile(filePath, content, &errorMessage); - - if (!saved) - qWarning() << Q_FUNC_INFO << "Unable to write file" << errorMessage; - - return saved; -} - -const CollectionDetails CollectionDetailsModel::upToDateConstCollection( - const CollectionReference &reference) const -{ - using Utils::FilePath; - using Utils::FileReader; - CollectionDetails collection; - - if (m_openedCollections.contains(reference)) { - collection = m_openedCollections.value(reference); - } else { - QUrl url = CollectionEditorUtils::getSourceCollectionPath(reference.node); - FilePath path = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() - : url.toString()); - FileReader file; - - if (!file.fetch(path)) - return collection; - - QJsonParseError jpe; - QJsonDocument document = QJsonDocument::fromJson(file.data(), &jpe); - - if (jpe.error != QJsonParseError::NoError) - return collection; - - collection = CollectionDetails::fromLocalJson(document, reference.name); - collection.resetReference(reference); - } - return collection; -} - -bool CollectionDetailsModel::collectionHasColumn(const CollectionReference &reference, - const QString &columnName) const -{ - const CollectionDetails collection = upToDateConstCollection(reference); - return collection.containsPropertyName(columnName); -} - -QString CollectionDetailsModel::getFirstColumnName(const CollectionReference &reference) const -{ - const CollectionDetails collection = upToDateConstCollection(reference); - return collection.propertyAt(0); -} - -void CollectionDetailsModel::updateEmpty() -{ - bool isEmptyNow = rowCount() == 0; - if (m_isEmpty != isEmptyNow) { - m_isEmpty = isEmptyNow; - emit isEmptyChanged(m_isEmpty); - } -} - -void CollectionDetailsModel::switchToCollection(const CollectionReference &collection) -{ - if (m_currentCollection.reference() == collection) - return; - - closeCurrentCollectionIfSaved(); - - if (!m_openedCollections.contains(collection)) - m_openedCollections.insert(collection, CollectionDetails(collection)); - - m_currentCollection = m_openedCollections.value(collection); - - setCollectionName(collection.name); -} - -void CollectionDetailsModel::closeCollectionIfSaved(const CollectionReference &collection) -{ - if (!m_openedCollections.contains(collection)) - return; - - const CollectionDetails &collectionDetails = m_openedCollections.value(collection); - - if (!collectionDetails.isChanged()) - m_openedCollections.remove(collection); -} - -void CollectionDetailsModel::closeCurrentCollectionIfSaved() -{ - if (m_currentCollection.hasValidReference()) { - closeCollectionIfSaved(m_currentCollection.reference()); - m_currentCollection = CollectionDetails{}; - } -} - -void CollectionDetailsModel::loadJsonCollection(const QString &filePath, const QString &collection) -{ - QJsonDocument document = readJsonFile(filePath); - - beginResetModel(); - m_currentCollection.resetData(document, collection); - ensureSingleCell(); - endResetModel(); -} - -void CollectionDetailsModel::ensureSingleCell() -{ - if (!m_currentCollection.hasValidReference()) - return; - - if (!columnCount()) - addColumn(0, "Column 1", "String"); - - if (!rowCount()) - insertRow(0); - - updateEmpty(); -} - -QJsonDocument CollectionDetailsModel::readJsonFile(const QUrl &url) -{ - using Utils::FilePath; - using Utils::FileReader; - FilePath path = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() : url.toString()); - FileReader file; - - if (!file.fetch(path)) { - emit warning(tr("File reading problem"), file.errorString()); - return {}; - } - - QJsonParseError jpe; - QJsonDocument document = QJsonDocument::fromJson(file.data(), &jpe); - - if (jpe.error != QJsonParseError::NoError) - emit warning(tr("Json parse error"), jpe.errorString()); - - return document; -} - -void CollectionDetailsModel::setCollectionName(const QString &newCollectionName) -{ - if (m_collectionName != newCollectionName) { - m_collectionName = newCollectionName; - emit this->collectionNameChanged(m_collectionName); - } -} - -QString CollectionDetailsModel::warningToString(DataTypeWarning::Warning warning) const -{ - return DataTypeWarning::getDataTypeWarningString(warning); -} - -void CollectionDetailsModel::setJsonFilePath(const Utils::FilePath &filePath) -{ - m_jsonFilePath = filePath; -} - -void CollectionDetailsModel::setHasUnsavedChanges(bool val) -{ - if (m_hasUnsavedChanges == val) - return; - m_hasUnsavedChanges = val; - emit hasUnsavedChangesChanged(); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h deleted file mode 100644 index fbe90a6f567..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "collectiondetails.h" - -#include - -#include -#include - -namespace QmlDesigner { - -class ModelNode; - -class CollectionDetailsModel : public QAbstractTableModel -{ - Q_OBJECT - - Q_PROPERTY(QString collectionName MEMBER m_collectionName NOTIFY collectionNameChanged) - Q_PROPERTY(int selectedColumn READ selectedColumn WRITE selectColumn NOTIFY selectedColumnChanged) - Q_PROPERTY(int selectedRow READ selectedRow WRITE selectRow NOTIFY selectedRowChanged) - Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) - Q_PROPERTY(bool hasUnsavedChanges MEMBER m_hasUnsavedChanges WRITE setHasUnsavedChanges NOTIFY hasUnsavedChangesChanged) - -public: - enum DataRoles { SelectedRole = Qt::UserRole + 1, DataTypeRole, ColumnDataTypeRole, DataTypeWarningRole }; - explicit CollectionDetailsModel(QObject *parent = nullptr); - - QHash roleNames() const override; - int rowCount(const QModelIndex &parent = {}) const override; - int columnCount(const QModelIndex &parent = {}) const override; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; - bool setHeaderData(int section, - Qt::Orientation orientation, - const QVariant &value, - int role = Qt::EditRole) override; - bool insertRows(int row, int count, const QModelIndex &parent = {}) override; - bool removeColumns(int column, int count, const QModelIndex &parent = {}) override; - bool removeRows(int row, int count, const QModelIndex &parent = {}) override; - - Qt::ItemFlags flags(const QModelIndex &index) const override; - QVariant headerData(int section, - Qt::Orientation orientation, - int role = Qt::DisplayRole) const override; - - CollectionDetails::DataType propertyDataType(int column) const; - - int selectedColumn() const; - int selectedRow() const; - Q_INVOKABLE QString propertyName(int column) const; - Q_INVOKABLE QString propertyType(int column) const; - - Q_INVOKABLE bool isPropertyAvailable(const QString &name); - Q_INVOKABLE bool addColumn(int column, const QString &name, const QString &propertyType = {}); - Q_INVOKABLE bool selectColumn(int section); - Q_INVOKABLE bool renameColumn(int section, const QString &newValue); - Q_INVOKABLE bool setPropertyType(int column, const QString &newValue); - Q_INVOKABLE bool selectRow(int row); - Q_INVOKABLE void deselectAll(); - Q_INVOKABLE QString warningToString(DataTypeWarning::Warning warning) const; - - void setJsonFilePath(const Utils::FilePath &filePath); - void loadCollection(const ModelNode &sourceNode, const QString &collection); - void removeCollection(const ModelNode &sourceNode, const QString &collection); - void removeAllCollections(); - void renameCollection(const ModelNode &sourceNode, const QString &oldName, const QString &newName); - - Q_INVOKABLE bool saveDataStoreCollections(); - Q_INVOKABLE bool exportCollection(const QUrl &url); - - const CollectionDetails upToDateConstCollection(const CollectionReference &reference) const; - bool collectionHasColumn(const CollectionReference &reference, const QString &columnName) const; - QString getFirstColumnName(const CollectionReference &reference) const; - void setHasUnsavedChanges(bool val); - -signals: - void collectionNameChanged(const QString &collectionName); - void selectedColumnChanged(int); - void selectedRowChanged(int); - void isEmptyChanged(bool); - void hasUnsavedChangesChanged(); - void warning(const QString &title, const QString &body); - -private slots: - void updateEmpty(); - -private: - void switchToCollection(const CollectionReference &collection); - void closeCollectionIfSaved(const CollectionReference &collection); - void closeCurrentCollectionIfSaved(); - void setCollectionName(const QString &newCollectionName); - void loadJsonCollection(const QString &filePath, const QString &collection); - void ensureSingleCell(); - QJsonDocument readJsonFile(const QUrl &url); - - Utils::FilePath m_jsonFilePath; - QHash m_openedCollections; - CollectionDetails m_currentCollection; - bool m_isEmpty = true; - bool m_hasUnsavedChanges = false; - int m_selectedColumn = -1; - int m_selectedRow = -1; - - QString m_collectionName; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp deleted file mode 100644 index 2cc6ac05a6b..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "collectiondetailssortfiltermodel.h" - -#include "collectiondetailsmodel.h" -#include "collectioneditorutils.h" - -#include - -namespace QmlDesigner { - -CollectionDetailsSortFilterModel::CollectionDetailsSortFilterModel(QObject *parent) - : QSortFilterProxyModel(parent) -{ - connect(this, &CollectionDetailsSortFilterModel::rowsInserted, - this, &CollectionDetailsSortFilterModel::updateRowCountChanges); - connect(this, &CollectionDetailsSortFilterModel::rowsRemoved, - this, &CollectionDetailsSortFilterModel::updateRowCountChanges); - connect(this, &CollectionDetailsSortFilterModel::modelReset, - this, &CollectionDetailsSortFilterModel::updateRowCountChanges); - - setDynamicSortFilter(true); -} - -void CollectionDetailsSortFilterModel::setSourceModel(CollectionDetailsModel *model) -{ - m_source = model; - Super::setSourceModel(model); - connect(m_source, &CollectionDetailsModel::selectedColumnChanged, - this, &CollectionDetailsSortFilterModel::updateSelectedColumn); - - connect(m_source, &CollectionDetailsModel::selectedRowChanged, - this, &CollectionDetailsSortFilterModel::updateSelectedRow); -} - -int CollectionDetailsSortFilterModel::selectedRow() const -{ - QTC_ASSERT(m_source, return -1); - - return mapFromSource(m_source->index(m_source->selectedRow(), 0)).row(); -} - -int CollectionDetailsSortFilterModel::selectedColumn() const -{ - QTC_ASSERT(m_source, return -1); - - return mapFromSource(m_source->index(0, m_source->selectedColumn())).column(); -} - -bool CollectionDetailsSortFilterModel::selectRow(int row) -{ - QTC_ASSERT(m_source, return false); - - return m_source->selectRow(mapToSource(index(row, 0)).row()); -} - -bool CollectionDetailsSortFilterModel::selectColumn(int column) -{ - QTC_ASSERT(m_source, return false); - - return m_source->selectColumn(mapToSource(index(0, column)).column()); -} - -void CollectionDetailsSortFilterModel::deselectAll() -{ - QTC_ASSERT(m_source, return); - m_source->deselectAll(); -} - -CollectionDetailsSortFilterModel::~CollectionDetailsSortFilterModel() = default; - -bool CollectionDetailsSortFilterModel::filterAcceptsRow(int sourceRow, - const QModelIndex &sourceParent) const -{ - QTC_ASSERT(m_source, return false); - QModelIndex sourceIndex(m_source->index(sourceRow, 0, sourceParent)); - return sourceIndex.isValid(); -} - -bool CollectionDetailsSortFilterModel::lessThan(const QModelIndex &sourceleft, - const QModelIndex &sourceRight) const -{ - QTC_ASSERT(m_source, return false); - - if (sourceleft.column() == sourceRight.column()) { - int column = sourceleft.column(); - CollectionDetails::DataType columnType = m_source->propertyDataType(column); - return CollectionEditorUtils::variantIslessThan(sourceleft.data(), - sourceRight.data(), - columnType); - } - - return false; -} - -void CollectionDetailsSortFilterModel::updateEmpty() -{ - bool newValue = rowCount() == 0; - if (m_isEmpty != newValue) { - m_isEmpty = newValue; - emit isEmptyChanged(m_isEmpty); - } -} - -void CollectionDetailsSortFilterModel::updateSelectedRow() -{ - const int upToDateSelectedRow = selectedRow(); - if (m_selectedRow == upToDateSelectedRow) - return; - - const int rows = rowCount(); - const int columns = columnCount(); - const int previousRow = m_selectedRow; - - m_selectedRow = upToDateSelectedRow; - emit this->selectedRowChanged(m_selectedRow); - - auto notifySelectedDataChanged = [this, rows, columns](int notifyingRow) { - if (notifyingRow > -1 && notifyingRow < rows && columns) { - emit dataChanged(index(notifyingRow, 0), - index(notifyingRow, columns - 1), - {CollectionDetailsModel::SelectedRole}); - } - }; - - notifySelectedDataChanged(previousRow); - notifySelectedDataChanged(m_selectedRow); -} - -void CollectionDetailsSortFilterModel::updateSelectedColumn() -{ - const int upToDateSelectedColumn = selectedColumn(); - if (m_selectedColumn == upToDateSelectedColumn) - return; - - const int rows = rowCount(); - const int columns = columnCount(); - const int previousColumn = m_selectedColumn; - - m_selectedColumn = upToDateSelectedColumn; - emit this->selectedColumnChanged(m_selectedColumn); - - auto notifySelectedDataChanged = [this, rows, columns](int notifyingCol) { - if (notifyingCol > -1 && notifyingCol < columns && rows) { - emit dataChanged(index(0, notifyingCol), - index(rows - 1, notifyingCol), - {CollectionDetailsModel::SelectedRole}); - } - }; - - notifySelectedDataChanged(previousColumn); - notifySelectedDataChanged(m_selectedColumn); -} - -void CollectionDetailsSortFilterModel::updateRowCountChanges() -{ - updateEmpty(); - updateSelectedRow(); - invalidate(); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h deleted file mode 100644 index 10f6e09b057..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include -#include - -namespace QmlDesigner { - -class CollectionDetailsModel; - -class CollectionDetailsSortFilterModel : public QSortFilterProxyModel -{ - Q_OBJECT - - Q_PROPERTY(int selectedColumn READ selectedColumn WRITE selectColumn NOTIFY selectedColumnChanged) - Q_PROPERTY(int selectedRow READ selectedRow WRITE selectRow NOTIFY selectedRowChanged) - Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) - - using Super = QSortFilterProxyModel; - -public: - explicit CollectionDetailsSortFilterModel(QObject *parent = nullptr); - virtual ~CollectionDetailsSortFilterModel(); - - void setSourceModel(CollectionDetailsModel *model); - - int selectedRow() const; - int selectedColumn() const; - - Q_INVOKABLE bool selectRow(int row); - Q_INVOKABLE bool selectColumn(int column); - Q_INVOKABLE void deselectAll(); - -signals: - void selectedColumnChanged(int); - void selectedRowChanged(int); - void isEmptyChanged(bool); - -protected: - using Super::setSourceModel; - bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; - bool lessThan(const QModelIndex &sourceleft, const QModelIndex &sourceRight) const override; - -private: - void updateEmpty(); - void updateSelectedRow(); - void updateSelectedColumn(); - void updateRowCountChanges(); - - QPointer m_source; - int m_selectedColumn = -1; - int m_selectedRow = -1; - bool m_isEmpty = true; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h deleted file mode 100644 index 2228c58518c..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -namespace QmlDesigner::CollectionEditorConstants { - -enum class SourceFormat { Unknown, Json }; - -inline constexpr char SOURCEFILE_PROPERTY[] = "source"; -inline constexpr char ALLMODELS_PROPERTY[] = "allModels"; -inline constexpr char JSONCHILDMODELNAME_PROPERTY[] = "modelName"; - -inline constexpr char COLLECTIONMODEL_IMPORT[] = "QtQuick.Studio.Utils"; -inline constexpr char JSONCOLLECTIONMODEL_TYPENAME[] = "QtQuick.Studio.Utils.JsonListModel"; -inline constexpr char JSONCOLLECTIONCHILDMODEL_TYPENAME[] = "QtQuick.Studio.Utils.ChildListModel"; -inline constexpr char JSONBACKEND_TYPENAME[] = "JsonData"; - -inline constexpr QStringView DEFAULT_DATA_JSON_FILENAME = u"data.json"; -inline constexpr QStringView DEFAULT_MODELS_JSON_FILENAME = u"models.json"; -inline constexpr QStringView DEFAULT_DATASTORE_QML_FILENAME = u"DataStore.qml"; -inline constexpr QStringView DEFAULT_JSONDATA_QML_FILENAME = u"JsonData.qml"; - -} // namespace QmlDesigner::CollectionEditorConstants diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp deleted file mode 100644 index a991e2d729c..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "collectioneditorutils.h" - -#include "collectiondatatypemodel.h" -#include "collectioneditorconstants.h" -#include "model.h" -#include "nodemetainfo.h" -#include "propertymetainfo.h" -#include "variantproperty.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -using DataType = QmlDesigner::CollectionDetails::DataType; - -namespace { - -using CollectionDataVariant = std::variant; - -inline bool operator<(const QColor &a, const QColor &b) -{ - return a.name(QColor::HexArgb) < b.name(QColor::HexArgb); -} - -inline CollectionDataVariant valueToVariant(const QVariant &value, DataType type) -{ - switch (type) { - case DataType::String: - return value.toString(); - case DataType::Real: - return value.toDouble(); - case DataType::Integer: - return value.toInt(); - case DataType::Boolean: - return value.toBool(); - case DataType::Color: - return value.value(); - case DataType::Image: - case DataType::Url: - return value.value(); - default: - return false; - } -} - -struct LessThanVisitor -{ - template - bool operator()(const T1 &a, const T2 &b) const - { - return CollectionDataVariant(a).index() < CollectionDataVariant(b).index(); - } - - template - bool operator()(const T &a, const T &b) const - { - return a < b; - } -}; - -} // namespace - -namespace QmlDesigner::CollectionEditorUtils { - -bool variantIslessThan(const QVariant &a, const QVariant &b, DataType type) -{ - return std::visit(LessThanVisitor{}, valueToVariant(a, type), valueToVariant(b, type)); -} - -bool canAcceptCollectionAsModel(const ModelNode &node) -{ - const NodeMetaInfo nodeMetaInfo = node.metaInfo(); - if (!nodeMetaInfo.isValid()) - return false; - - const PropertyMetaInfo modelProperty = nodeMetaInfo.property("model"); - if (!modelProperty.isValid()) - return false; - - return modelProperty.isWritable() && !modelProperty.isPrivate() - && modelProperty.propertyType().isVariant(); -} - -bool hasTextRoleProperty(const ModelNode &node) -{ - const NodeMetaInfo nodeMetaInfo = node.metaInfo(); - if (!nodeMetaInfo.isValid()) - return false; - - const PropertyMetaInfo textRoleProperty = nodeMetaInfo.property("textRole"); - if (!textRoleProperty.isValid()) - return false; - - return textRoleProperty.isWritable() && !textRoleProperty.isPrivate() - && textRoleProperty.propertyType().isString(); -} - -QString getSourceCollectionPath(const ModelNode &dataStoreNode) -{ - using Utils::FilePath; - if (!dataStoreNode.isValid()) - return {}; - - const QUrl dataStoreUrl = dataStoreNode.model()->fileUrl(); - QUrl sourceValue = dataStoreNode.property("source").toVariantProperty().value().toUrl(); - - QUrl sourceUrl = sourceValue.isRelative() ? dataStoreUrl.resolved(sourceValue) : sourceValue; - - const FilePath expectedFile = FilePath::fromUrl(sourceUrl); - - if (expectedFile.isFile() && expectedFile.exists()) - return expectedFile.toFSPathString(); - - const FilePath defaultJsonFile = FilePath::fromUrl( - dataStoreUrl.resolved(CollectionEditorConstants::DEFAULT_MODELS_JSON_FILENAME.toString())); - - if (defaultJsonFile.exists()) - return defaultJsonFile.toFSPathString(); - - return {}; -} - -QJsonObject defaultCollection() -{ - QJsonObject collectionObject; - - QJsonArray columns; - QJsonObject defaultColumn; - defaultColumn.insert("name", "Column 1"); - defaultColumn.insert("type", CollectionDataTypeModel::dataTypeToString(DataType::String)); - columns.append(defaultColumn); - - QJsonArray collectionData; - QJsonArray cellData; - cellData.append(QString{}); - collectionData.append(cellData); - - collectionObject.insert("columns", columns); - collectionObject.insert("data", collectionData); - - return collectionObject; -} - -QJsonObject defaultColorCollection() -{ - using Utils::FilePath; - using Utils::FileReader; - const FilePath templatePath = findFile(Core::ICore::resourcePath(), "Colors.json.tpl"); - - FileReader fileReader; - if (!fileReader.fetch(templatePath)) { - qWarning() << __FUNCTION__ << "Can't read the content of the file" << templatePath; - return {}; - } - - QJsonParseError parseError; - const QList collections = CollectionDetails::fromImportedJson(fileReader.data(), - &parseError); - - if (parseError.error != QJsonParseError::NoError) { - qWarning() << __FUNCTION__ << "Error in template file" << parseError.errorString(); - return {}; - } - - if (!collections.size()) { - qWarning() << __FUNCTION__ << "Can not generate collections from template file!"; - return {}; - } - - const CollectionDetails collection = collections.first(); - return collection.toLocalJson(); -} - -Utils::FilePath findFile(const Utils::FilePath &path, const QString &fileName) -{ - QDirIterator it(path.toString(), QDirIterator::Subdirectories); - - while (it.hasNext()) { - QFileInfo file(it.next()); - if (file.isDir()) - continue; - - if (file.fileName() == fileName) - return Utils::FilePath::fromFileInfo(file); - } - return {}; -} - -bool writeToJsonDocument(const Utils::FilePath &path, const QJsonDocument &document, QString *errorString) -{ - Core::FileChangeBlocker fileBlocker(path); - Utils::FileSaver jsonFile(path); - if (jsonFile.write(document.toJson())) - jsonFile.finalize(); - if (errorString) - *errorString = jsonFile.errorString(); - - return !jsonFile.hasError(); -} - -} // namespace QmlDesigner::CollectionEditorUtils diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h deleted file mode 100644 index 7afc6f233fe..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "collectiondetails.h" - -QT_BEGIN_NAMESPACE -class QJsonArray; -class QJsonObject; -QT_END_NAMESPACE - -namespace Utils { -class FilePath; -} - -namespace QmlDesigner::CollectionEditorUtils { - -bool variantIslessThan(const QVariant &a, const QVariant &b, CollectionDetails::DataType type); - -QString getSourceCollectionPath(const QmlDesigner::ModelNode &dataStoreNode); - -Utils::FilePath findFile(const Utils::FilePath &path, const QString &fileName); - -bool writeToJsonDocument(const Utils::FilePath &path, - const QJsonDocument &document, - QString *errorString = nullptr); - -bool canAcceptCollectionAsModel(const ModelNode &node); - -bool hasTextRoleProperty(const ModelNode &node); - -QJsonObject defaultCollection(); - -QJsonObject defaultColorCollection(); - -} // namespace QmlDesigner::CollectionEditorUtils diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionjsonparser.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionjsonparser.cpp deleted file mode 100644 index 630abb7b4a4..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionjsonparser.cpp +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright (C) 2024 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "collectionjsonparser.h" - -#include -#include -#include - -#include -#include -#include - -namespace QmlDesigner { - -/** - * @brief A json object is a plain object, if it has only primitive properties (not arrays or objects) - * @return true if @param jsonObject is a plain object - */ -inline static bool isPlainObject(const QJsonObject &jsonObj) -{ - return !Utils::anyOf(jsonObj, [](const QJsonValueConstRef &val) { - return val.isArray() || val.isObject(); - }); -} - -static bool isPlainObject(const QJsonValueConstRef &value) -{ - if (!value.isObject()) - return false; - return isPlainObject(value.toObject()); -} - -static QJsonArray parsePlainObject(const QJsonObject &jsonObj) -{ - QJsonObject result; - auto item = jsonObj.constBegin(); - const auto itemEnd = jsonObj.constEnd(); - while (item != itemEnd) { - QJsonValueConstRef ref = item.value(); - if (!ref.isArray() && !ref.isObject()) - result.insert(item.key(), ref); - ++item; - } - if (!result.isEmpty()) - return QJsonArray{result}; - - return {}; -} - -static QJsonArray parseArray(const QJsonArray &array, - QList &plainCollections, - JsonKeyChain &chainTracker) -{ - chainTracker.append(0); - QJsonArray plainArray; - int i = -1; - for (const QJsonValueConstRef &item : array) { - chainTracker.last() = ++i; - if (isPlainObject(item)) { - const QJsonObject plainObject = item.toObject(); - if (plainObject.count()) - plainArray.append(plainObject); - } else if (item.isArray()) { - parseArray(item.toArray(), plainCollections, chainTracker); - } - } - chainTracker.removeLast(); - return plainArray; -} - -static void parseObject(const QJsonObject &jsonObj, - QList &plainCollections, - JsonKeyChain &chainTracker) -{ - chainTracker.append(QString{}); - auto item = jsonObj.constBegin(); - const auto itemEnd = jsonObj.constEnd(); - while (item != itemEnd) { - chainTracker.last() = item.key(); - QJsonValueConstRef ref = item.value(); - QJsonArray parsedArray; - if (ref.isArray()) { - parsedArray = parseArray(ref.toArray(), plainCollections, chainTracker); - } else if (ref.isObject()) { - if (isPlainObject(ref)) - parsedArray = parsePlainObject(ref.toObject()); - else - parseObject(ref.toObject(), plainCollections, chainTracker); - } - if (!parsedArray.isEmpty()) - plainCollections.append({item.key(), parsedArray, chainTracker}); - ++item; - } - chainTracker.removeLast(); -} - -static QList parseDocument(const QJsonDocument &document, - const QString &defaultName = "Model") -{ - QList plainCollections; - JsonKeyChain chainTracker; - if (document.isObject()) { - const QJsonObject documentObject = document.object(); - if (isPlainObject(documentObject)) { - QJsonArray parsedArray = parsePlainObject(documentObject); - if (!parsedArray.isEmpty()) - plainCollections.append({defaultName, parsedArray}); - } else { - parseObject(document.object(), plainCollections, chainTracker); - } - } else { - QJsonArray parsedArray = parseArray(document.array(), plainCollections, chainTracker); - if (!parsedArray.isEmpty()) - plainCollections.append({defaultName, parsedArray, {0}}); - } - return plainCollections; -} - -QList JsonCollectionParser::parseCollectionObjects(const QByteArray &json, - QJsonParseError *error) -{ - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(json, &parseError); - if (error) - *error = parseError; - - if (parseError.error != QJsonParseError::NoError) - return {}; - - QList allCollections = parseDocument(document); - QList keyChains = Utils::transform(allCollections, [](const CollectionObject &obj) { - return obj.keyChain; - }); - - JsonCollectionParser jsonVisitor(QString::fromLatin1(json), keyChains); - - for (CollectionObject &collection : allCollections) - collection.propertyOrder = jsonVisitor.collectionPaths.value(collection.keyChain); - - return allCollections; -} - -JsonCollectionParser::JsonCollectionParser(const QString &jsonContent, - const QList &keyChains) -{ - for (const JsonKeyChain &chain : keyChains) - collectionPaths.insert(chain, {}); - - QmlJS::Document::MutablePtr newDoc = QmlJS::Document::create(Utils::FilePath::fromString( - ""), - QmlJS::Dialect::Json); - - newDoc->setSource(jsonContent); - newDoc->parseExpression(); - - if (!newDoc->isParsedCorrectly()) - return; - - newDoc->ast()->accept(this); -} - -bool JsonCollectionParser::visit([[maybe_unused]] QmlJS::AST::ObjectPattern *objectPattern) -{ - propertyOrderStack.push({}); - return true; -} - -void JsonCollectionParser::endVisit([[maybe_unused]] QmlJS::AST::ObjectPattern *objectPattern) - -{ - if (!propertyOrderStack.isEmpty()) { - QStringList objectProperties = propertyOrderStack.top(); - propertyOrderStack.pop(); - checkPropertyUpdates(keyStack, objectProperties); - } -} - -bool JsonCollectionParser::visit(QmlJS::AST::PatternProperty *patternProperty) -{ - const QString propertyName = patternProperty->name->asString(); - if (!propertyOrderStack.isEmpty()) - propertyOrderStack.top().append(propertyName); - - keyStack.push(propertyName); - return true; -} - -void JsonCollectionParser::endVisit(QmlJS::AST::PatternProperty *patternProperty) -{ - const QString propertyName = patternProperty->name->asString(); - - if (auto curIndex = std::get_if(&keyStack.top())) { - if (*curIndex == propertyName) - keyStack.pop(); - } -} - -bool JsonCollectionParser::visit([[maybe_unused]] QmlJS::AST::PatternElementList *patternElementList) -{ - keyStack.push(-1); - return true; -} - -void JsonCollectionParser::endVisit([[maybe_unused]] QmlJS::AST::PatternElementList *patternElementList) -{ - if (std::get_if(&keyStack.top())) - keyStack.pop(); -} - -bool JsonCollectionParser::visit([[maybe_unused]] QmlJS::AST::PatternElement *patternElement) -{ - if (auto curIndex = std::get_if(&keyStack.top())) - *curIndex += 1; - return true; -} - -void JsonCollectionParser::checkPropertyUpdates(QStack stack, - const QStringList &objectProperties) -{ - bool shouldUpdate = collectionPaths.contains(stack); - if (!shouldUpdate && !stack.isEmpty()) { - if (std::get_if(&stack.top())) { - stack.pop(); - shouldUpdate = collectionPaths.contains(stack); - } - } - if (!shouldUpdate) - return; - - QStringList propertyList = collectionPaths.value(stack); - QSet allKeys; - for (const QString &val : std::as_const(propertyList)) - allKeys.insert(val); - - std::optional prevVal; - for (const QString &val : objectProperties) { - if (!allKeys.contains(val)) { - if (prevVal.has_value()) { - const int idx = propertyList.indexOf(prevVal); - propertyList.insert(idx + 1, val); - } else { - propertyList.append(val); - } - allKeys.insert(val); - } - prevVal = val; - } - collectionPaths.insert(stack, propertyList); -} - -void JsonCollectionParser::throwRecursionDepthError() -{ - qWarning() << __FUNCTION__ << "Recursion Depth Error"; -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionjsonparser.h b/src/plugins/qmldesigner/components/collectioneditor/collectionjsonparser.h deleted file mode 100644 index 16069d3f4cc..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionjsonparser.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (C) 2024 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include - -#include -#include - -QT_BEGIN_NAMESPACE -struct QJsonParseError; -QT_END_NAMESPACE - -using JsonKey = std::variant; // Key can be either int (index) or string (property name) - -using JsonKeyChain = QList; // A chain of keys leading to a specific json value - -namespace QmlDesigner { - -struct CollectionObject -{ - QString name; - QJsonArray array = {}; - JsonKeyChain keyChain = {}; - QStringList propertyOrder = {}; -}; - -class JsonCollectionParser : public QmlJS::AST::Visitor -{ -public: - static QList parseCollectionObjects(const QByteArray &json, - QJsonParseError *error = nullptr); - -private: - JsonCollectionParser(const QString &jsonContent, const QList &keyChains); - - bool visit(QmlJS::AST::ObjectPattern *objectPattern) override; - void endVisit(QmlJS::AST::ObjectPattern *objectPattern) override; - - bool visit(QmlJS::AST::PatternProperty *patternProperty) override; - void endVisit(QmlJS::AST::PatternProperty *patternProperty) override; - - bool visit(QmlJS::AST::PatternElementList *patternElementList) override; - void endVisit(QmlJS::AST::PatternElementList *patternElementList) override; - - bool visit(QmlJS::AST::PatternElement *patternElement) override; - - void checkPropertyUpdates(QStack stack, const QStringList &objectProperties); - - void throwRecursionDepthError() override; - - QStack keyStack; - QStack propertyOrderStack; - QMap collectionPaths; // Key chains, Priorities -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp deleted file mode 100644 index 320bc1bc043..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp +++ /dev/null @@ -1,522 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "collectionlistmodel.h" - -#include "collectioneditorconstants.h" -#include "collectioneditorutils.h" - -#include -#include -#include - -#include -#include -#include - -namespace { - -template -bool containsItem(const std::initializer_list &container, const ValueType &value) -{ - auto begin = std::cbegin(container); - auto end = std::cend(container); - - auto it = std::find(begin, end, value); - return it != end; -} - -bool sameCollectionNames(QStringList a, QStringList b) -{ - if (a.size() != b.size()) - return false; - - a.sort(Qt::CaseSensitive); - b.sort(Qt::CaseSensitive); - - return a == b; -} - -} // namespace - -namespace QmlDesigner { - -CollectionListModel::CollectionListModel() - : QAbstractListModel() -{ - connect(this, &CollectionListModel::modelReset, this, &CollectionListModel::updateEmpty); - connect(this, &CollectionListModel::rowsRemoved, this, &CollectionListModel::updateEmpty); - connect(this, &CollectionListModel::rowsInserted, this, &CollectionListModel::updateEmpty); -} - -QHash CollectionListModel::roleNames() const -{ - static QHash roles; - if (roles.isEmpty()) { - roles.insert(Super::roleNames()); - roles.insert({ - {IdRole, "collectionId"}, - {NameRole, "collectionName"}, - {SelectedRole, "collectionIsSelected"}, - }); - } - return roles; -} - -int CollectionListModel::rowCount([[maybe_unused]] const QModelIndex &parent) const -{ - return m_data.count(); -} - -bool CollectionListModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - if (!index.isValid()) - return false; - - if (containsItem({Qt::EditRole, Qt::DisplayRole, NameRole}, role)) { - if (collectionExists(value.toString())) - return false; - - QString oldName = collectionNameAt(index.row()); - bool nameChanged = value != data(index); - if (nameChanged) { - QString newName = value.toString(); - QString errorString; - if (renameCollectionInDataStore(oldName, newName, errorString)) { - m_data.replace(index.row(), newName); - emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole, NameRole}); - emit this->collectionNameChanged(oldName, newName); - if (m_selectedCollectionName == oldName) - updateSelectedCollectionName(); - return true; - } else { - emit warning("Rename Model", errorString); - return false; - } - } - } else if (role == SelectedRole) { - if (value.toBool() != index.data(SelectedRole).toBool()) { - setSelectedIndex(value.toBool() ? index.row() : -1); - return true; - } - } - return false; -} - -bool CollectionListModel::removeRows(int row, int count, const QModelIndex &parent) -{ - const int rows = rowCount(parent); - if (row >= rows) - return false; - - row = qBound(0, row, rows - 1); - count = qBound(0, count, rows - row); - - if (count < 1) - return false; - - QString errorString; - QStringList removedCollections = m_data.mid(row, count); - if (removeCollectionsFromDataStore(removedCollections, errorString)) { - beginRemoveRows(parent, row, row + count - 1); - m_data.remove(row, count); - endRemoveRows(); - - emit collectionsRemoved(removedCollections); - if (m_selectedIndex >= row) { - int preferredIndex = m_selectedIndex - count; - if (preferredIndex < 0) // If the selected item is deleted, reset selection - selectCollectionIndex(-1); - selectCollectionIndex(preferredIndex, true); - } - - updateSelectedCollectionName(); - return true; - } else { - emit warning("Remove Model", errorString); - return false; - } -} - -QVariant CollectionListModel::data(const QModelIndex &index, int role) const -{ - QTC_ASSERT(index.isValid(), return {}); - - switch (role) { - case IdRole: - return index.row(); - case SelectedRole: - return index.row() == m_selectedIndex; - case NameRole: - default: - return m_data.at(index.row()); - } -} - -void CollectionListModel::setDataStoreNode(const ModelNode &dataStoreNode) -{ - m_dataStoreNode = dataStoreNode; - update(); -} - -int CollectionListModel::selectedIndex() const -{ - return m_selectedIndex; -} - -ModelNode CollectionListModel::sourceNode() const -{ - return m_dataStoreNode; -} - -bool CollectionListModel::collectionExists(const QString &collectionName) const -{ - return m_data.contains(collectionName); -} - -QStringList CollectionListModel::collections() const -{ - return m_data; -} - -QString CollectionListModel::getUniqueCollectionName(const QString &baseName) const -{ - QString name = baseName.isEmpty() ? "Model" : baseName; - QString nameTemplate = name + "%1"; - - int num = 0; - - while (collectionExists(name)) - name = nameTemplate.arg(++num, 2, 10, QChar('0')); - - return name; -} - -void CollectionListModel::selectCollectionIndex(int idx, bool selectAtLeastOne) -{ - int collectionCount = m_data.size(); - int preferredIndex = -1; - if (collectionCount) { - if (selectAtLeastOne) - preferredIndex = std::max(0, std::min(idx, collectionCount - 1)); - else if (idx > -1 && idx < collectionCount) - preferredIndex = idx; - } - - setSelectedIndex(preferredIndex); -} - -void CollectionListModel::selectCollectionName(QString collectionName, bool selectAtLeastOne) -{ - int idx = m_data.indexOf(collectionName); - if (idx > -1) - selectCollectionIndex(idx); - else - selectCollectionIndex(selectedIndex(), selectAtLeastOne); - - collectionName = collectionNameAt(selectedIndex()); - if (m_selectedCollectionName == collectionName) - return; - - m_selectedCollectionName = collectionName; - emit selectedCollectionNameChanged(m_selectedCollectionName); -} - -QString CollectionListModel::collectionNameAt(int idx) const -{ - return index(idx).data(NameRole).toString(); -} - -QString CollectionListModel::selectedCollectionName() const -{ - return m_selectedCollectionName; -} - -void CollectionListModel::update() -{ - using Utils::FilePath; - using Utils::FileReader; - - FileReader sourceFile; - QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode); - FilePath path = FilePath::fromUserInput(sourceFileAddress); - bool fileRead = false; - if (path.exists()) { - fileRead = sourceFile.fetch(path); - if (!fileRead) - emit this->warning(tr("Model Editor"), - tr("Cannot read the dataStore file\n%1").arg(sourceFile.errorString())); - } - - QStringList collectionNames; - if (fileRead) { - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(sourceFile.data(), &parseError); - if (parseError.error != QJsonParseError::NoError) { - emit this->warning(tr("Model Editor"), - tr("There is an error in the JSON file.\n%1") - .arg(parseError.errorString())); - } else { - if (document.isObject()) - collectionNames = document.object().toVariantMap().keys(); - else - emit this->warning(tr("Model Editor"), tr("The JSON document be an object.")); - } - } - - if (!sameCollectionNames(m_data, collectionNames)) { - QString prevSelectedCollection = selectedIndex() > -1 ? m_data.at(selectedIndex()) - : QString(); - beginResetModel(); - m_data = collectionNames; - endResetModel(); - emit this->collectionNamesChanged(collections()); - selectCollectionName(prevSelectedCollection, true); - } -} - -bool CollectionListModel::addCollection(const QString &collectionName, - const QJsonObject &localCollection) -{ - if (collectionExists(collectionName)) { - emit warning(tr("Add Model"), tr("Model \"%1\" already exists.").arg(collectionName)); - return false; - } - - QString errorMessage; - if (addCollectionToDataStore(collectionName, localCollection, errorMessage)) { - int row = rowCount(); - beginInsertRows({}, row, row); - m_data.append(collectionName); - endInsertRows(); - - selectCollectionName(collectionName); - emit collectionAdded(collectionName); - return true; - } else { - emit warning(tr("Add Collection"), errorMessage); - } - return false; -} - -void CollectionListModel::setSelectedIndex(int idx) -{ - idx = (idx > -1 && idx < rowCount()) ? idx : -1; - - if (m_selectedIndex != idx) { - QModelIndex previousIndex = index(m_selectedIndex); - QModelIndex newIndex = index(idx); - - m_selectedIndex = idx; - - if (previousIndex.isValid()) - emit dataChanged(previousIndex, previousIndex, {SelectedRole}); - - if (newIndex.isValid()) - emit dataChanged(newIndex, newIndex, {SelectedRole}); - - emit selectedIndexChanged(idx); - updateSelectedCollectionName(); - } -} - -bool CollectionListModel::removeCollectionsFromDataStore(const QStringList &removedCollections, - QString &error) const -{ - using Utils::FilePath; - using Utils::FileReader; - auto setErrorAndReturn = [&error](const QString &msg) -> bool { - error = msg; - return false; - }; - - if (m_dataStoreNode.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) - return setErrorAndReturn(tr("Invalid node type")); - - QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode); - - QFileInfo sourceFileInfo(sourceFileAddress); - if (!sourceFileInfo.isFile()) - return setErrorAndReturn(tr("The selected node has an invalid source address")); - - FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress); - FileReader jsonFile; - if (!jsonFile.fetch(jsonPath)) { - return setErrorAndReturn(tr("Can't read file \"%1\".\n%2") - .arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); - } - - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError); - if (parseError.error != QJsonParseError::NoError) { - return setErrorAndReturn(tr("\"%1\" is corrupted.\n%2") - .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); - } - - if (document.isObject()) { - QJsonObject rootObject = document.object(); - - for (const QString &collectionName : removedCollections) { - bool sourceContainsCollection = rootObject.contains(collectionName); - if (sourceContainsCollection) { - rootObject.remove(collectionName); - } else { - setErrorAndReturn(tr("The model group doesn't contain the model name (%1).") - .arg(sourceContainsCollection)); - } - } - - document.setObject(rootObject); - - if (CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) { - error.clear(); - return true; - } else { - return setErrorAndReturn( - tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); - } - } else { - return setErrorAndReturn(tr("Local Json Document should be an object")); - } - - return false; -} - -bool CollectionListModel::renameCollectionInDataStore(const QString &oldName, - const QString &newName, - QString &error) -{ - using Utils::FilePath; - using Utils::FileReader; - using Utils::FileSaver; - - auto setErrorAndReturn = [&error](const QString &msg) -> bool { - error = msg; - return false; - }; - - if (m_dataStoreNode.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME) - return setErrorAndReturn(tr("Invalid node type")); - - QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode); - - QFileInfo sourceFileInfo(sourceFileAddress); - if (!sourceFileInfo.isFile()) - return setErrorAndReturn(tr("Selected node must have a valid source file address")); - - FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress); - FileReader jsonFile; - if (!jsonFile.fetch(jsonPath)) { - return setErrorAndReturn( - tr("Can't read \"%1\".\n%2").arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); - } - - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError); - if (parseError.error != QJsonParseError::NoError) { - return setErrorAndReturn(tr("\"%1\" is corrupted.\n%2") - .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); - } - - if (document.isObject()) { - QJsonObject rootObject = document.object(); - - bool collectionContainsOldName = rootObject.contains(oldName); - bool collectionContainsNewName = rootObject.contains(newName); - - if (!collectionContainsOldName) { - return setErrorAndReturn( - tr("The model group doesn't contain the old model name (%1).").arg(oldName)); - } - - if (collectionContainsNewName) { - return setErrorAndReturn( - tr("The model name \"%1\" already exists in the model group.").arg(newName)); - } - - QJsonValue oldValue = rootObject.value(oldName); - rootObject.insert(newName, oldValue); - rootObject.remove(oldName); - - document.setObject(rootObject); - - if (CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) { - error.clear(); - return true; - } else { - return setErrorAndReturn( - tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); - } - } else { - return setErrorAndReturn(tr("Local Json Document should be an object")); - } - return false; -} - -bool CollectionListModel::addCollectionToDataStore(const QString &collectionName, - const QJsonObject &localCollection, - QString &errorString) const -{ - using Utils::FilePath; - using Utils::FileReader; - auto returnError = [&errorString](const QString &msg) -> bool { - errorString = msg; - return false; - }; - - if (collectionExists(collectionName)) - return returnError(tr("A model with the identical name already exists.")); - - QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode); - - QFileInfo sourceFileInfo(sourceFileAddress); - if (!sourceFileInfo.isFile()) - return returnError(tr("Selected node must have a valid source file address")); - - FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress); - FileReader jsonFile; - if (!jsonFile.fetch(jsonPath)) { - return returnError( - tr("Can't read \"%1\".\n%2").arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString())); - } - - QJsonParseError parseError; - QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError); - if (parseError.error != QJsonParseError::NoError) - return returnError(tr("\"%1\" is corrupted.\n%2") - .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString())); - - if (document.isObject()) { - QJsonObject sourceObject = document.object(); - sourceObject.insert(collectionName, localCollection); - document.setObject(sourceObject); - - if (CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) - return true; - else - return returnError(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath())); - } else { - return returnError(tr("JSON document type should be an object containing models.")); - } -} - -void CollectionListModel::updateEmpty() -{ - bool isEmptyNow = m_data.isEmpty(); - if (m_isEmpty != isEmptyNow) { - m_isEmpty = isEmptyNow; - emit isEmptyChanged(m_isEmpty); - - if (m_isEmpty) - setSelectedIndex(-1); - } -} - -void CollectionListModel::updateSelectedCollectionName() -{ - QString selectedCollectionByIndex = collectionNameAt(selectedIndex()); - if (selectedCollectionByIndex != selectedCollectionName()) - selectCollectionName(selectedCollectionByIndex); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h deleted file mode 100644 index 7902fd59097..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include -#include - -#include "modelnode.h" - -namespace QmlDesigner { - -class CollectionListModel : public QAbstractListModel -{ - Q_OBJECT - - Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged) - Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged) - Q_PROPERTY(QString selectedCollectionName - READ selectedCollectionName - WRITE selectCollectionName - NOTIFY selectedCollectionNameChanged) - -public: - enum Roles { IdRole = Qt::UserRole + 1, NameRole, SelectedRole }; - - explicit CollectionListModel(); - QHash roleNames() const override; - - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - bool setData(const QModelIndex &index, const QVariant &value, int role) override; - bool removeRows(int row, int count, const QModelIndex &parent = {}) override; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - void setDataStoreNode(const ModelNode &dataStoreNode = {}); - - Q_INVOKABLE int selectedIndex() const; - Q_INVOKABLE ModelNode sourceNode() const; - Q_INVOKABLE bool collectionExists(const QString &collectionName) const; - Q_INVOKABLE QStringList collections() const; - Q_INVOKABLE QString getUniqueCollectionName(const QString &baseName = {}) const; - - void selectCollectionIndex(int idx, bool selectAtLeastOne = false); - void selectCollectionName(QString collectionName, bool selectAtLeastOne = false); - QString collectionNameAt(int idx) const; - QString selectedCollectionName() const; - - void update(); - bool addCollection(const QString &collectionName, const QJsonObject &localCollection); - -signals: - void selectedIndexChanged(int idx); - void isEmptyChanged(bool); - void collectionNameChanged(const QString &oldName, const QString &newName); - void collectionNamesChanged(const QStringList &collectionNames); - void collectionsRemoved(const QStringList &names); - void collectionAdded(const QString &name); - void selectedCollectionNameChanged(const QString &selectedCollectionName); - void warning(const QString &title, const QString &body); - -private: - void setSelectedIndex(int idx); - bool removeCollectionsFromDataStore(const QStringList &removedCollections, QString &error) const; - bool renameCollectionInDataStore(const QString &oldName, const QString &newName, QString &error); - bool addCollectionToDataStore(const QString &collectionName, - const QJsonObject &localCollection, - QString &errorString) const; - - void updateEmpty(); - void updateSelectedCollectionName(); - - using Super = QAbstractListModel; - int m_selectedIndex = -1; - bool m_isEmpty = false; - ModelNode m_dataStoreNode; - QString m_selectedCollectionName; - QStringList m_data; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp deleted file mode 100644 index 8a9e6a8276b..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp +++ /dev/null @@ -1,686 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "collectionview.h" - -#include "collectiondatatypemodel.h" -#include "collectiondetailsmodel.h" -#include "collectioneditorconstants.h" -#include "collectioneditorutils.h" -#include "collectionlistmodel.h" -#include "collectionwidget.h" -#include "datastoremodelnode.h" -#include "designmodecontext.h" -#include "nodeabstractproperty.h" -#include "nodemetainfo.h" -#include "nodeproperty.h" -#include "qmldesignerplugin.h" -#include "variantproperty.h" - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -namespace { - -bool isStudioCollectionModel(const QmlDesigner::ModelNode &node) -{ - return node.metaInfo().isQtQuickStudioUtilsJsonListModel(); -} - -inline bool isProjectImport(const QmlDesigner::Import &import) -{ - ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::startupProject(); - return currentProject && import.toString() == currentProject->displayName(); -} - -inline void setVariantPropertyValue(const QmlDesigner::ModelNode &node, - const QmlDesigner::PropertyName &propertyName, - const QVariant &value) -{ - QmlDesigner::VariantProperty property = node.variantProperty(propertyName); - property.setValue(value); -} - -inline void setNodePropertyValue(const QmlDesigner::ModelNode &node, - const QmlDesigner::PropertyName &propertyName, - const QmlDesigner::ModelNode &value) -{ - QmlDesigner::NodeProperty nodeProperty = node.nodeProperty(propertyName); - // Remove the old model node if is available - if (nodeProperty.modelNode()) - nodeProperty.modelNode().destroy(); - - nodeProperty.setModelNode(value); -} - -inline void setBindingPropertyExpression(const QmlDesigner::ModelNode &node, - const QmlDesigner::PropertyName &propertyName, - const QString &expression) -{ - QmlDesigner::BindingProperty property = node.bindingProperty(propertyName); - property.setExpression(expression); -} - -} // namespace - -namespace QmlDesigner { - -CollectionView::CollectionView(ExternalDependenciesInterface &externalDependencies) - : AbstractView(externalDependencies) - , m_dataStore(std::make_unique()) - -{} - -CollectionView::~CollectionView() = default; - -bool CollectionView::hasWidget() const -{ - return true; -} - -QmlDesigner::WidgetInfo CollectionView::widgetInfo() -{ - if (!m_widget) { - m_widget = Utils::makeUniqueObjectPtr(this); - m_widget->setMinimumSize(m_widget->minimumSizeHint()); - connect(ProjectExplorer::ProjectManager::instance(), - &ProjectExplorer::ProjectManager::startupProjectChanged, m_widget.get(), [&] { - resetDataStoreNode(); - m_widget->collectionDetailsModel()->removeAllCollections(); - }); - - auto collectionEditorContext = new Internal::CollectionEditorContext(m_widget.get()); - Core::ICore::addContextObject(collectionEditorContext); - CollectionListModel *listModel = m_widget->listModel().data(); - - connect(listModel, - &CollectionListModel::selectedCollectionNameChanged, - this, - [this](const QString &collection) { - m_widget->collectionDetailsModel()->loadCollection(dataStoreNode(), collection); - }); - - connect(listModel, &CollectionListModel::isEmptyChanged, this, [this](bool isEmpty) { - if (isEmpty) - m_widget->collectionDetailsModel()->loadCollection({}, {}); - }); - - connect(listModel, &CollectionListModel::modelReset, this, [this] { - CollectionListModel *listModel = m_widget->listModel().data(); - if (listModel->sourceNode() == dataStoreNode()) - m_dataStore->setCollectionNames(listModel->collections()); - }); - - connect(listModel, - &CollectionListModel::collectionAdded, - this, - [this](const QString &collectionName) { m_dataStore->addCollection(collectionName); }); - - connect(listModel, - &CollectionListModel::collectionNameChanged, - this, - [this](const QString &oldName, const QString &newName) { - m_dataStore->renameCollection(oldName, newName); - m_widget->collectionDetailsModel()->renameCollection(dataStoreNode(), - oldName, - newName); - }); - - connect(listModel, - &CollectionListModel::collectionsRemoved, - this, - [this](const QStringList &collectionNames) { - m_dataStore->removeCollections(collectionNames); - for (const QString &collectionName : collectionNames) { - m_widget->collectionDetailsModel()->removeCollection(dataStoreNode(), - collectionName); - } - }); - } - - return createWidgetInfo(m_widget.get(), - "CollectionEditor", - WidgetInfo::LeftPane, - 0, - tr("Model Editor [beta]"), - tr("Model Editor view")); -} - -void CollectionView::modelAttached(Model *model) -{ - AbstractView::modelAttached(model); - m_widget->setProjectImportExists(Utils::anyOf(model->imports(), isProjectImport)); - resetDataStoreNode(); -} - -void CollectionView::modelAboutToBeDetached([[maybe_unused]] Model *model) -{ - unloadDataStore(); - m_widget->setProjectImportExists(false); -} - -void CollectionView::selectedNodesChanged(const QList &selectedNodeList, - [[maybe_unused]] const QList &lastSelectedNodeList) -{ - if (!m_widget) - return; - - QList selectedCollectionNodes = Utils::filtered(selectedNodeList, - &isStudioCollectionModel); - - bool singleNonCollectionNodeSelected = selectedNodeList.size() == 1 - && selectedCollectionNodes.isEmpty(); - - bool singleSelectedHasModelProperty = false; - if (singleNonCollectionNodeSelected) { - const ModelNode selectedNode = selectedNodeList.first(); - singleSelectedHasModelProperty = CollectionEditorUtils::canAcceptCollectionAsModel( - selectedNode); - } - - m_widget->setTargetNodeSelected(singleSelectedHasModelProperty); -} - -void CollectionView::importsChanged(const Imports &addedImports, const Imports &removedImports) -{ - if (Utils::anyOf(addedImports, isProjectImport)) { - m_widget->setProjectImportExists(true); - resetDataStoreNode(); - } else if (Utils::anyOf(removedImports, isProjectImport)) { - m_widget->setProjectImportExists(false); - unloadDataStore(); - } -} - -void CollectionView::customNotification(const AbstractView *, - const QString &identifier, - const QList &nodeList, - const QList &data) -{ - if (!m_widget) - return; - - if (identifier == QLatin1String("item_library_created_by_drop") && !nodeList.isEmpty()) - onItemLibraryNodeCreated(nodeList.first()); - else if (identifier == QLatin1String("open_collection_by_id") && !data.isEmpty()) - m_widget->openCollection(collectionNameFromDataStoreChildren(data.first().toByteArray())); - else if (identifier == "delete_selected_collection") - m_widget->deleteSelectedCollection(); -} - -void CollectionView::addResource(const QUrl &url, const QString &name) -{ - executeInTransaction(Q_FUNC_INFO, [this, &url, &name]() { - ensureStudioModelImport(); - QString sourceAddress; - if (url.isLocalFile()) { - Utils::FilePath fp = QmlDesignerPlugin::instance()->currentDesignDocument()->fileName().parentDir(); - sourceAddress = Utils::FilePath::calcRelativePath(url.toLocalFile(), - fp.absoluteFilePath().toString()); - } else { - sourceAddress = url.toString(); - } -#ifdef QDS_USE_PROJECTSTORAGE - ModelNode resourceNode = createModelNode("JsonListModel"); -#else - const NodeMetaInfo resourceMetaInfo = jsonCollectionMetaInfo(); - ModelNode resourceNode = createModelNode(resourceMetaInfo.typeName(), - resourceMetaInfo.majorVersion(), - resourceMetaInfo.minorVersion()); -#endif - VariantProperty sourceProperty = resourceNode.variantProperty( - CollectionEditorConstants::SOURCEFILE_PROPERTY); - VariantProperty nameProperty = resourceNode.variantProperty("objectName"); - sourceProperty.setValue(sourceAddress); - nameProperty.setValue(name); - resourceNode.setIdWithoutRefactoring(model()->generateNewId(name, "model")); - rootModelNode().defaultNodeAbstractProperty().reparentHere(resourceNode); - }); -} - -void CollectionView::addProjectImport() -{ - if (!m_widget) - return; - - ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::startupProject(); - if (!currentProject) - return; - - executeInTransaction(__FUNCTION__, [&] { - Import import = Import::createLibraryImport(currentProject->displayName()); - if (!model()->hasImport(import, true, true)) - model()->changeImports({import}, {}); - }); -} - -void CollectionView::assignCollectionToNode(const QString &collectionName, const ModelNode &node) -{ - if (!m_widget) - return; - - executeInTransaction("CollectionView::assignCollectionToNode", [&]() { - m_dataStore->assignCollectionToNode( - this, - node, - collectionName, - [&](const QString &collectionName, const QString &columnName) -> bool { - const CollectionReference reference{dataStoreNode(), collectionName}; - return m_widget->collectionDetailsModel()->collectionHasColumn(reference, columnName); - }, - [&](const QString &collectionName) -> QString { - const CollectionReference reference{dataStoreNode(), collectionName}; - return m_widget->collectionDetailsModel()->getFirstColumnName(reference); - }); - - // Create and assign a delegate to the list view item - if (node.metaInfo().isQtQuickListView()) { - ::setNodePropertyValue(node, "delegate", createListViewDelegate(collectionName)); - } else if (node.metaInfo().isQtQuickGridView()) { - QSize delegateSize; - ::setNodePropertyValue(node, - "delegate", - createGridViewDelegate(collectionName, delegateSize)); - ::setVariantPropertyValue(node, "cellWidth", delegateSize.width()); - ::setVariantPropertyValue(node, "cellHeight", delegateSize.height()); - } - }); -} - -void CollectionView::assignCollectionToSelectedNode(const QString &collectionName) -{ - QTC_ASSERT(dataStoreNode() && hasSingleSelectedModelNode(), return); - assignCollectionToNode(collectionName, singleSelectedModelNode()); -} - -void CollectionView::addNewCollection(const QString &collectionName, const QJsonObject &localCollection) -{ - if (!m_widget) - return; - - addTask(QSharedPointer( - new AddCollectionTask(this, m_widget->listModel(), localCollection, collectionName))); -} - -void CollectionView::openCollection(const QString &collectionName) -{ - if (!m_widget) - return; - - m_widget->openCollection(collectionName); -} - -void CollectionView::registerDeclarativeType() -{ - CollectionDetails::registerDeclarativeType(); - CollectionDataTypeModel::registerDeclarativeType(); -} - -void CollectionView::resetDataStoreNode() -{ - if (!m_widget) - return; - - auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); - m_dataStore->reloadModel(compUtils.projectModulePath()); - - ModelNode dataStore = dataStoreNode(); - m_widget->setDataStoreExists(dataStore.isValid()); - if (!dataStore || m_widget->listModel()->sourceNode() == dataStore) - return; - - bool dataStoreSingletonFound = m_dataStoreTypeFound; - if (!dataStoreSingletonFound && rewriterView() && rewriterView()->isAttached()) { - const QList types = rewriterView()->getQMLTypes(); - for (const QmlTypeData &cppTypeData : types) { - if (cppTypeData.isSingleton && cppTypeData.typeName == "DataStore") { - dataStoreSingletonFound = true; - break; - } - } - if (!dataStoreSingletonFound && !m_rewriterAmended) { - rewriterView()->forceAmend(); - m_rewriterAmended = true; - } - } - - if (dataStoreSingletonFound) { - m_widget->listModel()->setDataStoreNode(dataStore); - m_widget->collectionDetailsModel()->setJsonFilePath(m_dataStore->jsonFilePath()); - m_dataStoreTypeFound = true; - resetPuppet(); - - while (!m_delayedTasks.isEmpty()) - m_delayedTasks.takeFirst()->process(); - } else if (++m_reloadCounter < 50) { - QTimer::singleShot(200, this, &CollectionView::resetDataStoreNode); - } else { - QTC_ASSERT(false, m_delayedTasks.clear()); - } -} - -ModelNode CollectionView::dataStoreNode() const -{ - return m_dataStore->modelNode(); -} - -void CollectionView::ensureDataStoreExists() -{ - bool filesJustCreated = false; - bool filesExist = createDataStore(filesJustCreated); - if (filesExist && filesJustCreated) { - // Force code model reset to notice changes to existing module - if (auto modelManager = QmlJS::ModelManagerInterface::instance()) - modelManager->resetCodeModel(); - resetDataStoreNode(); - } -} - -QString CollectionView::collectionNameFromDataStoreChildren(const PropertyName &childPropertyName) const -{ - return dataStoreNode() - .nodeProperty(childPropertyName) - .modelNode() - .property(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY) - .toVariantProperty() - .value() - .toString(); -} - -NodeMetaInfo CollectionView::jsonCollectionMetaInfo() const -{ - return model()->metaInfo(CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME); -} - -void CollectionView::unloadDataStore() -{ - m_reloadCounter = 0; - m_rewriterAmended = false; - m_dataStoreTypeFound = false; - QTC_ASSERT(m_delayedTasks.isEmpty(), m_delayedTasks.clear()); - if (m_widget) { - m_widget->setDataStoreExists(dataStoreNode().isValid()); - m_widget->listModel()->setDataStoreNode(); - } -} - -bool CollectionView::createDataStore(bool &justCreated) const -{ - using Utils::FilePath; - using Utils::FileReader; - using Utils::FileSaver; - using namespace Qt::StringLiterals; - - auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); - FilePath projectModulePath = compUtils.projectModulePath(true); - - FilePath qmlTargetPath = projectModulePath.resolvePath( - CollectionEditorConstants::DEFAULT_DATASTORE_QML_FILENAME.toString()); - justCreated = false; - - auto extractDependency = [&justCreated](const FilePath &filePath) -> bool { - if (filePath.exists()) - return true; - - const QString templateFileName = filePath.fileName() + u".tpl"; - const FilePath templatePath = CollectionEditorUtils::findFile(Core::ICore::resourcePath(), - templateFileName); - if (!templatePath.exists()) { - qWarning() << Q_FUNC_INFO << __LINE__ << templateFileName << "does not exist"; - return false; - } - - if (!filePath.parentDir().ensureWritableDir()) { - qWarning() << Q_FUNC_INFO << __LINE__ << "Cannot create directory" - << filePath.parentDir(); - return false; - } - - if (templatePath.copyFile(filePath)) { - justCreated = true; - return true; - } - - qWarning() << Q_FUNC_INFO << __LINE__ << "Cannot copy" << templateFileName << "to" << filePath; - return false; - }; - - if (!extractDependency(projectModulePath.resolvePath( - CollectionEditorConstants::DEFAULT_MODELS_JSON_FILENAME.toString()))) { - return false; - } - - if (!extractDependency(projectModulePath.resolvePath( - CollectionEditorConstants::DEFAULT_DATA_JSON_FILENAME.toString()))) { - return false; - } - - if (!extractDependency(projectModulePath.resolvePath( - CollectionEditorConstants::DEFAULT_JSONDATA_QML_FILENAME.toString()))) { - return false; - } - - if (!qmlTargetPath.exists()) { - if (qmlTargetPath.ensureExistingFile()) { - justCreated = true; - } else { - qWarning() << Q_FUNC_INFO << __LINE__ << "Can't create DataStore Qml File"; - return false; - } - } - - FilePath qmlDirPath = projectModulePath.resolvePath("qmldir"_L1); - qmlDirPath.ensureExistingFile(); - - FileReader qmlDirReader; - if (!qmlDirReader.fetch(qmlDirPath)) { - qWarning() << Q_FUNC_INFO << __LINE__ << "Can't read the content of the qmldir"; - return false; - } - - QByteArray qmlDirContent = qmlDirReader.data(); - const QList qmlDirLines = qmlDirContent.split('\n'); - for (const QByteArray &line : qmlDirLines) { - if (line.startsWith("singleton DataStore ")) - return true; - } - - if (!qmlDirContent.isEmpty() && qmlDirContent.back() != '\n') - qmlDirContent.append("\n"); - qmlDirContent.append("singleton DataStore 1.0 DataStore.qml\n"); - - FileSaver qmlDirSaver(qmlDirPath); - qmlDirSaver.write(qmlDirContent); - - if (qmlDirSaver.finalize()) { - justCreated = true; - return true; - } - - qWarning() << Q_FUNC_INFO << __LINE__ << "Can't write to the qmldir file"; - return false; -} - -void CollectionView::ensureStudioModelImport() -{ - executeInTransaction(__FUNCTION__, [&] { - Import import = Import::createLibraryImport(CollectionEditorConstants::COLLECTIONMODEL_IMPORT); - try { - if (!model()->hasImport(import, true, true)) - model()->changeImports({import}, {}); - } catch (const Exception &) { - QTC_ASSERT(false, return); - } - }); -} - -void CollectionView::onItemLibraryNodeCreated(const ModelNode &node) -{ - if (!m_widget) - return; - - if (node.metaInfo().isListOrGridView()) { - addTask(QSharedPointer( - new DropListViewTask(this, m_widget->listModel(), node))); - } -} - -void CollectionView::addTask(QSharedPointer task) -{ - ensureDataStoreExists(); - if (m_dataStoreTypeFound) - task->process(); - else if (dataStoreNode()) - m_delayedTasks << task; -} - -ModelNode CollectionView::createListViewDelegate(const QString &collectionName) -{ - using DataType = CollectionDetails::DataType; - using namespace Qt::StringLiterals; - - CollectionDetails collection = m_widget->collectionDetailsModel()->upToDateConstCollection( - {dataStoreNode(), collectionName}); - - ModelNode rowItem(createModelNode("QtQuick.Row")); - ::setVariantPropertyValue(rowItem, "spacing", 5); - - const int columnsCount = collection.columns(); - for (int column = 0; column < columnsCount; ++column) { - const DataType dataType = collection.typeAt(column); - const QString columnName = collection.propertyAt(column); - ModelNode cellItem; - if (dataType == DataType::Color) { - cellItem = createModelNode("QtQuick.Rectangle"); - ::setBindingPropertyExpression(cellItem, "color", columnName); - ::setVariantPropertyValue(cellItem, "height", 20); - } else { - cellItem = createModelNode("QtQuick.Text"); - ::setBindingPropertyExpression(cellItem, "text", columnName); - ::setVariantPropertyValue(cellItem, "clip", true); - ::setBindingPropertyExpression(cellItem, "elide", "Text.ElideRight"_L1); - ::setVariantPropertyValue(cellItem, "leftPadding", 1); - ::setVariantPropertyValue(cellItem, "rightPadding", 1); - } - ::setVariantPropertyValue(cellItem, "width", 100); - rowItem.defaultNodeAbstractProperty().reparentHere(cellItem); - } - return rowItem; -} - -ModelNode CollectionView::createGridViewDelegate(const QString &collectionName, QSize &delegateSize) -{ - using DataType = CollectionDetails::DataType; - using namespace Qt::StringLiterals; - - CollectionDetails collection = m_widget->collectionDetailsModel()->upToDateConstCollection( - {dataStoreNode(), collectionName}); - - constexpr int spacing = 5; - int delegateWidth = 70; - int delegateHeight = 0; - - ModelNode rectItem(createModelNode("QtQuick.Rectangle")); - ::setVariantPropertyValue(rectItem, "clip", true); - ::setVariantPropertyValue(rectItem, "border.color", QColor(Qt::blue)); - ::setVariantPropertyValue(rectItem, "border.width", 1); - - ModelNode colItem(createModelNode("QtQuick.Column")); - ::setVariantPropertyValue(colItem, "spacing", spacing); - ::setVariantPropertyValue(colItem, "topPadding", spacing); - ::setBindingPropertyExpression(colItem, "anchors.fill", "parent"_L1); - - const int columnsCount = collection.columns(); - for (int column = 0; column < columnsCount; ++column) { - const DataType dataType = collection.typeAt(column); - const QString columnName = collection.propertyAt(column); - ModelNode cellItem; - if (dataType == DataType::Color) { - cellItem = createModelNode("QtQuick.Rectangle"); - ::setBindingPropertyExpression(cellItem, "color", columnName); - ::setVariantPropertyValue(cellItem, "width", 40); - ::setVariantPropertyValue(cellItem, "height", 40); - delegateHeight += 40; - } else { - cellItem = createModelNode("QtQuick.Text"); - ::setBindingPropertyExpression(cellItem, "width", "parent.width"_L1); - ::setVariantPropertyValue(cellItem, "height", 20); - ::setBindingPropertyExpression(cellItem, "text", columnName); - ::setVariantPropertyValue(cellItem, "clip", true); - ::setBindingPropertyExpression(cellItem, "elide", "Text.ElideRight"_L1); - ::setVariantPropertyValue(cellItem, "leftPadding", 1); - ::setVariantPropertyValue(cellItem, "rightPadding", 1); - ::setBindingPropertyExpression(cellItem, "horizontalAlignment", "Text.AlignHCenter"_L1); - delegateHeight += 20; - } - delegateHeight += spacing; - ::setBindingPropertyExpression(cellItem, - "anchors.horizontalCenter", - "parent.horizontalCenter"_L1); - colItem.defaultNodeAbstractProperty().reparentHere(cellItem); - } - - rectItem.defaultNodeAbstractProperty().reparentHere(colItem); - ::setVariantPropertyValue(rectItem, "width", delegateWidth); - ::setVariantPropertyValue(rectItem, "height", delegateHeight); - delegateSize.setWidth(delegateWidth); - delegateSize.setHeight(delegateHeight); - - return rectItem; -} - -CollectionTask::CollectionTask(CollectionView *view, CollectionListModel *listModel) - : m_collectionView(view) - , m_listModel(listModel) -{} - -DropListViewTask::DropListViewTask(CollectionView *view, - CollectionListModel *listModel, - const ModelNode &node) - : CollectionTask(view, listModel) - , m_node(node) -{} - -void DropListViewTask::process() -{ - AbstractView *view = m_node.view(); - if (!m_node || !m_collectionView || !m_listModel || !view) - return; - - const QString newCollectionName = m_listModel->getUniqueCollectionName("ListModel"); - m_listModel->addCollection(newCollectionName, CollectionEditorUtils::defaultColorCollection()); - m_collectionView->openCollection(newCollectionName); - m_collectionView->assignCollectionToNode(newCollectionName, m_node); -} - -AddCollectionTask::AddCollectionTask(CollectionView *view, - CollectionListModel *listModel, - const QJsonObject &localJsonObject, - const QString &collectionName) - : CollectionTask(view, listModel) - , m_localJsonObject(localJsonObject) - , m_name(collectionName) -{} - -void AddCollectionTask::process() -{ - if (!m_listModel) - return; - - const QString newCollectionName = m_listModel->collectionExists(m_name) - ? m_listModel->getUniqueCollectionName(m_name) - : m_name; - - m_listModel->addCollection(newCollectionName, m_localJsonObject); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h deleted file mode 100644 index 458ea6c60a5..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "datastoremodelnode.h" - -#include -#include - -#include - -#include - -namespace QmlJS { -class Document; -} - -namespace QmlDesigner { - -class CollectionDetails; -class CollectionListModel; -class CollectionTask; -class CollectionWidget; -class DataStoreModelNode; -class GeneratedComponentUtils; - -class CollectionView : public AbstractView -{ - Q_OBJECT - -public: - explicit CollectionView(ExternalDependenciesInterface &externalDependencies); - ~CollectionView(); - - bool hasWidget() const override; - WidgetInfo widgetInfo() override; - - void modelAttached(Model *model) override; - void modelAboutToBeDetached(Model *model) override; - - void selectedNodesChanged(const QList &selectedNodeList, - const QList &lastSelectedNodeList) override; - - void importsChanged(const Imports &addedImports, const Imports &removedImports) override; - - void customNotification(const AbstractView *view, - const QString &identifier, - const QList &nodeList, - const QList &data) override; - - void addResource(const QUrl &url, const QString &name); - - void addProjectImport(); - void assignCollectionToNode(const QString &collectionName, const ModelNode &node); - void assignCollectionToSelectedNode(const QString &collectionName); - void addNewCollection(const QString &collectionName, const QJsonObject &localCollection); - - void openCollection(const QString &collectionName); - - static void registerDeclarativeType(); - - void resetDataStoreNode(); - ModelNode dataStoreNode() const; - void ensureDataStoreExists(); - QString collectionNameFromDataStoreChildren(const PropertyName &childPropertyName) const; - -private: - friend class CollectionTask; - - NodeMetaInfo jsonCollectionMetaInfo() const; - void unloadDataStore(); - bool createDataStore(bool &justCreated) const; - void ensureStudioModelImport(); - void onItemLibraryNodeCreated(const ModelNode &node); - void addTask(QSharedPointer task); - ModelNode createListViewDelegate(const QString &collectionName); - ModelNode createGridViewDelegate(const QString &collectionName, QSize &delegateSize); - - std::unique_ptr m_dataStore; - Utils::UniqueObjectPtr m_widget; - QList> m_delayedTasks; - bool m_dataStoreTypeFound = false; - bool m_rewriterAmended = false; - int m_reloadCounter = 0; -}; - -class CollectionTask -{ -public: - CollectionTask(CollectionView *view, CollectionListModel *listModel); - CollectionTask() = delete; - virtual ~CollectionTask() = default; - - virtual void process() = 0; - -protected: - QPointer m_collectionView; - QPointer m_listModel; -}; - -class DropListViewTask : public CollectionTask -{ -public: - DropListViewTask(CollectionView *view, CollectionListModel *listModel, const ModelNode &node); - - void process() override; - -private: - ModelNode m_node; -}; - -class AddCollectionTask : public CollectionTask -{ -public: - AddCollectionTask(CollectionView *view, - CollectionListModel *listModel, - const QJsonObject &localJsonObject, - const QString &collectionName); - - void process() override; - -private: - QJsonObject m_localJsonObject; - QString m_name; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp deleted file mode 100644 index f6bc1829ee8..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp +++ /dev/null @@ -1,336 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "collectionwidget.h" - -#include "collectiondetails.h" -#include "collectiondetailsmodel.h" -#include "collectiondetailssortfiltermodel.h" -#include "collectioneditorutils.h" -#include "collectionlistmodel.h" -#include "collectionview.h" -#include "designmodewidget.h" -#include "qmldesignerconstants.h" -#include "qmldesignerplugin.h" -#include "theme.h" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace { - -QString collectionViewResourcesPath() -{ -#ifdef SHARE_QML_PATH - if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) - return QLatin1String(SHARE_QML_PATH) + "/collectionEditorQmlSource"; -#endif - return Core::ICore::resourcePath("qmldesigner/collectionEditorQmlSource").toString(); -} - -QString getPreferredCollectionName(const QUrl &url, const QString &collectionName) -{ - if (collectionName.isEmpty()) { - QFileInfo fileInfo(url.isLocalFile() ? url.toLocalFile() : url.toString()); - return fileInfo.completeBaseName(); - } - - return collectionName; -} - -} // namespace - -namespace QmlDesigner { - -CollectionWidget::CollectionWidget(CollectionView *view) - : m_view(view) - , m_listModel(new CollectionListModel) - , m_collectionDetailsModel(new CollectionDetailsModel) - , m_collectionDetailsSortFilterModel(std::make_unique()) - , m_quickWidget(Utils::makeUniqueObjectPtr(this)) -{ - setWindowTitle(tr("Model Editor", "Title of model editor widget")); - - Core::Context context(Constants::C_QMLCOLLECTIONEDITOR); - m_iContext = new Core::IContext(this); - m_iContext->setContext(context); - m_iContext->setWidget(this); - Core::ICore::addContextObject(m_iContext); - - connect(m_listModel, &CollectionListModel::warning, this, &CollectionWidget::warn); - - m_collectionDetailsSortFilterModel->setSourceModel(m_collectionDetailsModel); - - m_quickWidget->quickWidget()->setObjectName(Constants::OBJECT_NAME_COLLECTION_EDITOR); - m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); - m_quickWidget->engine()->addImportPath(collectionViewResourcesPath() + "/imports"); - m_quickWidget->setClearColor(Theme::getColor(Theme::Color::DSpanelBackground)); - - Theme::setupTheme(m_quickWidget->engine()); - m_quickWidget->quickWidget()->installEventFilter(this); - - auto layout = new QVBoxLayout(this); - layout->setContentsMargins({}); - layout->setSpacing(0); - layout->addWidget(m_quickWidget.get()); - - qmlRegisterAnonymousType("CollectionEditorBackend", 1); - auto map = m_quickWidget->registerPropertyMap("CollectionEditorBackend"); - map->setProperties({ - {"rootView", QVariant::fromValue(this)}, - {"model", QVariant::fromValue(m_listModel.data())}, - {"collectionDetailsModel", QVariant::fromValue(m_collectionDetailsModel.data())}, - {"collectionDetailsSortFilterModel", - QVariant::fromValue(m_collectionDetailsSortFilterModel.get())}, - }); - - auto hotReloadShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F4), this); - connect(hotReloadShortcut, &QShortcut::activated, this, &CollectionWidget::reloadQmlSource); - - reloadQmlSource(); - - QmlDesignerPlugin::trackWidgetFocusTime(this, Constants::EVENT_MODELEDITOR_TIME); -} - -CollectionWidget::~CollectionWidget() = default; - -void CollectionWidget::contextHelp(const Core::IContext::HelpCallback &callback) const -{ - if (m_view) - QmlDesignerPlugin::contextHelp(callback, m_view->contextHelpId()); - else - callback({}); -} - -QPointer CollectionWidget::listModel() const -{ - return m_listModel; -} - -QPointer CollectionWidget::collectionDetailsModel() const -{ - return m_collectionDetailsModel; -} - -void CollectionWidget::reloadQmlSource() -{ - const QString collectionViewQmlPath = collectionViewResourcesPath() + "/CollectionView.qml"; - - QTC_ASSERT(QFileInfo::exists(collectionViewQmlPath), return); - - m_quickWidget->setSource(QUrl::fromLocalFile(collectionViewQmlPath)); - - if (!m_quickWidget->rootObject()) { - QString errorString; - const auto errors = m_quickWidget->errors(); - for (const QQmlError &error : errors) - errorString.append("\n" + error.toString()); - - Core::AsynchronousMessageBox::warning(tr("Cannot Create QtQuick View"), - tr("StatesEditorWidget: %1 cannot be created.%2") - .arg(collectionViewQmlPath, errorString)); - return; - } -} - -QSize CollectionWidget::minimumSizeHint() const -{ - return {300, 300}; -} - -bool CollectionWidget::loadJsonFile(const QUrl &url, const QString &collectionName) -{ - if (!isJsonFile(url)) - return false; - - m_view->addResource(url, getPreferredCollectionName(url, collectionName)); - - return true; -} - -bool CollectionWidget::loadCsvFile(const QUrl &url, const QString &collectionName) -{ - m_view->addResource(url, getPreferredCollectionName(url, collectionName)); - - return true; -} - -bool CollectionWidget::isJsonFile(const QUrl &url) const -{ - Utils::FilePath filePath = Utils::FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() - : url.toString()); - Utils::FileReader file; - if (!file.fetch(filePath)) - return false; - - QJsonParseError error; - QJsonDocument::fromJson(file.data(), &error); - if (error.error) - return false; - - return true; -} - -bool CollectionWidget::isCsvFile(const QUrl &url) const -{ - QString filePath = url.isLocalFile() ? url.toLocalFile() : url.toString(); - QFileInfo fileInfo(filePath); - return fileInfo.exists() && !fileInfo.suffix().compare("csv", Qt::CaseInsensitive); -} - -bool CollectionWidget::isValidUrlToImport(const QUrl &url) const -{ - using Utils::FilePath; - FilePath fileInfo = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() - : url.toString()); - if (fileInfo.suffix() == "json") - return isJsonFile(url); - - if (fileInfo.suffix() == "csv") - return isCsvFile(url); - - return false; -} - -bool CollectionWidget::importFile(const QString &collectionName, - const QUrl &url, - const bool &firstRowIsHeader) -{ - using Utils::FilePath; - - FilePath fileInfo = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() - : url.toString()); - QByteArray fileContent; - - auto loadUrlContent = [&]() -> bool { - Utils::FileReader file; - if (file.fetch(fileInfo)) { - fileContent = file.data(); - return true; - } - - warn(tr("Import from file"), tr("Cannot import from file \"%1\"").arg(fileInfo.fileName())); - return false; - }; - - if (fileInfo.suffix() == "json") { - if (!loadUrlContent()) - return false; - - QJsonParseError parseError; - const QList loadedCollections = CollectionDetails::fromImportedJson( - fileContent, &parseError); - if (parseError.error != QJsonParseError::NoError) { - warn(tr("Json file Import error"), - tr("Cannot parse json content\n%1").arg(parseError.errorString())); - return false; - } - if (loadedCollections.size() > 1) { - for (const CollectionDetails &loadedCollection : loadedCollections) { - m_view->addNewCollection(loadedCollection.reference().name, - loadedCollection.toLocalJson()); - } - return true; - } else if (loadedCollections.size() == 1) { - m_view->addNewCollection(collectionName, loadedCollections.first().toLocalJson()); - return true; - } else { - warn(tr("Can not add a model to the JSON file"), - tr("The imported model is empty or is not supported.")); - } - } else if (fileInfo.suffix() == "csv") { - CollectionDetails loadedCollection; - if (!loadUrlContent()) - return false; - loadedCollection = CollectionDetails::fromImportedCsv(fileContent, firstRowIsHeader); - if (loadedCollection.columns()) { - m_view->addNewCollection(collectionName, loadedCollection.toLocalJson()); - return true; - } else { - warn(tr("Can not add a model to the JSON file"), - tr("The imported model is empty or is not supported.")); - } - } - - return false; -} - -void CollectionWidget::addProjectImport() -{ - m_view->addProjectImport(); -} - -void CollectionWidget::addCollectionToDataStore(const QString &collectionName) -{ - m_view->addNewCollection(collectionName, CollectionEditorUtils::defaultCollection()); -} - -void CollectionWidget::assignCollectionToSelectedNode(const QString collectionName) -{ - m_view->assignCollectionToSelectedNode(collectionName); -} - -void CollectionWidget::openCollection(const QString &collectionName) -{ - m_listModel->selectCollectionName(collectionName); - QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("CollectionEditor", true); -} - -ModelNode CollectionWidget::dataStoreNode() const -{ - return m_view->dataStoreNode(); -} - -void CollectionWidget::warn(const QString &title, const QString &body) -{ - QMetaObject::invokeMethod(m_quickWidget->rootObject(), - "showWarning", - Q_ARG(QVariant, title), - Q_ARG(QVariant, body)); -} - -void CollectionWidget::setTargetNodeSelected(bool selected) -{ - if (m_targetNodeSelected == selected) - return; - - m_targetNodeSelected = selected; - emit targetNodeSelectedChanged(m_targetNodeSelected); -} - -void CollectionWidget::setProjectImportExists(bool exists) -{ - if (m_projectImportExists == exists) - return; - - m_projectImportExists = exists; - emit projectImportExistsChanged(m_projectImportExists); -} - -void CollectionWidget::setDataStoreExists(bool exists) -{ - if (m_dataStoreExists == exists) - return; - - m_dataStoreExists = exists; - emit dataStoreExistsChanged(m_dataStoreExists); -} - -void CollectionWidget::deleteSelectedCollection() -{ - QMetaObject::invokeMethod(m_quickWidget->quickWidget()->rootObject(), "deleteSelectedCollection"); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h deleted file mode 100644 index f62d4664720..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include - -#include - -#include - -class StudioQuickWidget; - -namespace QmlDesigner { - -class CollectionDetailsModel; -class CollectionDetailsSortFilterModel; -class CollectionListModel; -class CollectionView; -class ModelNode; - -class CollectionWidget : public QFrame -{ - Q_OBJECT - - Q_PROPERTY(bool targetNodeSelected MEMBER m_targetNodeSelected NOTIFY targetNodeSelectedChanged) - Q_PROPERTY(bool projectImportExists MEMBER m_projectImportExists NOTIFY projectImportExistsChanged) - Q_PROPERTY(bool dataStoreExists MEMBER m_dataStoreExists NOTIFY dataStoreExistsChanged) - -public: - CollectionWidget(CollectionView *view); - ~CollectionWidget(); - void contextHelp(const Core::IContext::HelpCallback &callback) const; - - QPointer listModel() const; - QPointer collectionDetailsModel() const; - - void reloadQmlSource(); - - QSize minimumSizeHint() const override; - - Q_INVOKABLE bool loadJsonFile(const QUrl &url, const QString &collectionName = {}); - Q_INVOKABLE bool loadCsvFile(const QUrl &url, const QString &collectionName = {}); - Q_INVOKABLE bool isJsonFile(const QUrl &url) const; - Q_INVOKABLE bool isCsvFile(const QUrl &url) const; - Q_INVOKABLE bool isValidUrlToImport(const QUrl &url) const; - - Q_INVOKABLE bool importFile(const QString &collectionName, - const QUrl &url, - const bool &firstRowIsHeader = true); - - Q_INVOKABLE void addProjectImport(); - Q_INVOKABLE void addCollectionToDataStore(const QString &collectionName); - Q_INVOKABLE void assignCollectionToSelectedNode(const QString collectionName); - Q_INVOKABLE void openCollection(const QString &collectionName); - Q_INVOKABLE ModelNode dataStoreNode() const; - - void warn(const QString &title, const QString &body); - void setTargetNodeSelected(bool selected); - void setProjectImportExists(bool exists); - void setDataStoreExists(bool exists); - - void deleteSelectedCollection(); - -signals: - void targetNodeSelectedChanged(bool); - void projectImportExistsChanged(bool); - void dataStoreExistsChanged(bool); - -private: - QString generateUniqueCollectionName(const ModelNode &node, const QString &name); - - QPointer m_view; - QPointer m_listModel; - QPointer m_collectionDetailsModel; - QPointer m_iContext; - std::unique_ptr m_collectionDetailsSortFilterModel; - Utils::UniqueObjectPtr m_quickWidget; - bool m_targetNodeSelected = false; - bool m_projectImportExists = false; - bool m_dataStoreExists = false; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp deleted file mode 100644 index 5ffad6683c3..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp +++ /dev/null @@ -1,517 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "datastoremodelnode.h" - -#include "abstractview.h" -#include "collectioneditorconstants.h" -#include "collectioneditorutils.h" -#include "model/qmltextgenerator.h" -#include "plaintexteditmodifier.h" -#include "qmldesignerbase/qmldesignerbaseplugin.h" -#include "qmldesignerexternaldependencies.h" -#include "rewriterview.h" - -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include -#include - -#include -#include -#include - -#include - -namespace { - -inline constexpr char CHILDLISTMODEL_TYPENAME[] = "ChildListModel"; - -QmlDesigner::PropertyNameList createNameList(const QmlDesigner::ModelNode &node) -{ - using QmlDesigner::AbstractProperty; - using QmlDesigner::PropertyName; - using QmlDesigner::PropertyNameList; - static PropertyNameList defaultsNodeProps = { - "id", - QmlDesigner::CollectionEditorConstants::SOURCEFILE_PROPERTY, - QmlDesigner::CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY, - "backend"}; - PropertyNameList dynamicPropertyNames = Utils::transform( - node.dynamicProperties(), - [](const AbstractProperty &property) -> PropertyName { return property.name(); }); - - Utils::sort(dynamicPropertyNames); - - return defaultsNodeProps + dynamicPropertyNames; -} - -bool isValidCollectionPropertyName(const QString &collectionId) -{ - static const QmlDesigner::PropertyNameList reservedKeywords = { - QmlDesigner::CollectionEditorConstants::SOURCEFILE_PROPERTY, - QmlDesigner::CollectionEditorConstants::JSONBACKEND_TYPENAME, - "backend", - "models", - }; - - return QmlDesigner::ModelNode::isValidId(collectionId) - && !reservedKeywords.contains(collectionId.toLatin1()); -} - -QMap getModelIdMap(const QmlDesigner::ModelNode &rootNode) -{ - using namespace QmlDesigner; - QMap modelNameForId; - - const QList propertyNames = rootNode.dynamicProperties(); - - for (const AbstractProperty &property : std::as_const(propertyNames)) { - if (!property.isNodeProperty()) - continue; - - NodeProperty nodeProperty = property.toNodeProperty(); - if (!nodeProperty.hasDynamicTypeName(CHILDLISTMODEL_TYPENAME)) - continue; - - ModelNode childNode = nodeProperty.modelNode(); - if (childNode.hasProperty(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY)) { - QString modelName = childNode - .property(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY) - .toVariantProperty() - .value() - .toString(); - - if (!modelName.isEmpty()) - modelNameForId.insert(modelName, property.name()); - } - } - return modelNameForId; -} - -void setQmlContextToModel(QmlDesigner::Model *model, const QString &qmlContext) -{ - using namespace QmlDesigner; - Q_ASSERT(model); - - std::unique_ptr textEdit = std::make_unique(); - std::unique_ptr modifier = std::make_unique( - textEdit.get()); - textEdit->hide(); - textEdit->setPlainText(qmlContext); - QmlDesigner::ExternalDependencies externalDependencies{QmlDesignerBasePlugin::settings()}; - std::unique_ptr rewriter = std::make_unique( - externalDependencies, QmlDesigner::RewriterView::Validate); - - rewriter->setTextModifier(modifier.get()); - rewriter->setCheckSemanticErrors(false); - - model->attachView(rewriter.get()); - model->detachView(rewriter.get()); -} - -} // namespace - -namespace QmlDesigner { - -DataStoreModelNode::DataStoreModelNode() = default; - -void DataStoreModelNode::reloadModel(const Utils::FilePath &projectModulePath) -{ - using Utils::FilePath; - if (!projectModulePath.exists()) { - reset(); - return; - } - bool forceUpdate = false; - - const FilePath dataStoreQmlPath = projectModulePath.resolvePath( - CollectionEditorConstants::DEFAULT_DATASTORE_QML_FILENAME.toString()); - const FilePath dataStoreJsonPath = projectModulePath.resolvePath( - CollectionEditorConstants::DEFAULT_MODELS_JSON_FILENAME.toString()); - QUrl dataStoreQmlUrl = dataStoreQmlPath.toUrl(); - - if (dataStoreQmlPath.exists() && dataStoreJsonPath.exists()) { - if (!m_model.get() || m_model->fileUrl() != dataStoreQmlUrl) { -#ifdef QDS_USE_PROJECTSTORAGE - m_model = model()->createModel("JsonListModel"); - forceUpdate = true; - Import import = Import::createLibraryImport("QtQuick.Studio.Utils"); - m_model->changeImports({import}, {}); -#else - m_model = Model::create(CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME, 1, 1); - forceUpdate = true; - Import import = Import::createLibraryImport( - CollectionEditorConstants::COLLECTIONMODEL_IMPORT); - try { - if (!m_model->hasImport(import, true, true)) - m_model->changeImports({import}, {}); - } catch (const Exception &) { - QTC_ASSERT(false, return); - } -#endif - } - } else { - reset(); - } - - if (!m_model.get()) - return; - - if (forceUpdate) { - m_model->setFileUrl(dataStoreQmlUrl); - m_dataRelativePath = dataStoreJsonPath.relativePathFrom(dataStoreQmlPath).toFSPathString(); - preloadFile(); - update(); - } -} - -QStringList DataStoreModelNode::collectionNames() const -{ - return m_collectionPropertyNames.keys(); -} - -Model *DataStoreModelNode::model() const -{ - return m_model.get(); -} - -ModelNode DataStoreModelNode::modelNode() const -{ - if (!m_model.get()) - return {}; - return m_model->rootModelNode(); -} - -Utils::FilePath DataStoreModelNode::jsonFilePath() const -{ - QUrl modelUrl = m_model->fileUrl(); - return Utils::FilePath::fromUserInput(modelUrl.isLocalFile() ? modelUrl.toLocalFile() - : modelUrl.toString()) - .parentDir() - .resolvePath(CollectionEditorConstants::DEFAULT_MODELS_JSON_FILENAME.toString()); -} - -QString DataStoreModelNode::getModelQmlText() -{ - ModelNode node = modelNode(); - QTC_ASSERT(node, return {}); - - Internal::QmlTextGenerator textGen(createNameList(node), - QmlJSTools::QmlJSToolsSettings::globalCodeStyle()->tabSettings()); - - QString genText = textGen(node); - return genText; -} - -void DataStoreModelNode::reset() -{ - if (m_model) - m_model.reset(); - - m_dataRelativePath.clear(); - setCollectionNames({}); -} - -void DataStoreModelNode::preloadFile() -{ - using Utils::FilePath; - using Utils::FileReader; - - if (!m_model) - return; - - const FilePath dataStoreQmlPath = dataStoreQmlFilePath(); - FileReader dataStoreQmlFile; - QString sourceQmlContext; - - if (dataStoreQmlFile.fetch(dataStoreQmlPath)) - sourceQmlContext = QString::fromLatin1(dataStoreQmlFile.data()); - - setQmlContextToModel(m_model.get(), sourceQmlContext); - m_collectionPropertyNames = getModelIdMap(m_model->rootModelNode()); -} - -void DataStoreModelNode::updateDataStoreProperties() -{ - QTC_ASSERT(model(), return); - - ModelNode rootNode = modelNode(); - QTC_ASSERT(rootNode.isValid(), return); - - QSet collectionNamesToBeAdded; - const QStringList allCollectionNames = m_collectionPropertyNames.keys(); - for (const QString &collectionName : allCollectionNames) - collectionNamesToBeAdded << collectionName; - - const QList formerPropertyNames = rootNode.dynamicProperties(); - - // Remove invalid collection names from the properties - for (const AbstractProperty &property : formerPropertyNames) { - if (!property.isNodeProperty()) - continue; - - NodeProperty nodeProprty = property.toNodeProperty(); - if (!nodeProprty.hasDynamicTypeName(CHILDLISTMODEL_TYPENAME)) - continue; - - ModelNode childNode = nodeProprty.modelNode(); - if (childNode.hasProperty(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY)) { - QString modelName = childNode - .property(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY) - .toVariantProperty() - .value() - .toString(); - if (collectionNamesToBeAdded.contains(modelName)) { - m_collectionPropertyNames.insert(modelName, property.name()); - collectionNamesToBeAdded.remove(modelName); - } else { - rootNode.removeProperty(property.name()); - } - } else { - rootNode.removeProperty(property.name()); - } - } - - rootNode.setIdWithoutRefactoring("models"); - - QStringList collectionNamesLeft = collectionNamesToBeAdded.values(); - Utils::sort(collectionNamesLeft); - for (const QString &collectionName : std::as_const(collectionNamesLeft)) - addCollectionNameToTheModel(collectionName, getUniquePropertyName(collectionName)); - - // Backend Property - ModelNode backendNode = model()->createModelNode(CollectionEditorConstants::JSONBACKEND_TYPENAME); - NodeProperty backendProperty = rootNode.nodeProperty("backend"); - backendProperty.setDynamicTypeNameAndsetModelNode(CollectionEditorConstants::JSONBACKEND_TYPENAME, - backendNode); - // Source Property - VariantProperty sourceProp = rootNode.variantProperty( - CollectionEditorConstants::SOURCEFILE_PROPERTY); - sourceProp.setValue(m_dataRelativePath); -} - -void DataStoreModelNode::updateSingletonFile() -{ - using Utils::FilePath; - using Utils::FileSaver; - QTC_ASSERT(m_model.get(), return); - - const QString pragmaSingleTone = "pragma Singleton\n"; - QString imports; - - for (const Import &import : m_model->imports()) - imports += QStringLiteral("import %1\n").arg(import.toString(true)); - - QString content = pragmaSingleTone + imports + getModelQmlText(); - Core::DocumentManager::expectFileChange(dataStoreQmlFilePath()); - FileSaver file(dataStoreQmlFilePath()); - file.write(content.toLatin1()); - file.finalize(); -} - -void DataStoreModelNode::update() -{ - if (!m_model.get()) - return; - - updateDataStoreProperties(); - updateSingletonFile(); -} - -void DataStoreModelNode::addCollectionNameToTheModel(const QString &collectionName, - const PropertyName &dataStorePropertyName) -{ - ModelNode rootNode = modelNode(); - QTC_ASSERT(rootNode.isValid(), return); - - if (dataStorePropertyName.isEmpty()) { - qWarning() << __FUNCTION__ << __LINE__ - << QString("The property name cannot be generated from \"%1\"").arg(collectionName); - return; - } - - ModelNode collectionNode = model()->createModelNode(CHILDLISTMODEL_TYPENAME); - VariantProperty modelNameProperty = collectionNode.variantProperty( - CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY); - modelNameProperty.setValue(collectionName); - - NodeProperty nodeProp = rootNode.nodeProperty(dataStorePropertyName); - nodeProp.setDynamicTypeNameAndsetModelNode(CHILDLISTMODEL_TYPENAME, collectionNode); - - m_collectionPropertyNames.insert(collectionName, dataStorePropertyName); -} - -Utils::FilePath DataStoreModelNode::dataStoreQmlFilePath() const -{ - QUrl modelUrl = m_model->fileUrl(); - return Utils::FilePath::fromUserInput(modelUrl.isLocalFile() ? modelUrl.toLocalFile() - : modelUrl.toString()); -} - -PropertyName DataStoreModelNode::getUniquePropertyName(const QString &collectionName) -{ - ModelNode dataStoreNode = modelNode(); - QTC_ASSERT(!collectionName.isEmpty() && dataStoreNode.isValid(), return {}); - - QString newProperty; - - // convert to camel case - QStringList nameWords = collectionName.split(' '); - nameWords[0] = nameWords[0].at(0).toLower() + nameWords[0].mid(1); - for (int i = 1; i < nameWords.size(); ++i) - nameWords[i] = nameWords[i].at(0).toUpper() + nameWords[i].mid(1); - newProperty = nameWords.join(""); - - // if id starts with a number prepend an underscore - if (newProperty.at(0).isDigit()) - newProperty.prepend('_'); - - // If the new id is not valid (e.g. qml keyword match), prepend an underscore - if (!isValidCollectionPropertyName(newProperty)) - newProperty.prepend('_'); - - static const QRegularExpression rgx("\\d+$"); // matches a number at the end of a string - while (dataStoreNode.hasProperty(newProperty.toLatin1())) { // id exists - QRegularExpressionMatch match = rgx.match(newProperty); - if (match.hasMatch()) { // ends with a number, increment it - QString numStr = match.captured(); - int num = numStr.toInt() + 1; - newProperty = newProperty.mid(0, match.capturedStart()) + QString::number(num); - } else { - newProperty.append('1'); - } - } - - return newProperty.toLatin1(); -} - -void DataStoreModelNode::setCollectionNames(const QStringList &newCollectionNames) -{ - m_collectionPropertyNames.clear(); - for (const QString &collectionName : newCollectionNames) - m_collectionPropertyNames.insert(collectionName, {}); - update(); -} - -void DataStoreModelNode::addCollection(const QString &collectionName) -{ - if (!m_collectionPropertyNames.contains(collectionName)) { - m_collectionPropertyNames.insert(collectionName, {}); - update(); - } -} - -void DataStoreModelNode::renameCollection(const QString &oldName, const QString &newName) -{ - ModelNode dataStoreNode = modelNode(); - QTC_ASSERT(dataStoreNode.isValid(), return); - - if (m_collectionPropertyNames.contains(oldName)) { - const PropertyName oldPropertyName = m_collectionPropertyNames.value(oldName); - if (!oldPropertyName.isEmpty() && dataStoreNode.hasProperty(oldPropertyName)) { - NodeProperty collectionNode = dataStoreNode.property(oldPropertyName).toNodeProperty(); - if (collectionNode.isValid()) { - VariantProperty modelNameProperty = collectionNode.modelNode().variantProperty( - CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY); - modelNameProperty.setValue(newName); - m_collectionPropertyNames.remove(oldName); - m_collectionPropertyNames.insert(newName, collectionNode.name()); - update(); - return; - } - qWarning() << __FUNCTION__ << __LINE__ - << "There is no valid node for the old collection name"; - return; - } - qWarning() << __FUNCTION__ << __LINE__ << QString("Invalid old property name") - << oldPropertyName; - return; - } - qWarning() << __FUNCTION__ << __LINE__ - << QString("There is no old collection name registered with this name \"%1\"").arg(oldName); -} - -void DataStoreModelNode::removeCollections(const QStringList &collectionNames) -{ - bool updateRequired = false; - for (const QString &collectionName : collectionNames) { - if (m_collectionPropertyNames.contains(collectionName)) { - m_collectionPropertyNames.remove(collectionName); - updateRequired = true; - } - } - - if (updateRequired) - update(); -} - -void DataStoreModelNode::assignCollectionToNode(AbstractView *view, - const ModelNode &targetNode, - const QString &collectionName, - CollectionColumnFinder collectionHasColumn, - FirstColumnProvider firstColumnProvider) -{ - QTC_ASSERT(targetNode.isValid(), return); - - if (!CollectionEditorUtils::canAcceptCollectionAsModel(targetNode)) - return; - - if (!m_collectionPropertyNames.contains(collectionName)) { - qWarning() << __FUNCTION__ << __LINE__ << "Collection doesn't exist in the DataStore" - << collectionName; - return; - } - - PropertyName propertyName = m_collectionPropertyNames.value(collectionName); - - const ModelNode dataStore = modelNode(); - VariantProperty sourceProperty = dataStore.variantProperty(propertyName); - if (!sourceProperty.exists()) { - qWarning() << __FUNCTION__ << __LINE__ - << "The source property doesn't exist in the DataStore."; - return; - } - - view->executeInTransaction("assignCollectionToNode", [&]() { - QString identifier = QString("DataStore.%1").arg(QString::fromLatin1(sourceProperty.name())); - - // Remove the old model node property if exists - NodeProperty modelNodeProperty = targetNode.nodeProperty("model"); - if (modelNodeProperty.modelNode()) - modelNodeProperty.modelNode().destroy(); - - // Assign the collection to the node - BindingProperty modelProperty = targetNode.bindingProperty("model"); - modelProperty.setExpression(identifier); - - if (CollectionEditorUtils::hasTextRoleProperty(targetNode)) { - VariantProperty textRoleProperty = targetNode.variantProperty("textRole"); - const QVariant currentTextRoleValue = textRoleProperty.value(); - - if (currentTextRoleValue.isValid() && !currentTextRoleValue.isNull()) { - if (currentTextRoleValue.type() == QVariant::String) { - const QString currentTextRole = currentTextRoleValue.toString(); - if (collectionHasColumn(collectionName, currentTextRole)) - return; - } else { - return; - } - } - - QString textRoleValue = firstColumnProvider(collectionName); - textRoleProperty.setValue(textRoleValue); - } - }); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h deleted file mode 100644 index 9d438ef5f56..00000000000 --- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include - -#include - -namespace Utils { -class FilePath; -} - -namespace QmlDesigner { - -class Model; - -class DataStoreModelNode -{ -public: - using CollectionColumnFinder = std::function; - using FirstColumnProvider = std::function; - - DataStoreModelNode(); - - void reloadModel(const Utils::FilePath &projectModulePath); - QStringList collectionNames() const; - - Model *model() const; - ModelNode modelNode() const; - Utils::FilePath jsonFilePath() const; - - void setCollectionNames(const QStringList &newCollectionNames); - void addCollection(const QString &collectionName); - void renameCollection(const QString &oldName, const QString &newName); - void removeCollections(const QStringList &collectionNames); - - void assignCollectionToNode(AbstractView *view, - const ModelNode &targetNode, - const QString &collectionName, - CollectionColumnFinder collectionHasColumn, - FirstColumnProvider firstColumnProvider); - -private: - QString getModelQmlText(); - - void reset(); - void preloadFile(); - void updateDataStoreProperties(); - void updateSingletonFile(); - void update(); - void addCollectionNameToTheModel(const QString &collectionName, - const PropertyName &dataStorePropertyName); - Utils::FilePath dataStoreQmlFilePath() const; - - PropertyName getUniquePropertyName(const QString &collectionName); - - ModelPointer m_model; - QMap m_collectionPropertyNames; - QString m_dataRelativePath; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h index d992a6a5bf0..da7c5bf72eb 100644 --- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h +++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h @@ -69,7 +69,6 @@ const char mergeTemplateCommandId[] = "MergeTemplate"; const char goToImplementationCommandId[] = "GoToImplementation"; const char makeComponentCommandId[] = "MakeComponent"; const char editMaterialCommandId[] = "EditMaterial"; -const char editCollectionCommandId[] = "EditCollection"; const char addItemToStackedContainerCommandId[] = "AddItemToStackedContainer"; const char addTabBarToStackedContainerCommandId[] = "AddTabBarToStackedContainer"; const char increaseIndexOfStackedContainerCommandId[] = "IncreaseIndexOfStackedContainer"; @@ -128,7 +127,6 @@ const char mergeTemplateDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMen const char goToImplementationDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Go to Implementation"); const char makeComponentDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Create Component"); const char editMaterialDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Material"); -const char editCollectionDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Model"); const char editAnnotationsDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Annotations"); const char addMouseAreaFillDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add Mouse Area"); const char editIn3dViewDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit in 3D View"); @@ -214,7 +212,6 @@ enum PrioritiesEnum : int { ArrangeCategory, EditCategory, EditListModel, - EditCollection, /******** Section *****************************/ PositionSection = 2000, SnappingCategory, diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index fd2321f82a3..bbe64935f61 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -113,7 +113,6 @@ void DesignerActionManager::polishActions() const Core::Context qmlDesignerNavigatorContext(Constants::C_QMLNAVIGATOR); Core::Context qmlDesignerMaterialBrowserContext(Constants::C_QMLMATERIALBROWSER); Core::Context qmlDesignerAssetsLibraryContext(Constants::C_QMLASSETSLIBRARY); - Core::Context qmlDesignerCollectionEditorContext(Constants::C_QMLCOLLECTIONEDITOR); Core::Context qmlDesignerUIContext; qmlDesignerUIContext.add(qmlDesignerFormEditorContext); @@ -121,7 +120,6 @@ void DesignerActionManager::polishActions() const qmlDesignerUIContext.add(qmlDesignerNavigatorContext); qmlDesignerUIContext.add(qmlDesignerMaterialBrowserContext); qmlDesignerUIContext.add(qmlDesignerAssetsLibraryContext); - qmlDesignerUIContext.add(qmlDesignerCollectionEditorContext); for (auto *action : actions) { if (!action->menuId().isEmpty()) { @@ -2011,16 +2009,6 @@ void DesignerActionManager::createDefaultDesignerActions() addDesignerAction(new EditListModelAction); - addDesignerAction(new ModelNodeContextMenuAction(editCollectionCommandId, - editCollectionDisplayName, - contextIcon(DesignerIcons::EditIcon), - rootCategory, - QKeySequence("Alt+e"), - ComponentCoreConstants::Priorities::EditCollection, - &editCollection, - &hasCollectionAsModel, - &hasCollectionAsModel)); - addDesignerAction(new ModelNodeContextMenuAction(openSignalDialogCommandId, openSignalDialogDisplayName, {}, diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h index 1a1284f35ee..6734bac568b 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h @@ -82,18 +82,6 @@ inline bool hasEditableMaterial(const SelectionContext &selectionState) return prop.exists() && (!prop.expression().isEmpty() || !prop.resolveToModelNodeList().empty()); } -inline bool hasCollectionAsModel(const SelectionContext &selectionState) -{ - if (!selectionState.isInBaseState() || !selectionState.singleNodeIsSelected()) - return false; - - const ModelNode singleSelectedNode = selectionState.currentSingleSelectedNode(); - - return singleSelectedNode.metaInfo().isListOrGridView() - && singleSelectedNode.property("model").toBindingProperty().expression().startsWith( - "DataStore."); -} - inline bool selectionEnabled(const SelectionContext &selectionState) { return selectionState.showSelectionTools(); diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 1cb58d3c6b3..7c32814b82c 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -846,30 +846,6 @@ void editMaterial(const SelectionContext &selectionContext) } } -// Open a collection in the collection editor -void editCollection(const SelectionContext &selectionContext) -{ - ModelNode modelNode = selectionContext.targetNode(); - - if (!modelNode) - modelNode = selectionContext.currentSingleSelectedNode(); - - if (!modelNode) - return; - - const QString dataStoreExpression = "DataStore."; - - BindingProperty prop = modelNode.bindingProperty("model"); - if (!prop.exists() || !prop.expression().startsWith(dataStoreExpression)) - return; - - AbstractView *view = selectionContext.view(); - const QString collectionId = prop.expression().mid(dataStoreExpression.size()); - - // to CollectionEditor... - view->emitCustomNotification("open_collection_by_id", {}, {collectionId}); -} - void addItemToStackedContainer(const SelectionContext &selectionContext) { AbstractView *view = selectionContext.view(); diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h index a67cef49424..26562f429a0 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h @@ -92,7 +92,6 @@ void layoutGridLayout(const SelectionContext &selectionState); void goImplementation(const SelectionContext &selectionState); void addNewSignalHandler(const SelectionContext &selectionState); void editMaterial(const SelectionContext &selectionContext); -void editCollection(const SelectionContext &selectionContext); void addSignalHandlerOrGotoImplementation(const SelectionContext &selectionState, bool addAlwaysNewSlot); void removeLayout(const SelectionContext &selectionContext); void removePositioner(const SelectionContext &selectionContext); diff --git a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp index 025ef5cb73a..a56735862f3 100644 --- a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -42,14 +41,6 @@ namespace QmlDesigner { -static bool enableModelEditor() -{ - Utils::QtcSettings *settings = Core::ICore::settings(); - const Utils::Key enableModelManagerKey = "QML/Designer/UseExperimentalFeatures44"; - - return settings->value(enableModelManagerKey, false).toBool(); -} - static Q_LOGGING_CATEGORY(viewBenchmark, "qtc.viewmanager.attach", QtWarningMsg) class ViewManagerData @@ -64,7 +55,6 @@ public: : connectionManager, externalDependencies, true) - , collectionView{externalDependencies} , contentLibraryView{imageCache, externalDependencies} , componentView{externalDependencies} #ifndef QTC_USE_QML_DESIGNER_LITE @@ -90,7 +80,6 @@ public: Internal::DebugView debugView; DesignerActionManagerView designerActionManagerView; NodeInstanceView nodeInstanceView; - CollectionView collectionView; ContentLibraryView contentLibraryView; ComponentView componentView; #ifndef QTC_USE_QML_DESIGNER_LITE @@ -235,9 +224,6 @@ QList ViewManager::standardViews() const &d->designerActionManagerView}; #endif - if (enableModelEditor()) - list.append(&d->collectionView); - if (QmlDesignerPlugin::instance() ->settings() .value(DesignerSettingsKey::ENABLE_DEBUGVIEW) @@ -418,8 +404,6 @@ QList ViewManager::widgetInfos() const widgetInfoList.append(d->textureEditorView.widgetInfo()); #endif widgetInfoList.append(d->statesEditorView.widgetInfo()); - if (enableModelEditor()) - widgetInfoList.append(d->collectionView.widgetInfo()); if (checkEnterpriseLicense()) widgetInfoList.append(d->contentLibraryView.widgetInfo()); diff --git a/src/plugins/qmldesigner/designmodecontext.cpp b/src/plugins/qmldesigner/designmodecontext.cpp index 43fb0d9d756..796261935af 100644 --- a/src/plugins/qmldesigner/designmodecontext.cpp +++ b/src/plugins/qmldesigner/designmodecontext.cpp @@ -3,7 +3,6 @@ #include "designmodecontext.h" #include "assetslibrarywidget.h" -#include "collectionwidget.h" #include "designmodewidget.h" #include "edit3dwidget.h" #include "formeditorwidget.h" @@ -98,15 +97,4 @@ void TextEditorContext::contextHelp(const HelpCallback &callback) const qobject_cast(m_widget)->contextHelp(callback); } -CollectionEditorContext::CollectionEditorContext(QWidget *widget) - : IContext(widget) -{ - setWidget(widget); - setContext(Core::Context(Constants::C_QMLCOLLECTIONEDITOR, Constants::C_QT_QUICK_TOOLS_MENU)); -} - -void CollectionEditorContext::contextHelp(const HelpCallback &callback) const -{ - qobject_cast(m_widget)->contextHelp(callback); -} } // namespace QmlDesigner::Internal diff --git a/src/plugins/qmldesigner/designmodecontext.h b/src/plugins/qmldesigner/designmodecontext.h index 12f0113d977..1d146deb7d8 100644 --- a/src/plugins/qmldesigner/designmodecontext.h +++ b/src/plugins/qmldesigner/designmodecontext.h @@ -73,14 +73,5 @@ public: TextEditorContext(QWidget *widget); void contextHelp(const Core::IContext::HelpCallback &callback) const override; }; - -class CollectionEditorContext : public Core::IContext -{ - Q_OBJECT - -public: - CollectionEditorContext(QWidget *widget); - void contextHelp(const Core::IContext::HelpCallback &callback) const override; -}; } // namespace Internal } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index f2f78a1526f..537e65c5b41 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -19,7 +19,6 @@ inline constexpr char C_QMLNAVIGATOR[] = "QmlDesigner::Navigator"; inline constexpr char C_QMLTEXTEDITOR[] = "QmlDesigner::TextEditor"; inline constexpr char C_QMLMATERIALBROWSER[] = "QmlDesigner::MaterialBrowser"; inline constexpr char C_QMLASSETSLIBRARY[] = "QmlDesigner::AssetsLibrary"; -inline constexpr char C_QMLCOLLECTIONEDITOR[] = "QmlDesigner::CollectionEditor"; // Special context for preview menu, shared b/w designer and text editor inline constexpr char C_QT_QUICK_TOOLS_MENU[] = "QmlDesigner::ToolsMenu"; @@ -186,7 +185,6 @@ inline constexpr char OBJECT_NAME_EFFECT_COMPOSER[] = "QQuickWidgetEffectCompose inline constexpr char OBJECT_NAME_MATERIAL_BROWSER[] = "QQuickWidgetMaterialBrowser"; inline constexpr char OBJECT_NAME_MATERIAL_EDITOR[] = "QQuickWidgetMaterialEditor"; inline constexpr char OBJECT_NAME_PROPERTY_EDITOR[] = "QQuickWidgetPropertyEditor"; -inline constexpr char OBJECT_NAME_COLLECTION_EDITOR[] = "QQuickWidgetQDSCollectionEditor"; inline constexpr char OBJECT_NAME_STATES_EDITOR[] = "QQuickWidgetStatesEditor"; inline constexpr char OBJECT_NAME_TEXTURE_EDITOR[] = "QQuickWidgetTextureEditor"; inline constexpr char OBJECT_NAME_TOP_TOOLBAR[] = "QQuickWidgetTopToolbar"; diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index 601cf966813..d2518f41e40 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -4,7 +4,6 @@ #include "qmldesignerplugin.h" #include "qmldesignertr.h" -#include "collectioneditor/collectionview.h" #include "coreplugin/iwizardfactory.h" #include "designmodecontext.h" #include "designmodewidget.h" @@ -298,7 +297,6 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e //TODO Move registering those types out of the property editor, since they are used also in the states editor Quick2PropertyEditorView::registerQmlTypes(); - CollectionView::registerDeclarativeType(); StudioQuickWidget::registerDeclarativeType(); QmlDesignerBase::WindowManager::registerDeclarativeType(); @@ -392,7 +390,6 @@ void QmlDesignerPlugin::integrateIntoQtCreator(QWidget *modeWidget) Core::Context qmlDesignerNavigatorContext(Constants::C_QMLNAVIGATOR); Core::Context qmlDesignerMaterialBrowserContext(Constants::C_QMLMATERIALBROWSER); Core::Context qmlDesignerAssetsLibraryContext(Constants::C_QMLASSETSLIBRARY); - Core::Context qmlDesignerCollectionEditorContext(Constants::C_QMLCOLLECTIONEDITOR); context->context().add(qmlDesignerMainContext); context->context().add(qmlDesignerFormEditorContext); @@ -400,7 +397,6 @@ void QmlDesignerPlugin::integrateIntoQtCreator(QWidget *modeWidget) context->context().add(qmlDesignerNavigatorContext); context->context().add(qmlDesignerMaterialBrowserContext); context->context().add(qmlDesignerAssetsLibraryContext); - context->context().add(qmlDesignerCollectionEditorContext); context->context().add(ProjectExplorer::Constants::QMLJS_LANGUAGE_ID); d->shortCutManager.registerActions(qmlDesignerMainContext, qmlDesignerFormEditorContext, diff --git a/src/plugins/qmldesigner/shortcutmanager.cpp b/src/plugins/qmldesigner/shortcutmanager.cpp index 961aa1c9520..79642c15f5e 100644 --- a/src/plugins/qmldesigner/shortcutmanager.cpp +++ b/src/plugins/qmldesigner/shortcutmanager.cpp @@ -231,12 +231,10 @@ void ShortCutManager::registerActions(const Core::Context &qmlDesignerMainContex connect(Core::ICore::instance(), &Core::ICore::contextChanged, this, [&](const Core::Context &context) { isMatBrowserActive = context.contains(Constants::C_QMLMATERIALBROWSER); isAssetsLibraryActive = context.contains(Constants::C_QMLASSETSLIBRARY); - isCollectionEditorActive = context.contains(Constants::C_QMLCOLLECTIONEDITOR); if (!context.contains(Constants::C_QMLFORMEDITOR) && !context.contains(Constants::C_QMLEDITOR3D) && !context.contains(Constants::C_QMLNAVIGATOR)) { - m_deleteAction.setEnabled(isMatBrowserActive || isAssetsLibraryActive - || isCollectionEditorActive); + m_deleteAction.setEnabled(isMatBrowserActive || isAssetsLibraryActive); m_cutAction.setEnabled(false); m_copyAction.setEnabled(false); m_pasteAction.setEnabled(false); @@ -293,8 +291,6 @@ void ShortCutManager::deleteSelected() actionManager.view()->emitCustomNotification("delete_selected_material"); else if (isAssetsLibraryActive) actionManager.view()->emitCustomNotification("delete_selected_assets"); - else if (isCollectionEditorActive) - actionManager.view()->emitCustomNotification("delete_selected_collection"); else if (currentDesignDocument()) currentDesignDocument()->deleteSelected(); } diff --git a/src/plugins/qmldesigner/shortcutmanager.h b/src/plugins/qmldesigner/shortcutmanager.h index 8714bb5fbcd..70b019217ca 100644 --- a/src/plugins/qmldesigner/shortcutmanager.h +++ b/src/plugins/qmldesigner/shortcutmanager.h @@ -64,7 +64,6 @@ private: bool isMatBrowserActive = false; bool isAssetsLibraryActive = false; - bool isCollectionEditorActive = false; }; } // namespace QmlDesigner