1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 21:42:51 -05:00

Consolidate launcher (#615)

* refactor: Consolidate Icon Renderer for launcher

Launcher icons are built on 2 places Spotlight and AppDrawer
This duplicates the maintanance effort, every time something
changes on one place must be replicated on the other.

This commit consolidates the Icon renderer in a shared component.

* refactor: Consolidate Launcher list and grid

List and GRid builders were split in 2 components.

this commit adds separate delegates to be reused as shared components.
This commit is contained in:
Bruno Cesar Rocha
2025-11-03 15:58:52 +00:00
committed by GitHub
parent ed0b80008f
commit 3abee7f2f5
5 changed files with 372 additions and 466 deletions

View File

@@ -73,125 +73,22 @@ Rectangle {
appLauncher.keyboardNavigationActive = false
}
delegate: Rectangle {
width: ListView.view.width
height: resultsList.itemHeight
radius: Theme.cornerRadius
color: ListView.isCurrentItem ? Theme.primaryPressed : listMouseArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceContainerHigh
Row {
anchors.fill: parent
anchors.margins: Theme.spacingM
spacing: Theme.spacingL
Item {
width: resultsList.iconSize
height: resultsList.iconSize
anchors.verticalCenter: parent.verticalCenter
visible: model.icon !== undefined && model.icon !== ""
property string iconValue: model.icon || ""
property bool isMaterial: iconValue.indexOf("material:") === 0
property bool isUnicode: iconValue.indexOf("unicode:") === 0
property string materialName: isMaterial ? iconValue.substring(9) : ""
property string unicodeChar: isUnicode ? iconValue.substring(8) : ""
DankIcon {
anchors.centerIn: parent
name: parent.materialName
size: resultsList.iconSize
color: Theme.surfaceText
visible: parent.isMaterial
}
StyledText {
anchors.centerIn: parent
text: parent.unicodeChar
font.pixelSize: resultsList.iconSize * 0.8
color: Theme.surfaceText
visible: parent.isUnicode
}
IconImage {
id: listIconImg
anchors.fill: parent
source: parent.isMaterial || parent.isUnicode ? "" : Quickshell.iconPath(parent.iconValue, true)
asynchronous: true
visible: !parent.isMaterial && !parent.isUnicode && status === Image.Ready
}
Rectangle {
anchors.fill: parent
visible: !parent.isMaterial && !parent.isUnicode && !listIconImg.visible
color: Theme.surfaceLight
radius: Theme.cornerRadius
border.width: 1
border.color: Theme.primarySelected
StyledText {
anchors.centerIn: parent
text: (model.name && model.name.length > 0) ? model.name.charAt(0).toUpperCase() : "A"
font.pixelSize: resultsList.iconSize * 0.4
color: Theme.primary
font.weight: Font.Bold
}
}
}
Column {
anchors.verticalCenter: parent.verticalCenter
width: (model.icon !== undefined && model.icon !== "") ? (parent.width - resultsList.iconSize - Theme.spacingL) : parent.width
spacing: Theme.spacingXS
StyledText {
width: parent.width
text: model.name || ""
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
elide: Text.ElideRight
wrapMode: Text.NoWrap
maximumLineCount: 1
}
StyledText {
width: parent.width
text: model.comment || "Application"
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
elide: Text.ElideRight
maximumLineCount: 1
visible: resultsList.showDescription && model.comment && model.comment.length > 0
}
}
}
MouseArea {
id: listMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton
z: 10
onEntered: () => {
if (resultsList.hoverUpdatesSelection && !resultsList.keyboardNavigationActive)
resultsList.currentIndex = index
}
onPositionChanged: () => {
resultsList.keyboardNavigationReset()
}
onClicked: mouse => {
if (mouse.button === Qt.LeftButton) {
resultsList.itemClicked(index, model)
} else if (mouse.button === Qt.RightButton && !model.isPlugin) {
const globalPos = mapToItem(null, mouse.x, mouse.y)
const modalPos = resultsContainer.parent.mapFromItem(null, globalPos.x, globalPos.y)
resultsList.itemRightClicked(index, model, modalPos.x, modalPos.y)
}
}
delegate: AppLauncherListDelegate {
listView: resultsList
itemHeight: resultsList.itemHeight
iconSize: resultsList.iconSize
showDescription: resultsList.showDescription
hoverUpdatesSelection: resultsList.hoverUpdatesSelection
keyboardNavigationActive: resultsList.keyboardNavigationActive
isCurrentItem: ListView.isCurrentItem
iconMaterialSizeAdjustment: 0
iconUnicodeScale: 0.8
onItemClicked: (idx, modelData) => resultsList.itemClicked(idx, modelData)
onItemRightClicked: (idx, modelData, mouseX, mouseY) => {
const modalPos = resultsContainer.parent.mapFromItem(null, mouseX, mouseY)
resultsList.itemRightClicked(idx, modelData, modalPos.x, modalPos.y)
}
onKeyboardNavigationReset: resultsList.keyboardNavigationReset
}
}
@@ -260,113 +157,23 @@ Rectangle {
appLauncher.keyboardNavigationActive = false
}
delegate: Rectangle {
width: resultsGrid.cellWidth - resultsGrid.cellPadding
height: resultsGrid.cellHeight - resultsGrid.cellPadding
radius: Theme.cornerRadius
color: resultsGrid.currentIndex === index ? Theme.primaryPressed : gridMouseArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceContainerHigh
Column {
anchors.centerIn: parent
spacing: Theme.spacingS
Item {
property int iconSize: Math.min(resultsGrid.maxIconSize, Math.max(resultsGrid.minIconSize, resultsGrid.cellWidth * resultsGrid.iconSizeRatio))
width: iconSize
height: iconSize
anchors.horizontalCenter: parent.horizontalCenter
visible: model.icon !== undefined && model.icon !== ""
property string iconValue: model.icon || ""
property bool isMaterial: iconValue.indexOf("material:") === 0
property bool isUnicode: iconValue.indexOf("unicode:") === 0
property string materialName: isMaterial ? iconValue.substring(9) : ""
property string unicodeChar: isUnicode ? iconValue.substring(8) : ""
DankIcon {
anchors.centerIn: parent
name: parent.materialName
size: parent.iconSize
color: Theme.surfaceText
visible: parent.isMaterial
}
StyledText {
anchors.centerIn: parent
text: parent.unicodeChar
font.pixelSize: parent.iconSize * 0.8
color: Theme.surfaceText
visible: parent.isUnicode
}
IconImage {
id: gridIconImg
anchors.fill: parent
source: parent.isMaterial || parent.isUnicode ? "" : Quickshell.iconPath(parent.iconValue, true)
smooth: true
asynchronous: true
visible: !parent.isMaterial && !parent.isUnicode && status === Image.Ready
}
Rectangle {
anchors.fill: parent
visible: !parent.isMaterial && !parent.isUnicode && !gridIconImg.visible
color: Theme.surfaceLight
radius: Theme.cornerRadius
border.width: 1
border.color: Theme.primarySelected
StyledText {
anchors.centerIn: parent
text: (model.name && model.name.length > 0) ? model.name.charAt(0).toUpperCase() : "A"
font.pixelSize: Math.min(28, parent.width * 0.5)
color: Theme.primary
font.weight: Font.Bold
}
}
}
StyledText {
anchors.horizontalCenter: parent.horizontalCenter
width: resultsGrid.cellWidth - 12
text: model.name || ""
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Medium
elide: Text.ElideRight
horizontalAlignment: Text.AlignHCenter
maximumLineCount: 1
wrapMode: Text.NoWrap
}
}
MouseArea {
id: gridMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton
z: 10
onEntered: () => {
if (resultsGrid.hoverUpdatesSelection && !resultsGrid.keyboardNavigationActive)
resultsGrid.currentIndex = index
}
onPositionChanged: () => {
resultsGrid.keyboardNavigationReset()
}
onClicked: mouse => {
if (mouse.button === Qt.LeftButton) {
resultsGrid.itemClicked(index, model)
} else if (mouse.button === Qt.RightButton && !model.isPlugin) {
const globalPos = mapToItem(null, mouse.x, mouse.y)
const modalPos = resultsContainer.parent.mapFromItem(null, globalPos.x, globalPos.y)
resultsGrid.itemRightClicked(index, model, modalPos.x, modalPos.y)
}
}
delegate: AppLauncherGridDelegate {
gridView: resultsGrid
cellWidth: resultsGrid.cellWidth
cellHeight: resultsGrid.cellHeight
cellPadding: resultsGrid.cellPadding
minIconSize: resultsGrid.minIconSize
maxIconSize: resultsGrid.maxIconSize
iconSizeRatio: resultsGrid.iconSizeRatio
hoverUpdatesSelection: resultsGrid.hoverUpdatesSelection
keyboardNavigationActive: resultsGrid.keyboardNavigationActive
currentIndex: resultsGrid.currentIndex
onItemClicked: (idx, modelData) => resultsGrid.itemClicked(idx, modelData)
onItemRightClicked: (idx, modelData, mouseX, mouseY) => {
const modalPos = resultsContainer.parent.mapFromItem(null, mouseX, mouseY)
resultsGrid.itemRightClicked(idx, modelData, modalPos.x, modalPos.y)
}
onKeyboardNavigationReset: resultsGrid.keyboardNavigationReset
}
}
}

View File

@@ -389,134 +389,27 @@ DankPopout {
appLauncher.keyboardNavigationActive = false
}
delegate: Rectangle {
width: ListView.view.width
height: appList.itemHeight
radius: Theme.cornerRadius
color: ListView.isCurrentItem ? Theme.primaryPressed : listMouseArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceContainerHigh
Row {
anchors.fill: parent
anchors.margins: Theme.spacingM
spacing: Theme.spacingL
Item {
width: appList.iconSize
height: appList.iconSize
anchors.verticalCenter: parent.verticalCenter
visible: model.icon !== undefined && model.icon !== ""
property string iconValue: model.icon || ""
property bool isMaterial: iconValue.indexOf("material:") === 0
property bool isUnicode: iconValue.indexOf("unicode:") === 0
property string materialName: isMaterial ? iconValue.substring(9) : ""
property string unicodeChar: isUnicode ? iconValue.substring(8) : ""
DankIcon {
anchors.centerIn: parent
name: parent.materialName
size: appList.iconSize - Theme.spacingM
color: Theme.surfaceText
visible: parent.isMaterial
}
StyledText {
anchors.centerIn: parent
text: parent.unicodeChar
font.pixelSize: appList.iconSize * 0.7
color: Theme.surfaceText
visible: parent.isUnicode
}
IconImage {
id: listIconImg
anchors.fill: parent
anchors.margins: Theme.spacingXS
source: parent.isMaterial || parent.isUnicode ? "" : Quickshell.iconPath(parent.iconValue, true)
smooth: true
asynchronous: true
visible: !parent.isMaterial && !parent.isUnicode && status === Image.Ready
}
Rectangle {
anchors.fill: parent
anchors.leftMargin: Theme.spacingS
anchors.rightMargin: Theme.spacingS
anchors.bottomMargin: Theme.spacingM
visible: !parent.isMaterial && !parent.isUnicode && listIconImg.status !== Image.Ready
color: Theme.surfaceLight
radius: Theme.cornerRadius
border.width: 0
border.color: Theme.primarySelected
StyledText {
anchors.centerIn: parent
text: (model.name && model.name.length > 0) ? model.name.charAt(0).toUpperCase() : "A"
font.pixelSize: appList.iconSize * 0.4
color: Theme.primary
font.weight: Font.Bold
}
}
}
Column {
anchors.verticalCenter: parent.verticalCenter
width: (model.icon !== undefined && model.icon !== "") ? (parent.width - appList.iconSize - Theme.spacingL) : parent.width
spacing: Theme.spacingXS
StyledText {
width: parent.width
text: model.name || ""
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
elide: Text.ElideRight
wrapMode: Text.NoWrap
maximumLineCount: 1
}
StyledText {
width: parent.width
text: model.comment || "Application"
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
elide: Text.ElideRight
maximumLineCount: 1
visible: appList.showDescription && model.comment && model.comment.length > 0
}
}
}
MouseArea {
id: listMouseArea
anchors.fill: parent
anchors.leftMargin: Theme.spacingS
anchors.rightMargin: Theme.spacingS
anchors.bottomMargin: Theme.spacingM
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton
z: 10
onEntered: {
if (appList.hoverUpdatesSelection && !appList.keyboardNavigationActive)
appList.currentIndex = index
}
onPositionChanged: {
appList.keyboardNavigationReset()
}
onClicked: mouse => {
if (mouse.button === Qt.LeftButton) {
appList.itemClicked(index, model)
} else if (mouse.button === Qt.RightButton) {
var globalPos = mapToItem(null, mouse.x, mouse.y)
var panelPos = contextMenu.parent.mapFromItem(null, globalPos.x, globalPos.y)
appList.itemRightClicked(index, model, panelPos.x, panelPos.y)
}
}
delegate: AppLauncherListDelegate {
listView: appList
itemHeight: appList.itemHeight
iconSize: appList.iconSize
showDescription: appList.showDescription
hoverUpdatesSelection: appList.hoverUpdatesSelection
keyboardNavigationActive: appList.keyboardNavigationActive
isCurrentItem: ListView.isCurrentItem
mouseAreaLeftMargin: Theme.spacingS
mouseAreaRightMargin: Theme.spacingS
mouseAreaBottomMargin: Theme.spacingM
iconMargins: Theme.spacingXS
iconFallbackLeftMargin: Theme.spacingS
iconFallbackRightMargin: Theme.spacingS
iconFallbackBottomMargin: Theme.spacingM
onItemClicked: (idx, modelData) => appList.itemClicked(idx, modelData)
onItemRightClicked: (idx, modelData, mouseX, mouseY) => {
const panelPos = contextMenu.parent.mapFromItem(null, mouseX, mouseY)
appList.itemRightClicked(idx, modelData, panelPos.x, panelPos.y)
}
onKeyboardNavigationReset: appList.keyboardNavigationReset
}
}
@@ -587,122 +480,30 @@ DankPopout {
appLauncher.keyboardNavigationActive = false
}
delegate: Rectangle {
width: appGrid.cellWidth - appGrid.cellPadding
height: appGrid.cellHeight - appGrid.cellPadding
radius: Theme.cornerRadius
color: appGrid.currentIndex === index ? Theme.primaryPressed : gridMouseArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceContainerHigh
Column {
anchors.centerIn: parent
spacing: Theme.spacingS
Item {
property int iconSize: Math.min(appGrid.maxIconSize, Math.max(appGrid.minIconSize, appGrid.cellWidth * appGrid.iconSizeRatio))
width: iconSize
height: iconSize
anchors.horizontalCenter: parent.horizontalCenter
visible: model.icon !== undefined && model.icon !== ""
property string iconValue: model.icon || ""
property bool isMaterial: iconValue.indexOf("material:") === 0
property bool isUnicode: iconValue.indexOf("unicode:") === 0
property string materialName: isMaterial ? iconValue.substring(9) : ""
property string unicodeChar: isUnicode ? iconValue.substring(8) : ""
DankIcon {
anchors.centerIn: parent
name: parent.materialName
size: parent.iconSize - Theme.spacingL
color: Theme.surfaceText
visible: parent.isMaterial
}
StyledText {
anchors.centerIn: parent
text: parent.unicodeChar
font.pixelSize: parent.iconSize * 0.8
color: Theme.surfaceText
visible: parent.isUnicode
}
IconImage {
id: gridIconImg
anchors.fill: parent
anchors.leftMargin: Theme.spacingS
anchors.rightMargin: Theme.spacingS
anchors.bottomMargin: Theme.spacingS
source: parent.isMaterial || parent.isUnicode ? "" : Quickshell.iconPath(parent.iconValue, true)
smooth: true
asynchronous: true
visible: !parent.isMaterial && !parent.isUnicode && status === Image.Ready
}
Rectangle {
anchors.fill: parent
anchors.leftMargin: Theme.spacingS
anchors.rightMargin: Theme.spacingS
anchors.bottomMargin: Theme.spacingS
visible: !parent.isMaterial && !parent.isUnicode && gridIconImg.status !== Image.Ready
color: Theme.surfaceLight
radius: Theme.cornerRadius
border.width: 0
border.color: Theme.primarySelected
StyledText {
anchors.centerIn: parent
text: (model.name && model.name.length > 0) ? model.name.charAt(0).toUpperCase() : "A"
font.pixelSize: Math.min(28, parent.width * 0.5)
color: Theme.primary
font.weight: Font.Bold
}
}
}
StyledText {
anchors.horizontalCenter: parent.horizontalCenter
width: appGrid.cellWidth - 12
text: model.name || ""
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Medium
elide: Text.ElideRight
horizontalAlignment: Text.AlignHCenter
maximumLineCount: 1
wrapMode: Text.NoWrap
}
}
MouseArea {
id: gridMouseArea
anchors.fill: parent
anchors.leftMargin: Theme.spacingS
anchors.rightMargin: Theme.spacingS
anchors.bottomMargin: Theme.spacingS
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton
z: 10
onEntered: {
if (appGrid.hoverUpdatesSelection && !appGrid.keyboardNavigationActive)
appGrid.currentIndex = index
}
onPositionChanged: {
appGrid.keyboardNavigationReset()
}
onClicked: mouse => {
if (mouse.button === Qt.LeftButton) {
appGrid.itemClicked(index, model)
} else if (mouse.button === Qt.RightButton) {
var globalPos = mapToItem(null, mouse.x, mouse.y)
var panelPos = contextMenu.parent.mapFromItem(null, globalPos.x, globalPos.y)
appGrid.itemRightClicked(index, model, panelPos.x, panelPos.y)
}
}
delegate: AppLauncherGridDelegate {
gridView: appGrid
cellWidth: appGrid.cellWidth
cellHeight: appGrid.cellHeight
cellPadding: appGrid.cellPadding
minIconSize: appGrid.minIconSize
maxIconSize: appGrid.maxIconSize
iconSizeRatio: appGrid.iconSizeRatio
hoverUpdatesSelection: appGrid.hoverUpdatesSelection
keyboardNavigationActive: appGrid.keyboardNavigationActive
currentIndex: appGrid.currentIndex
mouseAreaLeftMargin: Theme.spacingS
mouseAreaRightMargin: Theme.spacingS
mouseAreaBottomMargin: Theme.spacingS
iconFallbackLeftMargin: Theme.spacingS
iconFallbackRightMargin: Theme.spacingS
iconFallbackBottomMargin: Theme.spacingS
iconMaterialSizeAdjustment: Theme.spacingL
onItemClicked: (idx, modelData) => appGrid.itemClicked(idx, modelData)
onItemRightClicked: (idx, modelData, mouseX, mouseY) => {
const panelPos = contextMenu.parent.mapFromItem(null, mouseX, mouseY)
appGrid.itemRightClicked(idx, modelData, panelPos.x, panelPos.y)
}
onKeyboardNavigationReset: appGrid.keyboardNavigationReset
}
}
}

View File

@@ -0,0 +1,80 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Widgets
import qs.Common
Item {
id: root
required property string iconValue
required property int iconSize
property string fallbackText: "A"
property color iconColor: Theme.surfaceText
property color fallbackBackgroundColor: Theme.surfaceLight
property color fallbackTextColor: Theme.primary
property real materialIconSizeAdjustment: Theme.spacingM
property real unicodeIconScale: 0.7
property real fallbackTextScale: 0.4
property alias iconMargins: iconImg.anchors.margins
property real fallbackLeftMargin: 0
property real fallbackRightMargin: 0
property real fallbackTopMargin: 0
property real fallbackBottomMargin: 0
property bool isMaterial: iconValue.indexOf("material:") === 0
property bool isUnicode: iconValue.indexOf("unicode:") === 0
property string materialName: isMaterial ? iconValue.substring(9) : ""
property string unicodeChar: isUnicode ? iconValue.substring(8) : ""
visible: iconValue !== undefined && iconValue !== ""
DankIcon {
anchors.centerIn: parent
name: root.materialName
size: root.iconSize - root.materialIconSizeAdjustment
color: root.iconColor
visible: root.isMaterial
}
StyledText {
anchors.centerIn: parent
text: root.unicodeChar
font.pixelSize: root.iconSize * root.unicodeIconScale
color: root.iconColor
visible: root.isUnicode
}
IconImage {
id: iconImg
anchors.fill: parent
source: root.isMaterial || root.isUnicode ? "" : Quickshell.iconPath(root.iconValue, true)
smooth: true
asynchronous: true
visible: !root.isMaterial && !root.isUnicode && status === Image.Ready
}
Rectangle {
id: fallbackRect
anchors.fill: parent
anchors.leftMargin: root.fallbackLeftMargin
anchors.rightMargin: root.fallbackRightMargin
anchors.topMargin: root.fallbackTopMargin
anchors.bottomMargin: root.fallbackBottomMargin
visible: !root.isMaterial && !root.isUnicode && iconImg.status !== Image.Ready
color: root.fallbackBackgroundColor
radius: Theme.cornerRadius
border.width: 0
border.color: Theme.primarySelected
StyledText {
anchors.centerIn: parent
text: root.fallbackText
font.pixelSize: root.iconSize * root.fallbackTextScale
color: root.fallbackTextColor
font.weight: Font.Bold
}
}
}

View File

@@ -0,0 +1,104 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import qs.Common
import qs.Widgets
Rectangle {
id: root
required property var model
required property int index
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
property bool hoverUpdatesSelection: true
property bool keyboardNavigationActive: false
property int currentIndex: -1
property bool isPlugin: model?.isPlugin || false
property real mouseAreaLeftMargin: 0
property real mouseAreaRightMargin: 0
property real mouseAreaBottomMargin: 0
property real iconFallbackLeftMargin: 0
property real iconFallbackRightMargin: 0
property real iconFallbackBottomMargin: 0
property real iconMaterialSizeAdjustment: 0
property real iconUnicodeScale: 0.8
signal itemClicked(int index, var modelData)
signal itemRightClicked(int index, var modelData, real mouseX, real mouseY)
signal keyboardNavigationReset()
width: cellWidth - cellPadding
height: cellHeight - cellPadding
radius: Theme.cornerRadius
color: currentIndex === index ? Theme.primaryPressed : mouseArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceContainerHigh
Column {
anchors.centerIn: parent
spacing: Theme.spacingS
AppIconRenderer {
property int computedIconSize: Math.min(root.maxIconSize, Math.max(root.minIconSize, root.cellWidth * root.iconSizeRatio))
width: computedIconSize
height: computedIconSize
anchors.horizontalCenter: parent.horizontalCenter
iconValue: model.icon || ""
iconSize: computedIconSize
fallbackText: (model.name && model.name.length > 0) ? model.name.charAt(0).toUpperCase() : "A"
materialIconSizeAdjustment: root.iconMaterialSizeAdjustment
unicodeIconScale: root.iconUnicodeScale
fallbackTextScale: Math.min(28, computedIconSize * 0.5) / computedIconSize
iconMargins: 0
fallbackLeftMargin: root.iconFallbackLeftMargin
fallbackRightMargin: root.iconFallbackRightMargin
fallbackBottomMargin: root.iconFallbackBottomMargin
}
StyledText {
anchors.horizontalCenter: parent.horizontalCenter
width: root.cellWidth - 12
text: model.name || ""
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Medium
elide: Text.ElideRight
horizontalAlignment: Text.AlignHCenter
maximumLineCount: 1
wrapMode: Text.NoWrap
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
anchors.leftMargin: root.mouseAreaLeftMargin
anchors.rightMargin: root.mouseAreaRightMargin
anchors.bottomMargin: root.mouseAreaBottomMargin
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton
z: 10
onEntered: {
if (root.hoverUpdatesSelection && !root.keyboardNavigationActive)
root.gridView.currentIndex = root.index
}
onPositionChanged: {
root.keyboardNavigationReset()
}
onClicked: mouse => {
if (mouse.button === Qt.LeftButton) {
root.itemClicked(root.index, root.model)
} else 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)
}
}
}
}

View File

@@ -0,0 +1,114 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import qs.Common
import qs.Widgets
Rectangle {
id: root
required property var model
required property int index
required property var listView
property int itemHeight: 60
property int iconSize: 40
property bool showDescription: true
property bool hoverUpdatesSelection: true
property bool keyboardNavigationActive: false
property bool isCurrentItem: false
property bool isPlugin: model?.isPlugin || false
property real mouseAreaLeftMargin: 0
property real mouseAreaRightMargin: 0
property real mouseAreaBottomMargin: 0
property real iconMargins: 0
property real iconFallbackLeftMargin: 0
property real iconFallbackRightMargin: 0
property real iconFallbackBottomMargin: 0
property real iconMaterialSizeAdjustment: Theme.spacingM
property real iconUnicodeScale: 0.7
signal itemClicked(int index, var modelData)
signal itemRightClicked(int index, var modelData, real mouseX, real mouseY)
signal keyboardNavigationReset()
width: listView.width
height: itemHeight
radius: Theme.cornerRadius
color: isCurrentItem ? Theme.primaryPressed : mouseArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceContainerHigh
Row {
anchors.fill: parent
anchors.margins: Theme.spacingM
spacing: Theme.spacingL
AppIconRenderer {
width: root.iconSize
height: root.iconSize
anchors.verticalCenter: parent.verticalCenter
iconValue: model.icon || ""
iconSize: root.iconSize
fallbackText: (model.name && model.name.length > 0) ? model.name.charAt(0).toUpperCase() : "A"
iconMargins: root.iconMargins
fallbackLeftMargin: root.iconFallbackLeftMargin
fallbackRightMargin: root.iconFallbackRightMargin
fallbackBottomMargin: root.iconFallbackBottomMargin
materialIconSizeAdjustment: root.iconMaterialSizeAdjustment
unicodeIconScale: root.iconUnicodeScale
}
Column {
anchors.verticalCenter: parent.verticalCenter
width: (model.icon !== undefined && model.icon !== "") ? (parent.width - root.iconSize - Theme.spacingL) : parent.width
spacing: Theme.spacingXS
StyledText {
width: parent.width
text: model.name || ""
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
elide: Text.ElideRight
wrapMode: Text.NoWrap
maximumLineCount: 1
}
StyledText {
width: parent.width
text: model.comment || "Application"
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
elide: Text.ElideRight
maximumLineCount: 1
visible: root.showDescription && model.comment && model.comment.length > 0
}
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
anchors.leftMargin: root.mouseAreaLeftMargin
anchors.rightMargin: root.mouseAreaRightMargin
anchors.bottomMargin: root.mouseAreaBottomMargin
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton
z: 10
onEntered: {
if (root.hoverUpdatesSelection && !root.keyboardNavigationActive)
root.listView.currentIndex = root.index
}
onPositionChanged: {
root.keyboardNavigationReset()
}
onClicked: mouse => {
if (mouse.button === Qt.LeftButton) {
root.itemClicked(root.index, root.model)
} else 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)
}
}
}
}