Compare commits

...

3 commits

Author SHA1 Message Date
406ffd6c15
qs tray: close on click 2025-07-24 18:53:50 -07:00
54e0964b71
qs internal pragma 2025-07-24 18:52:44 -07:00
6c73e52f6d
qs bluetooth 2025-07-24 18:52:16 -07:00
24 changed files with 289 additions and 7 deletions

View file

@ -10,12 +10,12 @@ in {
qt6.qtdeclarative # qtdecl types in path
(quickshell.packages.${system}.default.override (prevqs: {
debug = true;
qt6 = prevqs.qt6.overrideScope (_: prevqt: {
/*qt6 = prevqs.qt6.overrideScope (_: prevqt: {
qtdeclarative = prevqt.qtdeclarative.overrideAttrs (prev: {
cmakeBuildType = "Debug";
dontStrip = true;
});
});
});*/
}))
grim imagemagick # screenshot
];

View file

@ -0,0 +1,19 @@
import QtQuick
import QtQuick.Controls
ClickableIcon {
id: root
property bool showAction: false
showPressed: mouseArea.pressed || showAction
BusyIndicator {
parent: root
anchors.centerIn: parent
opacity: root.showAction ? 1 : 0
Behavior on opacity { SmoothedAnimation { velocity: 8 }}
visible: opacity != 0
width: root.width - 3 + opacity * 11
height: width
padding: 0
}
}

View file

@ -1,11 +1,12 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Quickshell
import qs.bar.systray as SysTray
import qs.bar.audio as Audio
import qs.bar.mpris as Mpris
import qs.bar.connections as Connections
import qs.bar.power as Power
import qs.notifications as Notifs
@ -14,7 +15,6 @@ BarContainment {
property bool isSoleBar: Quickshell.screens.length == 1;
ColumnLayout {
anchors {
left: parent.left
right: parent.right
@ -75,6 +75,11 @@ BarContainment {
Layout.fillWidth: true
}
Connections.Connections {
bar: root
Layout.fillWidth: true
}
Power.Power {
bar: root
Layout.fillWidth: true
@ -84,6 +89,5 @@ BarContainment {
bar: root
Layout.fillWidth: true
}
}
}

View file

@ -0,0 +1,129 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Quickshell
import Quickshell.Widgets
import Quickshell.Bluetooth
import qs
import qs.bar
ClickableIcon {
id: root
required property var bar;
readonly property BluetoothAdapter adapter: Bluetooth.defaultAdapter
readonly property bool connected: adapter.devices.values.some(device => device.connected)
property bool showMenu: false
onPressed: event => {
event.accepted = true;
if (event.button === Qt.RightButton) {
showMenu = !showMenu;
}
}
onClicked: event => {
if (event.button === Qt.LeftButton) {
adapter.enabled = !adapter.enabled;
}
}
showPressed: showMenu || (pressedButtons & ~Qt.RightButton)
implicitHeight: width
fillWindowWidth: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
image: adapter.enabled
? (connected ? "root:/icons/bluetooth-connected.svg" : "root:/icons/bluetooth.svg")
: "root:/icons/bluetooth-slash.svg"
property var tooltip: TooltipItem {
tooltip: bar.tooltip
owner: root
show: root.containsMouse
Label { text: "Bluetooth" }
}
property var rightclickMenu: TooltipItem {
id: rightclickMenu
tooltip: bar.tooltip
owner: root
isMenu: true
show: root.showMenu
onClose: root.showMenu = false
Loader {
width: 400
active: root.showMenu || rightclickMenu.visible
sourceComponent: Column {
spacing: 5
move: Transition {
SmoothedAnimation { property: "y"; velocity: 350 }
}
RowLayout {
width: parent.width
ClickableIcon {
image: root.image
implicitHeight: 40
implicitWidth: height
onClicked: root.adapter.enabled = !root.adapter.enabled
}
Label {
text: `Bluetooth (${root.adapter.adapterId})`
}
Item { Layout.fillWidth: true }
ClickableIcon {
image: root.adapter.enabled ? "root:/icons/bluetooth-slash.svg" : "root:/icons/bluetooth.svg"
implicitHeight: 24
implicitWidth: height
onClicked: root.adapter.enabled = !root.adapter.enabled
}
ActivityButton {
image: "root:/icons/binoculars.svg"
implicitHeight: 24
implicitWidth: height
onClicked: root.adapter.discovering = !root.adapter.discovering
showAction: root.adapter.discovering
Layout.rightMargin: 4
}
}
Rectangle {
width: parent.width
implicitHeight: 1
visible: linkTracker.linkGroups.length > 0
color: ShellGlobals.colors.separator
}
Repeater {
model: ScriptModel {
values: [...root.adapter.devices.values].sort((a, b) => {
if (a.connected && !b.connected) return -1;
if (b.connected && !a.connected) return 1;
if (a.bonded && !b.bonded) return -1;
if (b.bonded && !a.bonded) return 1;
return b.name - a.name;
})
}
delegate: BluetoothDeviceDelegate {
required property BluetoothDevice modelData
device: modelData
width: parent.width
}
}
}
}
}
}

View file

@ -0,0 +1,88 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Quickshell
import Quickshell.Widgets
import Quickshell.Bluetooth
import qs
import qs.bar
WrapperMouseArea {
id: root
required property BluetoothDevice device
property bool menuOpen: false
readonly property bool showBg: false//pairingContext.attentionRequested //device.connected//menuOpen //|| containsMouse
hoverEnabled: true
onClicked: menuOpen = !menuOpen
WrapperRectangle {
color: root.showBg ? ShellGlobals.colors.widget : "transparent"
border.width: 1
border.color: root.showBg ? ShellGlobals.colors.widgetOutline : "transparent"
radius: 4
rightMargin: 2
ColumnLayout {
RowLayout {
ClickableIcon {
image: Quickshell.iconPath(root.device.icon)
implicitHeight: 40
implicitWidth: height
}
Label {
text: root.device.name
}
Item { Layout.fillWidth: true }
ActivityButton {
image: root.device.connected ? "root:/icons/plugs-connected.svg" : "root:/icons/plugs.svg"
implicitHeight: 24
implicitWidth: height
showAction: root.device.pairing || root.device.state === BluetoothDeviceState.Connecting || root.device.state === BluetoothDeviceState.Disconnecting
onClicked: {
if (showAction) return;
else if (root.device.connected) root.device.disconnect();
else if (root.device.paired) root.device.connect();
else root.device.pair();
}
}
ClickableIcon {
image: "root:/icons/trash.svg"
implicitHeight: 24
implicitWidth: height
visible: root.device.bonded
onClicked: root.device.forget()
}
}
/*RowLayout {
Layout.margins: 3
Layout.topMargin: 0
visible: root.showBg
BluetoothPairingContext {
id: pairingContext
device: root.device
}
Label {
text: `Pairing Code: ${pairingContext.pairingCode}`
}
Button {
text: "Accept"
onClicked: pairingContext.confirmCode(true)
}
Button {
text: "Reject"
onClicked: pairingContext.confirmCode(false)
}
}*/
}
}
}

View file

@ -0,0 +1 @@
import Quickshell

View file

@ -0,0 +1,23 @@
import QtQuick
import QtQuick.Layouts
import qs.bar
BarWidgetInner {
id: root
required property var bar;
implicitHeight: column.implicitHeight + 10
ColumnLayout {
id: column
anchors {
fill: parent
margins: 5
}
Bluetooth {
Layout.fillWidth: true
bar: root.bar
}
}
}

View file

@ -1,3 +1,6 @@
//@ pragma Internal
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

View file

@ -1,3 +1,4 @@
//@ pragma Internal
import QtQuick
import QtQuick.Templates as T

View file

@ -1,3 +1,4 @@
//@ pragma Internal
import QtQuick
import QtQuick.Shapes
import qs

View file

@ -1,3 +1,4 @@
//@ pragma Internal
import QtQuick
import QtQuick.Shapes
import Quickshell

View file

@ -1,3 +1,4 @@
//@ pragma Internal
import QtQuick
import QtQuick.Layouts
import Quickshell
@ -24,7 +25,7 @@ MouseArea {
if (entry.hasChildren) childrenRevealer.expanded = !childrenRevealer.expanded
else {
entry.triggered();
if (entry.toggleType == ToggleButtonType.None) close();
close();
}
}

View file

@ -1,3 +1,4 @@
//@ pragma Internal
import QtQuick
import qs

View file

@ -1,7 +1,7 @@
//@ pragma Internal
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.DBusMenu
import qs
ColumnLayout {

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#f0f0f0" viewBox="0 0 256 256"><path d="M237.2,151.87v0a47.1,47.1,0,0,0-2.35-5.45L193.26,51.8a7.82,7.82,0,0,0-1.66-2.44,32,32,0,0,0-45.26,0A8,8,0,0,0,144,55V80H112V55a8,8,0,0,0-2.34-5.66,32,32,0,0,0-45.26,0,7.82,7.82,0,0,0-1.66,2.44L21.15,146.4a47.1,47.1,0,0,0-2.35,5.45v0A48,48,0,1,0,112,168V96h32v72a48,48,0,1,0,93.2-16.13ZM76.71,59.75a16,16,0,0,1,19.29-1v73.51a47.9,47.9,0,0,0-46.79-9.92ZM64,200a32,32,0,1,1,32-32A32,32,0,0,1,64,200ZM160,58.74a16,16,0,0,1,19.29,1l27.5,62.58A47.9,47.9,0,0,0,160,132.25ZM192,200a32,32,0,1,1,32-32A32,32,0,0,1,192,200Z"></path></svg>

After

Width:  |  Height:  |  Size: 636 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#f0f0f0" viewBox="0 0 256 256"><path d="M196.8,169.6,141.33,128,196.8,86.4a8,8,0,0,0,0-12.8l-64-48A8,8,0,0,0,120,32v80L68.8,73.6a8,8,0,0,0-9.6,12.8L114.67,128,59.2,169.6a8,8,0,1,0,9.6,12.8L120,144v80a8,8,0,0,0,12.8,6.4l64-48a8,8,0,0,0,0-12.8ZM136,48l42.67,32L136,112Zm0,160V144l42.67,32ZM60,140a12,12,0,1,1,12-12A12,12,0,0,1,60,140Zm156-12a12,12,0,1,1-12-12A12,12,0,0,1,216,128Z"></path></svg>

After

Width:  |  Height:  |  Size: 462 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#f0f0f0" viewBox="0 0 256 256"><path d="M221.92,210.62l-160-176A8,8,0,0,0,50.08,45.38l70.84,77.93L59.2,169.6a8,8,0,1,0,9.6,12.8L120,144v80a8,8,0,0,0,12.8,6.4l50.83-38.12,26.45,29.1a8,8,0,1,0,11.84-10.76ZM136,208V144l11.73,8.8,25.08,27.59ZM120,71.63V32a8,8,0,0,1,12.8-6.4l64,48a8,8,0,0,1,0,12.8l-33.53,25.15a8,8,0,0,1-9.6-12.8l25-18.75L136,48V71.63a8,8,0,0,1-16,0Z"></path></svg>

After

Width:  |  Height:  |  Size: 447 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#f0f0f0" viewBox="0 0 256 256"><path d="M180.8,169.6,125.33,128l23.47-17.6a8,8,0,0,0-9.6-12.8L120,112V48l19.2,14.4a8,8,0,1,0,9.6-12.8l-32-24A8,8,0,0,0,104,32v80L52.8,73.6a8,8,0,0,0-9.6,12.8L98.67,128,43.2,169.6a8,8,0,1,0,9.6,12.8L104,144v80a8,8,0,0,0,12.8,6.4l64-48a8,8,0,0,0,0-12.8ZM120,208V144l42.67,32ZM237.66,98.34a8,8,0,0,1-11.32,11.32L208,91.31l-18.34,18.35a8,8,0,0,1-11.32-11.32L196.69,80,178.34,61.66a8,8,0,0,1,11.32-11.32L208,68.69l18.34-18.35a8,8,0,0,1,11.32,11.32L219.31,80Z"></path></svg>

After

Width:  |  Height:  |  Size: 569 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#f0f0f0" viewBox="0 0 256 256"><path d="M196.8,169.6,141.33,128,196.8,86.4a8,8,0,0,0,0-12.8l-64-48A8,8,0,0,0,120,32v80L68.8,73.6a8,8,0,0,0-9.6,12.8L114.67,128,59.2,169.6a8,8,0,1,0,9.6,12.8L120,144v80a8,8,0,0,0,12.8,6.4l64-48a8,8,0,0,0,0-12.8ZM136,48l42.67,32L136,112Zm0,160V144l42.67,32Z"></path></svg>

After

Width:  |  Height:  |  Size: 371 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#f0f0f0" viewBox="0 0 256 256"><path d="M237.66,18.34a8,8,0,0,0-11.32,0l-52.4,52.41-5.37-5.38a32.05,32.05,0,0,0-45.26,0L100,88.69l-6.34-6.35A8,8,0,0,0,82.34,93.66L88.69,100,65.37,123.31a32,32,0,0,0,0,45.26l5.38,5.37-52.41,52.4a8,8,0,0,0,11.32,11.32l52.4-52.41,5.37,5.38a32,32,0,0,0,45.26,0L156,167.31l6.34,6.35a8,8,0,0,0,11.32-11.32L167.31,156l23.32-23.31a32,32,0,0,0,0-45.26l-5.38-5.37,52.41-52.4A8,8,0,0,0,237.66,18.34Zm-116.29,161a16,16,0,0,1-22.62,0L76.69,157.25a16,16,0,0,1,0-22.62L100,111.31,144.69,156Zm57.94-57.94L156,144.69,111.31,100l23.32-23.31a16,16,0,0,1,22.62,0l22.06,22A16,16,0,0,1,179.31,121.37ZM88.57,35A8,8,0,0,1,103.43,29l8,20A8,8,0,0,1,96.57,55ZM24.57,93A8,8,0,0,1,35,88.57l20,8A8,8,0,0,1,49,111.43l-20-8A8,8,0,0,1,24.57,93ZM231.43,163a8,8,0,0,1-10.4,4.46l-20-8A8,8,0,1,1,207,144.57l20,8A8,8,0,0,1,231.43,163Zm-64,58.06A8,8,0,0,1,152.57,227l-8-20A8,8,0,0,1,159.43,201Z"></path></svg>

After

Width:  |  Height:  |  Size: 972 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#f0f0f0" viewBox="0 0 256 256"><path d="M149.66,138.34a8,8,0,0,0-11.32,0L120,156.69,99.31,136l18.35-18.34a8,8,0,0,0-11.32-11.32L88,124.69,69.66,106.34a8,8,0,0,0-11.32,11.32L64.69,124,41.37,147.31a32,32,0,0,0,0,45.26l5.38,5.37-28.41,28.4a8,8,0,0,0,11.32,11.32l28.4-28.41,5.37,5.38a32,32,0,0,0,45.26,0L132,191.31l6.34,6.35a8,8,0,0,0,11.32-11.32L131.31,168l18.35-18.34A8,8,0,0,0,149.66,138.34Zm-52.29,65a16,16,0,0,1-22.62,0L52.69,181.25a16,16,0,0,1,0-22.62L76,135.31,120.69,180Zm140.29-185a8,8,0,0,0-11.32,0l-28.4,28.41-5.37-5.38a32.05,32.05,0,0,0-45.26,0L124,64.69l-6.34-6.35a8,8,0,0,0-11.32,11.32l80,80a8,8,0,0,0,11.32-11.32L191.31,132l23.32-23.31a32,32,0,0,0,0-45.26l-5.38-5.37,28.41-28.4A8,8,0,0,0,237.66,18.34Zm-34.35,79L180,120.69,135.31,76l23.32-23.31a16,16,0,0,1,22.62,0l22.06,22A16,16,0,0,1,203.31,97.37Z"></path></svg>

After

Width:  |  Height:  |  Size: 894 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#f0f0f0" viewBox="0 0 256 256"><path d="M216,48H176V40a24,24,0,0,0-24-24H104A24,24,0,0,0,80,40v8H40a8,8,0,0,0,0,16h8V208a16,16,0,0,0,16,16H192a16,16,0,0,0,16-16V64h8a8,8,0,0,0,0-16ZM96,40a8,8,0,0,1,8-8h48a8,8,0,0,1,8,8v8H96Zm96,168H64V64H192ZM112,104v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm48,0v64a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Z"></path></svg>

After

Width:  |  Height:  |  Size: 415 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#f0f0f0" viewBox="0 0 256 256"><path d="M213.92,210.62a8,8,0,1,1-11.84,10.76l-52-57.15a60,60,0,0,0-57.41,7.24,8,8,0,1,1-9.42-12.93A75.43,75.43,0,0,1,128,144c1.28,0,2.55,0,3.82.1L104.9,114.49A108,108,0,0,0,61,135.31,8,8,0,0,1,49.73,134,8,8,0,0,1,51,122.77a124.27,124.27,0,0,1,41.71-21.66L69.37,75.4a155.43,155.43,0,0,0-40.29,24A8,8,0,0,1,18.92,87,171.87,171.87,0,0,1,58,62.86L42.08,45.38A8,8,0,1,1,53.92,34.62ZM128,192a12,12,0,1,0,12,12A12,12,0,0,0,128,192ZM237.08,87A172.3,172.3,0,0,0,106,49.4a8,8,0,1,0,2,15.87A158.33,158.33,0,0,1,128,64a156.25,156.25,0,0,1,98.92,35.37A8,8,0,0,0,237.08,87ZM195,135.31a8,8,0,0,0,11.24-1.3,8,8,0,0,0-1.3-11.24,124.25,124.25,0,0,0-51.73-24.2A8,8,0,1,0,150,114.24,108.12,108.12,0,0,1,195,135.31Z"></path></svg>

After

Width:  |  Height:  |  Size: 810 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#f0f0f0" viewBox="0 0 256 256"><path d="M140,204a12,12,0,1,1-12-12A12,12,0,0,1,140,204ZM237.08,87A172,172,0,0,0,18.92,87,8,8,0,0,0,29.08,99.37a156,156,0,0,1,197.84,0A8,8,0,0,0,237.08,87ZM205,122.77a124,124,0,0,0-153.94,0A8,8,0,0,0,61,135.31a108,108,0,0,1,134.06,0,8,8,0,0,0,11.24-1.3A8,8,0,0,0,205,122.77Zm-32.26,35.76a76.05,76.05,0,0,0-89.42,0,8,8,0,0,0,9.42,12.94,60,60,0,0,1,70.58,0,8,8,0,1,0,9.42-12.94Z"></path></svg>

After

Width:  |  Height:  |  Size: 491 B