Compare commits
11 commits
c3a1bb4553
...
9873324823
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9873324823 | ||
|
|
89cf6f315b | ||
|
|
38fce255d8 | ||
|
|
4047c52ac9 | ||
|
|
30a0da7032 | ||
|
|
350a9f4fb2 | ||
|
|
f904c78132 | ||
|
|
7d1c466ec5 | ||
|
|
cb6cf28c13 | ||
|
|
871e654af2 | ||
|
|
3d34780820 |
14 changed files with 98 additions and 169 deletions
|
|
@ -1,11 +1,10 @@
|
||||||
import qs.config
|
import qs.config
|
||||||
import qs.constants
|
import qs.constants
|
||||||
import qs.styled
|
|
||||||
import qs.widgets
|
import qs.widgets
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
|
|
||||||
StyledButton {
|
StyledButton {
|
||||||
id: clickable
|
id: root
|
||||||
|
|
||||||
border.color: process.running ? Theme.palette.secondary : 'transparent'
|
border.color: process.running ? Theme.palette.secondary : 'transparent'
|
||||||
border.width: 2
|
border.width: 2
|
||||||
|
|
@ -27,7 +26,7 @@ StyledButton {
|
||||||
font.bold: true
|
font.bold: true
|
||||||
text: Icons.coffee
|
text: Icons.coffee
|
||||||
|
|
||||||
color: clickable.containsMouse ? Theme.palette.base300 : Theme.palette.basecontent
|
color: root.containsMouse ? Theme.palette.base300 : Theme.palette.basecontent
|
||||||
}
|
}
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
|
|
|
||||||
|
|
@ -1,53 +1,34 @@
|
||||||
import qs.config
|
import qs.config
|
||||||
|
import qs.services
|
||||||
import qs.widgets
|
import qs.widgets
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell.Services.Pipewire
|
|
||||||
|
|
||||||
StyledButton {
|
StyledButton {
|
||||||
id: clickable
|
id: root
|
||||||
|
|
||||||
property var sink: Pipewire.defaultAudioSink
|
|
||||||
|
|
||||||
PwObjectTracker {
|
|
||||||
id: bound
|
|
||||||
objects: [clickable.sink]
|
|
||||||
}
|
|
||||||
|
|
||||||
onClicked: mouse => {
|
onClicked: mouse => {
|
||||||
if (!sink) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (mouse.button == Qt.LeftButton) {
|
if (mouse.button == Qt.LeftButton) {
|
||||||
sink.audio.muted = !sink?.audio.muted;
|
Pipewire.toggleMute();
|
||||||
} else if (mouse.button == Qt.RightButton)
|
} else if (mouse.button == Qt.RightButton) {
|
||||||
// show menu
|
popup.opened = !popup.opened;
|
||||||
{}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onWheel: event => {
|
onWheel: event => {
|
||||||
if (event.angleDelta.y > 0) {
|
if (event.angleDelta.y > 0) {
|
||||||
sink.audio.volume = Math.min(sink.audio.volume + 0.02, 1.0);
|
Pipewire.incrementVolume();
|
||||||
} else if (event.angleDelta.y < 0) {
|
} else if (event.angleDelta.y < 0) {
|
||||||
sink.audio.volume -= 0.02;
|
Pipewire.decrementVolume();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
states: [
|
states: [
|
||||||
State {
|
State {
|
||||||
name: "muted"
|
name: "muted"
|
||||||
when: clickable.sink?.audio.muted ?? false
|
when: Pipewire.muted
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
text {
|
root {
|
||||||
icon: " "
|
color: Theme.palette.error
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
State {
|
|
||||||
name: "off"
|
|
||||||
when: clickable.sink?.audio.volume <= 0
|
|
||||||
PropertyChanges {
|
|
||||||
text {
|
|
||||||
icon: " "
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -55,9 +36,9 @@ StyledButton {
|
||||||
|
|
||||||
content: StyledText {
|
content: StyledText {
|
||||||
id: text
|
id: text
|
||||||
property string icon: " "
|
property string icon: Pipewire.muted ? " " : Pipewire.volume <= 0 ? " " : " "
|
||||||
text: `${icon} ${(clickable.sink?.audio.volume * 100).toFixed()}%`
|
text: `${icon} ${(Pipewire.volume * 100).toFixed()}%`
|
||||||
font.pixelSize: Dimensions.pipewire.fontSize
|
font.pixelSize: Dimensions.pipewire.fontSize
|
||||||
color: clickable.containsMouse ? Theme.palette.base300 : Theme.palette.basecontent
|
color: root.containsMouse || Pipewire.muted ? Theme.palette.base300 : Theme.palette.basecontent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,7 @@ import qs.widgets
|
||||||
StyledButton {
|
StyledButton {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
onClicked: {
|
onClicked: popup.toggle()
|
||||||
popup.opened = !popup.opened;
|
|
||||||
}
|
|
||||||
|
|
||||||
content: StyledText {
|
content: StyledText {
|
||||||
id: text
|
id: text
|
||||||
|
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
pragma ComponentBehavior: Bound
|
|
||||||
|
|
||||||
import qs.config
|
|
||||||
import qs.styled
|
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import Quickshell.Bluetooth
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: root
|
|
||||||
required property var devices
|
|
||||||
signal deviceActivated(device: BluetoothDevice)
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: root.devices
|
|
||||||
delegate: Clickable {
|
|
||||||
id: device
|
|
||||||
required property var modelData
|
|
||||||
|
|
||||||
implicitWidth: row.width
|
|
||||||
implicitHeight: row.height
|
|
||||||
Layout.fillWidth: true
|
|
||||||
onClicked: {
|
|
||||||
root.deviceActivated(modelData);
|
|
||||||
}
|
|
||||||
|
|
||||||
RowLayout {
|
|
||||||
id: row
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
topPadding: 8
|
|
||||||
bottomPadding: 8
|
|
||||||
leftPadding: 8
|
|
||||||
rightPadding: 8
|
|
||||||
text: device.modelData.deviceName
|
|
||||||
color: device.containsMouse ? Theme.palette.base300 : Theme.palette.basecontent
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: 200
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,54 +1,42 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import qs.widgets
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Services.SystemTray
|
import Quickshell.Services.SystemTray
|
||||||
import Quickshell.Widgets
|
import Quickshell.Widgets
|
||||||
import "../../../../config/"
|
|
||||||
import "../../../../styled/"
|
|
||||||
import "menu/"
|
import "menu/"
|
||||||
|
|
||||||
Clickable {
|
StyledButton {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property SystemTrayItem trayItem
|
property SystemTrayItem trayItem
|
||||||
property bool menuOpened: false
|
|
||||||
|
|
||||||
implicitWidth: Dimensions.tray.width
|
onClicked: menu.toggle()
|
||||||
implicitHeight: Dimensions.tray.height
|
|
||||||
|
|
||||||
onClicked: toggleMenu()
|
content: IconImage {
|
||||||
|
|
||||||
function toggleMenu() {
|
|
||||||
menuOpened = !menuOpened;
|
|
||||||
}
|
|
||||||
|
|
||||||
IconImage {
|
|
||||||
id: icon
|
id: icon
|
||||||
anchors.margins: 6
|
|
||||||
anchors.fill: parent
|
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
|
implicitSize: 18
|
||||||
source: {
|
source: {
|
||||||
let icon = modelData.icon;
|
let icon = root.trayItem.icon;
|
||||||
if (icon.includes("?path=")) {
|
if (icon.includes("?path=")) {
|
||||||
const [name, path] = icon.split("?path=");
|
const [name, path] = icon.split("?path=");
|
||||||
icon = `file://${path}/${name.slice(name.lastIndexOf("/") + 1)}`;
|
icon = `file://${path}/${name.slice(name.lastIndexOf("/") + 1)}`;
|
||||||
}
|
}
|
||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu {
|
Menu {
|
||||||
id: menu
|
id: menu
|
||||||
|
|
||||||
opened: root.menuOpened
|
|
||||||
|
|
||||||
anchor.item: root
|
anchor.item: root
|
||||||
anchor.rect.x: root.width / 2 - width / 2
|
anchor.rect.x: root.width / 2 - width / 2
|
||||||
anchor.rect.y: root.height + 8
|
anchor.rect.y: root.height + 8
|
||||||
|
|
||||||
menuOpener: QsMenuOpener {
|
menuOpener: QsMenuOpener {
|
||||||
menu: trayItem.menu
|
menu: root.trayItem.menu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ StyledPopupWindow {
|
||||||
Repeater {
|
Repeater {
|
||||||
model: window.menuOpener.children
|
model: window.menuOpener.children
|
||||||
delegate: Loader {
|
delegate: Loader {
|
||||||
|
id: loader
|
||||||
required property QsMenuEntry modelData
|
required property QsMenuEntry modelData
|
||||||
|
|
||||||
active: true
|
active: true
|
||||||
|
|
@ -34,7 +35,7 @@ StyledPopupWindow {
|
||||||
color: Theme.palette.base100
|
color: Theme.palette.base100
|
||||||
}
|
}
|
||||||
property Component menuItem: MenuItem {
|
property Component menuItem: MenuItem {
|
||||||
menuEntry: modelData
|
menuEntry: loader.modelData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
48
services/Pipewire.qml
Normal file
48
services/Pipewire.qml
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.Pipewire
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property PwNode sink: Pipewire.defaultAudioSink
|
||||||
|
readonly property PwNode source: Pipewire.defaultAudioSource
|
||||||
|
|
||||||
|
readonly property bool muted: sink?.audio?.muted ?? false
|
||||||
|
readonly property real volume: sink?.audio?.volume ?? 0
|
||||||
|
|
||||||
|
function setVolume(volume: real): void {
|
||||||
|
if (sink?.ready && sink?.audio) {
|
||||||
|
sink.audio.muted = false;
|
||||||
|
sink.audio.volume = volume;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function incrementVolume() {
|
||||||
|
if (!sink?.ready || !sink?.audio) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sink.audio.muted = false;
|
||||||
|
sink.audio.volume = Math.min(sink.audio.volume + 0.02, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function decrementVolume() {
|
||||||
|
if (!sink?.ready || !sink?.audio) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sink.audio.volume -= 0.02;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleMute() {
|
||||||
|
if (!sink?.ready || !sink?.audio) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sink.audio.muted = !sink.audio.muted;
|
||||||
|
}
|
||||||
|
|
||||||
|
PwObjectTracker {
|
||||||
|
objects: [Pipewire.defaultAudioSink, Pipewire.defaultAudioSource]
|
||||||
|
}
|
||||||
|
}
|
||||||
16
services/Visibility.qml
Normal file
16
services/Visibility.qml
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import qs.widgets
|
||||||
|
import Quickshell
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
property StyledPopupWindow activePopup
|
||||||
|
|
||||||
|
function togglePopup(popup: StyledPopupWindow) {
|
||||||
|
if (activePopup && popup != activePopup) {
|
||||||
|
activePopup.state = "";
|
||||||
|
}
|
||||||
|
popup.state = popup.state == "opened" ? "" : "opened";
|
||||||
|
activePopup = popup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
import QtQuick
|
|
||||||
import "../config/"
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: mouseArea
|
|
||||||
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
property alias color: rectangle.color
|
|
||||||
property alias border: rectangle.border
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: rectangle
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
radius: Dimensions.radius
|
|
||||||
color: mouseArea.containsMouse ? Theme.palette.primary : Theme.palette.base100
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
properties: "color"
|
|
||||||
duration: 200
|
|
||||||
easing.type: Easing.InOutQuad
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
import QtQuick
|
|
||||||
import QtQuick.Effects
|
|
||||||
import "../config/"
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property int size: 18
|
|
||||||
property color color: Theme.palette.basecontent
|
|
||||||
|
|
||||||
width: size
|
|
||||||
height: size
|
|
||||||
|
|
||||||
MultiEffect {
|
|
||||||
id: effect
|
|
||||||
source: root
|
|
||||||
anchors.fill: root
|
|
||||||
colorization: 1.0
|
|
||||||
colorizationColor: color
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
import QtQuick
|
|
||||||
import "../config/"
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
color: Theme.palette.base100
|
|
||||||
|
|
||||||
radius: Dimensions.radius
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
import QtQuick
|
|
||||||
import "root:config"
|
|
||||||
|
|
||||||
Text {
|
|
||||||
color: Theme.palette.basecontent
|
|
||||||
font.family: Theme.fontFamily
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import qs.services
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
import Quickshell.Widgets
|
||||||
|
|
@ -9,10 +10,15 @@ PopupWindow {
|
||||||
property alias margins: background.margin
|
property alias margins: background.margin
|
||||||
property alias backgroundColor: background.color
|
property alias backgroundColor: background.color
|
||||||
property alias radius: background.radius
|
property alias radius: background.radius
|
||||||
|
property alias state: background.state
|
||||||
required property Component content
|
required property Component content
|
||||||
|
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
||||||
|
function toggle() {
|
||||||
|
Visibility.togglePopup(this);
|
||||||
|
}
|
||||||
|
|
||||||
implicitWidth: background.width
|
implicitWidth: background.width
|
||||||
Behavior on implicitWidth {
|
Behavior on implicitWidth {
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue