Compare commits

..

11 commits

Author SHA1 Message Date
Benjamin Palko
9873324823 Visibility service for handling active popups 2025-07-31 11:47:46 -04:00
Benjamin Palko
89cf6f315b pipewire service 2025-07-31 11:31:46 -04:00
Benjamin Palko
38fce255d8 change id 2025-07-31 09:22:13 -04:00
Benjamin Palko
4047c52ac9 huuuh lol 2025-07-30 21:59:16 -04:00
Benjamin Palko
30a0da7032 move styledwindow 2025-07-30 21:53:00 -04:00
Benjamin Palko
350a9f4fb2 no more references styled/styletext 2025-07-30 21:52:55 -04:00
Benjamin Palko
f904c78132 no more references on styled/stylelabel 2025-07-30 21:51:09 -04:00
Benjamin Palko
7d1c466ec5 no reference on icon 2025-07-30 21:50:43 -04:00
Benjamin Palko
cb6cf28c13 no more references on clickable 2025-07-30 21:49:56 -04:00
Benjamin Palko
871e654af2 migrate to widgets 2025-07-30 21:49:38 -04:00
Benjamin Palko
3d34780820 not referenced anymore 2025-07-30 21:40:06 -04:00
14 changed files with 98 additions and 169 deletions

View file

@ -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 {

View file

@ -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
} }
} }

View file

@ -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

View file

@ -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
}
}
}
}
}
}
}

View file

@ -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
} }
} }
} }

View file

@ -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
View 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
View 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;
}
}

View file

@ -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
}
}
}
}

View file

@ -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
}
}

View file

@ -1,8 +0,0 @@
import QtQuick
import "../config/"
Rectangle {
color: Theme.palette.base100
radius: Dimensions.radius
}

View file

@ -1,7 +0,0 @@
import QtQuick
import "root:config"
Text {
color: Theme.palette.basecontent
font.family: Theme.fontFamily
}

View file

@ -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 {