forked from qt-creator/qt-creator
QmlDesigner: Implement a horizontal layout for Component Library
The view from vertical to horizontal is changed with Loader, depending on Library's width. Vertical layout (old) is mostly the same as before. Added new property categorySelected for categories. Items are also now hoverable. Task-number: QDS-4764 Change-Id: I031f3916f0d011fd76a963b247c238997d7a55d8 Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io> Reviewed-by: Samuel Ghinet <samuel.ghinet@qt.io> Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
@@ -41,7 +41,7 @@ Item {
|
|||||||
anchors.topMargin: 1
|
anchors.topMargin: 1
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
color: StudioTheme.Values.themePanelBackground
|
color: mouseRegion.containsMouse ? StudioTheme.Values.themeControlBackgroundHover : StudioTheme.Values.themePanelBackground
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: itemIcon // to be set by model
|
id: itemIcon // to be set by model
|
||||||
|
@@ -73,7 +73,7 @@ itemLibraryModel [
|
|||||||
]
|
]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ScrollView {
|
Item {
|
||||||
id: itemsView
|
id: itemsView
|
||||||
|
|
||||||
property string importToRemove: ""
|
property string importToRemove: ""
|
||||||
@@ -81,6 +81,11 @@ ScrollView {
|
|||||||
property var currentItem: null
|
property var currentItem: null
|
||||||
property var currentCategory: null
|
property var currentCategory: null
|
||||||
property var currentImport: null
|
property var currentImport: null
|
||||||
|
property bool isHorizontalView: false
|
||||||
|
|
||||||
|
// horizontal component lib variables
|
||||||
|
property var selectedCategory: null
|
||||||
|
property var selectedCategoryImport: null
|
||||||
|
|
||||||
// called from C++ to close context menu on focus out
|
// called from C++ to close context menu on focus out
|
||||||
function closeContextMenu()
|
function closeContextMenu()
|
||||||
@@ -91,15 +96,21 @@ ScrollView {
|
|||||||
|
|
||||||
function showImportCategories()
|
function showImportCategories()
|
||||||
{
|
{
|
||||||
currentImport.importCatVisibleState = true
|
if (itemLibraryModel.isAllCategoriesHidden()) {
|
||||||
|
itemsView.currentImport.importCatVisibleState = true
|
||||||
|
if (!itemLibraryModel.getIsAnyCategoryHidden())
|
||||||
|
itemLibraryModel.isAnyCategoryHidden = false
|
||||||
|
|
||||||
|
itemsView.selectedCategory = itemLibraryModel.selectImportFirstVisibleCategory()
|
||||||
|
}
|
||||||
|
|
||||||
|
itemsView.currentImport.importCatVisibleState = true
|
||||||
if (!itemLibraryModel.getIsAnyCategoryHidden())
|
if (!itemLibraryModel.getIsAnyCategoryHidden())
|
||||||
itemLibraryModel.isAnyCategoryHidden = false
|
itemLibraryModel.isAnyCategoryHidden = false
|
||||||
}
|
}
|
||||||
|
|
||||||
onContentHeightChanged: {
|
onWidthChanged: {
|
||||||
var maxPosition = Math.max(contentHeight - height, 0)
|
itemsView.isHorizontalView = itemsView.width > widthLimit
|
||||||
if (contentY > maxPosition)
|
|
||||||
contentY = maxPosition
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
@@ -112,66 +123,68 @@ ScrollView {
|
|||||||
property int cellVerticalMargin: 4
|
property int cellVerticalMargin: 4
|
||||||
|
|
||||||
// the following depend on the actual shape of the item delegate
|
// the following depend on the actual shape of the item delegate
|
||||||
property int cellWidth: textWidth + 2 * cellHorizontalMargin
|
property int cellWidth: styleConstants.textWidth + 2 * styleConstants.cellHorizontalMargin
|
||||||
property int cellHeight: itemLibraryIconHeight + textHeight +
|
property int cellHeight: itemLibraryIconHeight + styleConstants.textHeight +
|
||||||
2 * cellVerticalMargin + cellVerticalSpacing
|
2 * styleConstants.cellVerticalMargin + styleConstants.cellVerticalSpacing
|
||||||
|
|
||||||
StudioControls.Menu {
|
StudioControls.Menu {
|
||||||
id: moduleContextMenu
|
id: moduleContextMenu
|
||||||
|
|
||||||
StudioControls.MenuItem {
|
StudioControls.MenuItem {
|
||||||
text: qsTr("Remove Module")
|
text: qsTr("Remove Module")
|
||||||
visible: currentCategory === null
|
visible: itemsView.currentCategory === null
|
||||||
height: visible ? implicitHeight : 0
|
height: visible ? implicitHeight : 0
|
||||||
enabled: importToRemove !== ""
|
enabled: itemsView.importToRemove !== ""
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
showImportCategories()
|
showImportCategories()
|
||||||
rootView.removeImport(importToRemove)
|
rootView.removeImport(itemsView.importToRemove)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StudioControls.MenuSeparator {
|
StudioControls.MenuSeparator {
|
||||||
visible: currentCategory === null
|
visible: itemsView.currentCategory === null
|
||||||
height: StudioTheme.Values.border
|
height: StudioTheme.Values.border
|
||||||
}
|
}
|
||||||
|
|
||||||
StudioControls.MenuItem {
|
StudioControls.MenuItem {
|
||||||
text: qsTr("Expand All")
|
text: qsTr("Expand All")
|
||||||
visible: currentCategory === null
|
visible: itemsView.currentCategory === null
|
||||||
height: visible ? implicitHeight : 0
|
height: visible ? implicitHeight : 0
|
||||||
onTriggered: itemLibraryModel.expandAll()
|
onTriggered: itemLibraryModel.expandAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
StudioControls.MenuItem {
|
StudioControls.MenuItem {
|
||||||
text: qsTr("Collapse All")
|
text: qsTr("Collapse All")
|
||||||
visible: currentCategory === null
|
visible: itemsView.currentCategory === null
|
||||||
height: visible ? implicitHeight : 0
|
height: visible ? implicitHeight : 0
|
||||||
onTriggered: itemLibraryModel.collapseAll()
|
onTriggered: itemLibraryModel.collapseAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
StudioControls.MenuSeparator {
|
StudioControls.MenuSeparator {
|
||||||
visible: currentCategory === null
|
visible: itemsView.currentCategory === null
|
||||||
height: StudioTheme.Values.border
|
height: StudioTheme.Values.border
|
||||||
}
|
}
|
||||||
|
|
||||||
StudioControls.MenuItem {
|
StudioControls.MenuItem {
|
||||||
text: qsTr("Hide Category")
|
text: qsTr("Hide Category")
|
||||||
visible: currentCategory
|
visible: itemsView.currentCategory
|
||||||
height: visible ? implicitHeight : 0
|
height: visible ? implicitHeight : 0
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
itemLibraryModel.isAnyCategoryHidden = true
|
itemLibraryModel.isAnyCategoryHidden = true
|
||||||
currentCategory.categoryVisible = false
|
itemsView.currentCategory.categoryVisible = false
|
||||||
|
itemsView.currentCategory.categorySelected = false
|
||||||
|
itemsView.selectedCategory = itemLibraryModel.selectImportFirstVisibleCategory()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StudioControls.MenuSeparator {
|
StudioControls.MenuSeparator {
|
||||||
visible: currentCategory
|
visible: itemsView.currentCategory
|
||||||
height: StudioTheme.Values.border
|
height: StudioTheme.Values.border
|
||||||
}
|
}
|
||||||
|
|
||||||
StudioControls.MenuItem {
|
StudioControls.MenuItem {
|
||||||
text: qsTr("Show Module Hidden Categories")
|
text: qsTr("Show Module Hidden Categories")
|
||||||
enabled: currentImport && !currentImport.importCatVisibleState
|
enabled: itemsView.currentImport && !itemsView.currentImport.importCatVisibleState
|
||||||
onTriggered: showImportCategories()
|
onTriggered: showImportCategories()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,6 +192,12 @@ ScrollView {
|
|||||||
text: qsTr("Show All Hidden Categories")
|
text: qsTr("Show All Hidden Categories")
|
||||||
enabled: itemLibraryModel.isAnyCategoryHidden
|
enabled: itemLibraryModel.isAnyCategoryHidden
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
|
if (itemLibraryModel.isAllCategoriesHidden()) {
|
||||||
|
itemLibraryModel.showHiddenCategories()
|
||||||
|
itemsView.selectedCategory = itemLibraryModel.selectImportFirstVisibleCategory()
|
||||||
|
itemLibraryModel.isAnyCategoryHidden = false
|
||||||
|
}
|
||||||
|
|
||||||
itemLibraryModel.isAnyCategoryHidden = false
|
itemLibraryModel.isAnyCategoryHidden = false
|
||||||
itemLibraryModel.showHiddenCategories()
|
itemLibraryModel.showHiddenCategories()
|
||||||
}
|
}
|
||||||
@@ -192,94 +211,117 @@ ScrollView {
|
|||||||
|
|
||||||
StudioControls.MenuItem {
|
StudioControls.MenuItem {
|
||||||
id: importMenuItem
|
id: importMenuItem
|
||||||
text: qsTr("Add Module: ") + importToAdd
|
text: qsTr("Add Module: ") + itemsView.importToAdd
|
||||||
enabled: importToAdd !== ""
|
enabled: itemsView.importToAdd !== ""
|
||||||
onTriggered: rootView.addImportForItem(importToAdd)
|
onTriggered: rootView.addImportForItem(itemsView.importToAdd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
Loader {
|
||||||
spacing: 2
|
anchors.fill: parent
|
||||||
Repeater {
|
sourceComponent: itemsView.isHorizontalView ? horizontalView : verticalView
|
||||||
model: itemLibraryModel // to be set in Qml context
|
}
|
||||||
delegate: Section {
|
|
||||||
width: itemsView.width -
|
|
||||||
(itemsView.verticalScrollBarVisible ? itemsView.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: {
|
Component {
|
||||||
if (categoryModel.rowCount() > 0)
|
id: verticalView
|
||||||
importExpanded = !importExpanded
|
|
||||||
}
|
|
||||||
onShowContextMenu: {
|
|
||||||
importToRemove = importRemovable ? importUrl : ""
|
|
||||||
currentImport = model
|
|
||||||
currentCategory = null
|
|
||||||
if (!rootView.isSearchActive())
|
|
||||||
moduleContextMenu.popup()
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
ScrollView {
|
||||||
spacing: 2
|
id: verticalScrollView
|
||||||
property var currentImportModel: model // allows accessing the import model from inside the category section
|
width: itemsView.width
|
||||||
Repeater {
|
height: itemsView.height
|
||||||
model: categoryModel
|
onContentHeightChanged: {
|
||||||
delegate: Section {
|
var maxPosition = Math.max(contentHeight - verticalScrollView.height, 0)
|
||||||
width: itemsView.width -
|
if (contentY > maxPosition)
|
||||||
(itemsView.verticalScrollBarVisible ? itemsView.verticalThickness : 0)
|
contentY = maxPosition
|
||||||
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
|
|
||||||
onShowContextMenu: {
|
|
||||||
currentCategory = model
|
|
||||||
currentImport = parent.currentImportModel
|
|
||||||
if (!rootView.isSearchActive())
|
|
||||||
moduleContextMenu.popup()
|
|
||||||
}
|
|
||||||
|
|
||||||
Grid {
|
Column {
|
||||||
id: itemGrid
|
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
|
||||||
|
if (!rootView.isSearchActive())
|
||||||
|
moduleContextMenu.popup()
|
||||||
|
}
|
||||||
|
|
||||||
property real actualWidth: parent.width - itemGrid.leftPadding -itemGrid.rightPadding
|
Column {
|
||||||
property int flexibleWidth: (itemGrid.actualWidth / columns) - styleConstants.cellWidth
|
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: {
|
||||||
|
itemsView.currentCategory = model
|
||||||
|
itemsView.currentImport = parent.currentImportModel
|
||||||
|
if (!rootView.isSearchActive())
|
||||||
|
moduleContextMenu.popup()
|
||||||
|
}
|
||||||
|
|
||||||
leftPadding: 6
|
Grid {
|
||||||
rightPadding: 6
|
id: itemGrid
|
||||||
columns: itemGrid.actualWidth / styleConstants.cellWidth
|
|
||||||
|
|
||||||
Repeater {
|
property real actualWidth: parent.width - itemGrid.leftPadding -itemGrid.rightPadding
|
||||||
model: itemModel
|
property int flexibleWidth: (itemGrid.actualWidth / columns) - styleConstants.cellWidth
|
||||||
delegate: ItemDelegate {
|
|
||||||
visible: itemVisible
|
leftPadding: 6
|
||||||
textColor: importUnimported ? StudioTheme.Values.themeUnimportedModuleColor
|
rightPadding: 6
|
||||||
: StudioTheme.Values.themeTextColor
|
columns: itemGrid.actualWidth / styleConstants.cellWidth
|
||||||
width: styleConstants.cellWidth + itemGrid.flexibleWidth
|
rowSpacing: 7
|
||||||
height: styleConstants.cellHeight
|
|
||||||
onShowContextMenu: {
|
Repeater {
|
||||||
if (!itemUsable) {
|
model: itemModel
|
||||||
importToAdd = itemRequiredImport
|
delegate: ItemDelegate {
|
||||||
itemContextMenu.popup()
|
visible: itemVisible
|
||||||
|
textColor: importUnimported ? StudioTheme.Values.themeUnimportedModuleColor
|
||||||
|
: StudioTheme.Values.themeTextColor
|
||||||
|
width: styleConstants.cellWidth + itemGrid.flexibleWidth
|
||||||
|
height: styleConstants.cellHeight
|
||||||
|
onShowContextMenu: {
|
||||||
|
if (!itemUsable) {
|
||||||
|
itemsView.importToAdd = itemRequiredImport
|
||||||
|
itemContextMenu.popup()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -291,4 +333,163 @@ ScrollView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: horizontalView
|
||||||
|
|
||||||
|
Row {
|
||||||
|
padding: 5
|
||||||
|
|
||||||
|
ScrollView {
|
||||||
|
id: horizontalScrollView
|
||||||
|
width: 270
|
||||||
|
height: itemsView.height
|
||||||
|
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
|
||||||
|
if (!rootView.isSearchActive())
|
||||||
|
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)
|
||||||
|
itemsView.selectedCategory = model
|
||||||
|
itemsView.selectedCategoryImport = parent.parent.currentImportModel
|
||||||
|
|
||||||
|
if (mouse.button === Qt.RightButton && !rootView.isSearchActive() && categoryModel.rowCount() !== 1) {
|
||||||
|
itemsView.currentCategory = model
|
||||||
|
itemsView.currentImport = parent.parent.currentImportModel
|
||||||
|
moduleContextMenu.popup()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (categorySelected)
|
||||||
|
categorySelected = !categorySelected
|
||||||
|
itemsView.selectedCategory = itemLibraryModel.selectImportFirstVisibleCategory()
|
||||||
|
if (itemsView.selectedCategory === categorySelected)
|
||||||
|
itemsView.selectedCategoryImport = itemsView.selectedCategory.parent.currentImportModel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
property int flexibleWidth: (hItemGrid.actualWidth / hItemGrid.columns) - styleConstants.cellWidth
|
||||||
|
|
||||||
|
leftPadding: 9
|
||||||
|
rightPadding: 9
|
||||||
|
bottomPadding: 15
|
||||||
|
columns: hItemGrid.actualWidth / styleConstants.cellWidth
|
||||||
|
rowSpacing: 7
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: itemsView.selectedCategory ? itemsView.selectedCategory.itemModel : null
|
||||||
|
delegate: ItemDelegate {
|
||||||
|
visible: itemVisible
|
||||||
|
textColor: itemsView.selectedCategoryImport && itemsView.selectedCategoryImport.importUnimported
|
||||||
|
? StudioTheme.Values.themeUnimportedModuleColor : StudioTheme.Values.themeTextColor
|
||||||
|
width: styleConstants.cellWidth + hItemGrid.flexibleWidth
|
||||||
|
height: styleConstants.cellHeight
|
||||||
|
onShowContextMenu: {
|
||||||
|
if (!itemUsable) {
|
||||||
|
itemsView.importToAdd = itemRequiredImport
|
||||||
|
itemContextMenu.popup()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -144,6 +144,17 @@ void ItemLibraryCategoriesModel::resetModel()
|
|||||||
endResetModel();
|
endResetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ItemLibraryCategoriesModel::isAllCategoriesHidden() const
|
||||||
|
{
|
||||||
|
for (const auto &category : std::as_const(m_categoryList)) {
|
||||||
|
// ignore "All Other Components" as its categoryVisible is always true
|
||||||
|
if (category->isCategoryVisible() && category->categoryName() != "All Other Components")
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void ItemLibraryCategoriesModel::showAllCategories(bool show)
|
void ItemLibraryCategoriesModel::showAllCategories(bool show)
|
||||||
{
|
{
|
||||||
for (const auto &category : std::as_const(m_categoryList)) {
|
for (const auto &category : std::as_const(m_categoryList)) {
|
||||||
@@ -153,9 +164,43 @@ void ItemLibraryCategoriesModel::showAllCategories(bool show)
|
|||||||
category->ownerImport()->importName());
|
category->ownerImport()->importName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emit dataChanged(index(0), index(m_categoryList.size() - 1), {m_roleNames.key("categoryVisible")});
|
emit dataChanged(index(0), index(m_categoryList.size() - 1), {m_roleNames.key("categoryVisible")});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QObject *ItemLibraryCategoriesModel::selectFirstVisibleCategory()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_categoryList.length(); ++i) {
|
||||||
|
const auto category = m_categoryList.at(i);
|
||||||
|
|
||||||
|
if (category->isCategoryVisible()) {
|
||||||
|
category->setCategorySelected(true);
|
||||||
|
emit dataChanged(index(i),index(i), {m_roleNames.key("categorySelected")});
|
||||||
|
return category;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ItemLibraryCategoriesModel::clearSelectedCategories()
|
||||||
|
{
|
||||||
|
for (const auto &category : std::as_const(m_categoryList))
|
||||||
|
category->setCategorySelected(false);
|
||||||
|
|
||||||
|
emit dataChanged(index(0), index(m_categoryList.size() - 1), {m_roleNames.key("categorySelected")});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ItemLibraryCategoriesModel::selectCategory(int categoryIndex)
|
||||||
|
{
|
||||||
|
const auto category = m_categoryList.at(categoryIndex);
|
||||||
|
if (!category->categorySelected()) {
|
||||||
|
clearSelectedCategories();
|
||||||
|
category->setCategorySelected(true);
|
||||||
|
emit dataChanged(index(categoryIndex),index(categoryIndex), {m_roleNames.key("categorySelected")});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ItemLibraryCategoriesModel::addRoleNames()
|
void ItemLibraryCategoriesModel::addRoleNames()
|
||||||
{
|
{
|
||||||
int role = 0;
|
int role = 0;
|
||||||
|
@@ -52,9 +52,13 @@ public:
|
|||||||
|
|
||||||
const QList<QPointer<ItemLibraryCategory>> &categorySections() const;
|
const QList<QPointer<ItemLibraryCategory>> &categorySections() const;
|
||||||
|
|
||||||
|
bool isAllCategoriesHidden() const;
|
||||||
void sortCategorySections();
|
void sortCategorySections();
|
||||||
void resetModel();
|
void resetModel();
|
||||||
void showAllCategories(bool show = true);
|
void showAllCategories(bool show = true);
|
||||||
|
void clearSelectedCategories();
|
||||||
|
QObject *selectFirstVisibleCategory();
|
||||||
|
void selectCategory(int categoryIndex);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void addRoleNames();
|
void addRoleNames();
|
||||||
|
@@ -26,6 +26,7 @@
|
|||||||
#include "itemlibrarycategory.h"
|
#include "itemlibrarycategory.h"
|
||||||
|
|
||||||
#include "itemlibraryitem.h"
|
#include "itemlibraryitem.h"
|
||||||
|
#include "itemlibrarywidget.h"
|
||||||
|
|
||||||
namespace QmlDesigner {
|
namespace QmlDesigner {
|
||||||
|
|
||||||
@@ -46,6 +47,11 @@ bool ItemLibraryCategory::categoryExpanded() const
|
|||||||
return m_categoryExpanded;
|
return m_categoryExpanded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ItemLibraryCategory::categorySelected() const
|
||||||
|
{
|
||||||
|
return m_categorySelected;
|
||||||
|
}
|
||||||
|
|
||||||
QString ItemLibraryCategory::sortingName() const
|
QString ItemLibraryCategory::sortingName() const
|
||||||
{
|
{
|
||||||
if (ItemLibraryModel::categorySortingHash.contains(categoryName()))
|
if (ItemLibraryModel::categorySortingHash.contains(categoryName()))
|
||||||
@@ -84,6 +90,10 @@ bool ItemLibraryCategory::updateItemVisibility(const QString &searchText, bool *
|
|||||||
hasVisibleItems = true;
|
hasVisibleItems = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update item model in horizontal view so search text matches item grid
|
||||||
|
if (ItemLibraryWidget::isHorizontalLayout)
|
||||||
|
m_itemModel.resetModel();
|
||||||
|
|
||||||
// expand category if it has an item matching search criteria
|
// expand category if it has an item matching search criteria
|
||||||
if (!searchText.isEmpty() && hasVisibleItems && !categoryExpanded())
|
if (!searchText.isEmpty() && hasVisibleItems && !categoryExpanded())
|
||||||
setExpanded(true);
|
setExpanded(true);
|
||||||
@@ -124,4 +134,9 @@ void ItemLibraryCategory::setExpanded(bool expanded)
|
|||||||
m_categoryExpanded = expanded;
|
m_categoryExpanded = expanded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ItemLibraryCategory::setCategorySelected(bool selected)
|
||||||
|
{
|
||||||
|
m_categorySelected = selected;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
@@ -39,6 +39,7 @@ class ItemLibraryCategory : public QObject
|
|||||||
Q_PROPERTY(QString categoryName READ categoryName FINAL)
|
Q_PROPERTY(QString categoryName READ categoryName FINAL)
|
||||||
Q_PROPERTY(bool categoryVisible READ isCategoryVisible WRITE setCategoryVisible NOTIFY categoryVisibilityChanged FINAL)
|
Q_PROPERTY(bool categoryVisible READ isCategoryVisible WRITE setCategoryVisible NOTIFY categoryVisibilityChanged FINAL)
|
||||||
Q_PROPERTY(bool categoryExpanded READ categoryExpanded WRITE setExpanded NOTIFY expandedChanged FINAL)
|
Q_PROPERTY(bool categoryExpanded READ categoryExpanded WRITE setExpanded NOTIFY expandedChanged FINAL)
|
||||||
|
Q_PROPERTY(bool categorySelected READ categorySelected WRITE setCategorySelected NOTIFY categorySelectedChanged FINAL)
|
||||||
Q_PROPERTY(QObject *itemModel READ itemModel NOTIFY itemModelChanged FINAL)
|
Q_PROPERTY(QObject *itemModel READ itemModel NOTIFY itemModelChanged FINAL)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -46,6 +47,7 @@ public:
|
|||||||
|
|
||||||
QString categoryName() const;
|
QString categoryName() const;
|
||||||
bool categoryExpanded() const;
|
bool categoryExpanded() const;
|
||||||
|
bool categorySelected() const;
|
||||||
QString sortingName() const;
|
QString sortingName() const;
|
||||||
|
|
||||||
void addItem(ItemLibraryItem *item);
|
void addItem(ItemLibraryItem *item);
|
||||||
@@ -60,6 +62,7 @@ public:
|
|||||||
void sortItems();
|
void sortItems();
|
||||||
|
|
||||||
void setExpanded(bool expanded);
|
void setExpanded(bool expanded);
|
||||||
|
void setCategorySelected(bool selected);
|
||||||
|
|
||||||
ItemLibraryImport *ownerImport() const { return m_ownerImport; }
|
ItemLibraryImport *ownerImport() const { return m_ownerImport; }
|
||||||
|
|
||||||
@@ -68,6 +71,7 @@ signals:
|
|||||||
void visibilityChanged();
|
void visibilityChanged();
|
||||||
void expandedChanged();
|
void expandedChanged();
|
||||||
void categoryVisibilityChanged();
|
void categoryVisibilityChanged();
|
||||||
|
void categorySelectedChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ItemLibraryItemsModel m_itemModel;
|
ItemLibraryItemsModel m_itemModel;
|
||||||
@@ -75,6 +79,7 @@ private:
|
|||||||
QString m_name;
|
QString m_name;
|
||||||
bool m_categoryExpanded = true;
|
bool m_categoryExpanded = true;
|
||||||
bool m_isVisible = true;
|
bool m_isVisible = true;
|
||||||
|
bool m_categorySelected = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
@@ -138,6 +138,26 @@ void ItemLibraryImport::showAllCategories(bool show)
|
|||||||
m_categoryModel.showAllCategories(show);
|
m_categoryModel.showAllCategories(show);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ItemLibraryImport::selectCategory(int categoryIndex)
|
||||||
|
{
|
||||||
|
m_categoryModel.selectCategory(categoryIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
QObject *ItemLibraryImport::selectFirstVisibleCategory()
|
||||||
|
{
|
||||||
|
return m_categoryModel.selectFirstVisibleCategory();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ItemLibraryImport::clearSelectedCategories()
|
||||||
|
{
|
||||||
|
m_categoryModel.clearSelectedCategories();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ItemLibraryImport::isAllCategoriesHidden() const
|
||||||
|
{
|
||||||
|
return m_categoryModel.isAllCategoriesHidden();
|
||||||
|
}
|
||||||
|
|
||||||
Import ItemLibraryImport::importEntry() const
|
Import ItemLibraryImport::importEntry() const
|
||||||
{
|
{
|
||||||
return m_import;
|
return m_import;
|
||||||
|
@@ -68,6 +68,7 @@ public:
|
|||||||
bool importCatVisibleState() const;
|
bool importCatVisibleState() const;
|
||||||
bool hasCategories() const;
|
bool hasCategories() const;
|
||||||
bool hasSingleCategory() const;
|
bool hasSingleCategory() const;
|
||||||
|
bool isAllCategoriesHidden() const;
|
||||||
ItemLibraryCategory *getCategorySection(const QString &categoryName) const;
|
ItemLibraryCategory *getCategorySection(const QString &categoryName) const;
|
||||||
|
|
||||||
void addCategory(ItemLibraryCategory *category);
|
void addCategory(ItemLibraryCategory *category);
|
||||||
@@ -80,6 +81,9 @@ public:
|
|||||||
void setImportCatVisibleState(bool show);
|
void setImportCatVisibleState(bool show);
|
||||||
void expandCategories(bool expand = true);
|
void expandCategories(bool expand = true);
|
||||||
void showAllCategories(bool show = true);
|
void showAllCategories(bool show = true);
|
||||||
|
void selectCategory(int categoryIndex);
|
||||||
|
QObject *selectFirstVisibleCategory();
|
||||||
|
void clearSelectedCategories();
|
||||||
|
|
||||||
static QString userComponentsTitle();
|
static QString userComponentsTitle();
|
||||||
static QString quick3DAssetsTitle();
|
static QString quick3DAssetsTitle();
|
||||||
|
@@ -90,6 +90,40 @@ bool ItemLibraryModel::getIsAnyCategoryHidden() const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ItemLibraryModel::selectImportCategory(const QString importUrl, int categoryIndex)
|
||||||
|
{
|
||||||
|
ItemLibraryImport *selectedCategoryImport = importByUrl(importUrl);
|
||||||
|
|
||||||
|
for (int i = 0; i < m_importList.length(); ++i) {
|
||||||
|
const auto importToSelect = m_importList.at(i);
|
||||||
|
|
||||||
|
if (selectedCategoryImport == importToSelect)
|
||||||
|
importToSelect->selectCategory(categoryIndex);
|
||||||
|
else
|
||||||
|
importToSelect->clearSelectedCategories();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ItemLibraryModel::isAllCategoriesHidden() const
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_importList.length(); ++i) {
|
||||||
|
if (!m_importList.at(i)->isAllCategoriesHidden())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QObject *ItemLibraryModel::selectImportFirstVisibleCategory()
|
||||||
|
{
|
||||||
|
for (const QPointer<ItemLibraryImport> &import : std::as_const(m_importList)) {
|
||||||
|
if (!import->isAllCategoriesHidden())
|
||||||
|
return import->selectFirstVisibleCategory();
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
bool ItemLibraryModel::isAnyCategoryHidden() const
|
bool ItemLibraryModel::isAnyCategoryHidden() const
|
||||||
{
|
{
|
||||||
return m_isAnyCategoryHidden;
|
return m_isAnyCategoryHidden;
|
||||||
|
@@ -77,6 +77,9 @@ public:
|
|||||||
Q_INVOKABLE void collapseAll();
|
Q_INVOKABLE void collapseAll();
|
||||||
Q_INVOKABLE void showHiddenCategories();
|
Q_INVOKABLE void showHiddenCategories();
|
||||||
Q_INVOKABLE bool getIsAnyCategoryHidden() const;
|
Q_INVOKABLE bool getIsAnyCategoryHidden() const;
|
||||||
|
Q_INVOKABLE void selectImportCategory(const QString importUrl, int categoryIndex);
|
||||||
|
Q_INVOKABLE QObject *selectImportFirstVisibleCategory();
|
||||||
|
Q_INVOKABLE bool isAllCategoriesHidden() const;
|
||||||
|
|
||||||
Import entryToImport(const ItemLibraryEntry &entry);
|
Import entryToImport(const ItemLibraryEntry &entry);
|
||||||
|
|
||||||
|
@@ -143,6 +143,11 @@ bool ItemLibraryWidget::eventFilter(QObject *obj, QEvent *event)
|
|||||||
return QObject::eventFilter(obj, event);
|
return QObject::eventFilter(obj, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ItemLibraryWidget::resizeEvent(QResizeEvent *event)
|
||||||
|
{
|
||||||
|
isHorizontalLayout = event->size().width() >= HORIZONTAL_LAYOUT_WIDTH_LIMIT;
|
||||||
|
}
|
||||||
|
|
||||||
ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache,
|
ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache,
|
||||||
AsynchronousImageCache &asynchronousFontImageCache,
|
AsynchronousImageCache &asynchronousFontImageCache,
|
||||||
SynchronousImageCache &synchronousFontImageCache)
|
SynchronousImageCache &synchronousFontImageCache)
|
||||||
@@ -196,6 +201,7 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache,
|
|||||||
{{"itemLibraryIconWidth"}, m_itemIconSize.width()},
|
{{"itemLibraryIconWidth"}, m_itemIconSize.width()},
|
||||||
{{"itemLibraryIconHeight"}, m_itemIconSize.height()},
|
{{"itemLibraryIconHeight"}, m_itemIconSize.height()},
|
||||||
{{"rootView"}, QVariant::fromValue(this)},
|
{{"rootView"}, QVariant::fromValue(this)},
|
||||||
|
{{"widthLimit"}, HORIZONTAL_LAYOUT_WIDTH_LIMIT},
|
||||||
{{"highlightColor"}, Utils::StyleHelper::notTooBrightHighlightColor()},
|
{{"highlightColor"}, Utils::StyleHelper::notTooBrightHighlightColor()},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -92,6 +92,8 @@ public:
|
|||||||
void setFlowMode(bool b);
|
void setFlowMode(bool b);
|
||||||
static QPair<QString, QByteArray> getAssetTypeAndData(const QString &assetPath);
|
static QPair<QString, QByteArray> getAssetTypeAndData(const QString &assetPath);
|
||||||
|
|
||||||
|
inline static bool isHorizontalLayout = false;
|
||||||
|
|
||||||
Q_INVOKABLE void startDragAndDrop(const QVariant &itemLibEntry, const QPointF &mousePos);
|
Q_INVOKABLE void startDragAndDrop(const QVariant &itemLibEntry, const QPointF &mousePos);
|
||||||
Q_INVOKABLE void startDragAsset(const QStringList &assetPaths, const QPointF &mousePos);
|
Q_INVOKABLE void startDragAsset(const QStringList &assetPaths, const QPointF &mousePos);
|
||||||
Q_INVOKABLE void removeImport(const QString &importUrl);
|
Q_INVOKABLE void removeImport(const QString &importUrl);
|
||||||
@@ -110,6 +112,7 @@ signals:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool eventFilter(QObject *obj, QEvent *event) override;
|
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||||
|
void resizeEvent(QResizeEvent *event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void reloadQmlSource();
|
void reloadQmlSource();
|
||||||
@@ -149,6 +152,8 @@ private:
|
|||||||
bool m_updateRetry = false;
|
bool m_updateRetry = false;
|
||||||
QString m_filterText;
|
QString m_filterText;
|
||||||
QPoint m_dragStartPoint;
|
QPoint m_dragStartPoint;
|
||||||
|
|
||||||
|
inline static int HORIZONTAL_LAYOUT_WIDTH_LIMIT = 600;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
Reference in New Issue
Block a user