waffles: action center: volume menu
@@ -0,0 +1 @@
|
||||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M10.295 19.716a1 1 0 0 0 1.404-1.425l-5.37-5.29h13.67a1 1 0 1 0 0-2H6.336L11.7 5.714a1 1 0 0 0-1.404-1.424l-6.924 6.822a1.25 1.25 0 0 0 0 1.78l6.924 6.823Z" fill="#212121"/></svg>
|
||||
|
After Width: | Height: | Size: 283 B |
@@ -0,0 +1 @@
|
||||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M10.733 19.79a.75.75 0 0 0 1.034-1.086L5.516 12.75H20.25a.75.75 0 0 0 0-1.5H5.516l6.251-5.955a.75.75 0 0 0-1.034-1.086l-7.42 7.067a.995.995 0 0 0-.3.58.754.754 0 0 0 .001.289.995.995 0 0 0 .3.579l7.419 7.067Z" fill="#212121"/></svg>
|
||||
|
After Width: | Height: | Size: 336 B |
@@ -0,0 +1 @@
|
||||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M6 21.25c0 .415.336.75.75.75h8.931a1.747 1.747 0 0 1-.708-.464L14 20.496v.004h-4l-.001-2.498H11V16a1 1 0 0 1 1-1h1.533l1.44-1.536c.862-.919 2.264-.624 2.804.331.366-.244.81-.34 1.238-.275a1.75 1.75 0 0 1 2.855-1.114c.042.035.085.073.129.113V5.25l-.005-.154A2.25 2.25 0 0 0 19.749 3H4.25l-.154.005A2.25 2.25 0 0 0 2 5.25v10.502l.005.154a2.25 2.25 0 0 0 2.245 2.096h4.249V20.5H6.75l-.102.007A.75.75 0 0 0 6 21.25Z" fill="#212121"/><path d="M22.143 14.302c-.328-.547-.665-.92-.913-1.128a.75.75 0 0 0-.96 1.153c.127.105.353.356.587.747.401.669.643 1.475.643 2.426 0 .952-.242 1.758-.643 2.427-.234.39-.46.642-.587.747a.75.75 0 0 0 .96 1.153c.248-.207.585-.581.913-1.128.536-.894.857-1.963.857-3.199 0-1.235-.32-2.304-.857-3.198Z" fill="#212121"/><path d="M19.874 15.397a3.075 3.075 0 0 0-.674-.747.75.75 0 0 0-.9 1.2c.062.047.19.175.326.379.234.351.374.771.374 1.271 0 .5-.14.921-.374 1.272a1.68 1.68 0 0 1-.326.379l-.084.073a.75.75 0 0 0 .984 1.127c.189-.142.435-.388.674-.747A3.734 3.734 0 0 0 20.5 17.5c0-.812-.235-1.517-.626-2.103ZM17 14.75a.75.75 0 0 0-1.314-.493L14.16 16h-1.41a.75.75 0 0 0-.75.75v1.5c0 .415.336.75.75.75h1.41l1.526 1.744A.75.75 0 0 0 17 20.25v-5.5Z" fill="#212121"/></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1 @@
|
||||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M6 21.25c0 .415.336.75.75.75h8.931a1.747 1.747 0 0 1-.708-.464L14 20.496v.004h-4l-.001-2.498H11v-1.5H4.25l-.102-.007a.75.75 0 0 1-.648-.743V5.25l.007-.102A.75.75 0 0 1 4.25 4.5h15.499l.102.007a.75.75 0 0 1 .648.743v6.769c.474-.069.974.056 1.371.387.042.035.085.073.129.113V5.25l-.005-.154A2.25 2.25 0 0 0 19.749 3H4.25l-.154.005A2.25 2.25 0 0 0 2 5.25v10.502l.005.154a2.25 2.25 0 0 0 2.245 2.096h4.249V20.5H6.75l-.102.007A.75.75 0 0 0 6 21.25Z" fill="#212121"/><path d="M22.143 14.302c-.328-.547-.665-.92-.913-1.128a.75.75 0 0 0-.96 1.153c.127.105.353.356.587.747.401.669.643 1.475.643 2.426 0 .952-.242 1.758-.643 2.427-.234.39-.46.642-.587.747a.75.75 0 0 0 .96 1.153c.248-.207.585-.581.913-1.128.536-.894.857-1.963.857-3.199 0-1.235-.32-2.304-.857-3.198Z" fill="#212121"/><path d="M19.874 15.397a3.075 3.075 0 0 0-.674-.747.75.75 0 0 0-.9 1.2c.062.047.19.175.326.379.234.351.374.771.374 1.271 0 .5-.14.921-.374 1.272a1.68 1.68 0 0 1-.326.379l-.084.073a.75.75 0 0 0 .984 1.127c.189-.142.435-.388.674-.747A3.734 3.734 0 0 0 20.5 17.5c0-.812-.235-1.517-.626-2.103ZM17 14.75a.75.75 0 0 0-1.314-.493L14.16 16h-1.41a.75.75 0 0 0-.75.75v1.5c0 .415.336.75.75.75h1.41l1.526 1.744A.75.75 0 0 0 17 20.25v-5.5Z" fill="#212121"/></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1 @@
|
||||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 2c5.523 0 10 4.477 10 10v7a3 3 0 0 1-3 3h-3a1 1 0 0 1-1-1v-6a1 1 0 0 1 1-1h4.5v-2a8.5 8.5 0 0 0-17 0v2H8a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H5a3 3 0 0 1-3-3v-7C2 6.477 6.477 2 12 2Z" fill="#212121"/></svg>
|
||||
|
After Width: | Height: | Size: 307 B |
@@ -0,0 +1 @@
|
||||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M22 12v7a3 3 0 0 1-2.824 2.995L19 22h-3a1 1 0 0 1-.993-.883L15 21v-6a1 1 0 0 1 .883-.993L16 14h4.5v-2a8.5 8.5 0 0 0-17 0v2H8a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H5a3 3 0 0 1-3-3v-7C2 6.477 6.477 2 12 2s10 4.477 10 10v7-7ZM7.5 15.5h-4V19A1.5 1.5 0 0 0 5 20.5h2.5v-5Zm13 0h-4v5H19a1.5 1.5 0 0 0 1.493-1.355L20.5 19v-3.5Z" fill="#212121"/></svg>
|
||||
|
After Width: | Height: | Size: 439 B |
@@ -0,0 +1 @@
|
||||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M15 4.25c0-1.079-1.274-1.65-2.08-.934L8.427 7.309a.75.75 0 0 1-.498.19H4.25A2.25 2.25 0 0 0 2 9.749v4.497a2.25 2.25 0 0 0 2.25 2.25h3.68a.75.75 0 0 1 .498.19l4.491 3.994c.806.716 2.081.144 2.081-.934V4.25ZM16.22 9.22a.75.75 0 0 1 1.06 0L19 10.94l1.72-1.72a.75.75 0 1 1 1.06 1.06L20.06 12l1.72 1.72a.75.75 0 1 1-1.06 1.06L19 13.06l-1.72 1.72a.75.75 0 1 1-1.06-1.06L17.94 12l-1.72-1.72a.75.75 0 0 1 0-1.06Z" fill="#212121"/></svg>
|
||||
|
After Width: | Height: | Size: 532 B |
@@ -0,0 +1 @@
|
||||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12.92 3.316c.806-.717 2.08-.145 2.08.934v15.496c0 1.078-1.274 1.65-2.08.934l-4.492-3.994a.75.75 0 0 0-.498-.19H4.25A2.25 2.25 0 0 1 2 14.247V9.75a2.25 2.25 0 0 1 2.25-2.25h3.68a.75.75 0 0 0 .498-.19l4.491-3.993Zm.58 1.49L9.425 8.43A2.25 2.25 0 0 1 7.93 9H4.25a.75.75 0 0 0-.75.75v4.497c0 .415.336.75.75.75h3.68a2.25 2.25 0 0 1 1.495.57l4.075 3.623V4.807ZM16.22 9.22a.75.75 0 0 1 1.06 0L19 10.94l1.72-1.72a.75.75 0 1 1 1.06 1.06L20.06 12l1.72 1.72a.75.75 0 1 1-1.06 1.06L19 13.06l-1.72 1.72a.75.75 0 1 1-1.06-1.06L17.94 12l-1.72-1.72a.75.75 0 0 1 0-1.06Z" fill="#212121"/></svg>
|
||||
|
After Width: | Height: | Size: 682 B |
@@ -1,12 +1,12 @@
|
||||
import QtQuick
|
||||
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
|
||||
Loader {
|
||||
id: root
|
||||
property bool shown: true
|
||||
property alias fade: opacityBehavior.enabled
|
||||
property alias animation: opacityBehavior.animation
|
||||
opacity: shown ? 1 : 0
|
||||
visible: opacity > 0
|
||||
active: opacity > 0
|
||||
|
||||
@@ -13,7 +13,9 @@ Item {
|
||||
property bool alternativeVisibleCondition: false
|
||||
property real horizontalPadding: 10
|
||||
property real verticalPadding: 5
|
||||
|
||||
property real horizontalMargin: horizontalPadding
|
||||
property real verticalMargin: verticalPadding
|
||||
|
||||
function updateAnchor() {
|
||||
tooltipLoader.item?.anchor.updateAnchor();
|
||||
}
|
||||
@@ -49,8 +51,8 @@ Item {
|
||||
}
|
||||
|
||||
color: "transparent"
|
||||
implicitWidth: root.contentItem.implicitWidth + root.horizontalPadding * 2
|
||||
implicitHeight: root.contentItem.implicitHeight + root.verticalPadding * 2
|
||||
implicitWidth: root.contentItem.implicitWidth + root.horizontalMargin * 2
|
||||
implicitHeight: root.contentItem.implicitHeight + root.verticalMargin * 2
|
||||
|
||||
data: [root.contentItem]
|
||||
}
|
||||
|
||||
@@ -10,15 +10,8 @@ import Quickshell.Services.Pipewire
|
||||
ColumnLayout {
|
||||
id: root
|
||||
required property bool isSink
|
||||
function correctType(node) {
|
||||
return (node.isSink === root.isSink) && node.audio
|
||||
}
|
||||
readonly property list<var> appPwNodes: Pipewire.nodes.values.filter((node) => { // Should be list<PwNode> but it breaks ScriptModel
|
||||
return root.correctType(node) && node.isStream
|
||||
})
|
||||
readonly property list<var> devices: Pipewire.nodes.values.filter(node => {
|
||||
return root.correctType(node) && !node.isStream
|
||||
})
|
||||
readonly property list<var> appPwNodes: isSink ? Audio.outputAppNodes : Audio.inputAppNodes
|
||||
readonly property list<var> devices: isSink ? Audio.outputDevices : Audio.inputDevices
|
||||
readonly property bool hasApps: appPwNodes.length > 0
|
||||
spacing: 16
|
||||
|
||||
@@ -44,21 +37,21 @@ ColumnLayout {
|
||||
Layout.fillHeight: false
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: 6
|
||||
model: root.devices.map(node => (node.nickname || node.description || Translation.tr("Unknown")))
|
||||
model: root.devices.map(node => Audio.friendlyDeviceName(node))
|
||||
currentIndex: root.devices.findIndex(item => {
|
||||
if (root.isSink) {
|
||||
return item.id === Pipewire.preferredDefaultAudioSink?.id
|
||||
return item.id === Pipewire.defaultAudioSink?.id
|
||||
} else {
|
||||
return item.id === Pipewire.preferredDefaultAudioSource?.id
|
||||
return item.id === Pipewire.defaultAudioSource?.id
|
||||
}
|
||||
})
|
||||
onActivated: (index) => {
|
||||
print(index)
|
||||
const item = root.devices[index]
|
||||
if (root.isSink) {
|
||||
Pipewire.preferredDefaultAudioSink = item
|
||||
Audio.setDefaultSink(item)
|
||||
} else {
|
||||
Pipewire.preferredDefaultAudioSource = item
|
||||
Audio.setDefaultSource(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ Item {
|
||||
id: root
|
||||
required property PwNode node
|
||||
PwObjectTracker {
|
||||
objects: [node]
|
||||
objects: [root.node]
|
||||
}
|
||||
|
||||
implicitHeight: rowLayout.implicitHeight
|
||||
@@ -47,7 +47,7 @@ Item {
|
||||
elide: Text.ElideRight
|
||||
text: {
|
||||
// application.name -> description -> name
|
||||
const app = root.node?.properties["application.name"] ?? (root.node.description != "" ? root.node.description : root.node.name);
|
||||
const app = Audio.appNodeDisplayName(root.node);
|
||||
const media = root.node.properties["media.name"];
|
||||
return media != undefined ? `${app} • ${media}` : app;
|
||||
}
|
||||
|
||||
@@ -7,32 +7,34 @@ import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.actionCenter.mainPage
|
||||
|
||||
WBarAttachedPanelContent {
|
||||
id: root
|
||||
|
||||
contentItem: StackView {
|
||||
implicitWidth: currentItem.implicitWidth
|
||||
implicitHeight: currentItem.implicitHeight
|
||||
contentItem: StackView { // TODO: Make this a WStackView with proper anim
|
||||
id: stackView
|
||||
anchors.fill: parent
|
||||
implicitWidth: initItem.implicitWidth
|
||||
implicitHeight: initItem.implicitHeight
|
||||
|
||||
initialItem: ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: 0
|
||||
|
||||
ActionCenterBody {}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillHeight: false
|
||||
Layout.fillWidth: true
|
||||
color: Looks.colors.bgPanelSeparator
|
||||
implicitHeight: 1
|
||||
}
|
||||
|
||||
ActionCenterFooter {}
|
||||
initialItem: PageColumn {
|
||||
id: initItem
|
||||
MainPageBody {}
|
||||
Separator {}
|
||||
MainPageFooter {}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
ActionCenterContext.stackView = this
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.BackButton
|
||||
onClicked: {
|
||||
ActionCenterContext.back()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,4 +9,17 @@ Singleton {
|
||||
|
||||
property StackView stackView
|
||||
|
||||
function push(component) {
|
||||
if (stackView) {
|
||||
item = stackView.push(component)
|
||||
stackView.implicitWidth = item.implicitWidth
|
||||
stackView.implicitHeight = item.implicitHeight
|
||||
}
|
||||
}
|
||||
|
||||
function back() {
|
||||
if (stackView && stackView.depth > 1) {
|
||||
stackView.pop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
Rectangle {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
color: Looks.colors.bgPanelBody
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
Rectangle {
|
||||
Layout.fillHeight: false
|
||||
Layout.fillWidth: true
|
||||
color: Looks.colors.bgPanelFooter
|
||||
|
||||
implicitWidth: 360
|
||||
implicitHeight: 47
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.actionCenter
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
|
||||
required property string title
|
||||
spacing: 4
|
||||
|
||||
WPanelIconButton {
|
||||
iconName: "arrow-left"
|
||||
onClicked: ActionCenterContext.back()
|
||||
}
|
||||
|
||||
WText {
|
||||
id: titleText
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
text: root.title
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 0
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
WText {
|
||||
Layout.leftMargin: 12
|
||||
Layout.rightMargin: 12
|
||||
Layout.topMargin: 6
|
||||
Layout.bottomMargin: 6
|
||||
|
||||
font {
|
||||
weight: Looks.font.weight.stronger
|
||||
pixelSize: Looks.font.pixelSize.large
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
Rectangle {
|
||||
Layout.fillHeight: false
|
||||
Layout.fillWidth: true
|
||||
color: Looks.colors.bgPanelSeparator
|
||||
implicitHeight: 1
|
||||
}
|
||||
@@ -54,7 +54,8 @@ Scope {
|
||||
|
||||
ActionCenterContent {
|
||||
id: content
|
||||
anchors.centerIn: parent
|
||||
anchors.fill: parent
|
||||
anchors.margins: visualMargin
|
||||
|
||||
focus: true
|
||||
Keys.onPressed: event => { // Esc to close
|
||||
|
||||
@@ -6,15 +6,10 @@ import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.actionCenter
|
||||
|
||||
Rectangle {
|
||||
BodyRectangle {
|
||||
id: root
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
color: Looks.colors.bgPanelBody
|
||||
|
||||
implicitWidth: 360
|
||||
implicitHeight: contentLayout.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
@@ -22,7 +17,7 @@ Rectangle {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
ActionCenterBodyToggles {
|
||||
MainPageBodyToggles {
|
||||
id: togglesContainer
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
@@ -33,7 +28,7 @@ Rectangle {
|
||||
color: Looks.colors.bg1Border
|
||||
}
|
||||
|
||||
ActionCenterBodySliders {
|
||||
MainPageBodySliders {
|
||||
Layout.margins: 12
|
||||
Layout.topMargin: 18
|
||||
Layout.bottomMargin: 14
|
||||
@@ -6,6 +6,8 @@ import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.actionCenter
|
||||
import qs.modules.waffle.actionCenter.volumeControl
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
@@ -58,6 +60,13 @@ ColumnLayout {
|
||||
}
|
||||
|
||||
WPanelIconButton {
|
||||
Component {
|
||||
id: volumeControlComp
|
||||
VolumeControl {}
|
||||
}
|
||||
onClicked: {
|
||||
ActionCenterContext.push(volumeControlComp)
|
||||
}
|
||||
contentItem: Item {
|
||||
anchors.centerIn: parent
|
||||
Row {
|
||||
@@ -6,14 +6,9 @@ import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.actionCenter
|
||||
|
||||
Rectangle {
|
||||
Layout.fillHeight: false
|
||||
Layout.fillWidth: true
|
||||
color: Looks.colors.bgPanelFooter
|
||||
|
||||
implicitWidth: 360
|
||||
implicitHeight: 47
|
||||
FooterRectangle {
|
||||
|
||||
// Battery button
|
||||
WPanelFooterButton {
|
||||
@@ -0,0 +1,160 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.actionCenter
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
implicitWidth: 360
|
||||
implicitHeight: 352
|
||||
|
||||
PageColumn {
|
||||
anchors.fill: parent
|
||||
|
||||
BodyRectangle {
|
||||
implicitHeight: 400
|
||||
implicitWidth: 50
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 4
|
||||
spacing: 4
|
||||
|
||||
HeaderRow {
|
||||
Layout.fillWidth: true
|
||||
title: qsTr("Sound output")
|
||||
}
|
||||
|
||||
StyledFlickable {
|
||||
id: flickable
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
contentHeight: contentLayout.implicitHeight
|
||||
contentWidth: width
|
||||
clip: true
|
||||
|
||||
AudioChoices {
|
||||
id: contentLayout
|
||||
width: flickable.width
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Separator {}
|
||||
|
||||
FooterRectangle {
|
||||
WButton {
|
||||
id: moreSettingsButton
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: parent.left
|
||||
}
|
||||
inset: 0
|
||||
implicitHeight: 40
|
||||
implicitWidth: contentItem.implicitWidth + 30
|
||||
color: "transparent"
|
||||
|
||||
onClicked: {
|
||||
Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "sidebarLeft", "toggle"]);
|
||||
Quickshell.execDetached(["bash", "-c", Config.options.apps.volumeMixer]);
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
anchors.centerIn: parent
|
||||
implicitWidth: buttonText.implicitWidth
|
||||
WText {
|
||||
id: buttonText
|
||||
anchors.centerIn: parent
|
||||
text: qsTr("More volume settings")
|
||||
color: moreSettingsButton.pressed ? Looks.colors.fg : Looks.colors.fg1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component AudioChoices: ColumnLayout {
|
||||
spacing: 4
|
||||
|
||||
SectionText {
|
||||
text: qsTr("Output device")
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: Audio.outputDevices
|
||||
}
|
||||
delegate: WChoiceButton {
|
||||
required property var modelData
|
||||
icon.name: WIcons.audioDeviceIcon(modelData)
|
||||
text: Audio.friendlyDeviceName(modelData)
|
||||
checked: Audio.sink === modelData
|
||||
onClicked: {
|
||||
Audio.setDefaultSink(modelData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Separator {
|
||||
visible: EasyEffects.available
|
||||
color: Looks.colors.bg2Hover
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
SectionText {
|
||||
visible: EasyEffects.available
|
||||
text: qsTr("Sound effects")
|
||||
}
|
||||
|
||||
WChoiceButton {
|
||||
visible: EasyEffects.available
|
||||
text: Translation.tr("Off")
|
||||
checked: !EasyEffects.active
|
||||
onClicked: EasyEffects.disable()
|
||||
}
|
||||
|
||||
WChoiceButton {
|
||||
visible: EasyEffects.available
|
||||
text: "EasyEffects"
|
||||
checked: EasyEffects.active
|
||||
onClicked: EasyEffects.enable()
|
||||
}
|
||||
|
||||
Separator {
|
||||
color: Looks.colors.bg2Hover
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
SectionText {
|
||||
visible: EasyEffects.available
|
||||
text: qsTr("Volume mixer")
|
||||
}
|
||||
|
||||
VolumeEntry {
|
||||
node: Audio.sink
|
||||
icon: "speaker"
|
||||
monochrome: true
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: Audio.outputAppNodes
|
||||
}
|
||||
delegate: VolumeEntry {
|
||||
required property var modelData
|
||||
node: modelData
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Services.Pipewire
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.actionCenter
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
required property PwNode node
|
||||
property alias icon: iconButton.iconName
|
||||
property alias monochrome: iconButton.monochrome
|
||||
monochrome: false
|
||||
|
||||
PwObjectTracker { // Necessary for useful info to be present in 'node'
|
||||
objects: [root.node]
|
||||
}
|
||||
|
||||
WPanelIconButton {
|
||||
id: iconButton
|
||||
iconName: WIcons.audioAppIcon(root.node)
|
||||
onClicked: root.node.audio.muted = !root.node?.audio.muted
|
||||
|
||||
FluentIcon {
|
||||
id: muteIcon
|
||||
visible: root.node?.audio.muted ?? false
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
right: parent.right
|
||||
margins: -1
|
||||
}
|
||||
implicitSize: 14
|
||||
icon: "speaker-mute"
|
||||
}
|
||||
|
||||
WToolTip {
|
||||
extraVisibleCondition: iconButton.shouldShowTooltip
|
||||
text: Audio.appNodeDisplayName(root.node)
|
||||
}
|
||||
}
|
||||
|
||||
WSlider {
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 10
|
||||
value: root.node?.audio.volume ?? 0
|
||||
onMoved: root.node.audio.volume = value
|
||||
}
|
||||
}
|
||||
@@ -5,41 +5,21 @@ import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
Button {
|
||||
WButton {
|
||||
id: root
|
||||
|
||||
property var altAction: () => {}
|
||||
property var middleClickAction: () => {}
|
||||
|
||||
property color colBackground: ColorUtils.transparentize(Looks.colors.bg1)
|
||||
property color colBackgroundHover: Looks.colors.bg1Hover
|
||||
property color colBackgroundActive: Looks.colors.bg1Active
|
||||
colBackground: ColorUtils.transparentize(Looks.colors.bg1)
|
||||
colBackgroundHover: Looks.colors.bg1Hover
|
||||
colBackgroundActive: Looks.colors.bg1Active
|
||||
property color colBackgroundBorder
|
||||
property color color
|
||||
Layout.fillHeight: true
|
||||
topInset: 4
|
||||
bottomInset: 4
|
||||
|
||||
signal hoverTimedOut()
|
||||
property bool shouldShowTooltip: false
|
||||
property Timer hoverTimer: Timer {
|
||||
id: hoverTimer
|
||||
running: root.hovered
|
||||
interval: 400
|
||||
onTriggered: {
|
||||
root.hoverTimedOut()
|
||||
}
|
||||
}
|
||||
onHoverTimedOut: {
|
||||
root.shouldShowTooltip = true
|
||||
}
|
||||
onHoveredChanged: {
|
||||
if (!root.hovered) {
|
||||
root.shouldShowTooltip = false
|
||||
root.hoverTimer.stop()
|
||||
}
|
||||
}
|
||||
|
||||
colBackgroundBorder: ColorUtils.transparentize(Looks.colors.bg1Border, (root.checked || root.hovered) ? Looks.contentTransparency : 1)
|
||||
color: {
|
||||
if (root.down) {
|
||||
|
||||
@@ -37,6 +37,7 @@ BarButton {
|
||||
}
|
||||
}
|
||||
FluentIcon {
|
||||
visible: Notifications.silent
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
icon: "alert-snooze"
|
||||
implicitSize: 18
|
||||
|
||||
@@ -105,8 +105,8 @@ RowLayout {
|
||||
BarToolTip {
|
||||
id: pinTooltip
|
||||
extraVisibleCondition: trayButton.Drag.active && pinDropArea.containsDrag && pinDropArea.willPin
|
||||
realContentHorizontalPadding: 6
|
||||
realContentVerticalPadding: 6
|
||||
horizontalPadding: 6
|
||||
verticalPadding: 6
|
||||
realContentItem: FluentIcon {
|
||||
anchors.centerIn: parent
|
||||
icon: "pin-off"
|
||||
|
||||
@@ -76,7 +76,8 @@ Singleton {
|
||||
}
|
||||
property QtObject pixelSize: QtObject {
|
||||
property real normal: 11
|
||||
property real large: 14
|
||||
property real large: 13
|
||||
property real larger: 15
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -78,11 +78,14 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Rectangle {
|
||||
id: contentArea
|
||||
color: "red"
|
||||
z: 0
|
||||
anchors.fill: borderRect
|
||||
anchors.margins: borderRect.border.width
|
||||
implicitWidth: contentItem.implicitWidth
|
||||
implicitHeight: contentItem.implicitHeight
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
|
||||
@@ -15,6 +15,7 @@ Button {
|
||||
property color colBackgroundToggled: Looks.colors.accent
|
||||
property color colBackgroundToggledHover: Looks.colors.accentHover
|
||||
property color colBackgroundToggledActive: Looks.colors.accentActive
|
||||
property color colForeground: Looks.colors.fg
|
||||
property alias backgroundOpacity: backgroundRect.opacity
|
||||
property color color: {
|
||||
if (root.checked) {
|
||||
@@ -35,7 +36,29 @@ Button {
|
||||
}
|
||||
}
|
||||
|
||||
// Hover stuff
|
||||
signal hoverTimedOut()
|
||||
property bool shouldShowTooltip: false
|
||||
property Timer hoverTimer: Timer {
|
||||
id: hoverTimer
|
||||
running: root.hovered
|
||||
interval: 400
|
||||
onTriggered: {
|
||||
root.hoverTimedOut()
|
||||
}
|
||||
}
|
||||
onHoverTimedOut: {
|
||||
root.shouldShowTooltip = true
|
||||
}
|
||||
onHoveredChanged: {
|
||||
if (!root.hovered) {
|
||||
root.shouldShowTooltip = false
|
||||
root.hoverTimer.stop()
|
||||
}
|
||||
}
|
||||
|
||||
property alias monochromeIcon: buttonIcon.monochrome
|
||||
property alias buttonSpacing: contentLayout.spacing
|
||||
property bool forceShowIcon: false
|
||||
|
||||
property var altAction: () => {}
|
||||
@@ -93,16 +116,16 @@ Button {
|
||||
spacing: 12
|
||||
FluentIcon {
|
||||
id: buttonIcon
|
||||
visible: root.icon.name !== "" || root.forceShowIcon
|
||||
monochrome: true
|
||||
implicitSize: 16
|
||||
Layout.leftMargin: 6
|
||||
Layout.leftMargin: root.iconLeftMargin
|
||||
Layout.fillWidth: false
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
icon: root.icon.name
|
||||
color: root.colForeground
|
||||
visible: root.icon.name !== ""
|
||||
}
|
||||
WText {
|
||||
Layout.rightMargin: 12
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
|
||||
text: root.text
|
||||
@@ -110,6 +133,7 @@ Button {
|
||||
font {
|
||||
pixelSize: Looks.font.pixelSize.large
|
||||
}
|
||||
color: root.colForeground
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
WButton {
|
||||
id: root
|
||||
|
||||
Layout.fillWidth: true
|
||||
implicitWidth: contentItem.implicitWidth
|
||||
horizontalPadding: 10
|
||||
verticalPadding: 11
|
||||
inset: 0
|
||||
buttonSpacing: 8
|
||||
|
||||
property color color: {
|
||||
if (root.checked) {
|
||||
if (root.down) {
|
||||
return root.colBackgroundHover;
|
||||
} else if (root.hovered && !root.down) {
|
||||
return root.colBackgroundActive;
|
||||
} else {
|
||||
return root.colBackgroundHover;
|
||||
}
|
||||
}
|
||||
if (root.down) {
|
||||
return root.colBackgroundActive;
|
||||
} else if (root.hovered && !root.down) {
|
||||
return root.colBackgroundHover;
|
||||
} else {
|
||||
return root.colBackground;
|
||||
}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
id: backgroundRect
|
||||
radius: Looks.radius.medium
|
||||
color: root.color
|
||||
Behavior on color {
|
||||
animation: Looks.transition.color.createObject(this)
|
||||
}
|
||||
|
||||
WFadeLoader {
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
shown: root.checked
|
||||
sourceComponent: Rectangle {
|
||||
implicitWidth: 3
|
||||
implicitHeight: 3
|
||||
radius: width / 2
|
||||
color: Looks.colors.accent
|
||||
Component.onCompleted: {
|
||||
implicitHeight = 16;
|
||||
}
|
||||
|
||||
Behavior on implicitHeight {
|
||||
animation: Looks.transition.opacity.createObject(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import QtQuick
|
||||
import qs.modules.common
|
||||
|
||||
// Yes, this is (mostly) a copy of FadeLoader.
|
||||
// The animation of a Behavior cannot be changed... I'd love to be proven wrong.
|
||||
Loader {
|
||||
id: root
|
||||
property bool shown: true
|
||||
property alias fade: opacityBehavior.enabled
|
||||
property alias animation: opacityBehavior.animation
|
||||
opacity: shown ? 1 : 0
|
||||
visible: opacity > 0
|
||||
active: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
id: opacityBehavior
|
||||
animation: Looks.transition.opacity.createObject(null)
|
||||
}
|
||||
}
|
||||
@@ -8,24 +8,34 @@ Singleton {
|
||||
id: root
|
||||
|
||||
property string internetIcon: {
|
||||
if (Network.ethernet) return "ethernet";
|
||||
if (Network.ethernet)
|
||||
return "ethernet";
|
||||
if (Network.wifiEnabled) {
|
||||
const strength = Network.networkStrength;
|
||||
if (strength > 75) return "wifi-1";
|
||||
if (strength > 50) return "wifi-2";
|
||||
if (strength > 25) return "wifi-3";
|
||||
if (strength > 75)
|
||||
return "wifi-1";
|
||||
if (strength > 50)
|
||||
return "wifi-2";
|
||||
if (strength > 25)
|
||||
return "wifi-3";
|
||||
return "wifi-4";
|
||||
}
|
||||
if (Network.wifiStatus === "connecting") return "wifi-4";
|
||||
if (Network.wifiStatus === "disconnected") return "wifi-off";
|
||||
if (Network.wifiStatus === "disabled") return "wifi-off";
|
||||
if (Network.wifiStatus === "connecting")
|
||||
return "wifi-4";
|
||||
if (Network.wifiStatus === "disconnected")
|
||||
return "wifi-off";
|
||||
if (Network.wifiStatus === "disabled")
|
||||
return "wifi-off";
|
||||
return "wifi-warning";
|
||||
}
|
||||
|
||||
property string batteryIcon: {
|
||||
if (Battery.isCharging) return "battery-charge";
|
||||
if (Battery.isCriticalAndNotCharging) return "battery-warning";
|
||||
if (Battery.percentage >= 0.9) return "battery-full";
|
||||
if (Battery.isCharging)
|
||||
return "battery-charge";
|
||||
if (Battery.isCriticalAndNotCharging)
|
||||
return "battery-warning";
|
||||
if (Battery.percentage >= 0.9)
|
||||
return "battery-full";
|
||||
return `battery-${Math.ceil(Battery.percentage * 10)}`;
|
||||
}
|
||||
|
||||
@@ -33,7 +43,7 @@ Singleton {
|
||||
const muted = Audio.sink?.audio.muted ?? false;
|
||||
const volume = Audio.sink?.audio.volume ?? 0;
|
||||
if (muted)
|
||||
return volume > 0 ? "speaker-off" : "speaker-none";
|
||||
return "speaker-mute";
|
||||
if (volume == 0)
|
||||
return "speaker-none";
|
||||
if (volume < 0.5)
|
||||
@@ -53,10 +63,39 @@ Singleton {
|
||||
property string notificationsIcon: Notifications.silent ? "alert-snooze" : "alert"
|
||||
|
||||
property string powerProfileIcon: {
|
||||
switch(PowerProfiles.profile) {
|
||||
case PowerProfile.PowerSaver: return "leaf-two";
|
||||
case PowerProfile.Balanced: return "flash-on";
|
||||
case PowerProfile.Performance: return "fire";
|
||||
switch (PowerProfiles.profile) {
|
||||
case PowerProfile.PowerSaver:
|
||||
return "leaf-two";
|
||||
case PowerProfile.Balanced:
|
||||
return "flash-on";
|
||||
case PowerProfile.Performance:
|
||||
return "fire";
|
||||
}
|
||||
}
|
||||
|
||||
function audioDeviceIcon(node) {
|
||||
if (!node.isSink)
|
||||
return "mic-on";
|
||||
const monitor = /monitor|hdmi/i;
|
||||
const headphones = /headset|headphone|bluez|wireless/i;
|
||||
const speakers = /speaker|output/i;
|
||||
if (monitor.test(node.nickname) || monitor.test(node.description) || monitor.test(node.name)) {
|
||||
return "desktop-speaker";
|
||||
}
|
||||
if (headphones.test(node.nickname) || headphones.test(node.description) || headphones.test(node.name)) {
|
||||
return "headphones";
|
||||
}
|
||||
if (speakers.test(node.nickname) || speakers.test(node.description) || speakers.test(node.name)) {
|
||||
return "speaker";
|
||||
}
|
||||
return "speaker";
|
||||
}
|
||||
|
||||
function audioAppIcon(node) {
|
||||
let icon;
|
||||
icon = AppSearch.guessIcon(node?.properties["application.icon-name"] ?? "");
|
||||
if (AppSearch.iconExists(icon)) return icon;
|
||||
icon = AppSearch.guessIcon(node?.properties["node.name"] ?? "");
|
||||
return icon;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ WButton {
|
||||
id: root
|
||||
|
||||
property alias iconName: iconContent.icon
|
||||
property alias monochrome: iconContent.monochrome
|
||||
inset: 0
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
|
||||
@@ -10,37 +10,22 @@ import qs.modules.waffle.looks
|
||||
PopupToolTip {
|
||||
id: root
|
||||
|
||||
property Item realContentItem
|
||||
required property Item realContentItem
|
||||
realContentItem: WText {
|
||||
text: root.text
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
property real visualMargin: 11
|
||||
verticalPadding: visualMargin
|
||||
horizontalPadding: visualMargin
|
||||
property real realContentVerticalPadding: 8
|
||||
property real realContentHorizontalPadding: 10
|
||||
verticalPadding: 8
|
||||
horizontalPadding: 10
|
||||
verticalMargin: visualMargin
|
||||
horizontalMargin: visualMargin
|
||||
|
||||
contentItem: Item {
|
||||
anchors.centerIn: parent
|
||||
implicitWidth: realContent.implicitWidth + 2 * 2
|
||||
implicitHeight: realContent.implicitHeight + 2 * 2
|
||||
|
||||
WAmbientShadow {
|
||||
target: realContent
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: realContent
|
||||
z: 1
|
||||
anchors.centerIn: parent
|
||||
implicitWidth: root.realContentItem.implicitWidth + root.realContentHorizontalPadding * 2
|
||||
implicitHeight: root.realContentItem.implicitHeight + root.realContentVerticalPadding * 2
|
||||
color: Looks.colors.bg1
|
||||
radius: Looks.radius.medium
|
||||
|
||||
children: [root.realContentItem]
|
||||
}
|
||||
contentItem: WToolTipContent {
|
||||
id: tooltipContent
|
||||
realContentItem: root.realContentItem
|
||||
horizontalPadding: root.horizontalPadding
|
||||
verticalPadding: root.verticalPadding
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,7 @@ Slider {
|
||||
id: root
|
||||
|
||||
property real trackWidth: 4
|
||||
// leftPadding: handle.width / 2
|
||||
// rightPadding: handle.width / 2
|
||||
property string tooltipContent: `${Math.round(value * 100)}`
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
|
||||
@@ -77,5 +76,14 @@ Slider {
|
||||
animation: Looks.transition.enter.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
WToolTip {
|
||||
id: tooltip
|
||||
extraVisibleCondition: root.pressed
|
||||
text: root.tooltipContent
|
||||
font.pixelSize: Looks.font.pixelSize.larger
|
||||
verticalPadding: 3
|
||||
horizontalPadding: 8
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
StyledToolTip {
|
||||
id: root
|
||||
|
||||
required property Item realContentItem
|
||||
font {
|
||||
family: Looks.font.family.ui
|
||||
pixelSize: Looks.font.pixelSize.normal
|
||||
weight: Looks.font.weight.regular
|
||||
}
|
||||
realContentItem: WText {
|
||||
text: root.text
|
||||
font: root.font
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
verticalPadding: 8
|
||||
horizontalPadding: 10
|
||||
|
||||
contentItem: WToolTipContent {
|
||||
id: tooltipContent
|
||||
realContentItem: root.realContentItem
|
||||
horizontalPadding: root.horizontalPadding
|
||||
verticalPadding: root.verticalPadding
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
Item {
|
||||
id: root
|
||||
anchors.centerIn: parent
|
||||
required property Item realContentItem
|
||||
property real verticalPadding: 8
|
||||
property real horizontalPadding: 10
|
||||
implicitWidth: realContent.implicitWidth + 2 * 2
|
||||
implicitHeight: realContent.implicitHeight + 2 * 2
|
||||
|
||||
WAmbientShadow {
|
||||
target: realContent
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: realContent
|
||||
z: 1
|
||||
anchors.centerIn: parent
|
||||
implicitWidth: root.realContentItem.implicitWidth + root.horizontalPadding * 2
|
||||
implicitHeight: root.realContentItem.implicitHeight + root.verticalPadding * 2
|
||||
color: Looks.colors.bg1
|
||||
radius: Looks.radius.medium
|
||||
|
||||
children: [root.realContentItem]
|
||||
}
|
||||
}
|
||||
@@ -11,15 +11,43 @@ import Quickshell.Services.Pipewire
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
// Misc props
|
||||
property bool ready: Pipewire.defaultAudioSink?.ready ?? false
|
||||
property PwNode sink: Pipewire.defaultAudioSink
|
||||
property PwNode source: Pipewire.defaultAudioSource
|
||||
readonly property real hardMaxValue: 2.00 // People keep joking about setting volume to 5172% so...
|
||||
property string audioTheme: Config.options.sounds.theme
|
||||
property real value: sink?.audio.volume ?? 0
|
||||
|
||||
function friendlyDeviceName(node) {
|
||||
return (node.nickname || node.description || Translation.tr("Unknown"));
|
||||
}
|
||||
function appNodeDisplayName(node) {
|
||||
return (node.properties["application.name"] || node.description || node.name)
|
||||
}
|
||||
|
||||
// Lists
|
||||
function correctType(node, isSink) {
|
||||
return (node.isSink === isSink) && node.audio
|
||||
}
|
||||
function appNodes(isSink) {
|
||||
return Pipewire.nodes.values.filter((node) => { // Should be list<PwNode> but it breaks ScriptModel
|
||||
return root.correctType(node, isSink) && node.isStream
|
||||
})
|
||||
}
|
||||
function devices(isSink) {
|
||||
return Pipewire.nodes.values.filter(node => {
|
||||
return root.correctType(node, isSink) && !node.isStream
|
||||
})
|
||||
}
|
||||
readonly property list<var> outputAppNodes: root.appNodes(true)
|
||||
readonly property list<var> inputAppNodes: root.appNodes(false)
|
||||
readonly property list<var> outputDevices: root.devices(true)
|
||||
readonly property list<var> inputDevices: root.devices(false)
|
||||
|
||||
// Signals
|
||||
signal sinkProtectionTriggered(string reason);
|
||||
|
||||
// Controls
|
||||
function toggleMute() {
|
||||
Audio.sink.audio.muted = !Audio.sink.audio.muted
|
||||
}
|
||||
@@ -39,8 +67,16 @@ Singleton {
|
||||
const step = currentVolume < 0.1 ? 0.01 : 0.02 || 0.2;
|
||||
Audio.sink.audio.volume -= step;
|
||||
}
|
||||
|
||||
|
||||
function setDefaultSink(node) {
|
||||
Pipewire.preferredDefaultAudioSink = node;
|
||||
}
|
||||
|
||||
function setDefaultSource(node) {
|
||||
Pipewire.preferredDefaultAudioSource = node;
|
||||
}
|
||||
|
||||
// Internals
|
||||
PwObjectTracker {
|
||||
objects: [sink, source]
|
||||
}
|
||||
|
||||