mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
@@ -88,7 +88,7 @@ SmartPanel {
|
||||
}
|
||||
|
||||
// Build powerOptions from settings, filtering enabled ones and adding metadata
|
||||
readonly property var powerOptions: (function () {
|
||||
property var powerOptions: {
|
||||
var options = []
|
||||
var settingsOptions = Settings.data.sessionMenu.powerOptions || []
|
||||
|
||||
@@ -100,13 +100,39 @@ SmartPanel {
|
||||
"action": settingOption.action,
|
||||
"icon": metadata.icon,
|
||||
"title": metadata.title,
|
||||
"isShutdown": metadata.isShutdown
|
||||
"isShutdown": metadata.isShutdown,
|
||||
"countdownEnabled": settingOption.countdownEnabled !== undefined ? settingOption.countdownEnabled : true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return options
|
||||
})()
|
||||
}
|
||||
|
||||
// Update powerOptions when settings change
|
||||
Connections {
|
||||
target: Settings.data.sessionMenu
|
||||
function onPowerOptionsChanged() {
|
||||
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,
|
||||
"countdownEnabled": settingOption.countdownEnabled !== undefined ? settingOption.countdownEnabled : true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
root.powerOptions = options
|
||||
}
|
||||
}
|
||||
|
||||
// Lifecycle handlers
|
||||
onOpened: {
|
||||
@@ -120,12 +146,27 @@ SmartPanel {
|
||||
|
||||
// Timer management
|
||||
function startTimer(action) {
|
||||
// If countdown is disabled, execute immediately
|
||||
// Check if global countdown is disabled
|
||||
if (!Settings.data.sessionMenu.enableCountdown) {
|
||||
executeAction(action)
|
||||
return
|
||||
}
|
||||
|
||||
// Check per-item countdown setting
|
||||
var option = null
|
||||
for (var i = 0; i < powerOptions.length; i++) {
|
||||
if (powerOptions[i].action === action) {
|
||||
option = powerOptions[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// If this specific action has countdown disabled, execute immediately
|
||||
if (option && option.countdownEnabled === false) {
|
||||
executeAction(action)
|
||||
return
|
||||
}
|
||||
|
||||
if (timerActive && pendingAction === action) {
|
||||
// Second click - execute immediately
|
||||
executeAction(action)
|
||||
|
||||
@@ -49,7 +49,8 @@ ColumnLayout {
|
||||
for (var i = 0; i < entriesModel.length; i++) {
|
||||
toSave.push({
|
||||
"action": entriesModel[i].id,
|
||||
"enabled": entriesModel[i].enabled
|
||||
"enabled": entriesModel[i].enabled,
|
||||
"countdownEnabled": entriesModel[i].countdownEnabled !== undefined ? entriesModel[i].countdownEnabled : true
|
||||
})
|
||||
}
|
||||
Settings.data.sessionMenu.powerOptions = toSave
|
||||
@@ -66,6 +67,8 @@ ColumnLayout {
|
||||
if (settingEntry.action === entriesDefault[j].id) {
|
||||
var entry = entriesDefault[j]
|
||||
entry.enabled = settingEntry.enabled
|
||||
// Default countdownEnabled to true for backward compatibility
|
||||
entry.countdownEnabled = settingEntry.countdownEnabled !== undefined ? settingEntry.countdownEnabled : true
|
||||
entriesModel.push(entry)
|
||||
}
|
||||
}
|
||||
@@ -83,6 +86,8 @@ ColumnLayout {
|
||||
|
||||
if (!found) {
|
||||
var entry = entriesDefault[i]
|
||||
// Default countdownEnabled to true for new entries
|
||||
entry.countdownEnabled = true
|
||||
entriesModel.push(entry)
|
||||
}
|
||||
}
|
||||
@@ -172,7 +177,7 @@ ColumnLayout {
|
||||
|
||||
// Entries Management Section
|
||||
ColumnLayout {
|
||||
spacing: Style.marginXXS
|
||||
spacing: Style.marginM
|
||||
Layout.fillWidth: true
|
||||
|
||||
NHeader {
|
||||
@@ -180,24 +185,253 @@ ColumnLayout {
|
||||
description: I18n.tr("settings.session-menu.entries.section.description")
|
||||
}
|
||||
|
||||
NReorderCheckboxes {
|
||||
// List of items
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
model: entriesModel
|
||||
disabledIds: []
|
||||
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()
|
||||
implicitHeight: listView.contentHeight
|
||||
|
||||
ListView {
|
||||
id: listView
|
||||
anchors.fill: parent
|
||||
spacing: Style.marginS
|
||||
interactive: false
|
||||
clip: true
|
||||
model: entriesModel
|
||||
|
||||
// Store reference to root's saveEntries function
|
||||
property var saveEntriesFunc: root.saveEntries
|
||||
|
||||
delegate: Item {
|
||||
id: delegateItem
|
||||
width: listView.width
|
||||
height: contentRow.height
|
||||
|
||||
required property int index
|
||||
required property var modelData
|
||||
|
||||
property bool dragging: false
|
||||
property int dragStartY: 0
|
||||
property int dragStartIndex: -1
|
||||
property int dragTargetIndex: -1
|
||||
property var saveEntriesFunc: listView.saveEntriesFunc
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: Style.radiusM
|
||||
color: delegateItem.dragging ? Color.mSurfaceVariant : Color.transparent
|
||||
border.color: delegateItem.dragging ? Color.mOutline : Color.transparent
|
||||
border.width: Style.borderS
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: contentRow
|
||||
width: parent.width
|
||||
spacing: Style.marginS
|
||||
|
||||
// Drag handle
|
||||
Rectangle {
|
||||
Layout.preferredWidth: Style.baseWidgetSize * 0.7
|
||||
Layout.preferredHeight: Style.baseWidgetSize * 0.7
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
radius: Style.radiusXS
|
||||
color: dragHandleMouseArea.containsMouse ? Color.mSurfaceVariant : Color.transparent
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: 2
|
||||
|
||||
Repeater {
|
||||
model: 3
|
||||
Rectangle {
|
||||
Layout.preferredWidth: Style.baseWidgetSize * 0.28
|
||||
Layout.preferredHeight: 2
|
||||
radius: 1
|
||||
color: Color.mOutline
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: dragHandleMouseArea
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.SizeVerCursor
|
||||
hoverEnabled: true
|
||||
preventStealing: false
|
||||
z: 1000
|
||||
|
||||
onPressed: mouse => {
|
||||
delegateItem.dragStartIndex = delegateItem.index
|
||||
delegateItem.dragTargetIndex = delegateItem.index
|
||||
delegateItem.dragStartY = delegateItem.y
|
||||
delegateItem.dragging = true
|
||||
delegateItem.z = 999
|
||||
preventStealing = true
|
||||
}
|
||||
|
||||
onPositionChanged: mouse => {
|
||||
if (delegateItem.dragging) {
|
||||
var dy = mouse.y - height / 2
|
||||
var newY = delegateItem.y + dy
|
||||
newY = Math.max(0, Math.min(newY, listView.contentHeight - delegateItem.height))
|
||||
delegateItem.y = newY
|
||||
var targetIndex = Math.floor((newY + delegateItem.height / 2) / (delegateItem.height + Style.marginS))
|
||||
targetIndex = Math.max(0, Math.min(targetIndex, listView.count - 1))
|
||||
delegateItem.dragTargetIndex = targetIndex
|
||||
}
|
||||
}
|
||||
|
||||
onReleased: {
|
||||
preventStealing = false
|
||||
if (delegateItem.dragStartIndex !== -1 && delegateItem.dragTargetIndex !== -1 && delegateItem.dragStartIndex !== delegateItem.dragTargetIndex) {
|
||||
var newModel = entriesModel.slice()
|
||||
var item = newModel.splice(delegateItem.dragStartIndex, 1)[0]
|
||||
newModel.splice(delegateItem.dragTargetIndex, 0, item)
|
||||
entriesModel = newModel
|
||||
root.saveEntries()
|
||||
}
|
||||
delegateItem.dragging = false
|
||||
delegateItem.dragStartIndex = -1
|
||||
delegateItem.dragTargetIndex = -1
|
||||
delegateItem.z = 0
|
||||
}
|
||||
|
||||
onCanceled: {
|
||||
preventStealing = false
|
||||
delegateItem.dragging = false
|
||||
delegateItem.dragStartIndex = -1
|
||||
delegateItem.dragTargetIndex = -1
|
||||
delegateItem.z = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Enable checkbox
|
||||
Rectangle {
|
||||
Layout.preferredWidth: Style.baseWidgetSize * 0.7
|
||||
Layout.preferredHeight: Style.baseWidgetSize * 0.7
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
radius: Style.radiusXS
|
||||
color: modelData.enabled ? Color.mPrimary : Color.mSurface
|
||||
border.color: Color.mOutline
|
||||
border.width: Style.borderS
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Style.animationFast
|
||||
}
|
||||
}
|
||||
|
||||
NIcon {
|
||||
visible: modelData.enabled
|
||||
anchors.centerIn: parent
|
||||
anchors.horizontalCenterOffset: -1
|
||||
icon: "check"
|
||||
color: Color.mOnPrimary
|
||||
pointSize: Math.max(Style.fontSizeXS, Style.baseWidgetSize * 0.35)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
var newModel = entriesModel.slice()
|
||||
newModel[index] = Object.assign({}, newModel[index], {
|
||||
"enabled": !modelData.enabled
|
||||
})
|
||||
entriesModel = newModel
|
||||
root.saveEntries()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Label
|
||||
NText {
|
||||
Layout.fillWidth: true
|
||||
text: modelData.text
|
||||
color: Color.mOnSurface
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
// Countdown toggle with icon (only shown when global countdown is enabled)
|
||||
RowLayout {
|
||||
visible: Settings.data.sessionMenu.enableCountdown
|
||||
spacing: Style.marginXS
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
NIcon {
|
||||
icon: "clock"
|
||||
color: Color.mOnSurfaceVariant
|
||||
pointSize: Style.fontSizeS
|
||||
}
|
||||
|
||||
NToggle {
|
||||
checked: modelData.countdownEnabled !== undefined ? modelData.countdownEnabled : true
|
||||
onToggled: function (checked) {
|
||||
var newModel = entriesModel.slice()
|
||||
newModel[delegateItem.index] = Object.assign({}, newModel[delegateItem.index], {
|
||||
"countdownEnabled": checked
|
||||
})
|
||||
entriesModel = newModel
|
||||
delegateItem.saveEntriesFunc()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Position binding for non-dragging state
|
||||
y: {
|
||||
if (delegateItem.dragging) {
|
||||
return delegateItem.y
|
||||
}
|
||||
|
||||
var draggedIndex = -1
|
||||
var targetIndex = -1
|
||||
for (var i = 0; i < listView.count; i++) {
|
||||
var item = listView.itemAtIndex(i)
|
||||
if (item && item.dragging) {
|
||||
draggedIndex = item.dragStartIndex
|
||||
targetIndex = item.dragTargetIndex
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (draggedIndex !== -1 && targetIndex !== -1 && draggedIndex !== targetIndex) {
|
||||
var currentIndex = delegateItem.index
|
||||
if (draggedIndex < targetIndex) {
|
||||
if (currentIndex > draggedIndex && currentIndex <= targetIndex) {
|
||||
return (currentIndex - 1) * (delegateItem.height + Style.marginS)
|
||||
}
|
||||
} else {
|
||||
if (currentIndex >= targetIndex && currentIndex < draggedIndex) {
|
||||
return (currentIndex + 1) * (delegateItem.height + Style.marginS)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return delegateItem.index * (delegateItem.height + Style.marginS)
|
||||
}
|
||||
|
||||
Behavior on y {
|
||||
enabled: !delegateItem.dragging
|
||||
NumberAnimation {
|
||||
duration: Style.animationNormal
|
||||
easing.type: Easing.OutQuad
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user