QmlDesigner: Make material browser responsive

Change-Id: Ic6b2b9583dc2190974de7c74f8b39e22aa59226d
Reviewed-by: Qt CI Patch Build Bot <ci_patchbuild_bot@qt.io>
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
This commit is contained in:
Henning Gruendl
2023-10-10 15:25:50 +02:00
committed by Henning Gründl
parent fed8f8c845
commit 7ffa49f217
3 changed files with 160 additions and 144 deletions

View File

@@ -12,8 +12,8 @@ Item {
id: root id: root
focus: true focus: true
readonly property int cellWidth: 100 readonly property real cellWidth: root.thumbnailSize
readonly property int cellHeight: 120 readonly property real cellHeight: root.thumbnailSize + 20
readonly property bool enableUiElements: materialBrowserModel.hasMaterialLibrary readonly property bool enableUiElements: materialBrowserModel.hasMaterialLibrary
&& materialBrowserModel.hasQuick3DImport && materialBrowserModel.hasQuick3DImport
@@ -22,30 +22,60 @@ Item {
property var materialBrowserModel: MaterialBrowserBackend.materialBrowserModel property var materialBrowserModel: MaterialBrowserBackend.materialBrowserModel
property var materialBrowserTexturesModel: MaterialBrowserBackend.materialBrowserTexturesModel property var materialBrowserTexturesModel: MaterialBrowserBackend.materialBrowserTexturesModel
property int numColumns: 0
property real thumbnailSize: 100
readonly property int minThumbSize: 100
readonly property int maxThumbSize: 150
function responsiveResize(width: int, height: int) {
width -= 2 * StudioTheme.Values.sectionPadding
let numColumns = Math.floor(width / root.minThumbSize)
let remainder = width % root.minThumbSize
let space = (numColumns - 1) * StudioTheme.Values.sectionGridSpacing
if (remainder < space)
numColumns -= 1
if (numColumns < 1)
return
let maxItems = Math.max(texturesRepeater.count, materialRepeater.count)
if (numColumns > maxItems)
numColumns = maxItems
let rest = width - (numColumns * root.minThumbSize)
- ((numColumns - 1) * StudioTheme.Values.sectionGridSpacing)
root.thumbnailSize = Math.min(root.minThumbSize + (rest / numColumns),
root.maxThumbSize)
root.numColumns = numColumns
}
onWidthChanged: root.responsiveResize(root.width, root.height)
// Called also from C++ to close context menu on focus out // Called also from C++ to close context menu on focus out
function closeContextMenu() function closeContextMenu() {
{
ctxMenu.close() ctxMenu.close()
ctxMenuTextures.close() ctxMenuTextures.close()
HelperWidgets.Controller.closeContextMenu() HelperWidgets.Controller.closeContextMenu()
} }
// Called from C++ to refresh a preview material after it changes // Called from C++ to refresh a preview material after it changes
function refreshPreview(idx) function refreshPreview(idx) {
{
var item = materialRepeater.itemAt(idx); var item = materialRepeater.itemAt(idx);
if (item) if (item)
item.refreshPreview(); item.refreshPreview()
} }
// Called from C++ // Called from C++
function clearSearchFilter() function clearSearchFilter() {
{ searchBox.clear()
searchBox.clear();
} }
function nextVisibleItem(idx, count, itemModel) function nextVisibleItem(idx, count, itemModel) {
{
if (count === 0) if (count === 0)
return idx return idx
@@ -66,8 +96,7 @@ Item {
return newIdx return newIdx
} }
function visibleItemCount(itemModel) function visibleItemCount(itemModel) {
{
let curIdx = 0 let curIdx = 0
let count = 0 let count = 0
@@ -79,8 +108,7 @@ Item {
return count return count
} }
function rowIndexOfItem(idx, rowSize, itemModel) function rowIndexOfItem(idx, rowSize, itemModel) {
{
if (rowSize === 1) if (rowSize === 1)
return 1 return 1
@@ -98,8 +126,7 @@ Item {
return count % rowSize return count % rowSize
} }
function selectNextVisibleItem(delta) function selectNextVisibleItem(delta) {
{
if (searchBox.activeFocus) if (searchBox.activeFocus)
return return
@@ -112,35 +139,35 @@ Item {
if (delta < 0) { if (delta < 0) {
if (matSecFocused) { if (matSecFocused) {
targetIdx = nextVisibleItem(materialBrowserModel.selectedIndex, targetIdx = root.nextVisibleItem(materialBrowserModel.selectedIndex,
delta, materialBrowserModel) delta, materialBrowserModel)
if (targetIdx >= 0) if (targetIdx >= 0)
materialBrowserModel.selectMaterial(targetIdx) materialBrowserModel.selectMaterial(targetIdx)
} else if (texSecFocused) { } else if (texSecFocused) {
targetIdx = nextVisibleItem(materialBrowserTexturesModel.selectedIndex, targetIdx = root.nextVisibleItem(materialBrowserTexturesModel.selectedIndex,
delta, materialBrowserTexturesModel) delta, materialBrowserTexturesModel)
if (targetIdx >= 0) { if (targetIdx >= 0) {
materialBrowserTexturesModel.selectTexture(targetIdx) materialBrowserTexturesModel.selectTexture(targetIdx)
} else if (!materialBrowserModel.isEmpty && materialsSection.expanded) { } else if (!materialBrowserModel.isEmpty && materialsSection.expanded) {
targetIdx = nextVisibleItem(materialBrowserModel.rowCount(), -1, materialBrowserModel) targetIdx = root.nextVisibleItem(materialBrowserModel.rowCount(), -1, materialBrowserModel)
if (targetIdx >= 0) { if (targetIdx >= 0) {
if (delta !== -1) { if (delta !== -1) {
// Try to match column when switching between materials/textures // Try to match column when switching between materials/textures
origRowIdx = rowIndexOfItem(materialBrowserTexturesModel.selectedIndex, origRowIdx = root.rowIndexOfItem(materialBrowserTexturesModel.selectedIndex,
-delta, materialBrowserTexturesModel) -delta, materialBrowserTexturesModel)
if (visibleItemCount(materialBrowserModel) > origRowIdx) { if (root.visibleItemCount(materialBrowserModel) > origRowIdx) {
rowIdx = rowIndexOfItem(targetIdx, -delta, materialBrowserModel) rowIdx = root.rowIndexOfItem(targetIdx, -delta, materialBrowserModel)
if (rowIdx >= origRowIdx) { if (rowIdx >= origRowIdx) {
newTargetIdx = nextVisibleItem(targetIdx, newTargetIdx = root.nextVisibleItem(targetIdx,
-(rowIdx - origRowIdx), -(rowIdx - origRowIdx),
materialBrowserModel) materialBrowserModel)
} else { } else {
newTargetIdx = nextVisibleItem(targetIdx, newTargetIdx = root.nextVisibleItem(targetIdx,
-(-delta - origRowIdx + rowIdx), -(-delta - origRowIdx + rowIdx),
materialBrowserModel) materialBrowserModel)
} }
} else { } else {
newTargetIdx = nextVisibleItem(materialBrowserModel.rowCount(), newTargetIdx = root.nextVisibleItem(materialBrowserModel.rowCount(),
-1, materialBrowserModel) -1, materialBrowserModel)
} }
if (newTargetIdx >= 0) if (newTargetIdx >= 0)
@@ -153,24 +180,24 @@ Item {
} }
} else if (delta > 0) { } else if (delta > 0) {
if (matSecFocused) { if (matSecFocused) {
targetIdx = nextVisibleItem(materialBrowserModel.selectedIndex, targetIdx = root.nextVisibleItem(materialBrowserModel.selectedIndex,
delta, materialBrowserModel) delta, materialBrowserModel)
if (targetIdx >= 0) { if (targetIdx >= 0) {
materialBrowserModel.selectMaterial(targetIdx) materialBrowserModel.selectMaterial(targetIdx)
} else if (!materialBrowserTexturesModel.isEmpty && texturesSection.expanded) { } else if (!materialBrowserTexturesModel.isEmpty && texturesSection.expanded) {
targetIdx = nextVisibleItem(-1, 1, materialBrowserTexturesModel) targetIdx = root.nextVisibleItem(-1, 1, materialBrowserTexturesModel)
if (targetIdx >= 0) { if (targetIdx >= 0) {
if (delta !== 1) { if (delta !== 1) {
// Try to match column when switching between materials/textures // Try to match column when switching between materials/textures
origRowIdx = rowIndexOfItem(materialBrowserModel.selectedIndex, origRowIdx = root.rowIndexOfItem(materialBrowserModel.selectedIndex,
delta, materialBrowserModel) delta, materialBrowserModel)
if (visibleItemCount(materialBrowserTexturesModel) > origRowIdx) { if (root.visibleItemCount(materialBrowserTexturesModel) > origRowIdx) {
if (origRowIdx > 0) { if (origRowIdx > 0) {
newTargetIdx = nextVisibleItem(targetIdx, origRowIdx, newTargetIdx = root.nextVisibleItem(targetIdx, origRowIdx,
materialBrowserTexturesModel) materialBrowserTexturesModel)
} }
} else { } else {
newTargetIdx = nextVisibleItem(materialBrowserTexturesModel.rowCount(), newTargetIdx = root.nextVisibleItem(materialBrowserTexturesModel.rowCount(),
-1, materialBrowserTexturesModel) -1, materialBrowserTexturesModel)
} }
if (newTargetIdx >= 0) if (newTargetIdx >= 0)
@@ -181,7 +208,7 @@ Item {
} }
} }
} else if (texSecFocused) { } else if (texSecFocused) {
targetIdx = nextVisibleItem(materialBrowserTexturesModel.selectedIndex, targetIdx = root.nextVisibleItem(materialBrowserTexturesModel.selectedIndex,
delta, materialBrowserTexturesModel) delta, materialBrowserTexturesModel)
if (targetIdx >= 0) if (targetIdx >= 0)
materialBrowserTexturesModel.selectTexture(targetIdx) materialBrowserTexturesModel.selectTexture(targetIdx)
@@ -190,24 +217,12 @@ Item {
} }
Keys.enabled: true Keys.enabled: true
Keys.onDownPressed: { Keys.onDownPressed: root.selectNextVisibleItem(gridMaterials.columns)
selectNextVisibleItem(gridMaterials.columns) Keys.onUpPressed: root.selectNextVisibleItem(-gridMaterials.columns)
} Keys.onLeftPressed: root.selectNextVisibleItem(-1)
Keys.onRightPressed: root.selectNextVisibleItem(1)
Keys.onUpPressed: { function handleEnterPress() {
selectNextVisibleItem(-gridMaterials.columns)
}
Keys.onLeftPressed: {
selectNextVisibleItem(-1)
}
Keys.onRightPressed: {
selectNextVisibleItem(1)
}
function handleEnterPress()
{
if (searchBox.activeFocus) if (searchBox.activeFocus)
return return
@@ -217,13 +232,8 @@ Item {
materialBrowserTexturesModel.openTextureEditor() materialBrowserTexturesModel.openTextureEditor()
} }
Keys.onEnterPressed: { Keys.onEnterPressed: root.handleEnterPress()
handleEnterPress() Keys.onReturnPressed: root.handleEnterPress()
}
Keys.onReturnPressed: {
handleEnterPress()
}
MouseArea { MouseArea {
id: focusGrabber id: focusGrabber
@@ -249,10 +259,10 @@ Item {
onClicked: (mouse) => { onClicked: (mouse) => {
if (!root.enableUiElements) if (!root.enableUiElements)
return; return
var matsSecBottom = mapFromItem(materialsSection, 0, materialsSection.y).y var matsSecBottom = mapFromItem(materialsSection, 0, materialsSection.y).y
+ materialsSection.height; + materialsSection.height
if (mouse.y < matsSecBottom) if (mouse.y < matsSecBottom)
ctxMenu.popupMenu() ctxMenu.popupMenu()
@@ -261,8 +271,7 @@ Item {
} }
} }
function ensureVisible(yPos, itemHeight) function ensureVisible(yPos, itemHeight) {
{
let currentY = contentYBehavior.targetValue && scrollViewAnim.running let currentY = contentYBehavior.targetValue && scrollViewAnim.running
? contentYBehavior.targetValue : scrollView.contentY ? contentYBehavior.targetValue : scrollView.contentY
@@ -286,18 +295,18 @@ Item {
return false return false
} }
function ensureSelectedVisible() function ensureSelectedVisible() {
{
if (rootView.materialSectionFocused && materialsSection.expanded && root.currMaterialItem if (rootView.materialSectionFocused && materialsSection.expanded && root.currMaterialItem
&& materialBrowserModel.isVisible(materialBrowserModel.selectedIndex)) { && materialBrowserModel.isVisible(materialBrowserModel.selectedIndex)) {
return ensureVisible(root.currMaterialItem.mapToItem(scrollView.contentItem, 0, 0).y, return root.ensureVisible(root.currMaterialItem.mapToItem(scrollView.contentItem, 0, 0).y,
root.currMaterialItem.height) root.currMaterialItem.height)
} else if (!rootView.materialSectionFocused && texturesSection.expanded) { } else if (!rootView.materialSectionFocused && texturesSection.expanded) {
let currItem = texturesRepeater.itemAt(materialBrowserTexturesModel.selectedIndex) let currItem = texturesRepeater.itemAt(materialBrowserTexturesModel.selectedIndex)
if (currItem && materialBrowserTexturesModel.isVisible(materialBrowserTexturesModel.selectedIndex)) if (currItem && materialBrowserTexturesModel.isVisible(materialBrowserTexturesModel.selectedIndex))
return ensureVisible(currItem.mapToItem(scrollView.contentItem, 0, 0).y, currItem.height) return root.ensureVisible(currItem.mapToItem(scrollView.contentItem, 0, 0).y,
currItem.height)
} else { } else {
return ensureVisible(0, 90) return root.ensureVisible(0, 90)
} }
} }
@@ -310,15 +319,14 @@ Item {
onTriggered: { onTriggered: {
// Redo until ensuring didn't change things // Redo until ensuring didn't change things
if (!root.ensureSelectedVisible()) { if (!root.ensureSelectedVisible()) {
stop() ensureTimer.stop()
interval = 20 ensureTimer.interval = 20
triggeredOnStart = true ensureTimer.triggeredOnStart = true
} }
} }
} }
function startDelayedEnsureTimer(delay) function startDelayedEnsureTimer(delay) {
{
// Ensuring visibility immediately in some cases like before new search results are rendered // Ensuring visibility immediately in some cases like before new search results are rendered
// causes mapToItem return incorrect values, leading to undesirable flicker, // causes mapToItem return incorrect values, leading to undesirable flicker,
// so delay ensuring visibility a bit. // so delay ensuring visibility a bit.
@@ -330,8 +338,7 @@ Item {
Connections { Connections {
target: materialBrowserModel target: materialBrowserModel
function onSelectedIndexChanged() function onSelectedIndexChanged() {
{
// commit rename upon changing selection // commit rename upon changing selection
if (root.currMaterialItem) if (root.currMaterialItem)
root.currMaterialItem.forceFinishEditing(); root.currMaterialItem.forceFinishEditing();
@@ -341,8 +348,7 @@ Item {
ensureTimer.start() ensureTimer.start()
} }
function onIsEmptyChanged() function onIsEmptyChanged() {
{
ensureTimer.start() ensureTimer.start()
} }
} }
@@ -350,13 +356,11 @@ Item {
Connections { Connections {
target: materialBrowserTexturesModel target: materialBrowserTexturesModel
function onSelectedIndexChanged() function onSelectedIndexChanged() {
{
ensureTimer.start() ensureTimer.start()
} }
function onIsEmptyChanged() function onIsEmptyChanged() {
{
ensureTimer.start() ensureTimer.start()
} }
} }
@@ -364,8 +368,7 @@ Item {
Connections { Connections {
target: rootView target: rootView
function onMaterialSectionFocusedChanged() function onMaterialSectionFocusedChanged() {
{
ensureTimer.start() ensureTimer.start()
} }
} }
@@ -614,9 +617,10 @@ Item {
id: scrollView id: scrollView
width: root.width width: root.width
height: root.height - toolbar.height height: root.height - toolbar.height - col.spacing
clip: true clip: true
visible: root.enableUiElements visible: root.enableUiElements
hideHorizontalScrollBar: true
interactive: !ctxMenu.opened && !ctxMenuTextures.opened && !rootView.isDragging interactive: !ctxMenu.opened && !ctxMenuTextures.opened && !rootView.isDragging
&& !HelperWidgets.Controller.contextMenuOpened && !HelperWidgets.Controller.contextMenuOpened
@@ -637,6 +641,12 @@ Item {
id: materialsSection id: materialsSection
width: root.width width: root.width
leftPadding: StudioTheme.Values.sectionPadding
rightPadding: StudioTheme.Values.sectionPadding
topPadding: StudioTheme.Values.sectionPadding
bottomPadding: StudioTheme.Values.sectionPadding
caption: qsTr("Materials") caption: qsTr("Materials")
dropEnabled: true dropEnabled: true
category: "MaterialBrowser" category: "MaterialBrowser"
@@ -671,11 +681,10 @@ Item {
Grid { Grid {
id: gridMaterials id: gridMaterials
width: scrollView.width width: scrollView.width - materialsSection.leftPadding
leftPadding: 5 - materialsSection.rightPadding
rightPadding: 5 spacing: StudioTheme.Values.sectionGridSpacing
bottomPadding: 5 columns: root.numColumns
columns: root.width / root.cellWidth
Repeater { Repeater {
id: materialRepeater id: materialRepeater
@@ -691,10 +700,10 @@ Item {
width: root.cellWidth width: root.cellWidth
height: root.cellHeight height: root.cellHeight
onShowContextMenu: { onShowContextMenu: ctxMenu.popupMenu(this, model)
ctxMenu.popupMenu(this, model)
}
} }
onCountChanged: root.responsiveResize(root.width, root.height)
} }
} }
@@ -727,6 +736,11 @@ Item {
id: texturesSection id: texturesSection
width: root.width width: root.width
leftPadding: StudioTheme.Values.sectionPadding
rightPadding: StudioTheme.Values.sectionPadding
topPadding: StudioTheme.Values.sectionPadding
bottomPadding: StudioTheme.Values.sectionPadding
caption: qsTr("Textures") caption: qsTr("Textures")
category: "MaterialBrowser" category: "MaterialBrowser"
@@ -768,11 +782,10 @@ Item {
Grid { Grid {
id: gridTextures id: gridTextures
width: scrollView.width width: scrollView.width - texturesSection.leftPadding
leftPadding: 5 - texturesSection.rightPadding
rightPadding: 5 spacing: StudioTheme.Values.sectionGridSpacing
bottomPadding: 5 columns: root.numColumns
columns: root.width / root.cellWidth
Repeater { Repeater {
id: texturesRepeater id: texturesRepeater
@@ -782,10 +795,10 @@ Item {
width: root.cellWidth width: root.cellWidth
height: root.cellHeight height: root.cellHeight
onShowContextMenu: { onShowContextMenu: ctxMenuTextures.popupMenu(model)
ctxMenuTextures.popupMenu(model)
}
} }
onCountChanged: root.responsiveResize(root.width, root.height)
} }
} }

View File

@@ -8,32 +8,24 @@ import HelperWidgets 2.0
import StudioTheme 1.0 as StudioTheme import StudioTheme 1.0 as StudioTheme
import MaterialBrowserBackend import MaterialBrowserBackend
Rectangle { Item {
id: root id: root
signal showContextMenu() signal showContextMenu()
function refreshPreview() function refreshPreview() {
{
img.source = "" img.source = ""
img.source = "image://materialBrowser/" + materialInternalId img.source = "image://materialBrowser/" + materialInternalId
} }
function forceFinishEditing() function forceFinishEditing() {
{
matName.commitRename() matName.commitRename()
} }
function startRename() function startRename() {
{
matName.startRename() matName.startRename()
} }
border.width: MaterialBrowserBackend.materialBrowserModel.selectedIndex === index ? MaterialBrowserBackend.rootView.materialSectionFocused ? 3 : 1 : 0
border.color: MaterialBrowserBackend.materialBrowserModel.selectedIndex === index
? StudioTheme.Values.themeControlOutlineInteraction
: "transparent"
color: "transparent"
visible: materialVisible visible: materialVisible
DropArea { DropArea {
@@ -81,12 +73,10 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
spacing: 1 spacing: 1
Item { width: 1; height: 5 } // spacer
Image { Image {
id: img id: img
width: root.width - 10 width: root.width
height: img.width height: img.width
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
source: "image://materialBrowser/" + materialInternalId source: "image://materialBrowser/" + materialInternalId
@@ -94,8 +84,8 @@ Rectangle {
} }
// Eat keys so they are not passed to parent while editing name // Eat keys so they are not passed to parent while editing name
Keys.onPressed: (e) => { Keys.onPressed: (event) => {
e.accepted = true; event.accepted = true
} }
MaterialBrowserItemName { MaterialBrowserItemName {
@@ -116,4 +106,14 @@ Rectangle {
} }
} }
} }
Rectangle {
id: marker
anchors.fill: parent
border.width: MaterialBrowserBackend.materialBrowserModel.selectedIndex === index ? MaterialBrowserBackend.rootView.materialSectionFocused ? 3 : 1 : 0
border.color: MaterialBrowserBackend.materialBrowserModel.selectedIndex === index
? StudioTheme.Values.themeControlOutlineInteraction
: "transparent"
color: "transparent"
}
} }

View File

@@ -9,22 +9,14 @@ import HelperWidgets
import StudioTheme as StudioTheme import StudioTheme as StudioTheme
import MaterialBrowserBackend import MaterialBrowserBackend
Rectangle { Item {
id: root id: root
visible: textureVisible visible: textureVisible
color: "transparent"
border.width: MaterialBrowserBackend.materialBrowserTexturesModel.selectedIndex === index
? !MaterialBrowserBackend.rootView.materialSectionFocused ? 3 : 1 : 0
border.color: MaterialBrowserBackend.materialBrowserTexturesModel.selectedIndex === index
? StudioTheme.Values.themeControlOutlineInteraction
: "transparent"
signal showContextMenu() signal showContextMenu()
function forceFinishEditing() function forceFinishEditing() {
{
txtId.commitRename() txtId.commitRename()
} }
@@ -68,12 +60,11 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
spacing: 1 spacing: 1
Item { width: 1; height: 5 } // spacer
Image { Image {
id: img id: img
source: "image://materialBrowserTex/" + textureSource source: "image://materialBrowserTex/" + textureSource
asynchronous: true asynchronous: true
width: root.width - 10 width: root.width
height: img.width height: img.width
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
smooth: true smooth: true
@@ -81,8 +72,8 @@ Rectangle {
} }
// Eat keys so they are not passed to parent while editing name // Eat keys so they are not passed to parent while editing name
Keys.onPressed: (e) => { Keys.onPressed: (event) => {
e.accepted = true; event.accepted = true
} }
MaterialBrowserItemName { MaterialBrowserItemName {
@@ -103,4 +94,16 @@ Rectangle {
} }
} }
} }
Rectangle {
id: marker
anchors.fill: parent
color: "transparent"
border.width: MaterialBrowserBackend.materialBrowserTexturesModel.selectedIndex === index
? !MaterialBrowserBackend.rootView.materialSectionFocused ? 3 : 1 : 0
border.color: MaterialBrowserBackend.materialBrowserTexturesModel.selectedIndex === index
? StudioTheme.Values.themeControlOutlineInteraction
: "transparent"
}
} }