forked from qt-creator/qt-creator
Task-number: QDS-10689 Task-number: QDS-10690 Change-Id: Ib74e27f06ca24ff13f3000297c88306a7a9bb579 Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io> Reviewed-by: Qt CI Patch Build Bot <ci_patchbuild_bot@qt.io>
485 lines
23 KiB
QML
485 lines
23 KiB
QML
// 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 QtQuickDesignerTheme 1.0
|
|
import HelperWidgets 2.0 as HelperWidgets
|
|
import StudioControls 1.0 as StudioControls
|
|
import StudioTheme 1.0 as StudioTheme
|
|
import ItemLibraryBackend
|
|
/* The view displaying the item grid.
|
|
The following Qml context properties have to be set:
|
|
- ItemLibraryModel itemLibraryModel
|
|
- int itemLibraryIconWidth
|
|
- int itemLibraryIconHeight
|
|
- ItemLibraryWidget rootView
|
|
- QColor highlightColor
|
|
itemLibraryModel structure:
|
|
itemLibraryModel [
|
|
ItemLibraryImport {
|
|
string importName
|
|
string importUrl
|
|
bool importVisible
|
|
bool importUsed
|
|
bool importExpanded
|
|
list categoryModel [
|
|
ItemLibraryCategory {
|
|
string categoryName
|
|
bool categoryVisible
|
|
bool categoryExpanded
|
|
list itemModel [
|
|
ItemLibraryItem {
|
|
string itemName
|
|
string itemLibraryIconPath
|
|
bool itemVisible
|
|
string componentPath
|
|
var itemLibraryEntry
|
|
},
|
|
... more items
|
|
]
|
|
},
|
|
... more categories
|
|
]
|
|
},
|
|
... more imports
|
|
]
|
|
*/
|
|
Item {
|
|
id: itemsView
|
|
|
|
property bool adsFocus: false
|
|
// objectName is used by the dock widget to find this particular ScrollView
|
|
// and set the ads focus on it.
|
|
objectName: "__mainSrollView"
|
|
|
|
property string importToRemove
|
|
property string importToAdd
|
|
property string componentSource
|
|
property var currentCategory: null
|
|
property var currentImport: null
|
|
property bool isHorizontalView: false
|
|
property bool isAddModuleView: false
|
|
|
|
property var tooltipBackend: ItemLibraryBackend.tooltipBackend
|
|
|
|
// Called also from C++ to close context menu on focus out
|
|
function closeContextMenu()
|
|
{
|
|
moduleContextMenu.close()
|
|
itemContextMenu.close()
|
|
}
|
|
// Called from C++
|
|
function clearSearchFilter()
|
|
{
|
|
searchBox.clear();
|
|
}
|
|
// Called also from C++
|
|
function switchToComponentsView()
|
|
{
|
|
ItemLibraryBackend.isAddModuleView = false
|
|
}
|
|
onWidthChanged: {
|
|
itemsView.isHorizontalView = itemsView.width > ItemLibraryBackend.widthLimit
|
|
}
|
|
onIsHorizontalViewChanged: closeContextMenu()
|
|
Item {
|
|
id: styleConstants
|
|
property int textWidth: 58
|
|
property int textHeight: Theme.smallFontPixelSize() * 2
|
|
property int cellHorizontalMargin: 1
|
|
property int cellVerticalSpacing: 2
|
|
property int cellVerticalMargin: 4
|
|
// the following depend on the actual shape of the item delegate
|
|
property int cellWidth: styleConstants.textWidth + 2 * styleConstants.cellHorizontalMargin
|
|
property int cellHeight: ItemLibraryBackend.itemLibraryIconHeight + styleConstants.textHeight +
|
|
2 * styleConstants.cellVerticalMargin + styleConstants.cellVerticalSpacing
|
|
StudioControls.Menu {
|
|
id: moduleContextMenu
|
|
StudioControls.MenuItem {
|
|
text: qsTr("Remove Module")
|
|
visible: itemsView.currentCategory === null
|
|
height: visible ? implicitHeight : 0
|
|
enabled: itemsView.importToRemove && !ItemLibraryBackend.rootView.subCompEditMode
|
|
onTriggered: ItemLibraryBackend.rootView.removeImport(itemsView.importToRemove)
|
|
}
|
|
StudioControls.MenuSeparator {
|
|
visible: itemsView.currentCategory === null && !ItemLibraryBackend.rootView.searchActive
|
|
height: visible ? StudioTheme.Values.border : 0
|
|
}
|
|
StudioControls.MenuItem {
|
|
text: qsTr("Expand All")
|
|
visible: itemsView.currentCategory === null && !ItemLibraryBackend.rootView.searchActive
|
|
height: visible ? implicitHeight : 0
|
|
onTriggered: ItemLibraryBackend.itemLibraryModel.expandAll()
|
|
}
|
|
StudioControls.MenuItem {
|
|
text: qsTr("Collapse All")
|
|
visible: itemsView.currentCategory === null && !ItemLibraryBackend.rootView.searchActive
|
|
height: visible ? implicitHeight : 0
|
|
onTriggered: ItemLibraryBackend.itemLibraryModel.collapseAll()
|
|
}
|
|
StudioControls.MenuSeparator {
|
|
visible: itemsView.currentCategory === null && !ItemLibraryBackend.rootView.searchActive
|
|
height: visible ? StudioTheme.Values.border : 0
|
|
}
|
|
StudioControls.MenuItem {
|
|
text: qsTr("Hide Category")
|
|
visible: itemsView.currentCategory
|
|
height: visible ? implicitHeight : 0
|
|
onTriggered: ItemLibraryBackend.itemLibraryModel.hideCategory(itemsView.currentImport.importUrl,
|
|
itemsView.currentCategory.categoryName)
|
|
}
|
|
StudioControls.MenuSeparator {
|
|
visible: itemsView.currentCategory
|
|
height: visible ? StudioTheme.Values.border : 0
|
|
}
|
|
StudioControls.MenuItem {
|
|
text: qsTr("Show Module Hidden Categories")
|
|
visible: !ItemLibraryBackend.rootView.searchActive
|
|
enabled: itemsView.currentImport && !itemsView.currentImport.allCategoriesVisible
|
|
height: visible ? implicitHeight : 0
|
|
onTriggered: ItemLibraryBackend.itemLibraryModel.showImportHiddenCategories(itemsView.currentImport.importUrl)
|
|
}
|
|
StudioControls.MenuItem {
|
|
text: qsTr("Show All Hidden Categories")
|
|
visible: !ItemLibraryBackend.rootView.searchActive
|
|
enabled: ItemLibraryBackend.itemLibraryModel.isAnyCategoryHidden
|
|
height: visible ? implicitHeight : 0
|
|
onTriggered: ItemLibraryBackend.itemLibraryModel.showAllHiddenCategories()
|
|
}
|
|
}
|
|
StudioControls.Menu {
|
|
id: itemContextMenu
|
|
// Workaround for menu item implicit width not properly propagating to menu
|
|
width: Math.max(importMenuItem.implicitWidth, openSourceItem.implicitWidth)
|
|
StudioControls.MenuItem {
|
|
id: importMenuItem
|
|
text: qsTr("Add Module: ") + itemsView.importToAdd
|
|
visible: itemsView.importToAdd
|
|
height: visible ? implicitHeight : 0
|
|
onTriggered: ItemLibraryBackend.rootView.addImportForItem(itemsView.importToAdd)
|
|
}
|
|
StudioControls.MenuItem {
|
|
id: openSourceItem
|
|
text: qsTr("Edit Component")
|
|
visible: itemsView.componentSource
|
|
height: visible ? implicitHeight : 0
|
|
onTriggered: ItemLibraryBackend.rootView.goIntoComponent(itemsView.componentSource)
|
|
}
|
|
}
|
|
}
|
|
Column {
|
|
id: col
|
|
width: parent.width
|
|
height: parent.height
|
|
spacing: 5
|
|
|
|
Rectangle {
|
|
width: parent.width
|
|
height: StudioTheme.Values.doubleToolbarHeight
|
|
color: StudioTheme.Values.themeToolbarBackground
|
|
|
|
Column {
|
|
anchors.fill: parent
|
|
anchors.topMargin: 6
|
|
anchors.bottomMargin: 6
|
|
anchors.leftMargin: 10
|
|
anchors.rightMargin: 10
|
|
spacing: 12
|
|
|
|
StudioControls.SearchBox {
|
|
id: searchBox
|
|
width: parent.width
|
|
style: StudioTheme.Values.searchControlStyle
|
|
|
|
onSearchChanged: (searchText) => ItemLibraryBackend.rootView.handleSearchFilterChanged(searchText)
|
|
}
|
|
|
|
Row {
|
|
width: parent.width
|
|
height: StudioTheme.Values.toolbarHeight
|
|
spacing: 6
|
|
|
|
HelperWidgets.AbstractButton {
|
|
id: addModuleButton
|
|
style: StudioTheme.Values.viewBarButtonStyle
|
|
buttonIcon: StudioTheme.Constants.add_medium
|
|
tooltip: qsTr("Add a module.")
|
|
onClicked: isAddModuleView = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Loader {
|
|
id: loader
|
|
width: col.width
|
|
height: col.height - y - 5
|
|
sourceComponent: isAddModuleView ? addModuleView
|
|
: itemsView.isHorizontalView ? horizontalView : verticalView
|
|
}
|
|
}
|
|
Component {
|
|
id: verticalView
|
|
HelperWidgets.ScrollView {
|
|
id: verticalScrollView
|
|
adsFocus: itemsView.adsFocus
|
|
anchors.fill: parent
|
|
clip: true
|
|
interactive: !itemContextMenu.opened && !moduleContextMenu.opened && !ItemLibraryBackend.rootView.isDragging
|
|
onContentHeightChanged: {
|
|
var maxPosition = Math.max(contentHeight - verticalScrollView.height, 0)
|
|
if (contentY > maxPosition)
|
|
contentY = maxPosition
|
|
}
|
|
Column {
|
|
spacing: 2
|
|
Repeater {
|
|
model: ItemLibraryBackend.itemLibraryModel // to be set in Qml context
|
|
delegate: HelperWidgets.Section {
|
|
width: itemsView.width -
|
|
(verticalScrollView.verticalScrollBarVisible
|
|
? verticalScrollView.verticalThickness : 0)
|
|
caption: importName
|
|
visible: importVisible
|
|
sectionHeight: 30
|
|
sectionFontSize: 15
|
|
showArrow: categoryModel.rowCount() > 0
|
|
labelColor: importUnimported ? StudioTheme.Values.themeUnimportedModuleColor
|
|
: StudioTheme.Values.themeTextColor
|
|
leftPadding: 0
|
|
rightPadding: 0
|
|
expanded: importExpanded
|
|
expandOnClick: false
|
|
useDefaulContextMenu: false
|
|
category: "ItemsView"
|
|
|
|
onToggleExpand: {
|
|
if (categoryModel.rowCount() > 0)
|
|
importExpanded = !importExpanded
|
|
}
|
|
onShowContextMenu: {
|
|
itemsView.importToRemove = importRemovable ? importUrl : ""
|
|
itemsView.currentImport = model
|
|
itemsView.currentCategory = null
|
|
moduleContextMenu.popup()
|
|
}
|
|
Column {
|
|
spacing: 2
|
|
property var currentImportModel: model // allows accessing the import model from inside the category section
|
|
Repeater {
|
|
model: categoryModel
|
|
delegate: HelperWidgets.Section {
|
|
width: itemsView.width -
|
|
(verticalScrollView.verticalScrollBarVisible
|
|
? verticalScrollView.verticalThickness : 0)
|
|
sectionBackgroundColor: "transparent"
|
|
showTopSeparator: index > 0
|
|
hideHeader: categoryModel.rowCount() <= 1
|
|
leftPadding: 0
|
|
rightPadding: 0
|
|
addTopPadding: categoryModel.rowCount() > 1
|
|
addBottomPadding: index !== categoryModel.rowCount() - 1
|
|
caption: categoryName + " (" + itemModel.rowCount() + ")"
|
|
visible: categoryVisible
|
|
expanded: categoryExpanded
|
|
expandOnClick: false
|
|
onToggleExpand: categoryExpanded = !categoryExpanded
|
|
useDefaulContextMenu: false
|
|
category: "ItemsView"
|
|
|
|
onShowContextMenu: {
|
|
if (!ItemLibraryBackend.rootView.searchActive) {
|
|
itemsView.currentCategory = model
|
|
itemsView.currentImport = parent.currentImportModel
|
|
moduleContextMenu.popup()
|
|
}
|
|
}
|
|
Grid {
|
|
id: itemGrid
|
|
property real actualWidth: parent.width - itemGrid.leftPadding - itemGrid.rightPadding
|
|
leftPadding: 6
|
|
rightPadding: 6
|
|
columns: itemGrid.actualWidth / styleConstants.cellWidth
|
|
rowSpacing: 7
|
|
Repeater {
|
|
model: itemModel
|
|
delegate: ItemDelegate {
|
|
visible: itemVisible
|
|
textColor: importUnimported ? StudioTheme.Values.themeUnimportedModuleColor
|
|
: StudioTheme.Values.themeTextColor
|
|
width: styleConstants.cellWidth
|
|
height: styleConstants.cellHeight
|
|
onShowContextMenu: {
|
|
if (!itemUsable || itemComponentSource) {
|
|
itemsView.importToAdd = !itemUsable ? itemRequiredImport : ""
|
|
itemsView.componentSource = itemComponentSource
|
|
itemContextMenu.popup()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Component {
|
|
id: horizontalView
|
|
Row {
|
|
leftPadding: 5
|
|
HelperWidgets.ScrollView {
|
|
id: horizontalScrollView
|
|
adsFocus: itemsView.adsFocus
|
|
width: 270
|
|
height: parent.height
|
|
clip: true
|
|
interactive: !itemContextMenu.opened && !moduleContextMenu.opened && !ItemLibraryBackend.rootView.isDragging
|
|
|
|
onContentHeightChanged: {
|
|
var maxPosition = Math.max(contentHeight - horizontalScrollView.height, 0)
|
|
if (contentY > maxPosition)
|
|
contentY = maxPosition
|
|
}
|
|
Column {
|
|
width: parent.width
|
|
spacing: 2
|
|
Repeater {
|
|
model: ItemLibraryBackend.itemLibraryModel // to be set in Qml context
|
|
delegate: HelperWidgets.Section {
|
|
width: 265 -
|
|
(horizontalScrollView.verticalScrollBarVisible
|
|
? horizontalScrollView.verticalThickness : 0)
|
|
caption: importName
|
|
visible: importVisible
|
|
sectionHeight: 30
|
|
sectionFontSize: 15
|
|
showArrow: categoryModel.rowCount() > 0
|
|
labelColor: importUnimported ? StudioTheme.Values.themeUnimportedModuleColor
|
|
: StudioTheme.Values.themeTextColor
|
|
leftPadding: 0
|
|
rightPadding: 0
|
|
expanded: importExpanded
|
|
expandOnClick: false
|
|
useDefaulContextMenu: false
|
|
category: "ItemsView"
|
|
|
|
onToggleExpand: {
|
|
if (categoryModel.rowCount() > 0)
|
|
importExpanded = !importExpanded
|
|
}
|
|
onShowContextMenu: {
|
|
itemsView.importToRemove = importRemovable ? importUrl : ""
|
|
itemsView.currentImport = model
|
|
itemsView.currentCategory = null
|
|
moduleContextMenu.popup()
|
|
}
|
|
Column {
|
|
spacing: 2
|
|
property var currentImportModel: model // allows accessing the import model from inside the category section
|
|
Repeater {
|
|
model: categoryModel
|
|
delegate: Rectangle {
|
|
width: 265 -
|
|
(horizontalScrollView.verticalScrollBarVisible
|
|
? horizontalScrollView.verticalThickness : 0)
|
|
height: 25
|
|
visible: categoryVisible
|
|
border.width: StudioTheme.Values.border
|
|
border.color: StudioTheme.Values.themeControlOutline
|
|
color: categorySelected
|
|
? StudioTheme.Values.themeControlBackgroundHover
|
|
: categoryMouseArea.containsMouse ? Qt.darker(StudioTheme.Values.themeControlBackgroundHover, 1.5)
|
|
: StudioTheme.Values.themeControlBackground
|
|
Text {
|
|
anchors.fill: parent
|
|
text: categoryName
|
|
color: StudioTheme.Values.themeTextColor
|
|
font.pixelSize: 13
|
|
font.capitalization: Font.AllUppercase
|
|
horizontalAlignment: Text.AlignHCenter
|
|
verticalAlignment: Text.AlignVCenter
|
|
}
|
|
MouseArea {
|
|
id: categoryMouseArea
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
|
onClicked: (mouse) => {
|
|
ItemLibraryBackend.itemLibraryModel.selectImportCategory(parent.parent.currentImportModel.importUrl, model.index)
|
|
if (mouse.button === Qt.RightButton
|
|
&& categoryModel.rowCount() !== 1
|
|
&& !ItemLibraryBackend.rootView.searchActive) {
|
|
itemsView.currentCategory = model
|
|
itemsView.currentImport = parent.parent.currentImportModel
|
|
moduleContextMenu.popup()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Rectangle { // separator between import/category column and item grid
|
|
id: separatingLine
|
|
height: itemsView.height - 10
|
|
width: 1
|
|
color: StudioTheme.Values.themeControlOutline
|
|
}
|
|
HelperWidgets.ScrollView {
|
|
id: itemScrollView
|
|
adsFocus: itemsView.adsFocus
|
|
width: itemsView.width - 275
|
|
height: itemsView.height
|
|
interactive: !itemContextMenu.opened && !moduleContextMenu.opened && !ItemLibraryBackend.rootView.isDragging
|
|
|
|
onContentHeightChanged: {
|
|
var maxPosition = Math.max(contentHeight - itemScrollView.height, 0)
|
|
if (contentY > maxPosition)
|
|
contentY = maxPosition
|
|
}
|
|
Grid {
|
|
id: hItemGrid
|
|
property real actualWidth: itemsView.width - 294
|
|
leftPadding: 9
|
|
rightPadding: 9
|
|
bottomPadding: 15
|
|
columns: hItemGrid.actualWidth / styleConstants.cellWidth
|
|
rowSpacing: 7
|
|
Repeater {
|
|
model: ItemLibraryBackend.itemLibraryModel.itemsModel
|
|
delegate: ItemDelegate {
|
|
visible: itemVisible
|
|
textColor: ItemLibraryBackend.itemLibraryModel.importUnimportedSelected
|
|
? StudioTheme.Values.themeUnimportedModuleColor : StudioTheme.Values.themeTextColor
|
|
width: styleConstants.cellWidth
|
|
height: styleConstants.cellHeight
|
|
onShowContextMenu: {
|
|
if (!itemUsable || itemComponentSource) {
|
|
itemsView.importToAdd = !itemUsable ? itemRequiredImport : ""
|
|
itemsView.componentSource = itemComponentSource
|
|
itemContextMenu.popup()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Component {
|
|
id: addModuleView
|
|
AddModuleView {
|
|
adsFocus: itemsView.adsFocus
|
|
onBack: isAddModuleView = false
|
|
}
|
|
}
|
|
}
|