1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-05 21:15:38 -05:00

launcher: consistent spacing of grid mode

This commit is contained in:
bbedward
2025-12-01 09:31:57 -05:00
parent bcd9ece077
commit 25951ddc55
4 changed files with 224 additions and 219 deletions

View File

@@ -192,7 +192,6 @@ Item {
}
}
SpotlightContextMenuPopup {
id: popupContextMenu
@@ -244,16 +243,17 @@ Item {
spacing: Theme.spacingM
clip: false
Row {
width: parent.width
spacing: Theme.spacingM
leftPadding: Theme.spacingS
topPadding: Theme.spacingS
Item {
id: searchRow
width: parent.width - Theme.spacingS * 2
height: 56
anchors.horizontalCenter: parent.horizontalCenter
DankTextField {
id: searchField
width: parent.width - 80 - Theme.spacingL
anchors.left: parent.left
anchors.right: buttonsContainer.left
anchors.rightMargin: Theme.spacingM
height: 56
cornerRadius: Theme.cornerRadius
backgroundColor: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
@@ -302,147 +302,159 @@ Item {
}
}
Row {
spacing: Theme.spacingXS
visible: searchMode === "apps" && appLauncher.model.count > 0
Item {
id: buttonsContainer
width: viewModeButtons.visible ? viewModeButtons.width : (fileSearchButtons.visible ? fileSearchButtons.width : 0)
height: 36
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
Rectangle {
width: 36
height: 36
radius: Theme.cornerRadius
color: appLauncher.viewMode === "list" ? Theme.primaryHover : listViewArea.containsMouse ? Theme.surfaceHover : "transparent"
Row {
id: viewModeButtons
spacing: Theme.spacingXS
visible: searchMode === "apps" && appLauncher.model.count > 0
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
DankIcon {
anchors.centerIn: parent
name: "view_list"
size: 18
color: appLauncher.viewMode === "list" ? Theme.primary : Theme.surfaceText
Rectangle {
width: 36
height: 36
radius: Theme.cornerRadius
color: appLauncher.viewMode === "list" ? Theme.primaryHover : listViewArea.containsMouse ? Theme.surfaceHover : "transparent"
DankIcon {
anchors.centerIn: parent
name: "view_list"
size: 18
color: appLauncher.viewMode === "list" ? Theme.primary : Theme.surfaceText
}
MouseArea {
id: listViewArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: () => {
appLauncher.setViewMode("list");
}
}
}
MouseArea {
id: listViewArea
Rectangle {
width: 36
height: 36
radius: Theme.cornerRadius
color: appLauncher.viewMode === "grid" ? Theme.primaryHover : gridViewArea.containsMouse ? Theme.surfaceHover : "transparent"
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: () => {
appLauncher.setViewMode("list");
DankIcon {
anchors.centerIn: parent
name: "grid_view"
size: 18
color: appLauncher.viewMode === "grid" ? Theme.primary : Theme.surfaceText
}
MouseArea {
id: gridViewArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: () => {
appLauncher.setViewMode("grid");
}
}
}
}
Rectangle {
width: 36
height: 36
radius: Theme.cornerRadius
color: appLauncher.viewMode === "grid" ? Theme.primaryHover : gridViewArea.containsMouse ? Theme.surfaceHover : "transparent"
Row {
id: fileSearchButtons
spacing: Theme.spacingXS
visible: searchMode === "files"
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
DankIcon {
anchors.centerIn: parent
name: "grid_view"
size: 18
color: appLauncher.viewMode === "grid" ? Theme.primary : Theme.surfaceText
}
Rectangle {
id: filenameFilterButton
MouseArea {
id: gridViewArea
width: 36
height: 36
radius: Theme.cornerRadius
color: fileSearchController.searchField === "filename" ? Theme.primaryHover : filenameFilterArea.containsMouse ? Theme.surfaceHover : "transparent"
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: () => {
appLauncher.setViewMode("grid");
DankIcon {
anchors.centerIn: parent
name: "title"
size: 18
color: fileSearchController.searchField === "filename" ? Theme.primary : Theme.surfaceText
}
MouseArea {
id: filenameFilterArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: () => {
fileSearchController.searchField = "filename";
}
onEntered: {
filenameTooltipLoader.active = true;
Qt.callLater(() => {
if (filenameTooltipLoader.item) {
const p = mapToItem(null, width / 2, height + Theme.spacingXS);
filenameTooltipLoader.item.show(I18n.tr("Search filenames"), p.x, p.y, null);
}
});
}
onExited: {
if (filenameTooltipLoader.item)
filenameTooltipLoader.item.hide();
filenameTooltipLoader.active = false;
}
}
}
}
}
Row {
spacing: Theme.spacingXS
visible: searchMode === "files"
anchors.verticalCenter: parent.verticalCenter
Rectangle {
id: contentFilterButton
Rectangle {
id: filenameFilterButton
width: 36
height: 36
radius: Theme.cornerRadius
color: fileSearchController.searchField === "body" ? Theme.primaryHover : contentFilterArea.containsMouse ? Theme.surfaceHover : "transparent"
width: 36
height: 36
radius: Theme.cornerRadius
color: fileSearchController.searchField === "filename" ? Theme.primaryHover : filenameFilterArea.containsMouse ? Theme.surfaceHover : "transparent"
DankIcon {
anchors.centerIn: parent
name: "title"
size: 18
color: fileSearchController.searchField === "filename" ? Theme.primary : Theme.surfaceText
}
MouseArea {
id: filenameFilterArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: () => {
fileSearchController.searchField = "filename";
DankIcon {
anchors.centerIn: parent
name: "description"
size: 18
color: fileSearchController.searchField === "body" ? Theme.primary : Theme.surfaceText
}
onEntered: {
filenameTooltipLoader.active = true;
Qt.callLater(() => {
if (filenameTooltipLoader.item) {
const p = mapToItem(null, width / 2, height + Theme.spacingXS);
filenameTooltipLoader.item.show(I18n.tr("Search filenames"), p.x, p.y, null);
}
});
}
onExited: {
if (filenameTooltipLoader.item)
filenameTooltipLoader.item.hide();
filenameTooltipLoader.active = false;
}
}
}
MouseArea {
id: contentFilterArea
Rectangle {
id: contentFilterButton
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: () => {
fileSearchController.searchField = "body";
}
onEntered: {
contentTooltipLoader.active = true;
Qt.callLater(() => {
if (contentTooltipLoader.item) {
const p = mapToItem(null, width / 2, height + Theme.spacingXS);
contentTooltipLoader.item.show(I18n.tr("Search file contents"), p.x, p.y, null);
}
});
}
onExited: {
if (contentTooltipLoader.item)
contentTooltipLoader.item.hide();
width: 36
height: 36
radius: Theme.cornerRadius
color: fileSearchController.searchField === "body" ? Theme.primaryHover : contentFilterArea.containsMouse ? Theme.surfaceHover : "transparent"
DankIcon {
anchors.centerIn: parent
name: "description"
size: 18
color: fileSearchController.searchField === "body" ? Theme.primary : Theme.surfaceText
}
MouseArea {
id: contentFilterArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: () => {
fileSearchController.searchField = "body";
}
onEntered: {
contentTooltipLoader.active = true;
Qt.callLater(() => {
if (contentTooltipLoader.item) {
const p = mapToItem(null, width / 2, height + Theme.spacingXS);
contentTooltipLoader.item.show(I18n.tr("Search file contents"), p.x, p.y, null);
}
});
}
onExited: {
if (contentTooltipLoader.item)
contentTooltipLoader.item.hide();
contentTooltipLoader.active = false;
contentTooltipLoader.active = false;
}
}
}
}

View File

@@ -1,6 +1,4 @@
import QtQuick
import Quickshell
import Quickshell.Widgets
import qs.Common
import qs.Widgets
@@ -12,28 +10,41 @@ Rectangle {
signal itemRightClicked(int index, var modelData, real mouseX, real mouseY)
function resetScroll() {
resultsList.contentY = 0
resultsList.contentY = 0;
if (gridLoader.item) {
gridLoader.item.contentY = 0
gridLoader.item.contentY = 0;
}
}
function getSelectedItemPosition() {
if (!appLauncher) return { x: 0, y: 0 };
if (!appLauncher)
return {
x: 0,
y: 0
};
const selectedIndex = appLauncher.selectedIndex;
if (appLauncher.viewMode === "list") {
const itemY = selectedIndex * (resultsList.itemHeight + resultsList.itemSpacing) - resultsList.contentY;
return { x: resultsList.width / 2, y: itemY + resultsList.itemHeight / 2 };
return {
x: resultsList.width / 2,
y: itemY + resultsList.itemHeight / 2
};
} else if (gridLoader.item) {
const grid = gridLoader.item;
const row = Math.floor(selectedIndex / grid.actualColumns);
const col = selectedIndex % grid.actualColumns;
const itemX = col * grid.cellWidth + grid.leftMargin + grid.cellWidth / 2;
const itemY = row * grid.cellHeight - grid.contentY + grid.cellHeight / 2;
return { x: itemX, y: itemY };
return {
x: itemX,
y: itemY
};
}
return { x: 0, y: 0 };
return {
x: 0,
y: 0
};
}
radius: Theme.cornerRadius
@@ -56,14 +67,13 @@ Rectangle {
function ensureVisible(index) {
if (index < 0 || index >= count)
return
const itemY = index * (itemHeight + itemSpacing)
const itemBottom = itemY + itemHeight
return;
const itemY = index * (itemHeight + itemSpacing);
const itemBottom = itemY + itemHeight;
if (itemY < contentY)
contentY = itemY
contentY = itemY;
else if (itemBottom > contentY + height)
contentY = itemBottom - height
contentY = itemBottom - height;
}
anchors.fill: parent
@@ -79,19 +89,19 @@ Rectangle {
reuseItems: true
onCurrentIndexChanged: {
if (keyboardNavigationActive)
ensureVisible(currentIndex)
ensureVisible(currentIndex);
}
onItemClicked: (index, modelData) => {
if (appLauncher)
appLauncher.launchApp(modelData)
}
if (appLauncher)
appLauncher.launchApp(modelData);
}
onItemRightClicked: (index, modelData, mouseX, mouseY) => {
resultsContainer.itemRightClicked(index, modelData, mouseX, mouseY);
}
onKeyboardNavigationReset: () => {
if (appLauncher)
appLauncher.keyboardNavigationActive = false
}
if (appLauncher)
appLauncher.keyboardNavigationActive = false;
}
delegate: AppLauncherListDelegate {
listView: resultsList
@@ -105,7 +115,7 @@ Rectangle {
iconUnicodeScale: 0.8
onItemClicked: (idx, modelData) => resultsList.itemClicked(idx, modelData)
onItemRightClicked: (idx, modelData, mouseX, mouseY) => {
resultsList.itemRightClicked(idx, modelData, mouseX, mouseY)
resultsList.itemRightClicked(idx, modelData, mouseX, mouseY);
}
onKeyboardNavigationReset: resultsList.keyboardNavigationReset
}
@@ -130,11 +140,11 @@ Rectangle {
onWidthChanged: {
if (visible && Math.abs(width - _lastWidth) > 1) {
_lastWidth = width
active = false
_lastWidth = width;
active = false;
Qt.callLater(() => {
active = true
})
active = true;
});
}
}
sourceComponent: Component {
@@ -148,14 +158,13 @@ Rectangle {
property bool adaptiveColumns: false
property int minCellWidth: 120
property int maxCellWidth: 160
property int cellPadding: 8
property real iconSizeRatio: 0.55
property int maxIconSize: 48
property int minIconSize: 32
property bool hoverUpdatesSelection: false
property bool keyboardNavigationActive: appLauncher ? appLauncher.keyboardNavigationActive : false
property int baseCellWidth: adaptiveColumns ? Math.max(minCellWidth, Math.min(maxCellWidth, width / columns)) : (width - Theme.spacingS * 2) / columns
property int baseCellHeight: baseCellWidth + 20
property real baseCellWidth: adaptiveColumns ? Math.max(minCellWidth, Math.min(maxCellWidth, width / columns)) : width / columns
property real baseCellHeight: baseCellWidth + 20
property int actualColumns: adaptiveColumns ? Math.floor(width / cellWidth) : columns
property int remainingSpace: width - (actualColumns * cellWidth)
@@ -165,47 +174,44 @@ Rectangle {
function ensureVisible(index) {
if (index < 0 || index >= count)
return
const itemY = Math.floor(index / actualColumns) * cellHeight
const itemBottom = itemY + cellHeight
return;
const itemY = Math.floor(index / actualColumns) * cellHeight;
const itemBottom = itemY + cellHeight;
if (itemY < contentY)
contentY = itemY
contentY = itemY;
else if (itemBottom > contentY + height)
contentY = itemBottom - height
contentY = itemBottom - height;
}
anchors.fill: parent
model: appLauncher ? appLauncher.model : null
clip: true
cellWidth: baseCellWidth
cellHeight: baseCellHeight
leftMargin: Math.max(Theme.spacingS, remainingSpace / 2)
rightMargin: leftMargin
focus: true
interactive: true
cacheBuffer: Math.max(0, Math.min(height * 2, 1000))
reuseItems: true
onCurrentIndexChanged: {
if (keyboardNavigationActive)
ensureVisible(currentIndex)
ensureVisible(currentIndex);
}
onItemClicked: (index, modelData) => {
if (appLauncher)
appLauncher.launchApp(modelData)
}
if (appLauncher)
appLauncher.launchApp(modelData);
}
onItemRightClicked: (index, modelData, mouseX, mouseY) => {
resultsContainer.itemRightClicked(index, modelData, mouseX, mouseY);
}
onKeyboardNavigationReset: () => {
if (appLauncher)
appLauncher.keyboardNavigationActive = false
}
if (appLauncher)
appLauncher.keyboardNavigationActive = false;
}
delegate: AppLauncherGridDelegate {
gridView: resultsGrid
cellWidth: resultsGrid.cellWidth
cellHeight: resultsGrid.cellHeight
cellPadding: resultsGrid.cellPadding
minIconSize: resultsGrid.minIconSize
maxIconSize: resultsGrid.maxIconSize
iconSizeRatio: resultsGrid.iconSizeRatio
@@ -214,7 +220,7 @@ Rectangle {
currentIndex: resultsGrid.currentIndex
onItemClicked: (idx, modelData) => resultsGrid.itemClicked(idx, modelData)
onItemRightClicked: (idx, modelData, mouseX, mouseY) => {
resultsGrid.itemRightClicked(idx, modelData, mouseX, mouseY)
resultsGrid.itemRightClicked(idx, modelData, mouseX, mouseY);
}
onKeyboardNavigationReset: resultsGrid.keyboardNavigationReset
}

View File

@@ -252,18 +252,19 @@ DankPopout {
}
}
Row {
width: parent.width
Item {
width: parent.width - Theme.spacingS * 2
height: 40
spacing: Theme.spacingM
anchors.horizontalCenter: parent.horizontalCenter
visible: searchField.text.length === 0
leftPadding: Theme.spacingS
Rectangle {
width: 180
height: 40
radius: Theme.cornerRadius
color: "transparent"
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
DankDropdown {
anchors.fill: parent
@@ -278,13 +279,9 @@ DankPopout {
}
}
Item {
width: parent.width - 290
height: 1
}
Row {
spacing: 4
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
DankActionButton {
@@ -314,7 +311,8 @@ DankPopout {
}
Rectangle {
width: parent.width
width: searchField.width
x: searchField.x
height: {
let usedHeight = 40 + Theme.spacingS;
usedHeight += 52 + Theme.spacingS;
@@ -350,8 +348,6 @@ DankPopout {
}
anchors.fill: parent
anchors.leftMargin: Theme.spacingS
anchors.rightMargin: Theme.spacingS
anchors.bottomMargin: Theme.spacingS
visible: appLauncher.viewMode === "list"
model: appLauncher.model
@@ -410,14 +406,13 @@ DankPopout {
property bool adaptiveColumns: false
property int minCellWidth: 120
property int maxCellWidth: 160
property int cellPadding: 8
property real iconSizeRatio: 0.6
property int maxIconSize: 56
property int minIconSize: 32
property bool hoverUpdatesSelection: false
property bool keyboardNavigationActive: appLauncher.keyboardNavigationActive
property int baseCellWidth: adaptiveColumns ? Math.max(minCellWidth, Math.min(maxCellWidth, width / columns)) : (width - Theme.spacingS * 2) / columns
property int baseCellHeight: baseCellWidth + 20
property real baseCellWidth: adaptiveColumns ? Math.max(minCellWidth, Math.min(maxCellWidth, width / columns)) : width / columns
property real baseCellHeight: baseCellWidth + 20
property int actualColumns: adaptiveColumns ? Math.floor(width / cellWidth) : columns
property int remainingSpace: width - (actualColumns * cellWidth)
@@ -438,16 +433,12 @@ DankPopout {
}
anchors.fill: parent
anchors.leftMargin: Theme.spacingS
anchors.rightMargin: Theme.spacingS
anchors.bottomMargin: Theme.spacingS
visible: appLauncher.viewMode === "grid"
model: appLauncher.model
clip: true
cellWidth: baseCellWidth
cellHeight: baseCellHeight
leftMargin: Math.max(Theme.spacingS, remainingSpace / 2)
rightMargin: leftMargin
focus: true
interactive: true
cacheBuffer: Math.max(0, Math.min(height * 2, 1000))
@@ -472,7 +463,6 @@ DankPopout {
gridView: appGrid
cellWidth: appGrid.cellWidth
cellHeight: appGrid.cellHeight
cellPadding: appGrid.cellPadding
minIconSize: appGrid.minIconSize
maxIconSize: appGrid.maxIconSize
iconSizeRatio: appGrid.iconSizeRatio

View File

@@ -1,6 +1,4 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import qs.Common
import qs.Widgets
@@ -12,7 +10,6 @@ Rectangle {
required property var gridView
property int cellWidth: 120
property int cellHeight: 120
property int cellPadding: 8
property int minIconSize: 32
property int maxIconSize: 64
property real iconSizeRatio: 0.5
@@ -31,10 +28,10 @@ Rectangle {
signal itemClicked(int index, var modelData)
signal itemRightClicked(int index, var modelData, real mouseX, real mouseY)
signal keyboardNavigationReset()
signal keyboardNavigationReset
width: cellWidth - cellPadding
height: cellHeight - cellPadding
width: cellWidth
height: cellHeight
radius: Theme.cornerRadius
color: currentIndex === index ? Theme.withAlpha(Theme.surfaceContainerHighest, Theme.popupTransparency) : mouseArea.containsMouse ? Theme.withAlpha(Theme.surfaceContainerHighest, Theme.popupTransparency) : "transparent"
@@ -87,27 +84,27 @@ Rectangle {
z: 10
onEntered: {
if (root.hoverUpdatesSelection && !root.keyboardNavigationActive)
root.gridView.currentIndex = root.index
root.gridView.currentIndex = root.index;
}
onPositionChanged: {
root.keyboardNavigationReset()
root.keyboardNavigationReset();
}
onClicked: mouse => {
if (mouse.button === Qt.LeftButton) {
root.itemClicked(root.index, root.model)
root.itemClicked(root.index, root.model);
}
}
onPressAndHold: mouse => {
if (!root.isPlugin) {
const globalPos = mapToItem(null, mouse.x, mouse.y)
root.itemRightClicked(root.index, root.model, globalPos.x, globalPos.y)
const globalPos = mapToItem(null, mouse.x, mouse.y);
root.itemRightClicked(root.index, root.model, globalPos.x, globalPos.y);
}
}
onPressed: mouse => {
if (mouse.button === Qt.RightButton && !root.isPlugin) {
const globalPos = mapToItem(null, mouse.x, mouse.y)
root.itemRightClicked(root.index, root.model, globalPos.x, globalPos.y)
mouse.accepted = true
const globalPos = mapToItem(null, mouse.x, mouse.y);
root.itemRightClicked(root.index, root.model, globalPos.x, globalPos.y);
mouse.accepted = true;
}
}
}