Files
qt-creator/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemsView.qml
Mahmoud Badri f09d4538e7 QmlDesigner: Implement Material Editor
Task-number: QDS-6438
Task-number: QDS-6439
Change-Id: I04e899a68aea665f0df8b65e21523632174ec76b
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Samuel Ghinet <samuel.ghinet@qt.io>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
2022-05-23 16:44:02 +00:00

520 lines
22 KiB
QML

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuickDesignerTheme 1.0
import HelperWidgets 2.0
import StudioControls 1.0 as StudioControls
import StudioTheme 1.0 as StudioTheme
/* 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 string importToRemove
property string importToAdd
property string componentSource
property var currentCategory: null
property var currentImport: null
property bool isHorizontalView: false
property bool isAddModuleView: false
// Called also from C++ to close context menu on focus out
function closeContextMenu()
{
moduleContextMenu.close()
itemContextMenu.close()
}
// Called also from C++
function switchToComponentsView()
{
isAddModuleView = false
}
onWidthChanged: {
itemsView.isHorizontalView = itemsView.width > 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: 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 && !rootView.subCompEditMode
onTriggered: rootView.removeImport(itemsView.importToRemove)
}
StudioControls.MenuSeparator {
visible: itemsView.currentCategory === null && !rootView.searchActive
height: visible ? StudioTheme.Values.border : 0
}
StudioControls.MenuItem {
text: qsTr("Expand All")
visible: itemsView.currentCategory === null && !rootView.searchActive
height: visible ? implicitHeight : 0
onTriggered: itemLibraryModel.expandAll()
}
StudioControls.MenuItem {
text: qsTr("Collapse All")
visible: itemsView.currentCategory === null && !rootView.searchActive
height: visible ? implicitHeight : 0
onTriggered: itemLibraryModel.collapseAll()
}
StudioControls.MenuSeparator {
visible: itemsView.currentCategory === null && !rootView.searchActive
height: visible ? StudioTheme.Values.border : 0
}
StudioControls.MenuItem {
text: qsTr("Hide Category")
visible: itemsView.currentCategory
height: visible ? implicitHeight : 0
onTriggered: 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: !rootView.searchActive
enabled: itemsView.currentImport && !itemsView.currentImport.allCategoriesVisible
height: visible ? implicitHeight : 0
onTriggered: itemLibraryModel.showImportHiddenCategories(itemsView.currentImport.importUrl)
}
StudioControls.MenuItem {
text: qsTr("Show All Hidden Categories")
visible: !rootView.searchActive
enabled: itemLibraryModel.isAnyCategoryHidden
height: visible ? implicitHeight : 0
onTriggered: 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: rootView.addImportForItem(itemsView.importToAdd)
}
StudioControls.MenuItem {
id: openSourceItem
text: qsTr("Go into Component")
visible: itemsView.componentSource
height: visible ? implicitHeight : 0
onTriggered: rootView.goIntoComponent(itemsView.componentSource)
}
}
}
Column {
id: col
width: parent.width
height: parent.height
y: 5
spacing: 5
Row {
width: parent.width
SearchBox {
id: searchBox
width: parent.width - addModuleButton.width - 5
}
IconButton {
id: addModuleButton
anchors.verticalCenter: parent.verticalCenter
tooltip: qsTr("Add a module.")
icon: StudioTheme.Constants.plus
buttonSize: parent.height
onClicked: isAddModuleView = true
}
}
Loader {
id: loader
width: col.width
height: col.height - y - 5
sourceComponent: isAddModuleView ? addModuleView
: itemsView.isHorizontalView ? horizontalView : verticalView
}
}
Component {
id: verticalView
ScrollView {
id: verticalScrollView
anchors.fill: parent
clip: true
interactive: !itemContextMenu.opened && !moduleContextMenu.opened
onContentHeightChanged: {
var maxPosition = Math.max(contentHeight - verticalScrollView.height, 0)
if (contentY > maxPosition)
contentY = maxPosition
}
Column {
spacing: 2
Repeater {
model: itemLibraryModel // to be set in Qml context
delegate: 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
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: 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
onShowContextMenu: {
if (!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
ScrollView {
id: horizontalScrollView
width: 270
height: parent.height
clip: true
interactive: !itemContextMenu.opened && !moduleContextMenu.opened
onContentHeightChanged: {
var maxPosition = Math.max(contentHeight - horizontalScrollView.height, 0)
if (contentY > maxPosition)
contentY = maxPosition
}
Column {
width: parent.width
spacing: 2
Repeater {
model: itemLibraryModel // to be set in Qml context
delegate: 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
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) => {
itemLibraryModel.selectImportCategory(parent.parent.currentImportModel.importUrl, model.index)
if (mouse.button === Qt.RightButton
&& categoryModel.rowCount() !== 1
&& !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
}
ScrollView {
id: itemScrollView
width: itemsView.width - 275
height: itemsView.height
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: itemLibraryModel.itemsModel
delegate: ItemDelegate {
visible: itemVisible
textColor: 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 {
onBack: isAddModuleView = false
}
}
}