SessionMenuTab: added tab & SessionMenu settings

SessionMenu: hook up settings, update height calculation
i18n: add translations
This commit is contained in:
Ly-sec
2025-11-12 19:46:19 +01:00
parent 4f6ed4335a
commit dbedf6c25c
16 changed files with 675 additions and 19 deletions
+31
View File
@@ -1348,6 +1348,37 @@
},
"title": "Sperrbildschirm"
},
"session-menu": {
"countdown-duration": {
"description": "Legen Sie fest, wie lange der Countdown-Timer vor der Ausführung von Energieaktionen dauert.",
"label": "Countdown-Dauer"
},
"enable-countdown": {
"description": "Zeige einen Countdown-Timer vor der Ausführung von Energieaktionen.",
"label": "Countdown-Timer aktivieren"
},
"position": {
"description": "Wählen Sie, wo das Sitzungsmenü-Panel beim Öffnen erscheint.",
"label": "Position"
},
"show-header": {
"description": "Zeige den Titel und die Schließen-Schaltfläche oben im Sitzungsmenü.",
"label": "Kopfzeile anzeigen"
},
"entries": {
"section": {
"description": "Anpassen, welche Energieaktionen im Sitzungsmenü angezeigt werden und in welcher Reihenfolge.",
"label": "Energieaktionen"
}
},
"general": {
"section": {
"description": "Konfigurieren Sie das Verhalten und Erscheinungsbild des Sitzungsmenü-Panels.",
"label": "Allgemein"
}
},
"title": "Sitzungsmenü"
},
"network": {
"bluetooth": {
"label": "Bluetooth aktivieren"
+31
View File
@@ -1351,6 +1351,37 @@
},
"title": "Lock screen"
},
"session-menu": {
"countdown-duration": {
"description": "Set how long the countdown timer lasts before executing power actions.",
"label": "Countdown duration"
},
"enable-countdown": {
"description": "Show a countdown timer before executing power actions.",
"label": "Enable countdown timer"
},
"position": {
"description": "Choose where the Session Menu panel appears when opened.",
"label": "Position"
},
"show-header": {
"description": "Display the title and close button at the top of the Session Menu.",
"label": "Show header"
},
"entries": {
"section": {
"description": "Customize which power actions appear in the Session Menu and in what order.",
"label": "Power actions"
}
},
"general": {
"section": {
"description": "Configure the Session Menu panel behavior and appearance.",
"label": "General"
}
},
"title": "Session Menu"
},
"network": {
"bluetooth": {
"label": "Enable Bluetooth"
+31
View File
@@ -1348,6 +1348,37 @@
},
"title": "Pantalla de bloqueo"
},
"session-menu": {
"countdown-duration": {
"description": "Establecer cuánto tiempo dura el temporizador de cuenta regresiva antes de ejecutar acciones de energía.",
"label": "Duración de la cuenta regresiva"
},
"enable-countdown": {
"description": "Mostrar un temporizador de cuenta regresiva antes de ejecutar acciones de energía.",
"label": "Activar temporizador de cuenta regresiva"
},
"position": {
"description": "Elige dónde aparece el panel del Menú de sesión cuando se abre.",
"label": "Posición"
},
"show-header": {
"description": "Mostrar el título y el botón de cerrar en la parte superior del Menú de sesión.",
"label": "Mostrar encabezado"
},
"entries": {
"section": {
"description": "Personalizar qué acciones de energía aparecen en el Menú de sesión y en qué orden.",
"label": "Acciones de energía"
}
},
"general": {
"section": {
"description": "Configurar el comportamiento y la apariencia del panel del menú de sesión.",
"label": "General"
}
},
"title": "Menú de sesión"
},
"network": {
"bluetooth": {
"label": "Activar Bluetooth"
+31
View File
@@ -1348,6 +1348,37 @@
},
"title": "Écran de verrouillage"
},
"session-menu": {
"countdown-duration": {
"description": "Définir la durée du compte à rebours avant d'exécuter les actions d'alimentation.",
"label": "Durée du compte à rebours"
},
"enable-countdown": {
"description": "Afficher un compte à rebours avant d'exécuter les actions d'alimentation.",
"label": "Activer le compte à rebours"
},
"position": {
"description": "Choisissez où le panneau du Menu de session apparaît lorsqu'il est ouvert.",
"label": "Position"
},
"show-header": {
"description": "Afficher le titre et le bouton de fermeture en haut du Menu de session.",
"label": "Afficher l'en-tête"
},
"entries": {
"section": {
"description": "Personnaliser les actions d'alimentation qui apparaissent dans le Menu de session et leur ordre.",
"label": "Actions d'alimentation"
}
},
"general": {
"section": {
"description": "Configurer le comportement et l'apparence du panneau du menu de session.",
"label": "Général"
}
},
"title": "Menu de session"
},
"network": {
"bluetooth": {
"label": "Activer le Bluetooth"
+31
View File
@@ -1348,6 +1348,37 @@
},
"title": "Vergrendelscherm"
},
"session-menu": {
"countdown-duration": {
"description": "Stel in hoe lang de afteltimer duurt voordat energieacties worden uitgevoerd.",
"label": "Afteltimer duur"
},
"enable-countdown": {
"description": "Toon een afteltimer voordat energieacties worden uitgevoerd.",
"label": "Afteltimer inschakelen"
},
"position": {
"description": "Kies waar het Sessiemenu-paneel verschijnt wanneer het wordt geopend.",
"label": "Positie"
},
"show-header": {
"description": "Toon de titel en sluitknop bovenaan het Sessiemenu.",
"label": "Koptekst tonen"
},
"entries": {
"section": {
"description": "Aanpassen welke energieacties in het Sessiemenu verschijnen en in welke volgorde.",
"label": "Energieacties"
}
},
"general": {
"section": {
"description": "Configureer het gedrag en uiterlijk van het sessiemenu-paneel.",
"label": "Algemeen"
}
},
"title": "Sessiemenu"
},
"network": {
"bluetooth": {
"label": "Bluetooth inschakelen"
+31
View File
@@ -1348,6 +1348,37 @@
},
"title": "Tela de bloqueio"
},
"session-menu": {
"countdown-duration": {
"description": "Definir por quanto tempo o temporizador de contagem regressiva dura antes de executar ações de energia.",
"label": "Duração da contagem regressiva"
},
"enable-countdown": {
"description": "Mostrar um temporizador de contagem regressiva antes de executar ações de energia.",
"label": "Ativar temporizador de contagem regressiva"
},
"position": {
"description": "Escolha onde o painel do Menu de sessão aparece quando aberto.",
"label": "Posição"
},
"show-header": {
"description": "Exibir o título e o botão de fechar na parte superior do Menu de sessão.",
"label": "Mostrar cabeçalho"
},
"entries": {
"section": {
"description": "Personalizar quais ações de energia aparecem no Menu de sessão e em que ordem.",
"label": "Ações de energia"
}
},
"general": {
"section": {
"description": "Configurar o comportamento e a aparência do painel do menu de sessão.",
"label": "Geral"
}
},
"title": "Menu de sessão"
},
"network": {
"bluetooth": {
"label": "Ativar Bluetooth"
+31
View File
@@ -1348,6 +1348,37 @@
},
"title": "Экран блокировки"
},
"session-menu": {
"countdown-duration": {
"description": "Установить, как долго длится таймер обратного отсчета перед выполнением действий питания.",
"label": "Длительность обратного отсчета"
},
"enable-countdown": {
"description": "Показывать таймер обратного отсчета перед выполнением действий питания.",
"label": "Включить таймер обратного отсчета"
},
"position": {
"description": "Выберите, где появляется панель меню сеанса при открытии.",
"label": "Позиция"
},
"show-header": {
"description": "Отображать заголовок и кнопку закрытия в верхней части меню сеанса.",
"label": "Показывать заголовок"
},
"entries": {
"section": {
"description": "Настроить, какие действия питания отображаются в меню сеанса и в каком порядке.",
"label": "Действия питания"
}
},
"general": {
"section": {
"description": "Настройка поведения и внешнего вида панели меню сеанса.",
"label": "Общие"
}
},
"title": "Меню сеанса"
},
"network": {
"bluetooth": {
"label": "Включить Bluetooth"
+31
View File
@@ -1348,6 +1348,37 @@
},
"title": "Ekran Kilit"
},
"session-menu": {
"countdown-duration": {
"description": "Güç eylemlerini çalıştırmadan önce geri sayım zamanlayıcısının ne kadar süreceğini ayarlayın.",
"label": "Geri sayım süresi"
},
"enable-countdown": {
"description": "Güç eylemlerini çalıştırmadan önce bir geri sayım zamanlayıcısı göster.",
"label": "Geri sayım zamanlayıcısını etkinleştir"
},
"position": {
"description": "Oturum Menüsü panelinin açıldığında nerede görüneceğini seçin.",
"label": "Konum"
},
"show-header": {
"description": "Oturum Menüsünün üst kısmında başlığı ve kapat düğmesini göster.",
"label": "Başlığı göster"
},
"entries": {
"section": {
"description": "Oturum Menüsünde hangi güç eylemlerinin görüneceğini ve hangi sırada görüneceğini özelleştirin.",
"label": "Güç eylemleri"
}
},
"general": {
"section": {
"description": "Oturum menüsü panelinin davranışını ve görünümünü yapılandırın.",
"label": "Genel"
}
},
"title": "Oturum Menüsü"
},
"network": {
"bluetooth": {
"label": "Bluetooth'u Etkinleştir"
+31
View File
@@ -1348,6 +1348,37 @@
},
"title": "Екран блокування"
},
"session-menu": {
"countdown-duration": {
"description": "Встановити, як довго триває таймер зворотного відліку перед виконанням дій живлення.",
"label": "Тривалість зворотного відліку"
},
"enable-countdown": {
"description": "Показувати таймер зворотного відліку перед виконанням дій живлення.",
"label": "Увімкнути таймер зворотного відліку"
},
"position": {
"description": "Виберіть, де з'являється панель меню сеансу при відкритті.",
"label": "Позиція"
},
"show-header": {
"description": "Відображати заголовок та кнопку закриття в верхній частині меню сеансу.",
"label": "Показувати заголовок"
},
"entries": {
"section": {
"description": "Налаштувати, які дії живлення відображаються в меню сеансу та в якому порядку.",
"label": "Дії живлення"
}
},
"general": {
"section": {
"description": "Налаштувати поведінку та зовнішній вигляд панелі меню сеансу.",
"label": "Загальні"
}
},
"title": "Меню сеансу"
},
"network": {
"bluetooth": {
"label": "Увімкнути Bluetooth"
+31
View File
@@ -1348,6 +1348,37 @@
},
"title": "锁屏"
},
"session-menu": {
"countdown-duration": {
"description": "设置倒计时计时器在执行电源操作之前持续多长时间。",
"label": "倒计时持续时间"
},
"enable-countdown": {
"description": "在执行电源操作之前显示倒计时计时器。",
"label": "启用倒计时计时器"
},
"position": {
"description": "选择会话菜单面板打开时出现的位置。",
"label": "位置"
},
"show-header": {
"description": "在会话菜单顶部显示标题和关闭按钮。",
"label": "显示标题"
},
"entries": {
"section": {
"description": "自定义会话菜单中显示哪些电源操作及其顺序。",
"label": "电源操作"
}
},
"general": {
"section": {
"description": "配置会话菜单面板的行为和外观。",
"label": "常规"
}
},
"title": "会话菜单"
},
"network": {
"bluetooth": {
"label": "启用蓝牙"
+28
View File
@@ -204,6 +204,34 @@
"network": {
"wifiEnabled": true
},
"sessionMenu": {
"enableCountdown": true,
"countdownDuration": 10000,
"position": "center",
"showHeader": true,
"powerOptions": [
{
"action": "lock",
"enabled": true
},
{
"action": "suspend",
"enabled": true
},
{
"action": "reboot",
"enabled": true
},
{
"action": "logout",
"enabled": true
},
{
"action": "shutdown",
"enabled": true
}
]
},
"notifications": {
"enabled": true,
"monitors": [],
+1
View File
@@ -122,6 +122,7 @@ Singleton {
"settings-osd": "picture-in-picture",
"settings-about": "info-square-rounded",
"settings-lock-screen": "lock",
"settings-session-menu": "power",
"bluetooth": "bluetooth",
"bt-device-generic": "bluetooth",
"bt-device-headphones": "headphones",
+24
View File
@@ -339,6 +339,30 @@ Singleton {
property bool wifiEnabled: true
}
// session menu
property JsonObject sessionMenu: JsonObject {
property bool enableCountdown: true
property int countdownDuration: 10000
property string position: "center"
property bool showHeader: true
property list<var> powerOptions: [{
"action": "lock",
"enabled": true
}, {
"action": "suspend",
"enabled": true
}, {
"action": "reboot",
"enabled": true
}, {
"action": "logout",
"enabled": true
}, {
"action": "shutdown",
"enabled": true
}]
}
// notifications
property JsonObject notifications: JsonObject {
property bool enabled: true
+75 -19
View File
@@ -16,41 +16,89 @@ SmartPanel {
id: root
preferredWidth: Math.round(400 * Style.uiScaleRatio)
preferredHeight: Math.round(340 * Style.uiScaleRatio)
preferredHeight: {
var headerHeight = Settings.data.sessionMenu.showHeader ? Style.baseWidgetSize * 0.6 : 0
panelAnchorHorizontalCenter: true
panelAnchorVerticalCenter: true
var dividerHeight = Settings.data.sessionMenu.showHeader ? Style.marginS : 0
var buttonHeight = Style.baseWidgetSize * 1.3 * Style.uiScaleRatio
var buttonSpacing = Style.marginS
var enabledCount = powerOptions.length
var headerSpacing = Settings.data.sessionMenu.showHeader ? (Style.marginL * 2) : 0
var baseHeight = (Style.marginL * 4) + headerHeight + dividerHeight + headerSpacing
var buttonsHeight = enabledCount > 0 ? (buttonHeight * enabledCount) + (buttonSpacing * (enabledCount - 1)) : 0
return Math.round(baseHeight + buttonsHeight)
}
// Positioning
readonly property string panelPosition: Settings.data.sessionMenu.position
panelAnchorHorizontalCenter: panelPosition === "center" || panelPosition.endsWith("_center")
panelAnchorVerticalCenter: panelPosition === "center"
panelAnchorLeft: panelPosition !== "center" && panelPosition.endsWith("_left")
panelAnchorRight: panelPosition !== "center" && panelPosition.endsWith("_right")
panelAnchorBottom: panelPosition.startsWith("bottom_")
panelAnchorTop: panelPosition.startsWith("top_")
// Timer properties
property int timerDuration: 9000 // 9 seconds
readonly property int timerDuration: Settings.data.sessionMenu.countdownDuration
property string pendingAction: ""
property bool timerActive: false
property int timeRemaining: 0
// Navigation properties
property int selectedIndex: 0
readonly property var powerOptions: [{
"action": "lock",
// Action metadata mapping
readonly property var actionMetadata: {
"lock": {
"icon": "lock",
"title": I18n.tr("session-menu.lock")
}, {
"action": "suspend",
"title": I18n.tr("session-menu.lock"),
"isShutdown": false
},
"suspend": {
"icon": "suspend",
"title": I18n.tr("session-menu.suspend")
}, {
"action": "reboot",
"title": I18n.tr("session-menu.suspend"),
"isShutdown": false
},
"reboot": {
"icon": "reboot",
"title": I18n.tr("session-menu.reboot")
}, {
"action": "logout",
"title": I18n.tr("session-menu.reboot"),
"isShutdown": false
},
"logout": {
"icon": "logout",
"title": I18n.tr("session-menu.logout")
}, {
"action": "shutdown",
"title": I18n.tr("session-menu.logout"),
"isShutdown": false
},
"shutdown": {
"icon": "shutdown",
"title": I18n.tr("session-menu.shutdown"),
"isShutdown": true
}]
}
}
// Build powerOptions from settings, filtering enabled ones and adding metadata
readonly property var powerOptions: (function () {
var options = []
var settingsOptions = Settings.data.sessionMenu.powerOptions || []
for (var i = 0; i < settingsOptions.length; i++) {
var settingOption = settingsOptions[i]
if (settingOption.enabled && actionMetadata[settingOption.action]) {
var metadata = actionMetadata[settingOption.action]
options.push({
"action": settingOption.action,
"icon": metadata.icon,
"title": metadata.title,
"isShutdown": metadata.isShutdown
})
}
}
return options
})()
// Lifecycle handlers
onOpened: {
@@ -64,6 +112,12 @@ SmartPanel {
// Timer management
function startTimer(action) {
// If countdown is disabled, execute immediately
if (!Settings.data.sessionMenu.enableCountdown) {
executeAction(action)
return
}
if (timerActive && pendingAction === action) {
// Second click - execute immediately
executeAction(action)
@@ -245,6 +299,7 @@ SmartPanel {
// Header with title and close button
RowLayout {
visible: Settings.data.sessionMenu.showHeader
Layout.fillWidth: true
Layout.preferredHeight: Style.baseWidgetSize * 0.6
@@ -283,6 +338,7 @@ SmartPanel {
}
NDivider {
visible: Settings.data.sessionMenu.showHeader
Layout.fillWidth: true
}
+10
View File
@@ -76,6 +76,7 @@ SmartPanel {
Network,
Notifications,
ScreenRecorder,
SessionMenu,
UserInterface,
Wallpaper
}
@@ -161,6 +162,10 @@ SmartPanel {
id: lockScreenTab
LockScreenTab {}
}
Component {
id: sessionMenuTab
SessionMenuTab {}
}
// Order *DOES* matter
function updateTabsModel() {
@@ -249,6 +254,11 @@ SmartPanel {
"label": "settings.hooks.title",
"icon": "settings-hooks",
"source": hooksTab
}, {
"id": SettingsPanel.Tab.SessionMenu,
"label": "settings.session-menu.title",
"icon": "settings-session-menu",
"source": sessionMenuTab
}, {
"id": SettingsPanel.Tab.About,
"label": "settings.about.title",
@@ -0,0 +1,227 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import qs.Commons
import qs.Services.UI
import qs.Widgets
ColumnLayout {
id: root
spacing: Style.marginL
property list<var> entriesModel: []
property list<var> entriesDefault: [{
"id": "lock",
"text": I18n.tr("session-menu.lock"),
"enabled": true,
"required": false
}, {
"id": "suspend",
"text": I18n.tr("session-menu.suspend"),
"enabled": true,
"required": false
}, {
"id": "reboot",
"text": I18n.tr("session-menu.reboot"),
"enabled": true,
"required": false
}, {
"id": "logout",
"text": I18n.tr("session-menu.logout"),
"enabled": true,
"required": false
}, {
"id": "shutdown",
"text": I18n.tr("session-menu.shutdown"),
"enabled": true,
"required": false
}]
// Handler for drag start - disables panel background clicks
function handleDragStart() {
var currentScreen = Quickshell.screens && Quickshell.screens.length > 0 ? Quickshell.screens[0] : null
if (currentScreen) {
var panel = PanelService.getPanel("settingsPanel", currentScreen)
if (panel && panel.disableBackgroundClick) {
panel.disableBackgroundClick()
}
}
}
// Handler for drag end - re-enables panel background clicks
function handleDragEnd() {
var currentScreen = Quickshell.screens && Quickshell.screens.length > 0 ? Quickshell.screens[0] : null
if (currentScreen) {
var panel = PanelService.getPanel("settingsPanel", currentScreen)
if (panel && panel.enableBackgroundClick) {
panel.enableBackgroundClick()
}
}
}
function saveEntries() {
var toSave = []
for (var i = 0; i < entriesModel.length; i++) {
toSave.push({
"action": entriesModel[i].id,
"enabled": entriesModel[i].enabled
})
}
Settings.data.sessionMenu.powerOptions = toSave
}
Component.onCompleted: {
entriesModel = []
// Add the entries available in settings
for (var i = 0; i < Settings.data.sessionMenu.powerOptions.length; i++) {
const settingEntry = Settings.data.sessionMenu.powerOptions[i]
for (var j = 0; j < entriesDefault.length; j++) {
if (settingEntry.action === entriesDefault[j].id) {
var entry = entriesDefault[j]
entry.enabled = settingEntry.enabled
entriesModel.push(entry)
}
}
}
// Add any missing entries from default
for (var i = 0; i < entriesDefault.length; i++) {
var found = false
for (var j = 0; j < entriesModel.length; j++) {
if (entriesModel[j].id === entriesDefault[i].id) {
found = true
break
}
}
if (!found) {
var entry = entriesDefault[i]
entriesModel.push(entry)
}
}
saveEntries()
}
NHeader {
label: I18n.tr("settings.session-menu.general.section.label")
description: I18n.tr("settings.session-menu.general.section.description")
}
NComboBox {
label: I18n.tr("settings.session-menu.position.label")
description: I18n.tr("settings.session-menu.position.description")
Layout.fillWidth: true
model: [{
"key": "center",
"name": I18n.tr("options.control-center.position.center")
}, {
"key": "top_center",
"name": I18n.tr("options.control-center.position.top_center")
}, {
"key": "top_left",
"name": I18n.tr("options.control-center.position.top_left")
}, {
"key": "top_right",
"name": I18n.tr("options.control-center.position.top_right")
}, {
"key": "bottom_center",
"name": I18n.tr("options.control-center.position.bottom_center")
}, {
"key": "bottom_left",
"name": I18n.tr("options.control-center.position.bottom_left")
}, {
"key": "bottom_right",
"name": I18n.tr("options.control-center.position.bottom_right")
}]
currentKey: Settings.data.sessionMenu.position
onSelected: function (key) {
Settings.data.sessionMenu.position = key
}
}
NToggle {
Layout.fillWidth: true
label: I18n.tr("settings.session-menu.show-header.label")
description: I18n.tr("settings.session-menu.show-header.description")
checked: Settings.data.sessionMenu.showHeader
onToggled: checked => Settings.data.sessionMenu.showHeader = checked
}
NToggle {
Layout.fillWidth: true
label: I18n.tr("settings.session-menu.enable-countdown.label")
description: I18n.tr("settings.session-menu.enable-countdown.description")
checked: Settings.data.sessionMenu.enableCountdown
onToggled: checked => Settings.data.sessionMenu.enableCountdown = checked
}
ColumnLayout {
visible: Settings.data.sessionMenu.enableCountdown
spacing: Style.marginXXS
Layout.fillWidth: true
NLabel {
label: I18n.tr("settings.session-menu.countdown-duration.label")
description: I18n.tr("settings.session-menu.countdown-duration.description")
}
NValueSlider {
Layout.fillWidth: true
from: 1000
to: 30000
stepSize: 1000
value: Settings.data.sessionMenu.countdownDuration
onMoved: value => Settings.data.sessionMenu.countdownDuration = value
text: Math.round(Settings.data.sessionMenu.countdownDuration / 1000) + "s"
}
}
NDivider {
Layout.fillWidth: true
Layout.topMargin: Style.marginL
Layout.bottomMargin: Style.marginL
}
// Entries Management Section
ColumnLayout {
spacing: Style.marginXXS
Layout.fillWidth: true
NHeader {
label: I18n.tr("settings.session-menu.entries.section.label")
description: I18n.tr("settings.session-menu.entries.section.description")
}
NReorderCheckboxes {
Layout.fillWidth: true
model: entriesModel
disabledIds: []
onDragPotentialStarted: {
root.handleDragStart()
}
onDragPotentialEnded: {
root.handleDragEnd()
}
onItemToggled: function (index, enabled) {
var newModel = entriesModel.slice()
newModel[index] = Object.assign({}, newModel[index], {
"enabled": enabled
})
entriesModel = newModel
saveEntries()
}
onItemsReordered: function (fromIndex, toIndex) {
var newModel = entriesModel.slice()
var item = newModel.splice(fromIndex, 1)[0]
newModel.splice(toIndex, 0, item)
entriesModel = newModel
saveEntries()
}
}
}
}