mirror of
https://github.com/zoriya/noctalia-shell.git
synced 2026-08-15 18:43:59 +00:00
Merge branch 'main' into open-panel-overlay-layer
This commit is contained in:
@@ -35,7 +35,10 @@ Item {
|
||||
readonly property bool showIcon: (widgetSettings.showIcon !== undefined) ? widgetSettings.showIcon : widgetMetadata.showIcon
|
||||
readonly property string hideMode: (widgetSettings.hideMode !== undefined) ? widgetSettings.hideMode : widgetMetadata.hideMode
|
||||
readonly property string scrollingMode: (widgetSettings.scrollingMode !== undefined) ? widgetSettings.scrollingMode : (widgetMetadata.scrollingMode !== undefined ? widgetMetadata.scrollingMode : "hover")
|
||||
readonly property int widgetWidth: (widgetSettings.width !== undefined) ? widgetSettings.width : Math.max(widgetMetadata.width, screen.width * 0.06)
|
||||
|
||||
// Maximum widget width with user settings support
|
||||
readonly property real maxWidth: (widgetSettings.maxWidth !== undefined) ? widgetSettings.maxWidth : Math.max(widgetMetadata.maxWidth, screen ? screen.width * 0.06 : 0)
|
||||
readonly property bool useFixedWidth: (widgetSettings.useFixedWidth !== undefined) ? widgetSettings.useFixedWidth : widgetMetadata.useFixedWidth
|
||||
|
||||
readonly property bool isVerticalBar: (Settings.data.bar.position === "left" || Settings.data.bar.position === "right")
|
||||
readonly property bool hasFocusedWindow: CompositorService.getFocusedWindow() !== null
|
||||
@@ -43,7 +46,7 @@ Item {
|
||||
readonly property string fallbackIcon: "user-desktop"
|
||||
|
||||
implicitHeight: visible ? (isVerticalBar ? calculatedVerticalDimension() : Style.barHeight) : 0
|
||||
implicitWidth: visible ? (isVerticalBar ? calculatedVerticalDimension() : widgetWidth) : 0
|
||||
implicitWidth: visible ? (isVerticalBar ? calculatedVerticalDimension() : dynamicWidth) : 0
|
||||
|
||||
// "visible": Always Visible, "hidden": Hide When Empty, "transparent": Transparent When Empty
|
||||
visible: hideMode !== "hidden" || hasFocusedWindow
|
||||
@@ -59,6 +62,43 @@ Item {
|
||||
return Math.round((Style.baseWidgetSize - 5) * scaling)
|
||||
}
|
||||
|
||||
function calculateContentWidth() {
|
||||
// Calculate the actual content width based on visible elements
|
||||
var contentWidth = 0
|
||||
var margins = Style.marginS * scaling * 2 // Left and right margins
|
||||
|
||||
// Icon width (if visible)
|
||||
if (showIcon) {
|
||||
contentWidth += 18 * scaling
|
||||
contentWidth += Style.marginS * scaling // Spacing after icon
|
||||
}
|
||||
|
||||
// Text width (use the measured width)
|
||||
contentWidth += fullTitleMetrics.contentWidth
|
||||
|
||||
// Additional small margin for text
|
||||
contentWidth += Style.marginXXS * 2
|
||||
|
||||
// Add container margins
|
||||
contentWidth += margins
|
||||
|
||||
return Math.ceil(contentWidth)
|
||||
}
|
||||
|
||||
// Dynamic width: adapt to content but respect maximum width setting
|
||||
readonly property real dynamicWidth: {
|
||||
// If using fixed width mode, always use maxWidth
|
||||
if (useFixedWidth) {
|
||||
return maxWidth
|
||||
}
|
||||
// Otherwise, adapt to content
|
||||
if (!hasFocusedWindow) {
|
||||
return maxWidth
|
||||
}
|
||||
// Use content width but don't exceed user-set maximum width
|
||||
return Math.min(calculateContentWidth(), maxWidth)
|
||||
}
|
||||
|
||||
function getAppIcon() {
|
||||
try {
|
||||
// Try CompositorService first
|
||||
@@ -117,11 +157,19 @@ Item {
|
||||
visible: root.visible
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: isVerticalBar ? root.width : widgetWidth
|
||||
width: isVerticalBar ? root.width : dynamicWidth
|
||||
height: isVerticalBar ? width : Style.capsuleHeight
|
||||
radius: isVerticalBar ? width / 2 : Style.radiusM
|
||||
color: Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent
|
||||
|
||||
// Smooth width transition
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: Style.animationNormal
|
||||
easing.type: Easing.InOutCubic
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: mainContainer
|
||||
anchors.fill: parent
|
||||
|
||||
@@ -38,8 +38,9 @@ Item {
|
||||
readonly property string visualizerType: (widgetSettings.visualizerType !== undefined && widgetSettings.visualizerType !== "") ? widgetSettings.visualizerType : widgetMetadata.visualizerType
|
||||
readonly property string scrollingMode: (widgetSettings.scrollingMode !== undefined) ? widgetSettings.scrollingMode : widgetMetadata.scrollingMode
|
||||
|
||||
// Fixed width - no expansion
|
||||
readonly property real widgetWidth: Math.max(145, screen.width * 0.06)
|
||||
// Maximum widget width with user settings support
|
||||
readonly property real maxWidth: (widgetSettings.maxWidth !== undefined) ? widgetSettings.maxWidth : Math.max(widgetMetadata.maxWidth, screen ? screen.width * 0.06 : 0)
|
||||
readonly property bool useFixedWidth: (widgetSettings.useFixedWidth !== undefined) ? widgetSettings.useFixedWidth : widgetMetadata.useFixedWidth
|
||||
|
||||
readonly property bool hasActivePlayer: MediaService.currentPlayer !== null
|
||||
readonly property string placeholderText: I18n.tr("bar.widget-settings.media-mini.no-active-player")
|
||||
@@ -60,7 +61,7 @@ Item {
|
||||
}
|
||||
|
||||
implicitHeight: visible ? (isVerticalBar ? calculatedVerticalDimension() : Style.barHeight) : 0
|
||||
implicitWidth: visible ? (isVerticalBar ? calculatedVerticalDimension() : widgetWidth) : 0
|
||||
implicitWidth: visible ? (isVerticalBar ? calculatedVerticalDimension() : dynamicWidth) : 0
|
||||
|
||||
// "visible": Always Visible, "hidden": Hide When Empty, "transparent": Transparent When Empty
|
||||
visible: hideMode !== "hidden" || hasActivePlayer
|
||||
@@ -80,6 +81,49 @@ Item {
|
||||
return Math.round((Style.baseWidgetSize - 5) * scaling)
|
||||
}
|
||||
|
||||
function calculateContentWidth() {
|
||||
// Calculate the actual content width based on visible elements
|
||||
var contentWidth = 0
|
||||
var margins = Style.marginS * scaling * 2 // Left and right margins
|
||||
|
||||
// Icon or album art width
|
||||
if (!hasActivePlayer || !showAlbumArt) {
|
||||
// Icon width
|
||||
contentWidth += Style.fontSizeL * scaling
|
||||
} else if (showAlbumArt && hasActivePlayer) {
|
||||
// Album art width
|
||||
contentWidth += 21 * scaling
|
||||
}
|
||||
|
||||
// Spacing between icon/art and text
|
||||
contentWidth += Style.marginS * scaling
|
||||
|
||||
// Text width (use the measured width)
|
||||
contentWidth += fullTitleMetrics.contentWidth
|
||||
|
||||
// Additional small margin for text
|
||||
contentWidth += Style.marginXXS * 2
|
||||
|
||||
// Add container margins
|
||||
contentWidth += margins
|
||||
|
||||
return Math.ceil(contentWidth)
|
||||
}
|
||||
|
||||
// Dynamic width: adapt to content but respect maximum width setting
|
||||
readonly property real dynamicWidth: {
|
||||
// If using fixed width mode, always use maxWidth
|
||||
if (useFixedWidth) {
|
||||
return maxWidth
|
||||
}
|
||||
// Otherwise, adapt to content
|
||||
if (!hasActivePlayer) {
|
||||
return maxWidth
|
||||
}
|
||||
// Use content width but don't exceed user-set maximum width
|
||||
return Math.min(calculateContentWidth(), maxWidth)
|
||||
}
|
||||
|
||||
// A hidden text element to safely measure the full title width
|
||||
NText {
|
||||
id: fullTitleMetrics
|
||||
@@ -95,11 +139,19 @@ Item {
|
||||
visible: root.visible
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: isVerticalBar ? root.width : (widgetWidth)
|
||||
width: isVerticalBar ? root.width : dynamicWidth
|
||||
height: isVerticalBar ? width : Style.capsuleHeight
|
||||
radius: isVerticalBar ? width / 2 : Style.radiusM
|
||||
color: Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent
|
||||
|
||||
// Smooth width transition
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: Style.animationNormal
|
||||
easing.type: Easing.InOutCubic
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: mainContainer
|
||||
anchors.fill: parent
|
||||
|
||||
+2
-14
@@ -63,7 +63,7 @@ Variants {
|
||||
readonly property int hideAnimationDuration: Style.animationFast
|
||||
readonly property int showAnimationDuration: Style.animationFast
|
||||
readonly property int peekHeight: 1
|
||||
readonly property int iconSize: 36
|
||||
readonly property int iconSize: Math.round(12 + 24 * (Settings.data.dock.size ?? 1))
|
||||
readonly property int floatingMargin: Settings.data.dock.floatingRatio * Style.marginL
|
||||
|
||||
// Bar detection and positioning properties
|
||||
@@ -199,15 +199,9 @@ Variants {
|
||||
color: Color.transparent
|
||||
|
||||
WlrLayershell.namespace: "noctalia-dock-peek"
|
||||
WlrLayershell.exclusionMode: ExclusionMode.Auto // Always exclusive
|
||||
|
||||
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
||||
implicitHeight: peekHeight
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: barAtBottom ? Qt.alpha(Color.mSurface, Settings.data.bar.backgroundOpacity) : Color.transparent
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: peekArea
|
||||
anchors.fill: parent
|
||||
@@ -261,12 +255,6 @@ Variants {
|
||||
}
|
||||
}
|
||||
|
||||
// Rectangle {
|
||||
// anchors.fill: parent
|
||||
// color: "#000FF0"
|
||||
// z: -1
|
||||
// }
|
||||
|
||||
// Wrapper item for scale/opacity animations
|
||||
Item {
|
||||
id: dockContainerWrapper
|
||||
|
||||
@@ -13,8 +13,8 @@ import qs.Widgets
|
||||
NPanel {
|
||||
id: root
|
||||
|
||||
preferredWidth: 320 * Style.uiScaleRatio
|
||||
preferredHeight: 360 * Style.uiScaleRatio
|
||||
preferredWidth: 400 * Style.uiScaleRatio
|
||||
preferredHeight: 340 * Style.uiScaleRatio
|
||||
panelAnchorHorizontalCenter: true
|
||||
panelAnchorVerticalCenter: true
|
||||
panelKeyboardFocus: true
|
||||
@@ -31,10 +31,6 @@ NPanel {
|
||||
"action": "lock",
|
||||
"icon": "lock",
|
||||
"title": I18n.tr("session-menu.lock")
|
||||
}, {
|
||||
"action": "lockAndSuspend",
|
||||
"icon": "lock-pause",
|
||||
"title": I18n.tr("session-menu.lock-and-suspend")
|
||||
}, {
|
||||
"action": "suspend",
|
||||
"icon": "suspend",
|
||||
@@ -96,11 +92,13 @@ NPanel {
|
||||
lockScreen.active = true
|
||||
}
|
||||
break
|
||||
case "lockAndSuspend":
|
||||
CompositorService.lockAndSuspend()
|
||||
break
|
||||
case "suspend":
|
||||
CompositorService.suspend()
|
||||
// Check if we should lock before suspending
|
||||
if (Settings.data.general.lockOnSuspend) {
|
||||
CompositorService.lockAndSuspend()
|
||||
} else {
|
||||
CompositorService.suspend()
|
||||
}
|
||||
break
|
||||
case "reboot":
|
||||
CompositorService.reboot()
|
||||
@@ -263,74 +261,77 @@ NPanel {
|
||||
root.activate()
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
NBox {
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: Style.marginM
|
||||
anchors.leftMargin: Style.marginM
|
||||
anchors.rightMargin: Style.marginM
|
||||
anchors.bottomMargin: Style.marginS
|
||||
spacing: Style.marginXS
|
||||
anchors.margins: Style.marginL
|
||||
|
||||
// Header with title and close button
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Style.baseWidgetSize * 0.6
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginL
|
||||
spacing: Style.marginL
|
||||
|
||||
NText {
|
||||
text: timerActive ? I18n.tr("session-menu.action-in-seconds", {
|
||||
"action": pendingAction.charAt(0).toUpperCase() + pendingAction.slice(1),
|
||||
"seconds": Math.ceil(timeRemaining / 1000)
|
||||
}) : I18n.tr("session-menu.title")
|
||||
font.weight: Style.fontWeightBold
|
||||
pointSize: Style.fontSizeM
|
||||
color: timerActive ? Color.mPrimary : Color.mOnSurface
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
Item {
|
||||
// Header with title and close button
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Layout.preferredHeight: Style.baseWidgetSize * 0.6
|
||||
|
||||
NIconButton {
|
||||
icon: timerActive ? "stop" : "close"
|
||||
tooltipText: timerActive ? I18n.tr("tooltips.cancel-timer") : I18n.tr("tooltips.close")
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
colorBg: timerActive ? Qt.alpha(Color.mError, 0.08) : Color.transparent
|
||||
colorFg: timerActive ? Color.mError : Color.mOnSurface
|
||||
onClicked: {
|
||||
if (timerActive) {
|
||||
cancelTimer()
|
||||
} else {
|
||||
cancelTimer()
|
||||
root.close()
|
||||
NText {
|
||||
text: timerActive ? I18n.tr("session-menu.action-in-seconds", {
|
||||
"action": pendingAction.charAt(0).toUpperCase() + pendingAction.slice(1),
|
||||
"seconds": Math.ceil(timeRemaining / 1000)
|
||||
}) : I18n.tr("session-menu.title")
|
||||
font.weight: Style.fontWeightBold
|
||||
pointSize: Style.fontSizeM
|
||||
color: timerActive ? Color.mPrimary : Color.mOnSurface
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
icon: timerActive ? "stop" : "close"
|
||||
tooltipText: timerActive ? I18n.tr("tooltips.cancel-timer") : I18n.tr("tooltips.close")
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
baseSize: Style.baseWidgetSize * 0.7
|
||||
colorBg: timerActive ? Qt.alpha(Color.mError, 0.08) : Color.transparent
|
||||
colorFg: timerActive ? Color.mError : Color.mOnSurface
|
||||
onClicked: {
|
||||
if (timerActive) {
|
||||
cancelTimer()
|
||||
} else {
|
||||
cancelTimer()
|
||||
root.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NDivider {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
NDivider {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
// Power options
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginS
|
||||
// Power options
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginS
|
||||
|
||||
Repeater {
|
||||
model: powerOptions
|
||||
delegate: PowerButton {
|
||||
Layout.fillWidth: true
|
||||
icon: modelData.icon
|
||||
title: modelData.title
|
||||
isShutdown: modelData.isShutdown || false
|
||||
isSelected: index === selectedIndex
|
||||
onClicked: {
|
||||
selectedIndex = index
|
||||
startTimer(modelData.action)
|
||||
Repeater {
|
||||
model: powerOptions
|
||||
delegate: PowerButton {
|
||||
Layout.fillWidth: true
|
||||
icon: modelData.icon
|
||||
title: modelData.title
|
||||
isShutdown: modelData.isShutdown || false
|
||||
isSelected: index === selectedIndex
|
||||
onClicked: {
|
||||
selectedIndex = index
|
||||
startTimer(modelData.action)
|
||||
}
|
||||
pending: timerActive && pendingAction === modelData.action
|
||||
}
|
||||
pending: timerActive && pendingAction === modelData.action
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -349,7 +350,7 @@ NPanel {
|
||||
|
||||
signal clicked
|
||||
|
||||
height: Style.baseWidgetSize * 1.2 * Style.uiScaleRatio
|
||||
height: Style.baseWidgetSize * 1.3 * Style.uiScaleRatio
|
||||
radius: Style.radiusS
|
||||
color: {
|
||||
if (pending) {
|
||||
|
||||
@@ -17,7 +17,8 @@ ColumnLayout {
|
||||
property bool valueShowIcon: widgetData.showIcon !== undefined ? widgetData.showIcon : widgetMetadata.showIcon
|
||||
property string valueHideMode: "hidden" // Default to 'Hide When Empty'
|
||||
property string valueScrollingMode: widgetData.scrollingMode || widgetMetadata.scrollingMode
|
||||
property int valueWidth: widgetData.width !== undefined ? widgetData.width : widgetMetadata.width
|
||||
property int valueMaxWidth: widgetData.maxWidth !== undefined ? widgetData.maxWidth : widgetMetadata.maxWidth
|
||||
property bool valueUseFixedWidth: widgetData.useFixedWidth !== undefined ? widgetData.useFixedWidth : widgetMetadata.useFixedWidth
|
||||
property bool valueColorizeIcons: widgetData.colorizeIcons !== undefined ? widgetData.colorizeIcons : widgetMetadata.colorizeIcons
|
||||
|
||||
Component.onCompleted: {
|
||||
@@ -31,7 +32,8 @@ ColumnLayout {
|
||||
settings.hideMode = valueHideMode
|
||||
settings.showIcon = valueShowIcon
|
||||
settings.scrollingMode = valueScrollingMode
|
||||
settings.width = parseInt(widthInput.text) || widgetMetadata.width
|
||||
settings.maxWidth = parseInt(widthInput.text) || widgetMetadata.maxWidth
|
||||
settings.useFixedWidth = valueUseFixedWidth
|
||||
settings.colorizeIcons = valueColorizeIcons
|
||||
return settings
|
||||
}
|
||||
@@ -73,10 +75,18 @@ ColumnLayout {
|
||||
NTextInput {
|
||||
id: widthInput
|
||||
Layout.fillWidth: true
|
||||
label: I18n.tr("bar.widget-settings.active-window.width.label")
|
||||
description: I18n.tr("bar.widget-settings.active-window.width.description")
|
||||
placeholderText: widgetMetadata.width
|
||||
text: valueWidth
|
||||
label: I18n.tr("bar.widget-settings.active-window.max-width.label")
|
||||
description: I18n.tr("bar.widget-settings.active-window.max-width.description")
|
||||
placeholderText: widgetMetadata.maxWidth
|
||||
text: valueMaxWidth
|
||||
}
|
||||
|
||||
NToggle {
|
||||
Layout.fillWidth: true
|
||||
label: I18n.tr("bar.widget-settings.active-window.use-fixed-width.label")
|
||||
description: I18n.tr("bar.widget-settings.active-window.use-fixed-width.description")
|
||||
checked: valueUseFixedWidth
|
||||
onToggled: checked => valueUseFixedWidth = checked
|
||||
}
|
||||
|
||||
NComboBox {
|
||||
|
||||
@@ -19,6 +19,8 @@ ColumnLayout {
|
||||
property bool valueShowVisualizer: widgetData.showVisualizer !== undefined ? widgetData.showVisualizer : widgetMetadata.showVisualizer
|
||||
property string valueVisualizerType: widgetData.visualizerType || widgetMetadata.visualizerType
|
||||
property string valueScrollingMode: widgetData.scrollingMode || widgetMetadata.scrollingMode
|
||||
property int valueMaxWidth: widgetData.maxWidth !== undefined ? widgetData.maxWidth : widgetMetadata.maxWidth
|
||||
property bool valueUseFixedWidth: widgetData.useFixedWidth !== undefined ? widgetData.useFixedWidth : widgetMetadata.useFixedWidth
|
||||
|
||||
Component.onCompleted: {
|
||||
if (widgetData && widgetData.hideMode !== undefined) {
|
||||
@@ -33,6 +35,8 @@ ColumnLayout {
|
||||
settings.showVisualizer = valueShowVisualizer
|
||||
settings.visualizerType = valueVisualizerType
|
||||
settings.scrollingMode = valueScrollingMode
|
||||
settings.maxWidth = parseInt(widthInput.text) || widgetMetadata.maxWidth
|
||||
settings.useFixedWidth = valueUseFixedWidth
|
||||
return settings
|
||||
}
|
||||
|
||||
@@ -87,6 +91,22 @@ ColumnLayout {
|
||||
minimumWidth: 200
|
||||
}
|
||||
|
||||
NTextInput {
|
||||
id: widthInput
|
||||
Layout.fillWidth: true
|
||||
label: I18n.tr("bar.widget-settings.media-mini.max-width.label")
|
||||
description: I18n.tr("bar.widget-settings.media-mini.max-width.description")
|
||||
placeholderText: widgetMetadata.maxWidth
|
||||
text: valueMaxWidth
|
||||
}
|
||||
|
||||
NToggle {
|
||||
label: I18n.tr("bar.widget-settings.media-mini.use-fixed-width.label")
|
||||
description: I18n.tr("bar.widget-settings.media-mini.use-fixed-width.description")
|
||||
checked: valueUseFixedWidth
|
||||
onToggled: checked => valueUseFixedWidth = checked
|
||||
}
|
||||
|
||||
NComboBox {
|
||||
label: I18n.tr("bar.widget-settings.media-mini.scrolling-mode.label")
|
||||
description: I18n.tr("bar.widget-settings.media-mini.scrolling-mode.description")
|
||||
|
||||
@@ -26,6 +26,7 @@ NPanel {
|
||||
Audio,
|
||||
Bar,
|
||||
ColorScheme,
|
||||
LockScreen,
|
||||
ControlCenter,
|
||||
OSD,
|
||||
Display,
|
||||
@@ -118,6 +119,11 @@ NPanel {
|
||||
id: userInterfaceTab
|
||||
UserInterfaceTab {}
|
||||
}
|
||||
Component {
|
||||
id: lockScreenTab
|
||||
LockScreenTab {}
|
||||
}
|
||||
|
||||
// Order *DOES* matter
|
||||
function updateTabsModel() {
|
||||
let newTabs = [{
|
||||
@@ -150,6 +156,11 @@ NPanel {
|
||||
"label": "settings.launcher.title",
|
||||
"icon": "settings-launcher",
|
||||
"source": launcherTab
|
||||
}, {
|
||||
"id": SettingsPanel.Tab.LockScreen,
|
||||
"label": "settings.lock-screen.title",
|
||||
"icon": "settings-lock-screen",
|
||||
"source": lockScreenTab
|
||||
}, {
|
||||
"id": SettingsPanel.Tab.Audio,
|
||||
"label": "settings.audio.title",
|
||||
|
||||
@@ -13,6 +13,24 @@ ColumnLayout {
|
||||
property var schemeColorsCache: ({})
|
||||
property int cacheVersion: 0 // Increment to trigger UI updates
|
||||
|
||||
// Time dropdown options (00:00 .. 23:30)
|
||||
ListModel {
|
||||
id: timeOptions
|
||||
}
|
||||
Component.onCompleted: {
|
||||
for (var h = 0; h < 24; h++) {
|
||||
for (var m = 0; m < 60; m += 30) {
|
||||
var hh = ("0" + h).slice(-2)
|
||||
var mm = ("0" + m).slice(-2)
|
||||
var key = hh + ":" + mm
|
||||
timeOptions.append({
|
||||
"key": key,
|
||||
"name": key
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spacing: Style.marginL
|
||||
|
||||
// Helper function to extract scheme name from path
|
||||
@@ -138,20 +156,91 @@ ColumnLayout {
|
||||
|
||||
// Dark Mode Toggle
|
||||
NToggle {
|
||||
label: I18n.tr("settings.color-scheme.color-source.dark-mode.label")
|
||||
description: I18n.tr("settings.color-scheme.color-source.dark-mode.description")
|
||||
label: I18n.tr("settings.color-scheme.dark-mode.switch.label")
|
||||
description: I18n.tr("settings.color-scheme.dark-mode.switch.description")
|
||||
checked: Settings.data.colorSchemes.darkMode
|
||||
enabled: true
|
||||
onToggled: checked => {
|
||||
Settings.data.colorSchemes.darkMode = checked
|
||||
root.cacheVersion++ // Force UI update for dark/light variants
|
||||
}
|
||||
}
|
||||
|
||||
NComboBox {
|
||||
label: I18n.tr("settings.color-scheme.dark-mode.mode.label")
|
||||
description: I18n.tr("settings.color-scheme.dark-mode.mode.description")
|
||||
|
||||
model: [{
|
||||
"name": I18n.tr("settings.color-scheme.dark-mode.mode.off"),
|
||||
"key": "off"
|
||||
}, {
|
||||
"name": I18n.tr("settings.color-scheme.dark-mode.mode.manual"),
|
||||
"key": "manual"
|
||||
}, {
|
||||
"name": I18n.tr("settings.color-scheme.dark-mode.mode.location"),
|
||||
"key": "location"
|
||||
}]
|
||||
|
||||
currentKey: Settings.data.colorSchemes.schedulingMode
|
||||
|
||||
onSelected: key => {
|
||||
Settings.data.colorSchemes.schedulingMode = key
|
||||
AppThemeService.generate()
|
||||
}
|
||||
}
|
||||
|
||||
// Manual scheduling
|
||||
ColumnLayout {
|
||||
spacing: Style.marginS
|
||||
visible: Settings.data.colorSchemes.schedulingMode === "manual"
|
||||
|
||||
NLabel {
|
||||
label: I18n.tr("settings.display.night-light.manual-schedule.label")
|
||||
description: I18n.tr("settings.display.night-light.manual-schedule.description")
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: false
|
||||
spacing: Style.marginS
|
||||
|
||||
NText {
|
||||
text: I18n.tr("settings.display.night-light.manual-schedule.sunrise")
|
||||
pointSize: Style.fontSizeM
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
|
||||
NComboBox {
|
||||
model: timeOptions
|
||||
currentKey: Settings.data.colorSchemes.manualSunrise
|
||||
placeholder: I18n.tr("settings.display.night-light.manual-schedule.select-start")
|
||||
onSelected: key => Settings.data.colorSchemes.manualSunrise = key
|
||||
minimumWidth: 120
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredWidth: 20
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("settings.display.night-light.manual-schedule.sunset")
|
||||
pointSize: Style.fontSizeM
|
||||
color: Color.mOnSurfaceVariant
|
||||
}
|
||||
|
||||
NComboBox {
|
||||
model: timeOptions
|
||||
currentKey: Settings.data.colorSchemes.manualSunset
|
||||
placeholder: I18n.tr("settings.display.night-light.manual-schedule.select-stop")
|
||||
onSelected: key => Settings.data.colorSchemes.manualSunset = key
|
||||
minimumWidth: 120
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use Wallpaper Colors
|
||||
NToggle {
|
||||
label: I18n.tr("settings.color-scheme.color-source.use-wallpaper-colors.label")
|
||||
description: I18n.tr("settings.color-scheme.color-source.use-wallpaper-colors.description")
|
||||
enabled: ProgramCheckerService.matugenAvailable
|
||||
checked: Settings.data.colorSchemes.useWallpaperColors
|
||||
onToggled: checked => {
|
||||
if (checked) {
|
||||
@@ -575,6 +664,23 @@ ColumnLayout {
|
||||
}
|
||||
}
|
||||
}
|
||||
NCheckbox {
|
||||
label: "Vicinae"
|
||||
description: ProgramCheckerService.vicinaeAvailable ? I18n.tr("settings.color-scheme.templates.programs.vicinae.description", {
|
||||
"filepath": "~/.local/share/vicinae/themes/matugen.toml"
|
||||
}) : I18n.tr("settings.color-scheme.templates.programs.vicinae.description-missing", {
|
||||
"app": "vicinae"
|
||||
})
|
||||
checked: Settings.data.templates.vicinae
|
||||
enabled: ProgramCheckerService.vicinaeAvailable
|
||||
opacity: ProgramCheckerService.vicinaeAvailable ? 1.0 : 0.6
|
||||
onToggled: checked => {
|
||||
if (ProgramCheckerService.vicinaeAvailable) {
|
||||
Settings.data.templates.vicinae = checked
|
||||
AppThemeService.generate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Miscellaneous
|
||||
@@ -590,6 +696,9 @@ ColumnLayout {
|
||||
checked: Settings.data.templates.enableUserTemplates
|
||||
onToggled: checked => {
|
||||
Settings.data.templates.enableUserTemplates = checked
|
||||
if (checked) {
|
||||
MatugenTemplates.writeUserTemplatesToml()
|
||||
}
|
||||
AppThemeService.generate()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +87,24 @@ ColumnLayout {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: Style.marginXXS
|
||||
Layout.fillWidth: true
|
||||
NLabel {
|
||||
label: I18n.tr("settings.dock.appearance.icon-size.label")
|
||||
description: I18n.tr("settings.dock.appearance.icon-size.description")
|
||||
}
|
||||
NValueSlider {
|
||||
Layout.fillWidth: true
|
||||
from: 0
|
||||
to: 2
|
||||
stepSize: 0.01
|
||||
value: Settings.data.dock.size
|
||||
onMoved: value => Settings.data.dock.size = value
|
||||
text: Math.floor(Settings.data.dock.size * 100) + "%"
|
||||
}
|
||||
}
|
||||
|
||||
NToggle {
|
||||
label: I18n.tr("settings.dock.monitors.only-same-output.label")
|
||||
description: I18n.tr("settings.dock.monitors.only-same-output.description")
|
||||
|
||||
@@ -189,4 +189,54 @@ ColumnLayout {
|
||||
Layout.topMargin: Style.marginXL
|
||||
Layout.bottomMargin: Style.marginXL
|
||||
}
|
||||
|
||||
// Language selection
|
||||
ColumnLayout {
|
||||
spacing: Style.marginL
|
||||
Layout.fillWidth: true
|
||||
|
||||
NHeader {
|
||||
label: I18n.tr("settings.general.language.section.label")
|
||||
description: I18n.tr("settings.general.language.section.description")
|
||||
}
|
||||
|
||||
NComboBox {
|
||||
Layout.fillWidth: true
|
||||
label: I18n.tr("settings.general.language.select.label")
|
||||
description: I18n.tr("settings.general.language.select.description")
|
||||
model: [{
|
||||
"key": "",
|
||||
"name": I18n.tr("settings.general.language.select.auto-detect") + " (" + I18n.systemDetectedLangCode + ")"
|
||||
}].concat(I18n.availableLanguages.map(function (langCode) {
|
||||
return {
|
||||
"key": langCode,
|
||||
"name": langCode
|
||||
}
|
||||
}))
|
||||
currentKey: Settings.data.general.language
|
||||
onSelected: key => {
|
||||
Settings.data.general.language = key
|
||||
if (key === "") {
|
||||
I18n.detectLanguage() // Re-detect system language if "Automatic" is selected
|
||||
} else {
|
||||
I18n.setLanguage(key) // Set specific language
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NDivider {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.marginXL
|
||||
Layout.bottomMargin: Style.marginXL
|
||||
}
|
||||
|
||||
NButton {
|
||||
visible: !DistroService.isNixOS
|
||||
text: I18n.tr("settings.general.launch-setup-wizard")
|
||||
onClicked: {
|
||||
setupWizardLoader.active = false
|
||||
setupWizardLoader.active = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.Commons
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
NToggle {
|
||||
label: I18n.tr("settings.lock-screen.lock-on-suspend.label")
|
||||
description: I18n.tr("settings.lock-screen.lock-on-suspend.description")
|
||||
checked: Settings.data.general.lockOnSuspend
|
||||
onToggled: Settings.data.general.lockOnSuspend = checked
|
||||
}
|
||||
|
||||
NToggle {
|
||||
label: I18n.tr("settings.lock-screen.compact-lockscreen.label")
|
||||
description: I18n.tr("settings.lock-screen.compact-lockscreen.description")
|
||||
checked: Settings.data.general.compactLockScreen
|
||||
onToggled: checked => Settings.data.general.compactLockScreen = checked
|
||||
}
|
||||
|
||||
NDivider {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.marginXL
|
||||
Layout.bottomMargin: Style.marginXL
|
||||
}
|
||||
}
|
||||
@@ -33,13 +33,6 @@ ColumnLayout {
|
||||
onToggled: checked => Settings.data.ui.tooltipsEnabled = checked
|
||||
}
|
||||
|
||||
NToggle {
|
||||
label: I18n.tr("settings.user-interface.compact-lockscreen.label")
|
||||
description: I18n.tr("settings.user-interface.compact-lockscreen.description")
|
||||
checked: Settings.data.general.compactLockScreen
|
||||
onToggled: checked => Settings.data.general.compactLockScreen = checked
|
||||
}
|
||||
|
||||
NToggle {
|
||||
label: I18n.tr("settings.user-interface.panels-overlay.label")
|
||||
description: I18n.tr("settings.user-interface.panels-overlay.description")
|
||||
|
||||
@@ -124,14 +124,14 @@ ColumnLayout {
|
||||
spacing: 2
|
||||
|
||||
NText {
|
||||
text: I18n.tr("settings.color-scheme.color-source.dark-mode.label")
|
||||
text: I18n.tr("settings.color-scheme.dark-mode.switch.label")
|
||||
pointSize: Style.fontSizeL
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("settings.color-scheme.color-source.dark-mode.description")
|
||||
text: I18n.tr("settings.color-scheme.dark-mode.switch.description")
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
wrapMode: Text.WordWrap
|
||||
@@ -167,7 +167,7 @@ ColumnLayout {
|
||||
color: Color.mSurface
|
||||
|
||||
NIcon {
|
||||
icon: "color-picker"
|
||||
icon: ProgramCheckerService.matugenAvailable ? "color-picker" : "alert-triangle"
|
||||
pointSize: Style.fontSizeL
|
||||
color: Color.mPrimary
|
||||
anchors.centerIn: parent
|
||||
@@ -196,7 +196,6 @@ ColumnLayout {
|
||||
|
||||
NToggle {
|
||||
enabled: ProgramCheckerService.matugenAvailable
|
||||
opacity: ProgramCheckerService.matugenAvailable ? 1.0 : 0.6
|
||||
checked: Settings.data.colorSchemes.useWallpaperColors && ProgramCheckerService.matugenAvailable
|
||||
onToggled: checked => {
|
||||
if (!ProgramCheckerService.matugenAvailable)
|
||||
@@ -214,34 +213,6 @@ ColumnLayout {
|
||||
}
|
||||
}
|
||||
|
||||
// Matugen not available notice
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginS
|
||||
visible: !ProgramCheckerService.matugenAvailable
|
||||
|
||||
Rectangle {
|
||||
width: 28
|
||||
height: 28
|
||||
radius: Style.radiusM
|
||||
color: Color.mSurface
|
||||
NIcon {
|
||||
icon: "alert-triangle"
|
||||
pointSize: Style.fontSizeL
|
||||
color: Color.mPrimary
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
NText {
|
||||
text: I18n.tr("settings.color-scheme.color-source.use-wallpaper-colors.description")
|
||||
// Reuse description; availability is visually indicated
|
||||
pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurfaceVariant
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
// Matugen scheme type (visible when wallpaper colors enabled and matugen available)
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
@@ -321,11 +321,36 @@ PopupWindow {
|
||||
completeHide()
|
||||
}
|
||||
|
||||
// Update text function for binding support
|
||||
// Update text function
|
||||
function updateText(newText) {
|
||||
if (visible && targetItem) {
|
||||
text = newText
|
||||
positionAndShow()
|
||||
|
||||
// Recalculate dimensions
|
||||
const tipWidth = Math.min(tooltipText.implicitWidth + (padding * 2), maxWidth)
|
||||
root.implicitWidth = tipWidth
|
||||
|
||||
const tipHeight = tooltipText.implicitHeight + (padding * 2)
|
||||
root.implicitHeight = tipHeight
|
||||
|
||||
// Reposition if necessary
|
||||
var targetGlobal = targetItem.mapToItem(null, 0, 0)
|
||||
const targetWidth = targetItem.width
|
||||
|
||||
// Adjust horizontal position to keep tooltip on screen if needed
|
||||
const globalX = targetGlobal.x + anchorX
|
||||
if (globalX < 0) {
|
||||
anchorX = -targetGlobal.x + margin
|
||||
} else if (globalX + tipWidth > screenWidth) {
|
||||
anchorX = screenWidth - targetGlobal.x - tipWidth - margin
|
||||
}
|
||||
|
||||
// Force anchor update
|
||||
Qt.callLater(() => {
|
||||
if (root.anchor && root.visible) {
|
||||
root.anchor.updateAnchor()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user