add noctalia and fuzzel

This commit is contained in:
2026-05-23 21:20:58 +02:00
parent 364801f1a3
commit 9a3eceb3ab
599 changed files with 204318 additions and 0 deletions
@@ -0,0 +1,62 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
ColumnLayout {
id: root
spacing: Style.marginL
Layout.fillWidth: true
enabled: Settings.data.notifications.enabled
NToggle {
label: I18n.tr("panels.notifications.duration-respect-expire-label")
description: I18n.tr("panels.notifications.duration-respect-expire-description")
checked: Settings.data.notifications.respectExpireTimeout
onToggled: checked => Settings.data.notifications.respectExpireTimeout = checked
defaultValue: Settings.getDefaultValue("notifications.respectExpireTimeout")
}
NValueSlider {
Layout.fillWidth: true
label: I18n.tr("panels.notifications.duration-low-urgency-label")
description: I18n.tr("panels.notifications.duration-low-urgency-description")
from: 1
to: 30
stepSize: 1
showReset: true
value: Settings.data.notifications.lowUrgencyDuration
onMoved: value => Settings.data.notifications.lowUrgencyDuration = value
text: Settings.data.notifications.lowUrgencyDuration + "s"
defaultValue: Settings.getDefaultValue("notifications.lowUrgencyDuration")
}
NValueSlider {
Layout.fillWidth: true
label: I18n.tr("panels.notifications.duration-normal-urgency-label")
description: I18n.tr("panels.notifications.duration-normal-urgency-description")
from: 1
to: 30
stepSize: 1
showReset: true
value: Settings.data.notifications.normalUrgencyDuration
onMoved: value => Settings.data.notifications.normalUrgencyDuration = value
text: Settings.data.notifications.normalUrgencyDuration + "s"
defaultValue: Settings.getDefaultValue("notifications.normalUrgencyDuration")
}
NValueSlider {
Layout.fillWidth: true
label: I18n.tr("panels.notifications.duration-critical-urgency-label")
description: I18n.tr("panels.notifications.duration-critical-urgency-description")
from: 1
to: 30
stepSize: 1
showReset: true
value: Settings.data.notifications.criticalUrgencyDuration
onMoved: value => Settings.data.notifications.criticalUrgencyDuration = value
text: Settings.data.notifications.criticalUrgencyDuration + "s"
defaultValue: Settings.getDefaultValue("notifications.criticalUrgencyDuration")
}
}
@@ -0,0 +1,149 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import qs.Commons
import qs.Services.Compositor
import qs.Services.System
import qs.Widgets
ColumnLayout {
id: root
spacing: Style.marginL
Layout.fillWidth: true
property var addMonitor
property var removeMonitor
NToggle {
label: I18n.tr("panels.notifications.settings-enabled-label")
description: I18n.tr("panels.notifications.settings-enabled-description")
checked: Settings.data.notifications.enabled !== false
onToggled: checked => Settings.data.notifications.enabled = checked
defaultValue: Settings.getDefaultValue("notifications.enabled")
}
ColumnLayout {
spacing: Style.marginL
enabled: Settings.data.notifications.enabled
NComboBox {
label: I18n.tr("panels.notifications.settings-density-label")
description: I18n.tr("panels.notifications.settings-density-description")
model: [
{
"key": "default",
"name": I18n.tr("options.notification-density.default")
},
{
"key": "compact",
"name": I18n.tr("options.notification-density.compact")
}
]
currentKey: Settings.data.notifications.density || "default"
onSelected: key => Settings.data.notifications.density = key
defaultValue: Settings.getDefaultValue("notifications.density")
}
NToggle {
label: I18n.tr("tooltips.do-not-disturb-enabled")
description: I18n.tr("panels.notifications.settings-do-not-disturb-description")
checked: NotificationService.doNotDisturb
onToggled: checked => NotificationService.doNotDisturb = checked
}
NComboBox {
label: I18n.tr("common.position")
description: I18n.tr("panels.notifications.settings-location-description")
model: [
{
"key": "top",
"name": I18n.tr("positions.top-center")
},
{
"key": "top_left",
"name": I18n.tr("positions.top-left")
},
{
"key": "top_right",
"name": I18n.tr("positions.top-right")
},
{
"key": "bottom",
"name": I18n.tr("positions.bottom-center")
},
{
"key": "bottom_left",
"name": I18n.tr("positions.bottom-left")
},
{
"key": "bottom_right",
"name": I18n.tr("positions.bottom-right")
}
]
currentKey: Settings.data.notifications.location || "top_right"
onSelected: key => Settings.data.notifications.location = key
defaultValue: Settings.getDefaultValue("notifications.location")
}
NToggle {
label: I18n.tr("panels.osd.always-on-top-label")
description: I18n.tr("panels.notifications.settings-always-on-top-description")
checked: Settings.data.notifications.overlayLayer
onToggled: checked => Settings.data.notifications.overlayLayer = checked
defaultValue: Settings.getDefaultValue("notifications.overlayLayer")
}
NValueSlider {
Layout.fillWidth: true
label: I18n.tr("panels.osd.background-opacity-label")
description: I18n.tr("panels.notifications.settings-background-opacity-description")
from: 0
to: 1
stepSize: 0.01
showReset: true
value: Settings.data.notifications.backgroundOpacity
onMoved: value => Settings.data.notifications.backgroundOpacity = value
text: Math.round(Settings.data.notifications.backgroundOpacity * 100) + "%"
defaultValue: Settings.getDefaultValue("notifications.backgroundOpacity")
}
NDivider {
Layout.fillWidth: true
}
NText {
text: I18n.tr("panels.notifications.monitors-desc")
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
Repeater {
model: Quickshell.screens || []
delegate: NCheckbox {
Layout.fillWidth: true
readonly property real compositorScale: {
const info = CompositorService.displayScales[modelData.name];
return (info && info.scale) ? info.scale : 1.0;
}
label: modelData.name || I18n.tr("common.unknown")
description: {
I18n.tr("system.monitor-description", {
"model": modelData.model,
"width": modelData.width * compositorScale,
"height": modelData.height * compositorScale,
"scale": compositorScale
});
}
checked: (Settings.data.notifications.monitors || []).indexOf(modelData.name) !== -1
onToggled: checked => {
if (checked) {
Settings.data.notifications.monitors = root.addMonitor(Settings.data.notifications.monitors, modelData.name);
} else {
Settings.data.notifications.monitors = root.removeMonitor(Settings.data.notifications.monitors, modelData.name);
}
}
}
}
}
}
@@ -0,0 +1,52 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
ColumnLayout {
id: root
spacing: Style.marginL
Layout.fillWidth: true
enabled: Settings.data.notifications.enabled
NToggle {
label: I18n.tr("panels.notifications.history-clear-dismiss-label")
description: I18n.tr("panels.notifications.history-clear-dismiss-description")
checked: Settings.data.notifications.clearDismissed
onToggled: checked => Settings.data.notifications.clearDismissed = checked
defaultValue: Settings.getDefaultValue("notifications.clearDismissed")
}
NToggle {
label: I18n.tr("panels.notifications.settings-markdown-label")
description: I18n.tr("panels.notifications.settings-markdown-description")
checked: Settings.data.notifications.enableMarkdown
onToggled: checked => Settings.data.notifications.enableMarkdown = checked
defaultValue: Settings.getDefaultValue("notifications.enableMarkdown")
}
NToggle {
label: I18n.tr("panels.notifications.history-low-urgency-label")
description: I18n.tr("panels.notifications.history-low-urgency-description")
checked: Settings.data.notifications?.saveToHistory?.low !== false
onToggled: checked => Settings.data.notifications.saveToHistory.low = checked
defaultValue: Settings.getDefaultValue("notifications.saveToHistory.low")
}
NToggle {
label: I18n.tr("panels.notifications.history-normal-urgency-label")
description: I18n.tr("panels.notifications.history-normal-urgency-description")
checked: Settings.data.notifications?.saveToHistory?.normal !== false
onToggled: checked => Settings.data.notifications.saveToHistory.normal = checked
defaultValue: Settings.getDefaultValue("notifications.saveToHistory.normal")
}
NToggle {
label: I18n.tr("panels.notifications.history-critical-urgency-label")
description: I18n.tr("panels.notifications.history-critical-urgency-description")
checked: Settings.data.notifications?.saveToHistory?.critical !== false
onToggled: checked => Settings.data.notifications.saveToHistory.critical = checked
defaultValue: Settings.getDefaultValue("notifications.saveToHistory.critical")
}
}
@@ -0,0 +1,130 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Services.System
import qs.Widgets
Popup {
id: root
modal: true
closePolicy: Popup.NoAutoClose
dim: true
anchors.centerIn: parent
width: Math.min(500 * Style.uiScaleRatio, parent.width * 0.9)
padding: Style.marginL
property int editIndex: -1
property string patternValue: ""
property string actionValue: "block"
signal saved(string pattern, string action)
property var _savedSlot: null
property string _selectedAction: "block"
background: Rectangle {
color: Color.mSurface
radius: Style.radiusL
border.color: Color.mOutline
border.width: Style.borderS
}
onOpened: {
patternInput.text = patternValue;
actionCombo.currentKey = actionValue;
_selectedAction = actionValue;
patternInput.forceActiveFocus();
}
contentItem: ColumnLayout {
id: contentLayout
spacing: Style.marginL
RowLayout {
Layout.fillWidth: true
NText {
text: editIndex >= 0 ? I18n.tr("panels.notifications.rules-edit") : I18n.tr("panels.notifications.rules-add")
font.weight: Style.fontWeightBold
pointSize: Style.fontSizeL
Layout.fillWidth: true
}
NIconButton {
icon: "close"
onClicked: root.close()
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: Style.marginM
NTextInput {
id: patternInput
Layout.fillWidth: true
label: I18n.tr("panels.notifications.rules-pattern-label")
placeholderText: I18n.tr("panels.notifications.rules-pattern-placeholder")
fontFamily: Settings.data.ui.fontFixed
}
NComboBox {
id: actionCombo
Layout.fillWidth: true
label: I18n.tr("panels.notifications.rules-action-label")
model: [
{
"key": "block",
"name": I18n.tr("panels.notifications.rules-action-block")
},
{
"key": "mute",
"name": I18n.tr("panels.notifications.rules-action-mute")
},
{
"key": "hide",
"name": I18n.tr("panels.notifications.rules-action-hide")
}
]
currentKey: actionValue
onSelected: key => {
actionValue = key;
_selectedAction = key;
}
}
NLabel {
Layout.fillWidth: true
label: _selectedAction === "block" ? I18n.tr("panels.notifications.rules-action-block-desc") : (_selectedAction === "mute" ? I18n.tr("panels.notifications.rules-action-mute-desc") : I18n.tr("panels.notifications.rules-action-hide-desc"))
labelColor: Color.mOnSurfaceVariant
}
}
RowLayout {
Layout.fillWidth: true
spacing: Style.marginM
Item {
Layout.fillWidth: true
}
NButton {
text: I18n.tr("common.cancel")
outlined: true
onClicked: root.close()
}
NButton {
text: I18n.tr("common.save")
icon: "check"
backgroundColor: Color.mPrimary
textColor: Color.mOnPrimary
enabled: patternInput.text.trim() !== ""
onClicked: {
root.saved(patternInput.text.trim(), _selectedAction || "block");
root.close();
}
}
}
}
}
@@ -0,0 +1,158 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import qs.Commons
import qs.Widgets
ColumnLayout {
id: root
spacing: 0
// Helper functions to update arrays immutably
function addMonitor(list, name) {
const arr = (list || []).slice();
if (!arr.includes(name))
arr.push(name);
return arr;
}
function removeMonitor(list, name) {
return (list || []).filter(function (n) {
return n !== name;
});
}
// File pickers for sound sub-tab
function openUnifiedSoundPicker() {
unifiedSoundFilePicker.open();
}
function openLowSoundPicker() {
lowSoundFilePicker.open();
}
function openNormalSoundPicker() {
normalSoundFilePicker.open();
}
function openCriticalSoundPicker() {
criticalSoundFilePicker.open();
}
NTabBar {
id: subTabBar
Layout.fillWidth: true
Layout.bottomMargin: Style.marginM
distributeEvenly: false // this is too cramped on this tab to split evenly
currentIndex: tabView.currentIndex
NTabButton {
text: I18n.tr("common.appearance")
tabIndex: 0
checked: subTabBar.currentIndex === 0
}
NTabButton {
text: I18n.tr("common.duration")
tabIndex: 1
checked: subTabBar.currentIndex === 1
}
NTabButton {
text: I18n.tr("common.history")
tabIndex: 2
checked: subTabBar.currentIndex === 2
}
NTabButton {
text: I18n.tr("common.sound")
tabIndex: 3
checked: subTabBar.currentIndex === 3
}
NTabButton {
text: I18n.tr("common.toast")
tabIndex: 4
checked: subTabBar.currentIndex === 4
}
NTabButton {
text: I18n.tr("panels.notifications.rules-tab")
tabIndex: 5
checked: subTabBar.currentIndex === 5
}
}
Item {
Layout.fillWidth: true
Layout.preferredHeight: Style.marginL
}
NTabView {
id: tabView
currentIndex: subTabBar.currentIndex
GeneralSubTab {
addMonitor: root.addMonitor
removeMonitor: root.removeMonitor
}
DurationSubTab {}
HistorySubTab {}
SoundSubTab {
onOpenUnifiedPicker: root.openUnifiedSoundPicker()
onOpenLowPicker: root.openLowSoundPicker()
onOpenNormalPicker: root.openNormalSoundPicker()
onOpenCriticalPicker: root.openCriticalSoundPicker()
}
ToastSubTab {}
RulesSubTab {}
}
// File Pickers for Sound Files
NFilePicker {
id: unifiedSoundFilePicker
title: I18n.tr("panels.notifications.sounds-files-unified-select-title")
selectionMode: "files"
initialPath: Quickshell.env("HOME")
nameFilters: ["*.wav", "*.mp3", "*.ogg", "*.flac", "*.m4a", "*.aac"]
onAccepted: paths => {
if (paths.length > 0) {
const soundPath = paths[0];
Settings.data.notifications.sounds.normalSoundFile = soundPath;
Settings.data.notifications.sounds.lowSoundFile = soundPath;
Settings.data.notifications.sounds.criticalSoundFile = soundPath;
}
}
}
NFilePicker {
id: lowSoundFilePicker
title: I18n.tr("panels.notifications.sounds-files-low-select-title")
selectionMode: "files"
initialPath: Quickshell.env("HOME")
nameFilters: ["*.wav", "*.mp3", "*.ogg", "*.flac", "*.m4a", "*.aac"]
onAccepted: paths => {
if (paths.length > 0) {
Settings.data.notifications.sounds.lowSoundFile = paths[0];
}
}
}
NFilePicker {
id: normalSoundFilePicker
title: I18n.tr("panels.notifications.sounds-files-normal-select-title")
selectionMode: "files"
initialPath: Quickshell.env("HOME")
nameFilters: ["*.wav", "*.mp3", "*.ogg", "*.flac", "*.m4a", "*.aac"]
onAccepted: paths => {
if (paths.length > 0) {
Settings.data.notifications.sounds.normalSoundFile = paths[0];
}
}
}
NFilePicker {
id: criticalSoundFilePicker
title: I18n.tr("panels.notifications.sounds-files-critical-select-title")
selectionMode: "files"
initialPath: Quickshell.env("HOME")
nameFilters: ["*.wav", "*.mp3", "*.ogg", "*.flac", "*.m4a", "*.aac"]
onAccepted: paths => {
if (paths.length > 0) {
Settings.data.notifications.sounds.criticalSoundFile = paths[0];
}
}
}
}
@@ -0,0 +1,114 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Services.System
import qs.Widgets
ColumnLayout {
id: root
spacing: Style.marginL
Layout.fillWidth: true
enabled: Settings.data.notifications.enabled
function _saveToService() {
NotificationRulesService.save();
}
function _removeRule(index) {
var arr = (NotificationRulesService.rules || []).slice();
arr.splice(index, 1);
NotificationRulesService.rules = arr;
_saveToService();
}
NotificationRuleEditPopup {
id: editPopup
parent: Overlay.overlay
}
function openEdit(index, patternVal, actionVal) {
editPopup.editIndex = index;
editPopup.patternValue = patternVal || "";
editPopup.actionValue = actionVal || "block";
try {
editPopup.saved.disconnect(editPopup._savedSlot);
} catch (e) {}
editPopup._savedSlot = function (pattern, action) {
const trimmed = (pattern || "").trim();
if (trimmed === "")
return;
var arr = (NotificationRulesService.rules || []).slice();
var rule = {
"pattern": trimmed,
"action": action
};
if (index >= 0 && index < arr.length) {
arr[index] = rule;
} else {
arr.push(rule);
}
NotificationRulesService.rules = arr;
_saveToService();
};
editPopup.saved.connect(editPopup._savedSlot);
editPopup.open();
}
NLabel {
label: I18n.tr("panels.notifications.rules-label")
description: I18n.tr("panels.notifications.rules-description")
}
NDivider {
Layout.fillWidth: true
Layout.topMargin: Style.marginS
Layout.bottomMargin: Style.marginS
}
Repeater {
model: NotificationRulesService.rules || []
delegate: RowLayout {
id: entryDelegate
required property int index
required property var modelData
property string pattern: modelData.pattern || ""
property string action: modelData.action || "block"
property bool isRegex: pattern.length >= 3 && pattern.startsWith("/") && pattern.endsWith("/")
spacing: Style.marginM
Layout.fillWidth: true
NLabel {
Layout.fillWidth: true
label: (entryDelegate.isRegex ? "regex: " : "") + entryDelegate.pattern
description: entryDelegate.action === "block" ? I18n.tr("panels.notifications.rules-action-block") : (entryDelegate.action === "mute" ? I18n.tr("panels.notifications.rules-action-mute") : I18n.tr("panels.notifications.rules-action-hide"))
labelColor: entryDelegate.pattern ? Color.mPrimary : Color.mOnSurface
}
NIconButton {
icon: "settings"
tooltipText: I18n.tr("common.edit")
onClicked: root.openEdit(entryDelegate.index, entryDelegate.pattern, entryDelegate.action)
}
NIconButton {
icon: "trash"
tooltipText: I18n.tr("panels.notifications.rules-delete")
onClicked: root._removeRule(entryDelegate.index)
}
}
}
NButton {
text: I18n.tr("panels.notifications.rules-add")
icon: "add"
enabled: Settings.data.notifications.enabled
onClicked: root.openEdit(-1, "", "block")
}
}
@@ -0,0 +1,210 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import qs.Commons
import qs.Services.System
import qs.Widgets
ColumnLayout {
id: root
spacing: Style.marginL
Layout.fillWidth: true
enabled: Settings.data.notifications.enabled
signal openUnifiedPicker
signal openLowPicker
signal openNormalPicker
signal openCriticalPicker
// QtMultimedia unavailable message
NBox {
Layout.fillWidth: true
visible: !SoundService.multimediaAvailable
implicitHeight: unavailableContent.implicitHeight + Style.margin2L
RowLayout {
id: unavailableContent
anchors.fill: parent
anchors.margins: Style.marginL
spacing: Style.marginM
NIcon {
icon: "warning"
color: Color.mOnSurfaceVariant
pointSize: Style.fontSizeXL
Layout.alignment: Qt.AlignVCenter
}
NLabel {
Layout.fillWidth: true
label: I18n.tr("panels.notifications.sounds-unavailable-label")
description: I18n.tr("panels.notifications.sounds-unavailable-description")
}
}
}
NToggle {
enabled: SoundService.multimediaAvailable
label: I18n.tr("panels.notifications.sounds-enabled-label")
description: I18n.tr("panels.notifications.sounds-enabled-description")
checked: Settings.data.notifications?.sounds?.enabled ?? false
onToggled: checked => Settings.data.notifications.sounds.enabled = checked
defaultValue: Settings.getDefaultValue("notifications.sounds.enabled")
}
// Sound Volume
NValueSlider {
enabled: SoundService.multimediaAvailable && (Settings.data.notifications?.sounds?.enabled ?? false)
Layout.fillWidth: true
label: I18n.tr("panels.notifications.sounds-volume-label")
description: I18n.tr("panels.notifications.sounds-volume-description")
from: 0
to: 1
stepSize: 0.01
showReset: true
value: Settings.data.notifications?.sounds?.volume ?? 0.5
onMoved: value => Settings.data.notifications.sounds.volume = value
text: Math.round((Settings.data.notifications?.sounds?.volume ?? 0.5) * 100) + "%"
defaultValue: Settings.getDefaultValue("notifications.sounds.volume")
}
// Separate Sounds Toggle
NToggle {
enabled: SoundService.multimediaAvailable && (Settings.data.notifications?.sounds?.enabled ?? false)
Layout.fillWidth: true
label: I18n.tr("panels.notifications.sounds-separate-label")
description: I18n.tr("panels.notifications.sounds-separate-description")
checked: Settings.data.notifications?.sounds?.separateSounds ?? false
onToggled: checked => Settings.data.notifications.sounds.separateSounds = checked
defaultValue: Settings.getDefaultValue("notifications.sounds.separateSounds")
}
// Unified Sound File (shown when separateSounds is false)
ColumnLayout {
enabled: SoundService.multimediaAvailable && (Settings.data.notifications?.sounds?.enabled ?? false)
visible: !(Settings.data.notifications?.sounds?.separateSounds ?? false)
spacing: Style.marginXXS
Layout.fillWidth: true
NLabel {
label: I18n.tr("panels.notifications.sounds-files-unified-label")
description: I18n.tr("panels.notifications.sounds-files-unified-description")
}
NTextInputButton {
enabled: parent.enabled
Layout.fillWidth: true
placeholderText: I18n.tr("panels.notifications.sounds-files-placeholder")
text: Settings.data.notifications?.sounds?.normalSoundFile ?? ""
buttonIcon: "folder-open"
buttonTooltip: I18n.tr("panels.notifications.sounds-files-select-file")
onInputTextChanged: text => {
// When separate sounds are enabled, this row is hidden but still
// bound to normalSoundFile; user edits to normal still update that
// key and would re-trigger this handler and wipe low/critical.
if (Settings.data.notifications.sounds.separateSounds) {
return;
}
const soundPath = text;
Settings.data.notifications.sounds.normalSoundFile = soundPath;
Settings.data.notifications.sounds.lowSoundFile = soundPath;
Settings.data.notifications.sounds.criticalSoundFile = soundPath;
}
onButtonClicked: root.openUnifiedPicker()
}
}
// Separate Sound Files (shown when separateSounds is true)
ColumnLayout {
visible: SoundService.multimediaAvailable && (Settings.data.notifications?.sounds?.enabled ?? false) && (Settings.data.notifications?.sounds?.separateSounds ?? false)
spacing: Style.marginXXS
Layout.fillWidth: true
// Low Urgency Sound File
ColumnLayout {
spacing: Style.marginXXS
Layout.fillWidth: true
NLabel {
label: I18n.tr("panels.notifications.sounds-files-low-label")
description: I18n.tr("panels.notifications.sounds-files-low-description")
}
NTextInputButton {
enabled: parent.enabled
Layout.fillWidth: true
placeholderText: I18n.tr("panels.notifications.sounds-files-placeholder")
text: Settings.data.notifications?.sounds?.lowSoundFile ?? ""
buttonIcon: "folder-open"
buttonTooltip: I18n.tr("panels.notifications.sounds-files-select-file")
onInputTextChanged: text => Settings.data.notifications.sounds.lowSoundFile = text
onButtonClicked: root.openLowPicker()
}
}
// Normal Urgency Sound File
ColumnLayout {
spacing: Style.marginXXS
Layout.fillWidth: true
NLabel {
label: I18n.tr("panels.notifications.sounds-files-normal-label")
description: I18n.tr("panels.notifications.sounds-files-normal-description")
}
NTextInputButton {
enabled: parent.enabled
Layout.fillWidth: true
placeholderText: I18n.tr("panels.notifications.sounds-files-placeholder")
text: Settings.data.notifications?.sounds?.normalSoundFile ?? ""
buttonIcon: "folder-open"
buttonTooltip: I18n.tr("panels.notifications.sounds-files-select-file")
onInputTextChanged: text => Settings.data.notifications.sounds.normalSoundFile = text
onButtonClicked: root.openNormalPicker()
}
}
// Critical Urgency Sound File
ColumnLayout {
spacing: Style.marginXXS
Layout.fillWidth: true
NLabel {
label: I18n.tr("panels.notifications.sounds-files-critical-label")
description: I18n.tr("panels.notifications.sounds-files-critical-description")
}
NTextInputButton {
enabled: parent.enabled
Layout.fillWidth: true
placeholderText: I18n.tr("panels.notifications.sounds-files-placeholder")
text: Settings.data.notifications?.sounds?.criticalSoundFile ?? ""
buttonIcon: "folder-open"
buttonTooltip: I18n.tr("panels.notifications.sounds-files-select-file")
onInputTextChanged: text => Settings.data.notifications.sounds.criticalSoundFile = text
onButtonClicked: root.openCriticalPicker()
}
}
}
// Excluded Apps List
ColumnLayout {
enabled: SoundService.multimediaAvailable && (Settings.data.notifications?.sounds?.enabled ?? false)
spacing: Style.marginXXS
Layout.fillWidth: true
NLabel {
label: I18n.tr("panels.notifications.sounds-excluded-apps-label")
description: I18n.tr("panels.notifications.sounds-excluded-apps-description")
}
NTextInput {
enabled: parent.enabled
Layout.fillWidth: true
placeholderText: I18n.tr("panels.notifications.sounds-excluded-apps-placeholder")
text: Settings.data.notifications?.sounds?.excludedApps ?? ""
onTextChanged: Settings.data.notifications.sounds.excludedApps = text
}
}
}
@@ -0,0 +1,35 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
ColumnLayout {
id: root
spacing: Style.marginL
Layout.fillWidth: true
NCheckbox {
Layout.fillWidth: true
label: I18n.tr("panels.notifications.toast-media-label")
description: I18n.tr("panels.notifications.toast-media-description")
checked: Settings.data.notifications.enableMediaToast
onToggled: checked => Settings.data.notifications.enableMediaToast = checked
}
NCheckbox {
Layout.fillWidth: true
label: I18n.tr("panels.notifications.toast-keyboard-label")
description: I18n.tr("panels.notifications.toast-keyboard-description")
checked: Settings.data.notifications.enableKeyboardLayoutToast
onToggled: checked => Settings.data.notifications.enableKeyboardLayoutToast = checked
}
NCheckbox {
Layout.fillWidth: true
label: I18n.tr("panels.notifications.toast-battery-label")
description: I18n.tr("panels.notifications.toast-battery-description")
checked: Settings.data.notifications.enableBatteryToast
onToggled: checked => Settings.data.notifications.enableBatteryToast = checked
}
}