Add Audio Settings, split NotificationHistory
This commit is contained in:
parent
0babca7b56
commit
faa6bcd222
10 changed files with 555 additions and 209 deletions
|
|
@ -15,6 +15,8 @@ Variants {
|
||||||
required property ShellScreen modelData
|
required property ShellScreen modelData
|
||||||
readonly property real scaling: Scaling.scale(screen)
|
readonly property real scaling: Scaling.scale(screen)
|
||||||
|
|
||||||
|
property var settingsPanel: null
|
||||||
|
|
||||||
screen: modelData
|
screen: modelData
|
||||||
implicitHeight: Style.barHeight * scaling
|
implicitHeight: Style.barHeight * scaling
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
|
||||||
29
Modules/Bar/NotificationHistory.qml
Normal file
29
Modules/Bar/NotificationHistory.qml
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Controls
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
NIconButton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property real scaling: Scaling.scale(screen)
|
||||||
|
sizeMultiplier: 0.8
|
||||||
|
showBorder: false
|
||||||
|
icon: "notifications"
|
||||||
|
tooltipText: "Notification History"
|
||||||
|
onClicked: {
|
||||||
|
if (!notificationHistoryPanelLoader.active) {
|
||||||
|
notificationHistoryPanelLoader.isLoaded = true
|
||||||
|
}
|
||||||
|
if (notificationHistoryPanelLoader.item) {
|
||||||
|
notificationHistoryPanelLoader.item.visible = !notificationHistoryPanelLoader.item.visible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationHistoryPanel {
|
||||||
|
id: notificationHistoryPanelLoader
|
||||||
|
}
|
||||||
|
}
|
||||||
162
Modules/Bar/NotificationHistoryPanel.qml
Normal file
162
Modules/Bar/NotificationHistoryPanel.qml
Normal file
|
|
@ -0,0 +1,162 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Controls
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Services.Notifications
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
// Loader for Notification History panel
|
||||||
|
NLoader {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
content: Component {
|
||||||
|
NPanel {
|
||||||
|
id: notificationPanel
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: notificationPanel
|
||||||
|
ignoreUnknownSignals: true
|
||||||
|
function onDismissed() {
|
||||||
|
notificationPanel.visible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: Colors.backgroundSecondary
|
||||||
|
radius: Style.radiusMedium * scaling
|
||||||
|
border.color: Colors.backgroundTertiary
|
||||||
|
border.width: Math.max(1, Style.borderMedium * scaling)
|
||||||
|
width: 400 * scaling
|
||||||
|
height: 500 * scaling
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.topMargin: Style.marginTiny * scaling
|
||||||
|
anchors.rightMargin: Style.marginTiny * scaling
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Style.marginLarge * scaling
|
||||||
|
spacing: Style.marginMedium * scaling
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Style.marginMedium * scaling
|
||||||
|
|
||||||
|
NText {
|
||||||
|
text: "notifications"
|
||||||
|
font.family: "Material Symbols Outlined"
|
||||||
|
font.pointSize: Style.fontSizeXL * scaling
|
||||||
|
color: Colors.accentPrimary
|
||||||
|
}
|
||||||
|
|
||||||
|
NText {
|
||||||
|
text: "Notification History"
|
||||||
|
font.pointSize: Style.fontSizeLarge * scaling
|
||||||
|
font.bold: true
|
||||||
|
color: Colors.textPrimary
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
NIconButton {
|
||||||
|
icon: "delete"
|
||||||
|
sizeMultiplier: 0.8
|
||||||
|
tooltipText: "Clear history"
|
||||||
|
onClicked: NotificationService.clearHistory()
|
||||||
|
}
|
||||||
|
|
||||||
|
NIconButton {
|
||||||
|
icon: "close"
|
||||||
|
sizeMultiplier: 0.8
|
||||||
|
onClicked: {
|
||||||
|
notificationPanel.visible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NDivider {}
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: notificationList
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
model: NotificationService.historyModel
|
||||||
|
spacing: Style.marginMedium * scaling
|
||||||
|
clip: true
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
width: notificationList ? (notificationList.width - 20) : 380 * scaling
|
||||||
|
height: 80
|
||||||
|
radius: Style.radiusMedium * scaling
|
||||||
|
color: notificationMouseArea.containsMouse ? Colors.accentPrimary : "transparent"
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 15
|
||||||
|
}
|
||||||
|
spacing: 15
|
||||||
|
|
||||||
|
// Notification content
|
||||||
|
Column {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
spacing: 5
|
||||||
|
|
||||||
|
NText {
|
||||||
|
text: (summary || "No summary").substring(0, 100)
|
||||||
|
font.pointSize: Style.fontSizeMedium * scaling
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: notificationMouseArea.containsMouse ? Colors.backgroundPrimary : Colors.textPrimary
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
width: parent.width - 30
|
||||||
|
maximumLineCount: 2
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
NText {
|
||||||
|
text: (body || "").substring(0, 150)
|
||||||
|
font.pointSize: Style.fontSizeSmall * scaling
|
||||||
|
color: notificationMouseArea.containsMouse ? Colors.backgroundPrimary : Colors.textSecondary
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
width: parent.width - 30
|
||||||
|
maximumLineCount: 3
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
NText {
|
||||||
|
text: NotificationService.formatTimestamp(timestamp)
|
||||||
|
font.pointSize: Style.fontSizeSmall * scaling
|
||||||
|
color: notificationMouseArea.containsMouse ? Colors.backgroundPrimary : Colors.textSecondary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: notificationMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
onClicked: {
|
||||||
|
console.log("[NotificationHistory] Removing notification:", summary)
|
||||||
|
NotificationService.historyModel.remove(index)
|
||||||
|
NotificationService.saveHistory()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollBar.vertical: ScrollBar {
|
||||||
|
active: true
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -10,6 +10,13 @@ Item {
|
||||||
width: pill.width
|
width: pill.width
|
||||||
height: pill.height
|
height: pill.height
|
||||||
|
|
||||||
|
// Reference to settings panel
|
||||||
|
property var settingsPanel: null
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
console.log("[Volume] settingsPanel received:", !!settingsPanel)
|
||||||
|
}
|
||||||
|
|
||||||
// Used to avoid opening the pill on Quickshell startup
|
// Used to avoid opening the pill on Quickshell startup
|
||||||
property bool firstVolumeReceived: false
|
property bool firstVolumeReceived: false
|
||||||
|
|
||||||
|
|
@ -71,7 +78,15 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onClicked: {
|
onClicked: {
|
||||||
audioDeviceSelector.isLoaded = !audioDeviceSelector.isLoaded
|
// Open settings panel and navigate to Audio tab
|
||||||
|
console.log("[Volume] Attempting to open settings panel...")
|
||||||
|
try {
|
||||||
|
settingsPanel.isLoaded = true
|
||||||
|
settingsPanel.content.currentTabIndex = 5 // Audio tab index
|
||||||
|
console.log("[Volume] Settings panel opened successfully")
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[Volume] Error opening settings panel:", error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,180 +0,0 @@
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import QtQuick.Controls
|
|
||||||
import Quickshell
|
|
||||||
import Quickshell.Wayland
|
|
||||||
import Quickshell.Services.Notifications
|
|
||||||
import qs.Services
|
|
||||||
import qs.Widgets
|
|
||||||
|
|
||||||
NIconButton {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
readonly property real scaling: Scaling.scale(screen)
|
|
||||||
sizeMultiplier: 0.8
|
|
||||||
showBorder: false
|
|
||||||
icon: "notifications"
|
|
||||||
tooltipText: "Notification History"
|
|
||||||
onClicked: {
|
|
||||||
notificationHistoryLoader.active = !notificationHistoryLoader.active
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loader for Notification History menu
|
|
||||||
NLoader {
|
|
||||||
id: notificationHistoryLoader
|
|
||||||
active: false
|
|
||||||
|
|
||||||
content: Component {
|
|
||||||
NPanel {
|
|
||||||
id: notificationPanel
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: notificationPanel
|
|
||||||
ignoreUnknownSignals: true
|
|
||||||
function onDismissed() {
|
|
||||||
notificationHistoryLoader.active = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
color: Colors.backgroundSecondary
|
|
||||||
radius: Style.radiusMedium * scaling
|
|
||||||
border.color: Colors.backgroundTertiary
|
|
||||||
border.width: Math.max(1, Style.borderMedium * scaling)
|
|
||||||
width: 400 * scaling
|
|
||||||
height: 500 * scaling
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.topMargin: Style.marginTiny * scaling
|
|
||||||
anchors.rightMargin: Style.marginTiny * scaling
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: Style.marginLarge * scaling
|
|
||||||
spacing: Style.marginMedium * scaling
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
spacing: Style.marginMedium * scaling
|
|
||||||
|
|
||||||
NText {
|
|
||||||
text: "notifications"
|
|
||||||
font.family: "Material Symbols Outlined"
|
|
||||||
font.pointSize: Style.fontSizeXL * scaling
|
|
||||||
color: Colors.accentPrimary
|
|
||||||
}
|
|
||||||
|
|
||||||
NText {
|
|
||||||
text: "Notification History"
|
|
||||||
font.pointSize: Style.fontSizeLarge * scaling
|
|
||||||
font.bold: true
|
|
||||||
color: Colors.textPrimary
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
|
|
||||||
NIconButton {
|
|
||||||
icon: "delete"
|
|
||||||
sizeMultiplier: 0.8
|
|
||||||
tooltipText: "Clear history"
|
|
||||||
onClicked: NotificationService.clearHistory()
|
|
||||||
}
|
|
||||||
|
|
||||||
NIconButton {
|
|
||||||
icon: "close"
|
|
||||||
sizeMultiplier: 0.8
|
|
||||||
onClicked: {
|
|
||||||
notificationHistoryLoader.active = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NDivider {}
|
|
||||||
|
|
||||||
ListView {
|
|
||||||
id: notificationList
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
model: NotificationService.historyModel
|
|
||||||
spacing: Style.marginMedium * scaling
|
|
||||||
clip: true
|
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
|
||||||
|
|
||||||
delegate: Rectangle {
|
|
||||||
width: notificationList ? (notificationList.width - 20) : 380 * scaling
|
|
||||||
height: 80
|
|
||||||
radius: Style.radiusMedium * scaling
|
|
||||||
color: notificationMouseArea.containsMouse ? Colors.accentPrimary : "transparent"
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
anchors {
|
|
||||||
fill: parent
|
|
||||||
margins: 15
|
|
||||||
}
|
|
||||||
spacing: 15
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Notification content
|
|
||||||
Column {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
spacing: 5
|
|
||||||
|
|
||||||
NText {
|
|
||||||
text: (summary || "No summary").substring(0, 100)
|
|
||||||
font.pointSize: Style.fontSizeMedium * scaling
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: notificationMouseArea.containsMouse ? Colors.backgroundPrimary : Colors.textPrimary
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
width: parent.width - 30
|
|
||||||
maximumLineCount: 2
|
|
||||||
elide: Text.ElideRight
|
|
||||||
}
|
|
||||||
|
|
||||||
NText {
|
|
||||||
text: (body || "").substring(0, 150)
|
|
||||||
font.pointSize: Style.fontSizeSmall * scaling
|
|
||||||
color: notificationMouseArea.containsMouse ? Colors.backgroundPrimary : Colors.textSecondary
|
|
||||||
wrapMode: Text.Wrap
|
|
||||||
width: parent.width - 30
|
|
||||||
maximumLineCount: 3
|
|
||||||
elide: Text.ElideRight
|
|
||||||
}
|
|
||||||
|
|
||||||
NText {
|
|
||||||
text: NotificationService.formatTimestamp(timestamp)
|
|
||||||
font.pointSize: Style.fontSizeSmall * scaling
|
|
||||||
color: notificationMouseArea.containsMouse ? Colors.backgroundPrimary : Colors.textSecondary
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: notificationMouseArea
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
onClicked: {
|
|
||||||
console.log("[NotificationHistory] Removing notification:", summary)
|
|
||||||
NotificationService.historyModel.remove(index)
|
|
||||||
NotificationService.saveHistory()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBar {
|
|
||||||
active: true
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -39,6 +39,10 @@ NLoader {
|
||||||
"label": "Network",
|
"label": "Network",
|
||||||
"icon": "wifi",
|
"icon": "wifi",
|
||||||
"source": "Tabs/Network.qml"
|
"source": "Tabs/Network.qml"
|
||||||
|
}, {
|
||||||
|
"label": "Audio",
|
||||||
|
"icon": "volume_up",
|
||||||
|
"source": "Tabs/Audio.qml"
|
||||||
}, {
|
}, {
|
||||||
"label": "Display",
|
"label": "Display",
|
||||||
"icon": "monitor",
|
"icon": "monitor",
|
||||||
|
|
@ -212,6 +216,7 @@ NLoader {
|
||||||
Tabs.TimeWeather {}
|
Tabs.TimeWeather {}
|
||||||
Tabs.ScreenRecorder {}
|
Tabs.ScreenRecorder {}
|
||||||
Tabs.Network {}
|
Tabs.Network {}
|
||||||
|
Tabs.Audio {}
|
||||||
Tabs.Display {}
|
Tabs.Display {}
|
||||||
Tabs.Wallpaper {}
|
Tabs.Wallpaper {}
|
||||||
Tabs.WallpaperSelector {}
|
Tabs.WallpaperSelector {}
|
||||||
|
|
|
||||||
308
Modules/Settings/Tabs/Audio.qml
Normal file
308
Modules/Settings/Tabs/Audio.qml
Normal file
|
|
@ -0,0 +1,308 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell.Services.Pipewire
|
||||||
|
|
||||||
|
import qs.Modules.Settings
|
||||||
|
import qs.Widgets
|
||||||
|
import qs.Services
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
ScrollView {
|
||||||
|
id: scrollView
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
padding: Style.marginMedium * scaling
|
||||||
|
clip: true
|
||||||
|
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||||
|
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
width: scrollView.availableWidth
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: Style.marginTiny * scaling
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
NText {
|
||||||
|
text: "Audio"
|
||||||
|
font.pointSize: Style.fontSizeXL * scaling
|
||||||
|
font.weight: Style.fontWeightBold
|
||||||
|
color: Colors.textPrimary
|
||||||
|
Layout.bottomMargin: Style.marginSmall * scaling
|
||||||
|
}
|
||||||
|
|
||||||
|
// Volume Controls
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: Style.marginSmall * scaling
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: Style.marginSmall * scaling
|
||||||
|
|
||||||
|
// Master Volume
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: Style.marginSmall * scaling
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
NText {
|
||||||
|
text: "Master Volume"
|
||||||
|
font.weight: Style.fontWeightBold
|
||||||
|
color: Colors.textPrimary
|
||||||
|
}
|
||||||
|
|
||||||
|
NText {
|
||||||
|
text: "System-wide volume level"
|
||||||
|
font.pointSize: Style.fontSizeSmall * scaling
|
||||||
|
color: Colors.textSecondary
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
NSlider {
|
||||||
|
id: masterVolumeSlider
|
||||||
|
Layout.fillWidth: true
|
||||||
|
from: 0
|
||||||
|
to: allowOverdrive.value ? 200 : 100
|
||||||
|
value: (Audio.volume || 0) * 100
|
||||||
|
stepSize: 5
|
||||||
|
onValueChanged: {
|
||||||
|
Audio.volumeSet(value / 100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NText {
|
||||||
|
text: Math.round(masterVolumeSlider.value) + "%"
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
color: Colors.textSecondary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NToggle {
|
||||||
|
id: allowOverdrive
|
||||||
|
label: "Allow Volume Overdrive"
|
||||||
|
description: "Enable volume levels above 100% (up to 200%)"
|
||||||
|
value: Settings.data.audio ? Settings.data.audio.volumeOverdrive : false
|
||||||
|
onToggled: function (checked) {
|
||||||
|
Settings.data.audio.volumeOverdrive = checked
|
||||||
|
|
||||||
|
// If overdrive is disabled and current volume is above 100%, cap it
|
||||||
|
if (!checked && Audio.volume > 1.0) {
|
||||||
|
Audio.volumeSet(1.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mute Toggle
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: Style.marginSmall * scaling
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: Style.marginMedium * scaling
|
||||||
|
|
||||||
|
NToggle {
|
||||||
|
label: "Mute Audio"
|
||||||
|
description: "Mute or unmute the default audio output"
|
||||||
|
value: Audio.muted
|
||||||
|
onToggled: function (newValue) {
|
||||||
|
if (Audio.sink && Audio.sink.audio) {
|
||||||
|
Audio.sink.audio.muted = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NDivider {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: Style.marginLarge * 2 * scaling
|
||||||
|
Layout.bottomMargin: Style.marginLarge * scaling
|
||||||
|
}
|
||||||
|
|
||||||
|
// Audio Devices
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: Style.marginLarge * scaling
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
NText {
|
||||||
|
text: "Audio Devices"
|
||||||
|
font.pointSize: Style.fontSizeXL * scaling
|
||||||
|
font.weight: Style.fontWeightBold
|
||||||
|
color: Colors.textPrimary
|
||||||
|
Layout.bottomMargin: Style.marginSmall * scaling
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output Device
|
||||||
|
NComboBox {
|
||||||
|
id: outputDeviceCombo
|
||||||
|
label: "Output Device"
|
||||||
|
description: "Default audio output device"
|
||||||
|
optionsKeys: outputDeviceKeys
|
||||||
|
optionsLabels: outputDeviceLabels
|
||||||
|
currentKey: Audio.sink ? Audio.sink.id.toString() : ""
|
||||||
|
onSelected: function (key) {
|
||||||
|
// Find the node by ID and set it as preferred
|
||||||
|
for (let i = 0; i < Pipewire.nodes.count; i++) {
|
||||||
|
let node = Pipewire.nodes.get(i)
|
||||||
|
if (node.id.toString() === key && node.isSink) {
|
||||||
|
Pipewire.preferredDefaultAudioSink = node
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input Device
|
||||||
|
NComboBox {
|
||||||
|
id: inputDeviceCombo
|
||||||
|
label: "Input Device"
|
||||||
|
description: "Default audio input device"
|
||||||
|
optionsKeys: inputDeviceKeys
|
||||||
|
optionsLabels: inputDeviceLabels
|
||||||
|
currentKey: Audio.source ? Audio.source.id.toString() : ""
|
||||||
|
onSelected: function (key) {
|
||||||
|
// Find the node by ID and set it as preferred
|
||||||
|
for (let i = 0; i < Pipewire.nodes.count; i++) {
|
||||||
|
let node = Pipewire.nodes.get(i)
|
||||||
|
if (node.id.toString() === key && !node.isSink) {
|
||||||
|
Pipewire.preferredDefaultAudioSource = node
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Divider
|
||||||
|
NDivider {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: Style.marginLarge * scaling
|
||||||
|
Layout.bottomMargin: Style.marginMedium * scaling
|
||||||
|
}
|
||||||
|
|
||||||
|
// Audio Visualizer Category
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: Style.marginSmall * scaling
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
NText {
|
||||||
|
text: "Audio Visualizer"
|
||||||
|
font.pointSize: Style.fontSizeXL * scaling
|
||||||
|
font.weight: Style.fontWeightBold
|
||||||
|
color: Colors.textPrimary
|
||||||
|
Layout.bottomMargin: Style.marginSmall * scaling
|
||||||
|
}
|
||||||
|
|
||||||
|
// Audio Visualizer section
|
||||||
|
NComboBox {
|
||||||
|
id: audioVisualizerCombo
|
||||||
|
label: "Visualization Type"
|
||||||
|
description: "Choose a visualization type for media playback"
|
||||||
|
optionsKeys: ["radial", "bars", "wave"]
|
||||||
|
optionsLabels: ["Radial", "Bars", "Wave"]
|
||||||
|
currentKey: Settings.data.audio ? Settings.data.audio.audioVisualizer.type : "radial"
|
||||||
|
onSelected: function (key) {
|
||||||
|
if (!Settings.data.audio) {
|
||||||
|
Settings.data.audio = {}
|
||||||
|
}
|
||||||
|
if (!Settings.data.audio.audioVisualizer) {
|
||||||
|
Settings.data.audio.audioVisualizer = {}
|
||||||
|
}
|
||||||
|
Settings.data.audio.audioVisualizer.type = key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Device list properties
|
||||||
|
property var outputDeviceKeys: ["default"]
|
||||||
|
property var outputDeviceLabels: ["Default Output"]
|
||||||
|
property var inputDeviceKeys: ["default"]
|
||||||
|
property var inputDeviceLabels: ["Default Input"]
|
||||||
|
|
||||||
|
// Bind Pipewire nodes
|
||||||
|
PwObjectTracker {
|
||||||
|
id: nodeTracker
|
||||||
|
objects: [Pipewire.nodes]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update device lists when component is completed
|
||||||
|
Component.onCompleted: {
|
||||||
|
updateDeviceLists()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timer to check if pipewire is ready and update device lists
|
||||||
|
Timer {
|
||||||
|
id: deviceUpdateTimer
|
||||||
|
interval: 100
|
||||||
|
repeat: true
|
||||||
|
running: !(Pipewire && Pipewire.ready)
|
||||||
|
onTriggered: {
|
||||||
|
if (Pipewire && Pipewire.ready) {
|
||||||
|
updateDeviceLists()
|
||||||
|
running = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update device lists when nodes change
|
||||||
|
Connections {
|
||||||
|
target: nodeTracker
|
||||||
|
function onObjectsChanged() {
|
||||||
|
updateDeviceLists()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
id: nodesRepeater
|
||||||
|
model: Pipewire.nodes
|
||||||
|
delegate: Item {
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (modelData && modelData.isSink && modelData.audio) {
|
||||||
|
// Add to output devices
|
||||||
|
let key = modelData.id.toString()
|
||||||
|
if (!outputDeviceKeys.includes(key)) {
|
||||||
|
outputDeviceKeys.push(key)
|
||||||
|
outputDeviceLabels.push(modelData.description || modelData.name || "Unknown Device")
|
||||||
|
}
|
||||||
|
} else if (modelData && !modelData.isSink && modelData.audio) {
|
||||||
|
// Add to input devices
|
||||||
|
let key = modelData.id.toString()
|
||||||
|
if (!inputDeviceKeys.includes(key)) {
|
||||||
|
inputDeviceKeys.push(key)
|
||||||
|
inputDeviceLabels.push(modelData.description || modelData.name || "Unknown Device")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateDeviceLists() {
|
||||||
|
if (Pipewire && Pipewire.ready) {
|
||||||
|
// Update comboboxes
|
||||||
|
if (outputDeviceCombo) {
|
||||||
|
outputDeviceCombo.optionsKeys = outputDeviceKeys
|
||||||
|
outputDeviceCombo.optionsLabels = outputDeviceLabels
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputDeviceCombo) {
|
||||||
|
inputDeviceCombo.optionsKeys = inputDeviceKeys
|
||||||
|
inputDeviceCombo.optionsLabels = inputDeviceLabels
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -40,17 +40,7 @@ ColumnLayout {
|
||||||
Layout.bottomMargin: Style.marginSmall * scaling
|
Layout.bottomMargin: Style.marginSmall * scaling
|
||||||
}
|
}
|
||||||
|
|
||||||
// Audio Visualizer section
|
|
||||||
NComboBox {
|
|
||||||
label: "Audio Visualizer"
|
|
||||||
description: "Choose a visualization type"
|
|
||||||
optionsKeys: ["radial", "bars", "wave"]
|
|
||||||
optionsLabels: ["Radial", "Bars", "Wave"]
|
|
||||||
currentKey: Settings.data.audioVisualizer.type
|
|
||||||
onSelected: function (key) {
|
|
||||||
Settings.data.audioVisualizer.type = key
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,13 @@ QtObject {
|
||||||
// Signal when notification is received
|
// Signal when notification is received
|
||||||
onNotification: function (notification) {
|
onNotification: function (notification) {
|
||||||
|
|
||||||
|
// Check if notifications are suppressed
|
||||||
|
if (Settings.data.notifications && Settings.data.notifications.suppressed) {
|
||||||
|
// Still add to history but don't show notification
|
||||||
|
root.addToHistory(notification)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Track the notification
|
// Track the notification
|
||||||
notification.tracked = true
|
notification.tracked = true
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@ Singleton {
|
||||||
// Used to access via Settings.data.xxx.yyy
|
// Used to access via Settings.data.xxx.yyy
|
||||||
property var data: adapter
|
property var data: adapter
|
||||||
|
|
||||||
|
// Needed to only have one NPanel loaded at a time. <--- VERY BROKEN
|
||||||
|
//property var openPanel: null
|
||||||
Item {
|
Item {
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
|
|
||||||
|
|
@ -36,17 +38,22 @@ Singleton {
|
||||||
}
|
}
|
||||||
|
|
||||||
FileView {
|
FileView {
|
||||||
|
|
||||||
|
// TBC ? needed for SWWW only ?
|
||||||
|
// Qt.callLater(function () {
|
||||||
|
// WallpaperManager.setCurrentWallpaper(settings.currentWallpaper, true);
|
||||||
|
// })
|
||||||
path: settingsFile
|
path: settingsFile
|
||||||
watchChanges: true
|
watchChanges: true
|
||||||
onFileChanged: reload()
|
onFileChanged: reload()
|
||||||
onAdapterUpdated: writeAdapter()
|
onAdapterUpdated: writeAdapter()
|
||||||
Component.onCompleted: {
|
Component.onCompleted: function () {
|
||||||
reload()
|
reload()
|
||||||
}
|
}
|
||||||
onLoaded: {
|
onLoaded: function () {
|
||||||
Qt.callLater(function () {
|
Qt.callLater(function () {
|
||||||
if (adapter.wallpaper.current !== "") {
|
if (adapter.wallpaper.current !== "") {
|
||||||
|
console.log("Settings: Initializing wallpaper to:", adapter.wallpaper.current)
|
||||||
Wallpapers.setCurrentWallpaper(adapter.wallpaper.current, true)
|
Wallpapers.setCurrentWallpaper(adapter.wallpaper.current, true)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -80,6 +87,7 @@ Singleton {
|
||||||
property string avatarImage: defaultAvatar
|
property string avatarImage: defaultAvatar
|
||||||
property bool dimDesktop: true
|
property bool dimDesktop: true
|
||||||
property bool showScreenCorners: false
|
property bool showScreenCorners: false
|
||||||
|
property bool showDock: false
|
||||||
}
|
}
|
||||||
|
|
||||||
// location
|
// location
|
||||||
|
|
@ -102,8 +110,9 @@ Singleton {
|
||||||
property string videoCodec: "h264"
|
property string videoCodec: "h264"
|
||||||
property string quality: "very_high"
|
property string quality: "very_high"
|
||||||
property string colorRange: "limited"
|
property string colorRange: "limited"
|
||||||
property string audioSource: "default_output"
|
|
||||||
property bool showCursor: true
|
property bool showCursor: true
|
||||||
|
// New: optional audio source selection (default: system output)
|
||||||
|
property string audioSource: "default_output"
|
||||||
}
|
}
|
||||||
|
|
||||||
// wallpaper
|
// wallpaper
|
||||||
|
|
@ -160,11 +169,16 @@ Singleton {
|
||||||
property list<string> monitors: []
|
property list<string> monitors: []
|
||||||
}
|
}
|
||||||
|
|
||||||
// audioVisualizer
|
// audio
|
||||||
property JsonObject audioVisualizer
|
property JsonObject audio
|
||||||
|
|
||||||
audioVisualizer: JsonObject {
|
audio: JsonObject {
|
||||||
property string type: "radial"
|
property bool volumeOverdrive: false
|
||||||
|
property JsonObject audioVisualizer
|
||||||
|
|
||||||
|
audioVisualizer: JsonObject {
|
||||||
|
property string type: "radial"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ui
|
// ui
|
||||||
|
|
@ -179,14 +193,8 @@ Singleton {
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: adapter.wallpaper
|
target: adapter.wallpaper
|
||||||
function onIsRandomChanged() {
|
function onIsRandomChanged() { Wallpapers.toggleRandomWallpaper() }
|
||||||
Wallpapers.toggleRandomWallpaper()
|
function onRandomIntervalChanged() { Wallpapers.restartRandomWallpaperTimer() }
|
||||||
}
|
function onDirectoryChanged() { Wallpapers.loadWallpapers() }
|
||||||
function onRandomIntervalChanged() {
|
|
||||||
Wallpapers.restartRandomWallpaperTimer()
|
|
||||||
}
|
|
||||||
function onDirectoryChanged() {
|
|
||||||
Wallpapers.loadWallpapers()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue