1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-29 07:52:50 -05:00

powermenu: restore grid as an option

fixes #712
This commit is contained in:
bbedward
2025-11-14 08:51:15 -05:00
parent c4ca3c8644
commit 16a779a41b
4 changed files with 312 additions and 74 deletions

View File

@@ -300,6 +300,7 @@ Singleton {
property bool powerActionConfirm: true property bool powerActionConfirm: true
property var powerMenuActions: ["reboot", "logout", "poweroff", "lock", "suspend", "restart"] property var powerMenuActions: ["reboot", "logout", "poweroff", "lock", "suspend", "restart"]
property string powerMenuDefaultAction: "logout" property string powerMenuDefaultAction: "logout"
property bool powerMenuGridLayout: false
property string customPowerActionLock: "" property string customPowerActionLock: ""
property string customPowerActionLogout: "" property string customPowerActionLogout: ""
property string customPowerActionSuspend: "" property string customPowerActionSuspend: ""

View File

@@ -211,6 +211,7 @@ var SPEC = {
powerActionConfirm: { def: true }, powerActionConfirm: { def: true },
powerMenuActions: { def: ["reboot", "logout", "poweroff", "lock", "suspend", "restart"] }, powerMenuActions: { def: ["reboot", "logout", "poweroff", "lock", "suspend", "restart"] },
powerMenuDefaultAction: { def: "logout" }, powerMenuDefaultAction: { def: "logout" },
powerMenuGridLayout: { def: false },
customPowerActionLock: { def: "" }, customPowerActionLock: { def: "" },
customPowerActionLogout: { def: "" }, customPowerActionLogout: { def: "" },
customPowerActionSuspend: { def: "" }, customPowerActionSuspend: { def: "" },

View File

@@ -11,9 +11,13 @@ DankModal {
layerNamespace: "dms:power-menu" layerNamespace: "dms:power-menu"
property int selectedIndex: 0 property int selectedIndex: 0
property int selectedRow: 0
property int selectedCol: 0
property rect parentBounds: Qt.rect(0, 0, 0, 0) property rect parentBounds: Qt.rect(0, 0, 0, 0)
property var parentScreen: null property var parentScreen: null
property var visibleActions: [] property var visibleActions: []
property int gridColumns: 3
property int gridRows: 2
signal powerActionRequested(string action, string title, string message) signal powerActionRequested(string action, string title, string message)
signal lockRequested signal lockRequested
@@ -39,6 +43,30 @@ DankModal {
return false return false
return true return true
}) })
if (!SettingsData.powerMenuGridLayout) return
const count = visibleActions.length
if (count === 0) {
gridColumns = 1
gridRows = 1
return
}
if (count <= 3) {
gridColumns = 1
gridRows = count
return
}
if (count === 4) {
gridColumns = 2
gridRows = 2
return
}
gridColumns = 3
gridRows = Math.ceil(count / 3)
} }
function getDefaultActionIndex() { function getDefaultActionIndex() {
@@ -147,7 +175,9 @@ DankModal {
} }
shouldBeVisible: false shouldBeVisible: false
width: 400 width: SettingsData.powerMenuGridLayout
? Math.min(550, gridColumns * 180 + Theme.spacingS * (gridColumns - 1) + Theme.spacingL * 2)
: 400
height: contentLoader.item ? contentLoader.item.implicitHeight : 300 height: contentLoader.item ? contentLoader.item.implicitHeight : 300
enableShadow: true enableShadow: true
screen: parentScreen screen: parentScreen
@@ -163,88 +193,286 @@ DankModal {
onBackgroundClicked: () => close() onBackgroundClicked: () => close()
onOpened: () => { onOpened: () => {
updateVisibleActions() updateVisibleActions()
selectedIndex = getDefaultActionIndex() const defaultIndex = getDefaultActionIndex()
if (SettingsData.powerMenuGridLayout) {
selectedRow = Math.floor(defaultIndex / gridColumns)
selectedCol = defaultIndex % gridColumns
selectedIndex = defaultIndex
} else {
selectedIndex = defaultIndex
}
Qt.callLater(() => modalFocusScope.forceActiveFocus()) Qt.callLater(() => modalFocusScope.forceActiveFocus())
} }
Component.onCompleted: updateVisibleActions() Component.onCompleted: updateVisibleActions()
modalFocusScope.Keys.onPressed: event => { modalFocusScope.Keys.onPressed: event => {
switch (event.key) { if (SettingsData.powerMenuGridLayout) {
case Qt.Key_Up: handleGridNavigation(event)
case Qt.Key_Backtab: } else {
selectedIndex = (selectedIndex - 1 + visibleActions.length) % visibleActions.length handleListNavigation(event)
event.accepted = true
break
case Qt.Key_Down:
case Qt.Key_Tab:
selectedIndex = (selectedIndex + 1) % visibleActions.length
event.accepted = true
break
case Qt.Key_Return:
case Qt.Key_Enter:
selectOption(getActionAtIndex(selectedIndex))
event.accepted = true
break
case Qt.Key_N:
if (event.modifiers & Qt.ControlModifier) {
selectedIndex = (selectedIndex + 1) % visibleActions.length
event.accepted = true
}
break
case Qt.Key_P:
if (!(event.modifiers & Qt.ControlModifier)) {
selectOption("poweroff")
event.accepted = true
} else {
selectedIndex = (selectedIndex - 1 + visibleActions.length) % visibleActions.length
event.accepted = true
}
break
case Qt.Key_J:
if (event.modifiers & Qt.ControlModifier) {
selectedIndex = (selectedIndex + 1) % visibleActions.length
event.accepted = true
}
break
case Qt.Key_K:
if (event.modifiers & Qt.ControlModifier) {
selectedIndex = (selectedIndex - 1 + visibleActions.length) % visibleActions.length
event.accepted = true
}
break
case Qt.Key_R:
selectOption("reboot")
event.accepted = true
break
case Qt.Key_X:
selectOption("logout")
event.accepted = true
break
case Qt.Key_L:
selectOption("lock")
event.accepted = true
break
case Qt.Key_S:
selectOption("suspend")
event.accepted = true
break
case Qt.Key_H:
selectOption("hibernate")
event.accepted = true
break
case Qt.Key_D:
selectOption("restart")
event.accepted = true
break
} }
} }
function handleListNavigation(event) {
switch (event.key) {
case Qt.Key_Up:
case Qt.Key_Backtab:
selectedIndex = (selectedIndex - 1 + visibleActions.length) % visibleActions.length
event.accepted = true
break
case Qt.Key_Down:
case Qt.Key_Tab:
selectedIndex = (selectedIndex + 1) % visibleActions.length
event.accepted = true
break
case Qt.Key_Return:
case Qt.Key_Enter:
selectOption(getActionAtIndex(selectedIndex))
event.accepted = true
break
case Qt.Key_N:
if (event.modifiers & Qt.ControlModifier) {
selectedIndex = (selectedIndex + 1) % visibleActions.length
event.accepted = true
}
break
case Qt.Key_P:
if (!(event.modifiers & Qt.ControlModifier)) {
selectOption("poweroff")
event.accepted = true
} else {
selectedIndex = (selectedIndex - 1 + visibleActions.length) % visibleActions.length
event.accepted = true
}
break
case Qt.Key_J:
if (event.modifiers & Qt.ControlModifier) {
selectedIndex = (selectedIndex + 1) % visibleActions.length
event.accepted = true
}
break
case Qt.Key_K:
if (event.modifiers & Qt.ControlModifier) {
selectedIndex = (selectedIndex - 1 + visibleActions.length) % visibleActions.length
event.accepted = true
}
break
case Qt.Key_R:
selectOption("reboot")
event.accepted = true
break
case Qt.Key_X:
selectOption("logout")
event.accepted = true
break
case Qt.Key_L:
selectOption("lock")
event.accepted = true
break
case Qt.Key_S:
selectOption("suspend")
event.accepted = true
break
case Qt.Key_H:
selectOption("hibernate")
event.accepted = true
break
case Qt.Key_D:
selectOption("restart")
event.accepted = true
break
}
}
function handleGridNavigation(event) {
switch (event.key) {
case Qt.Key_Left:
selectedCol = (selectedCol - 1 + gridColumns) % gridColumns
selectedIndex = selectedRow * gridColumns + selectedCol
event.accepted = true
break
case Qt.Key_Right:
selectedCol = (selectedCol + 1) % gridColumns
selectedIndex = selectedRow * gridColumns + selectedCol
event.accepted = true
break
case Qt.Key_Up:
case Qt.Key_Backtab:
selectedRow = (selectedRow - 1 + gridRows) % gridRows
selectedIndex = selectedRow * gridColumns + selectedCol
event.accepted = true
break
case Qt.Key_Down:
case Qt.Key_Tab:
selectedRow = (selectedRow + 1) % gridRows
selectedIndex = selectedRow * gridColumns + selectedCol
event.accepted = true
break
case Qt.Key_Return:
case Qt.Key_Enter:
selectOption(getActionAtIndex(selectedIndex))
event.accepted = true
break
case Qt.Key_N:
if (event.modifiers & Qt.ControlModifier) {
selectedCol = (selectedCol + 1) % gridColumns
selectedIndex = selectedRow * gridColumns + selectedCol
event.accepted = true
}
break
case Qt.Key_P:
if (!(event.modifiers & Qt.ControlModifier)) {
selectOption("poweroff")
event.accepted = true
} else {
selectedCol = (selectedCol - 1 + gridColumns) % gridColumns
selectedIndex = selectedRow * gridColumns + selectedCol
event.accepted = true
}
break
case Qt.Key_J:
if (event.modifiers & Qt.ControlModifier) {
selectedRow = (selectedRow + 1) % gridRows
selectedIndex = selectedRow * gridColumns + selectedCol
event.accepted = true
}
break
case Qt.Key_K:
if (event.modifiers & Qt.ControlModifier) {
selectedRow = (selectedRow - 1 + gridRows) % gridRows
selectedIndex = selectedRow * gridColumns + selectedCol
event.accepted = true
}
break
case Qt.Key_R:
selectOption("reboot")
event.accepted = true
break
case Qt.Key_X:
selectOption("logout")
event.accepted = true
break
case Qt.Key_L:
selectOption("lock")
event.accepted = true
break
case Qt.Key_S:
selectOption("suspend")
event.accepted = true
break
case Qt.Key_H:
selectOption("hibernate")
event.accepted = true
break
case Qt.Key_D:
selectOption("restart")
event.accepted = true
break
}
}
content: Component { content: Component {
Item { Item {
anchors.fill: parent anchors.fill: parent
implicitHeight: buttonColumn.implicitHeight + Theme.spacingL * 2 implicitHeight: SettingsData.powerMenuGridLayout
? buttonGrid.implicitHeight + Theme.spacingL * 2
: buttonColumn.implicitHeight + Theme.spacingL * 2
Grid {
id: buttonGrid
visible: SettingsData.powerMenuGridLayout
anchors.centerIn: parent
columns: root.gridColumns
columnSpacing: Theme.spacingS
rowSpacing: Theme.spacingS
Repeater {
model: root.visibleActions
Rectangle {
required property int index
required property string modelData
readonly property var actionData: root.getActionData(modelData)
readonly property bool isSelected: root.selectedIndex === index
readonly property bool showWarning: modelData === "reboot" || modelData === "poweroff"
width: (root.width - Theme.spacingL * 2 - Theme.spacingS * (root.gridColumns - 1)) / root.gridColumns
height: 100
radius: Theme.cornerRadius
color: {
if (isSelected)
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
if (mouseArea.containsMouse)
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08)
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08)
}
border.color: isSelected ? Theme.primary : "transparent"
border.width: isSelected ? 2 : 0
Column {
anchors.centerIn: parent
spacing: Theme.spacingS
DankIcon {
name: parent.parent.actionData.icon
size: Theme.iconSize + 8
color: {
if (parent.parent.showWarning && mouseArea.containsMouse) {
return parent.parent.modelData === "poweroff" ? Theme.error : Theme.warning
}
return Theme.surfaceText
}
anchors.horizontalCenter: parent.horizontalCenter
}
StyledText {
text: parent.parent.actionData.label
font.pixelSize: Theme.fontSizeMedium
color: {
if (parent.parent.showWarning && mouseArea.containsMouse) {
return parent.parent.modelData === "poweroff" ? Theme.error : Theme.warning
}
return Theme.surfaceText
}
font.weight: Font.Medium
anchors.horizontalCenter: parent.horizontalCenter
}
Rectangle {
width: 20
height: 16
radius: 4
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.1)
anchors.horizontalCenter: parent.horizontalCenter
StyledText {
text: parent.parent.parent.actionData.key
font.pixelSize: Theme.fontSizeSmall - 1
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
font.weight: Font.Medium
anchors.centerIn: parent
}
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
root.selectedRow = Math.floor(index / root.gridColumns)
root.selectedCol = index % root.gridColumns
root.selectOption(modelData)
}
}
}
}
}
Column { Column {
id: buttonColumn id: buttonColumn
visible: !SettingsData.powerMenuGridLayout
anchors { anchors {
left: parent.left left: parent.left
right: parent.right right: parent.right
@@ -271,7 +499,7 @@ DankModal {
color: { color: {
if (isSelected) if (isSelected)
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
if (mouseArea.containsMouse) if (listMouseArea.containsMouse)
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08)
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08) return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08)
} }
@@ -292,7 +520,7 @@ DankModal {
name: parent.parent.actionData.icon name: parent.parent.actionData.icon
size: Theme.iconSize + 4 size: Theme.iconSize + 4
color: { color: {
if (parent.parent.showWarning && mouseArea.containsMouse) { if (parent.parent.showWarning && listMouseArea.containsMouse) {
return parent.parent.modelData === "poweroff" ? Theme.error : Theme.warning return parent.parent.modelData === "poweroff" ? Theme.error : Theme.warning
} }
return Theme.surfaceText return Theme.surfaceText
@@ -304,7 +532,7 @@ DankModal {
text: parent.parent.actionData.label text: parent.parent.actionData.label
font.pixelSize: Theme.fontSizeMedium font.pixelSize: Theme.fontSizeMedium
color: { color: {
if (parent.parent.showWarning && mouseArea.containsMouse) { if (parent.parent.showWarning && listMouseArea.containsMouse) {
return parent.parent.modelData === "poweroff" ? Theme.error : Theme.warning return parent.parent.modelData === "poweroff" ? Theme.error : Theme.warning
} }
return Theme.surfaceText return Theme.surfaceText
@@ -335,7 +563,7 @@ DankModal {
} }
MouseArea { MouseArea {
id: mouseArea id: listMouseArea
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor

View File

@@ -367,6 +367,14 @@ Item {
wrapMode: Text.Wrap wrapMode: Text.Wrap
} }
DankToggle {
width: parent.width
text: I18n.tr("Use Grid Layout")
description: I18n.tr("Display power menu actions in a grid instead of a list")
checked: SettingsData.powerMenuGridLayout
onToggled: checked => SettingsData.set("powerMenuGridLayout", checked)
}
DankDropdown { DankDropdown {
id: defaultActionDropdown id: defaultActionDropdown
width: parent.width width: parent.width