Settings: large cleanup and factorization. Should look much better.
This commit is contained in:
parent
1206be34dc
commit
8302285388
21 changed files with 2434 additions and 2937 deletions
|
|
@ -7,11 +7,8 @@ import qs.Commons
|
|||
import qs.Services
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
property real localVolume: AudioService.volume
|
||||
|
||||
// Connection used to open the pill when volume changes
|
||||
Connections {
|
||||
target: AudioService.sink?.audio ? AudioService.sink?.audio : null
|
||||
function onVolumeChanged() {
|
||||
|
|
@ -19,463 +16,404 @@ ColumnLayout {
|
|||
}
|
||||
}
|
||||
|
||||
spacing: 0
|
||||
|
||||
ScrollView {
|
||||
id: scrollView
|
||||
|
||||
// Master Volume
|
||||
ColumnLayout {
|
||||
spacing: Style.marginS * scaling
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
padding: Style.marginM * scaling
|
||||
clip: true
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||
|
||||
NLabel {
|
||||
label: "Output Volume"
|
||||
description: "System-wide volume level."
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
// Pipewire seems a bit finicky, if we spam too many volume changes it breaks easily
|
||||
// Probably because they have some quick fades in and out to avoid clipping
|
||||
// We use a timer to space out the updates, to avoid lock up
|
||||
Timer {
|
||||
interval: Style.animationFast
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
if (Math.abs(localVolume - AudioService.volume) >= 0.01) {
|
||||
AudioService.setVolume(localVolume)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NSlider {
|
||||
Layout.fillWidth: true
|
||||
from: 0
|
||||
to: Settings.data.audio.volumeOverdrive ? 2.0 : 1.0
|
||||
value: localVolume
|
||||
stepSize: 0.01
|
||||
onMoved: {
|
||||
localVolume = value
|
||||
}
|
||||
}
|
||||
|
||||
NText {
|
||||
text: Math.floor(AudioService.volume * 100) + "%"
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.leftMargin: Style.marginS * scaling
|
||||
color: Color.mOnSurface
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mute Toggle
|
||||
ColumnLayout {
|
||||
spacing: Style.marginS * scaling
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.marginM * scaling
|
||||
|
||||
NToggle {
|
||||
label: "Mute Audio Output"
|
||||
description: "Mute or unmute the default audio output."
|
||||
checked: AudioService.muted
|
||||
onToggled: checked => {
|
||||
if (AudioService.sink && AudioService.sink.audio) {
|
||||
AudioService.sink.audio.muted = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Volume Step Size
|
||||
ColumnLayout {
|
||||
spacing: Style.marginS * scaling
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.marginM * scaling
|
||||
|
||||
NSpinBox {
|
||||
Layout.fillWidth: true
|
||||
label: "Volume Step Size"
|
||||
description: "Adjust the step size for volume changes (scroll wheel, keyboard shortcuts)."
|
||||
minimum: 1
|
||||
maximum: 25
|
||||
value: Settings.data.audio.volumeStep
|
||||
stepSize: 1
|
||||
suffix: "%"
|
||||
onValueChanged: {
|
||||
Settings.data.audio.volumeStep = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NDivider {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.marginXL * scaling
|
||||
Layout.bottomMargin: Style.marginXL * scaling
|
||||
}
|
||||
|
||||
// AudioService Devices
|
||||
ColumnLayout {
|
||||
spacing: Style.marginS * scaling
|
||||
|
||||
NText {
|
||||
text: "Audio Devices"
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mSecondary
|
||||
Layout.bottomMargin: Style.marginS * scaling
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Output Devices
|
||||
ButtonGroup {
|
||||
id: sinks
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
width: scrollView.availableWidth
|
||||
spacing: 0
|
||||
spacing: Style.marginXS * scaling
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: Style.marginL * scaling
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 0
|
||||
NLabel {
|
||||
label: "Output Device"
|
||||
description: "Select the desired audio output device."
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: Style.marginXS * scaling
|
||||
Repeater {
|
||||
model: AudioService.sinks
|
||||
NRadioButton {
|
||||
required property PwNode modelData
|
||||
ButtonGroup.group: sinks
|
||||
checked: AudioService.sink?.id === modelData.id
|
||||
onClicked: AudioService.setAudioSink(modelData)
|
||||
text: modelData.description
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Input Devices
|
||||
ButtonGroup {
|
||||
id: sources
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: Style.marginXS * scaling
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: Style.marginL * scaling
|
||||
|
||||
NLabel {
|
||||
label: "Input Device"
|
||||
description: "Select the desired audio input device."
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: AudioService.sources
|
||||
NRadioButton {
|
||||
required property PwNode modelData
|
||||
ButtonGroup.group: sources
|
||||
checked: AudioService.source?.id === modelData.id
|
||||
onClicked: AudioService.setAudioSource(modelData)
|
||||
text: modelData.description
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Divider
|
||||
NDivider {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.marginXL * scaling
|
||||
Layout.bottomMargin: Style.marginXL * scaling
|
||||
}
|
||||
|
||||
// Media Player Preferences
|
||||
ColumnLayout {
|
||||
spacing: Style.marginL * scaling
|
||||
|
||||
NText {
|
||||
text: "Media Player"
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mSecondary
|
||||
Layout.bottomMargin: Style.marginS * scaling
|
||||
}
|
||||
|
||||
// Miniplayer section
|
||||
NToggle {
|
||||
label: "Show Album Art In Bar Media Player"
|
||||
description: "Show the album art of the currently playing song next to the title."
|
||||
checked: Settings.data.audio.showMiniplayerAlbumArt
|
||||
onToggled: checked => {
|
||||
Settings.data.audio.showMiniplayerAlbumArt = checked
|
||||
}
|
||||
}
|
||||
|
||||
NToggle {
|
||||
label: "Show Audio Visualizer In Bar Media Player"
|
||||
description: "Shows an audio visualizer in the background of the miniplayer."
|
||||
checked: Settings.data.audio.showMiniplayerCava
|
||||
onToggled: checked => {
|
||||
Settings.data.audio.showMiniplayerCava = checked
|
||||
}
|
||||
}
|
||||
// Preferred player (persistent)
|
||||
NTextInput {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop
|
||||
label: "Preferred Player"
|
||||
description: "Substring to match MPRIS player (identity/bus/desktop)."
|
||||
placeholderText: "e.g. spotify, vlc, mpv"
|
||||
text: Settings.data.audio.preferredPlayer
|
||||
onTextChanged: {
|
||||
Settings.data.audio.preferredPlayer = text
|
||||
MediaService.updateCurrentPlayer()
|
||||
}
|
||||
}
|
||||
|
||||
// Blacklist editor
|
||||
ColumnLayout {
|
||||
spacing: Style.marginS * scaling
|
||||
Layout.fillWidth: true
|
||||
|
||||
RowLayout {
|
||||
spacing: Style.marginS * scaling
|
||||
Layout.fillWidth: true
|
||||
|
||||
NText {
|
||||
text: "Audio Output Volume"
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
Layout.bottomMargin: Style.marginS * scaling
|
||||
NTextInput {
|
||||
id: blacklistInput
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop
|
||||
label: "Blacklist player"
|
||||
description: "Substring, e.g. plex, shim, mpv."
|
||||
placeholderText: "type substring and press +"
|
||||
}
|
||||
|
||||
// Volume Controls
|
||||
ColumnLayout {
|
||||
spacing: Style.marginS * scaling
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.marginS * scaling
|
||||
|
||||
// Master Volume
|
||||
ColumnLayout {
|
||||
spacing: Style.marginS * scaling
|
||||
Layout.fillWidth: true
|
||||
|
||||
NLabel {
|
||||
label: "Master Volume"
|
||||
description: "System-wide volume level."
|
||||
// Button aligned to the center of the actual input field
|
||||
NIconButton {
|
||||
icon: "add"
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
Layout.bottomMargin: blacklistInput.description ? Style.marginS * scaling : 0
|
||||
onClicked: {
|
||||
const val = (blacklistInput.text || "").trim()
|
||||
if (val !== "") {
|
||||
const arr = (Settings.data.audio.mprisBlacklist || [])
|
||||
if (!arr.find(x => String(x).toLowerCase() === val.toLowerCase())) {
|
||||
Settings.data.audio.mprisBlacklist = [...arr, val]
|
||||
blacklistInput.text = ""
|
||||
MediaService.updateCurrentPlayer()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Current blacklist entries
|
||||
Flow {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Style.marginS * scaling
|
||||
spacing: Style.marginS * scaling
|
||||
|
||||
Repeater {
|
||||
model: Settings.data.audio.mprisBlacklist
|
||||
delegate: Rectangle {
|
||||
required property string modelData
|
||||
// Padding around the inner row
|
||||
property real pad: Style.marginS * scaling
|
||||
// Visuals
|
||||
color: Color.applyOpacity(Color.mOnSurface, "20")
|
||||
border.color: Color.applyOpacity(Color.mOnSurface, "50")
|
||||
border.width: Math.max(1, Style.borderS * scaling)
|
||||
|
||||
// Content
|
||||
RowLayout {
|
||||
// Pipewire seems a bit finicky, if we spam too many volume changes it breaks easily
|
||||
// Probably because they have some quick fades in and out to avoid clipping
|
||||
// We use a timer to space out the updates, to avoid lock up
|
||||
Timer {
|
||||
interval: Style.animationFast
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
if (Math.abs(localVolume - AudioService.volume) >= 0.01) {
|
||||
AudioService.setVolume(localVolume)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NSlider {
|
||||
Layout.fillWidth: true
|
||||
from: 0
|
||||
to: Settings.data.audio.volumeOverdrive ? 2.0 : 1.0
|
||||
value: localVolume
|
||||
stepSize: 0.01
|
||||
onMoved: {
|
||||
localVolume = value
|
||||
}
|
||||
}
|
||||
id: chipRow
|
||||
spacing: Style.marginXS * scaling
|
||||
anchors.fill: parent
|
||||
anchors.margins: pad
|
||||
|
||||
NText {
|
||||
text: Math.floor(AudioService.volume * 100) + "%"
|
||||
text: modelData
|
||||
color: Color.mOnSurface
|
||||
font.pointSize: Style.fontSizeS * scaling
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.leftMargin: Style.marginS * scaling
|
||||
color: Color.mOnSurface
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mute Toggle
|
||||
ColumnLayout {
|
||||
spacing: Style.marginS * scaling
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.marginM * scaling
|
||||
|
||||
NToggle {
|
||||
label: "Mute Audio Output"
|
||||
description: "Mute or unmute the default audio output."
|
||||
checked: AudioService.muted
|
||||
onToggled: checked => {
|
||||
if (AudioService.sink && AudioService.sink.audio) {
|
||||
AudioService.sink.audio.muted = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Volume Step Size
|
||||
ColumnLayout {
|
||||
spacing: Style.marginS * scaling
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.marginM * scaling
|
||||
|
||||
NSpinBox {
|
||||
Layout.fillWidth: true
|
||||
label: "Volume Step Size"
|
||||
description: "Adjust the step size for volume changes (scroll wheel, keyboard shortcuts)."
|
||||
minimum: 1
|
||||
maximum: 25
|
||||
value: Settings.data.audio.volumeStep
|
||||
stepSize: 1
|
||||
suffix: "%"
|
||||
onValueChanged: {
|
||||
Settings.data.audio.volumeStep = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NDivider {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.marginL * 2 * scaling
|
||||
Layout.bottomMargin: Style.marginL * scaling
|
||||
}
|
||||
|
||||
// AudioService Devices
|
||||
ColumnLayout {
|
||||
spacing: Style.marginL * scaling
|
||||
Layout.fillWidth: true
|
||||
|
||||
NText {
|
||||
text: "Audio Devices"
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
Layout.bottomMargin: Style.marginS * scaling
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Output Devices
|
||||
ButtonGroup {
|
||||
id: sinks
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: Style.marginXS * scaling
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: Style.marginL * scaling
|
||||
|
||||
NLabel {
|
||||
label: "Output Device"
|
||||
description: "Select the desired audio output device."
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: AudioService.sinks
|
||||
NRadioButton {
|
||||
required property PwNode modelData
|
||||
ButtonGroup.group: sinks
|
||||
checked: AudioService.sink?.id === modelData.id
|
||||
onClicked: AudioService.setAudioSink(modelData)
|
||||
text: modelData.description
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Input Devices
|
||||
ButtonGroup {
|
||||
id: sources
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: Style.marginXS * scaling
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: Style.marginL * scaling
|
||||
|
||||
NLabel {
|
||||
label: "Input Device"
|
||||
description: "Select the desired audio input device."
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: AudioService.sources
|
||||
NRadioButton {
|
||||
required property PwNode modelData
|
||||
ButtonGroup.group: sources
|
||||
checked: AudioService.source?.id === modelData.id
|
||||
onClicked: AudioService.setAudioSource(modelData)
|
||||
text: modelData.description
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Divider
|
||||
NDivider {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.marginL * scaling
|
||||
Layout.bottomMargin: Style.marginXL * scaling
|
||||
}
|
||||
|
||||
// MPRIS Player Preferences
|
||||
ColumnLayout {
|
||||
spacing: Style.marginL * scaling
|
||||
Layout.fillWidth: true
|
||||
|
||||
NText {
|
||||
text: "MPRIS Player Preferences"
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
Layout.bottomMargin: Style.marginS * scaling
|
||||
}
|
||||
|
||||
// Preferred player (persistent)
|
||||
NTextInput {
|
||||
label: "Preferred Player"
|
||||
description: "Substring to match MPRIS player (identity/bus/desktop)."
|
||||
placeholderText: "e.g. spotify, vlc, mpv"
|
||||
text: Settings.data.audio.preferredPlayer
|
||||
onTextChanged: {
|
||||
Settings.data.audio.preferredPlayer = text
|
||||
MediaService.updateCurrentPlayer()
|
||||
}
|
||||
}
|
||||
|
||||
// Blacklist editor
|
||||
ColumnLayout {
|
||||
spacing: Style.marginS * scaling
|
||||
Layout.fillWidth: true
|
||||
|
||||
RowLayout {
|
||||
spacing: Style.marginS * scaling
|
||||
Layout.fillWidth: true
|
||||
|
||||
NTextInput {
|
||||
id: blacklistInput
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop
|
||||
label: "Blacklist player"
|
||||
description: "Substring, e.g. plex, shim, mpv"
|
||||
placeholderText: "type substring and press +"
|
||||
}
|
||||
|
||||
// Button aligned to the center of the actual input field
|
||||
NIconButton {
|
||||
icon: "add"
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
Layout.bottomMargin: blacklistInput.description ? Style.marginS * scaling : 0
|
||||
onClicked: {
|
||||
const val = (blacklistInput.text || "").trim()
|
||||
if (val !== "") {
|
||||
NIconButton {
|
||||
icon: "close"
|
||||
sizeRatio: 0.8
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.rightMargin: Style.marginXS * scaling
|
||||
onClicked: {
|
||||
const arr = (Settings.data.audio.mprisBlacklist || [])
|
||||
if (!arr.find(x => String(x).toLowerCase() === val.toLowerCase())) {
|
||||
Settings.data.audio.mprisBlacklist = [...arr, val]
|
||||
blacklistInput.text = ""
|
||||
const idx = arr.findIndex(x => String(x) === modelData)
|
||||
if (idx >= 0) {
|
||||
arr.splice(idx, 1)
|
||||
Settings.data.audio.mprisBlacklist = arr
|
||||
MediaService.updateCurrentPlayer()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Intrinsic size derived from inner row + padding
|
||||
implicitWidth: chipRow.implicitWidth + pad * 2
|
||||
implicitHeight: Math.max(chipRow.implicitHeight + pad * 2, Style.baseWidgetSize * 0.8 * scaling)
|
||||
radius: Style.radiusM * scaling
|
||||
}
|
||||
|
||||
// Current blacklist entries
|
||||
Flow {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Style.marginS * scaling
|
||||
spacing: Style.marginS * scaling
|
||||
|
||||
Repeater {
|
||||
model: Settings.data.audio.mprisBlacklist
|
||||
delegate: Rectangle {
|
||||
required property string modelData
|
||||
// Padding around the inner row
|
||||
property real pad: Style.marginS * scaling
|
||||
// Visuals
|
||||
color: Color.applyOpacity(Color.mOnSurface, "20")
|
||||
border.color: Color.applyOpacity(Color.mOnSurface, "50")
|
||||
border.width: Math.max(1, Style.borderS * scaling)
|
||||
|
||||
// Content
|
||||
RowLayout {
|
||||
id: chipRow
|
||||
spacing: Style.marginXS * scaling
|
||||
anchors.fill: parent
|
||||
anchors.margins: pad
|
||||
|
||||
NText {
|
||||
text: modelData
|
||||
color: Color.mOnSurface
|
||||
font.pointSize: Style.fontSizeS * scaling
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.leftMargin: Style.marginS * scaling
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "close"
|
||||
sizeRatio: 0.8
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.rightMargin: Style.marginXS * scaling
|
||||
onClicked: {
|
||||
const arr = (Settings.data.audio.mprisBlacklist || [])
|
||||
const idx = arr.findIndex(x => String(x) === modelData)
|
||||
if (idx >= 0) {
|
||||
arr.splice(idx, 1)
|
||||
Settings.data.audio.mprisBlacklist = arr
|
||||
MediaService.updateCurrentPlayer()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Intrinsic size derived from inner row + padding
|
||||
implicitWidth: chipRow.implicitWidth + pad * 2
|
||||
implicitHeight: Math.max(chipRow.implicitHeight + pad * 2, Style.baseWidgetSize * 0.8 * scaling)
|
||||
radius: Style.radiusM * scaling
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Divider
|
||||
NDivider {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.marginXL * scaling
|
||||
Layout.bottomMargin: Style.marginXL * scaling
|
||||
}
|
||||
|
||||
// Bar Mini Media player
|
||||
ColumnLayout {
|
||||
spacing: Style.marginL * scaling
|
||||
Layout.fillWidth: true
|
||||
|
||||
NText {
|
||||
text: "Bar Media Player"
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
Layout.bottomMargin: Style.marginS * scaling
|
||||
}
|
||||
|
||||
// Miniplayer section
|
||||
NToggle {
|
||||
label: "Show Album Art In Bar Media Player"
|
||||
description: "Show the album art of the currently playing song next to the title."
|
||||
checked: Settings.data.audio.showMiniplayerAlbumArt
|
||||
onToggled: checked => {
|
||||
Settings.data.audio.showMiniplayerAlbumArt = checked
|
||||
}
|
||||
}
|
||||
|
||||
NToggle {
|
||||
label: "Show Audio Visualizer In Bar Media Player"
|
||||
description: "Shows an audio visualizer in the background of the miniplayer."
|
||||
checked: Settings.data.audio.showMiniplayerCava
|
||||
onToggled: checked => {
|
||||
Settings.data.audio.showMiniplayerCava = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Divider
|
||||
NDivider {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.marginXL * scaling
|
||||
Layout.bottomMargin: Style.marginXL * scaling
|
||||
}
|
||||
|
||||
// AudioService Visualizer Category
|
||||
ColumnLayout {
|
||||
spacing: Style.marginS * scaling
|
||||
Layout.fillWidth: true
|
||||
|
||||
NText {
|
||||
text: "Audio Visualizer"
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
Layout.bottomMargin: Style.marginS * scaling
|
||||
}
|
||||
|
||||
// AudioService Visualizer section
|
||||
NComboBox {
|
||||
id: audioVisualizerCombo
|
||||
label: "Visualization Type"
|
||||
description: "Choose a visualization type for media playback"
|
||||
model: ListModel {
|
||||
ListElement {
|
||||
key: "none"
|
||||
name: "None"
|
||||
}
|
||||
ListElement {
|
||||
key: "linear"
|
||||
name: "Linear"
|
||||
}
|
||||
ListElement {
|
||||
key: "mirrored"
|
||||
name: "Mirrored"
|
||||
}
|
||||
ListElement {
|
||||
key: "wave"
|
||||
name: "Wave"
|
||||
}
|
||||
}
|
||||
currentKey: Settings.data.audio.visualizerType
|
||||
onSelected: key => {
|
||||
Settings.data.audio.visualizerType = key
|
||||
}
|
||||
}
|
||||
|
||||
NComboBox {
|
||||
label: "Frame Rate"
|
||||
description: "Target frame rate for audio visualizer. (default: 60)"
|
||||
model: ListModel {
|
||||
ListElement {
|
||||
key: "30"
|
||||
name: "30 FPS"
|
||||
}
|
||||
ListElement {
|
||||
key: "60"
|
||||
name: "60 FPS"
|
||||
}
|
||||
ListElement {
|
||||
key: "100"
|
||||
name: "100 FPS"
|
||||
}
|
||||
ListElement {
|
||||
key: "120"
|
||||
name: "120 FPS"
|
||||
}
|
||||
ListElement {
|
||||
key: "144"
|
||||
name: "144 FPS"
|
||||
}
|
||||
ListElement {
|
||||
key: "165"
|
||||
name: "165 FPS"
|
||||
}
|
||||
ListElement {
|
||||
key: "240"
|
||||
name: "240 FPS"
|
||||
}
|
||||
}
|
||||
currentKey: Settings.data.audio.cavaFrameRate
|
||||
onSelected: key => {
|
||||
Settings.data.audio.cavaFrameRate = key
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Divider
|
||||
NDivider {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.marginXL * scaling
|
||||
Layout.bottomMargin: Style.marginXL * scaling
|
||||
}
|
||||
|
||||
// AudioService Visualizer Category
|
||||
ColumnLayout {
|
||||
spacing: Style.marginS * scaling
|
||||
Layout.fillWidth: true
|
||||
|
||||
NText {
|
||||
text: "Audio Visualizer"
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mSecondary
|
||||
Layout.bottomMargin: Style.marginS * scaling
|
||||
}
|
||||
|
||||
// AudioService Visualizer section
|
||||
NComboBox {
|
||||
id: audioVisualizerCombo
|
||||
label: "Visualization Type"
|
||||
description: "Choose a visualization type for media playback"
|
||||
model: ListModel {
|
||||
ListElement {
|
||||
key: "none"
|
||||
name: "None"
|
||||
}
|
||||
ListElement {
|
||||
key: "linear"
|
||||
name: "Linear"
|
||||
}
|
||||
ListElement {
|
||||
key: "mirrored"
|
||||
name: "Mirrored"
|
||||
}
|
||||
ListElement {
|
||||
key: "wave"
|
||||
name: "Wave"
|
||||
}
|
||||
}
|
||||
currentKey: Settings.data.audio.visualizerType
|
||||
onSelected: key => {
|
||||
Settings.data.audio.visualizerType = key
|
||||
}
|
||||
}
|
||||
|
||||
NComboBox {
|
||||
label: "Frame Rate"
|
||||
description: "Target frame rate for audio visualizer."
|
||||
model: ListModel {
|
||||
ListElement {
|
||||
key: "30"
|
||||
name: "30 FPS"
|
||||
}
|
||||
ListElement {
|
||||
key: "60"
|
||||
name: "60 FPS"
|
||||
}
|
||||
ListElement {
|
||||
key: "100"
|
||||
name: "100 FPS"
|
||||
}
|
||||
ListElement {
|
||||
key: "120"
|
||||
name: "120 FPS"
|
||||
}
|
||||
ListElement {
|
||||
key: "144"
|
||||
name: "144 FPS"
|
||||
}
|
||||
ListElement {
|
||||
key: "165"
|
||||
name: "165 FPS"
|
||||
}
|
||||
ListElement {
|
||||
key: "240"
|
||||
name: "240 FPS"
|
||||
}
|
||||
}
|
||||
currentKey: Settings.data.audio.cavaFrameRate
|
||||
onSelected: key => {
|
||||
Settings.data.audio.cavaFrameRate = key
|
||||
}
|
||||
}
|
||||
}
|
||||
// Divider
|
||||
NDivider {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Style.marginXL * scaling
|
||||
Layout.bottomMargin: Style.marginXL * scaling
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue