Compare commits

..
10 changed files with 134 additions and 200 deletions

View file

@ -8,7 +8,6 @@ GpuApplet 1.0 GpuApplet.qml
HexWaveBackground 1.0 HexWaveBackground.qml HexWaveBackground 1.0 HexWaveBackground.qml
MemoryApplet 1.0 MemoryApplet.qml MemoryApplet 1.0 MemoryApplet.qml
MprisApplet 1.0 MprisApplet.qml MprisApplet 1.0 MprisApplet.qml
NetworkApplet 1.0 NetworkApplet.qml
TemperatureApplet 1.0 TemperatureApplet.qml TemperatureApplet 1.0 TemperatureApplet.qml
VolumeApplet 1.0 VolumeApplet.qml VolumeApplet 1.0 VolumeApplet.qml
# keep-sorted end # keep-sorted end

View file

@ -10,12 +10,11 @@ Item {
readonly property real _fontSize: Math.max(48, screenHeight * 0.28) readonly property real _fontSize: Math.max(48, screenHeight * 0.28)
// Appear as wave reaches the clock (left edge), latch once fully revealed. // Appear as wave reaches the clock (left edge), latch once fully revealed
// With reducedMotion the wave never runs, so show immediately.
property bool _revealed: false property bool _revealed: false
on_RawProgressChanged: if (_rawProgress >= 1) on_RawProgressChanged: if (_rawProgress >= 1)
_revealed = true _revealed = true
readonly property real _rawProgress: S.Theme.reducedMotion ? 1 : Math.max(0, Math.min(1, wavePhase / 300)) readonly property real _rawProgress: Math.max(0, Math.min(1, wavePhase / 300))
readonly property real _progress: (_revealed ? 1 : _rawProgress) * unlockFade readonly property real _progress: (_revealed ? 1 : _rawProgress) * unlockFade
opacity: _progress opacity: _progress
property real _slideX: (1 - _progress) * -80 property real _slideX: (1 - _progress) * -80

View file

@ -12,12 +12,11 @@ Item {
property real screenWidth: 0 property real screenWidth: 0
property real unlockFade: 1 property real unlockFade: 1
// Fly in when wave exits the right edge, latch once fully revealed. // Fly in when wave exits the right edge, latch once fully revealed
// With reducedMotion the wave never runs, so show immediately.
property bool _revealed: false property bool _revealed: false
on_RawProgressChanged: if (_rawProgress >= 1) on_RawProgressChanged: if (_rawProgress >= 1)
_revealed = true _revealed = true
readonly property real _rawProgress: S.Theme.reducedMotion ? 1 : (screenWidth > 0 ? Math.max(0, Math.min(1, (wavePhase - screenWidth) / 500)) : 0) readonly property real _rawProgress: screenWidth > 0 ? Math.max(0, Math.min(1, (wavePhase - screenWidth) / 500)) : 0
readonly property real _progress: (_revealed ? 1 : _rawProgress) * unlockFade readonly property real _progress: (_revealed ? 1 : _rawProgress) * unlockFade
opacity: _progress opacity: _progress
property real _slideX: (1 - _progress) * 80 property real _slideX: (1 - _progress) * 80

View file

@ -181,9 +181,12 @@ PanelWindow {
// Connectivity // Connectivity
M.BarGroup { M.BarGroup {
M.NetworkModule { M.NetworkModule {
bar: bar
visible: S.Modules.network.enable visible: S.Modules.network.enable
} }
M.BluetoothModule {} M.BluetoothModule {
bar: bar
}
} }
// Controls // Controls

View file

@ -1,10 +1,46 @@
import QtQuick import QtQuick
import Quickshell
import "." as M
import "../services" as S import "../services" as S
Column { M.HoverPanel {
id: root id: menuWindow
required property color accentColor contentWidth: 250
panelNamespace: "nova-bluetooth"
popupMode: true
panelTitle: "Bluetooth"
titleActionsComponent: Component {
Item {
width: 20
height: 20
Text {
anchors.centerIn: parent
text: "\uF011"
color: S.BluetoothService.enabled ? menuWindow.accentColor : S.Theme.base04
font.pixelSize: S.Theme.fontSize
font.family: S.Theme.iconFontFamily
Behavior on color {
ColorAnimation {
duration: 100
}
}
}
HoverHandler {
cursorShape: Qt.PointingHandCursor
}
TapHandler {
onTapped: S.BluetoothService.setPower(!S.BluetoothService.enabled)
}
}
}
onVisibleChanged: if (visible)
S.BluetoothService.refresh()
Repeater { Repeater {
model: S.BluetoothService.devices model: S.BluetoothService.devices
@ -14,9 +50,7 @@ Column {
required property var modelData required property var modelData
required property int index required property int index
readonly property bool _pending: S.BluetoothService.pendingMac === entry.modelData.mac width: menuWindow.contentWidth
width: root.width
height: 32 height: 32
Rectangle { Rectangle {
@ -33,7 +67,7 @@ Column {
anchors.leftMargin: 12 anchors.leftMargin: 12
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
text: "\uF294" text: "\uF294"
color: entry._pending || entry.modelData.connected ? root.accentColor : S.Theme.base04 color: entry.modelData.connected ? menuWindow.accentColor : S.Theme.base04
font.pixelSize: S.Theme.fontSize + 1 font.pixelSize: S.Theme.fontSize + 1
font.family: S.Theme.iconFontFamily font.family: S.Theme.iconFontFamily
} }
@ -41,46 +75,27 @@ Column {
Text { Text {
anchors.left: btIcon.right anchors.left: btIcon.right
anchors.leftMargin: 8 anchors.leftMargin: 8
anchors.right: _statusLabel.left anchors.right: batLabel.left
anchors.rightMargin: 4 anchors.rightMargin: 4
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
text: entry.modelData.name text: entry.modelData.name
color: entry._pending || entry.modelData.connected ? root.accentColor : S.Theme.base05 color: entry.modelData.connected ? menuWindow.accentColor : S.Theme.base05
font.pixelSize: S.Theme.fontSize font.pixelSize: S.Theme.fontSize
font.family: S.Theme.fontFamily font.family: S.Theme.fontFamily
font.bold: entry.modelData.connected || entry._pending font.bold: entry.modelData.connected
elide: Text.ElideRight elide: Text.ElideRight
} }
Text { Text {
id: _statusLabel id: batLabel
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: 12 anchors.rightMargin: 12
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
text: entry._pending ? (entry.modelData.connected ? "disconnecting..." : "connecting...") : (entry.modelData.battery >= 0 ? entry.modelData.battery + "%" : "") text: entry.modelData.battery >= 0 ? entry.modelData.battery + "%" : ""
color: S.Theme.base04 color: S.Theme.base04
font.pixelSize: S.Theme.fontSize - 1 font.pixelSize: S.Theme.fontSize - 1
font.family: S.Theme.fontFamily font.family: S.Theme.fontFamily
font.italic: entry._pending width: entry.modelData.battery >= 0 ? implicitWidth : 0
width: text ? implicitWidth : 0
}
// Pulse animation while pending
SequentialAnimation on opacity {
loops: Animation.Infinite
running: entry._pending
NumberAnimation {
to: 0.5
duration: 400
easing.type: Easing.InOutQuad
}
NumberAnimation {
to: 1
duration: 400
easing.type: Easing.InOutQuad
}
onRunningChanged: if (!running)
entry.opacity = 1
} }
HoverHandler { HoverHandler {
@ -89,8 +104,8 @@ Column {
} }
TapHandler { TapHandler {
onTapped: { onTapped: {
if (!entry._pending) S.BluetoothService.toggleDevice(entry.modelData.mac, !entry.modelData.connected);
S.BluetoothService.toggleDevice(entry.modelData.mac, !entry.modelData.connected); menuWindow.keepOpen(500);
} }
} }
} }
@ -98,7 +113,7 @@ Column {
Text { Text {
visible: S.BluetoothService.devices.length === 0 visible: S.BluetoothService.devices.length === 0
width: root.width width: menuWindow.contentWidth
height: 32 height: 32
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter

View file

@ -2,30 +2,18 @@ import QtQuick
import Quickshell import Quickshell
import "." as M import "." as M
import "../services" as S import "../services" as S
import "../applets" as C
M.BarSection { M.BarSection {
id: root id: root
spacing: S.Theme.moduleSpacing spacing: S.Theme.moduleSpacing
opacity: S.Modules.bluetooth.enable && S.BluetoothService.state !== "unavailable" ? 1 : 0 opacity: S.Modules.bluetooth.enable && S.BluetoothService.state !== "unavailable" ? 1 : 0
visible: opacity > 0 visible: opacity > 0
tooltip: "" tooltip: {
if (S.BluetoothService.state === "off")
property bool _pinned: false return "Bluetooth: off";
readonly property bool _anyHover: root._hovered || hoverPanel.panelHovered if (S.BluetoothService.state === "connected")
readonly property bool _showPanel: _anyHover || _pinned return "Bluetooth: " + S.BluetoothService.device + (S.BluetoothService.batteryPct >= 0 ? "\nBattery: " + S.BluetoothService.batteryPct + "%" : "");
return "Bluetooth: on";
on_AnyHoverChanged: {
if (_anyHover)
_unpinTimer.stop();
else if (_pinned)
_unpinTimer.start();
}
Timer {
id: _unpinTimer
interval: 500
onTriggered: root._pinned = false
} }
M.BarIcon { M.BarIcon {
@ -33,7 +21,10 @@ M.BarSection {
color: S.BluetoothService.state === "off" ? S.Theme.base04 : root.accentColor color: S.BluetoothService.state === "off" ? S.Theme.base04 : root.accentColor
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
TapHandler { TapHandler {
onTapped: root._pinned = !root._pinned onTapped: {
M.FlyoutState.visible = false;
btLoader.active = true;
}
} }
} }
M.BarLabel { M.BarLabel {
@ -41,61 +32,23 @@ M.BarSection {
label: S.BluetoothService.device + (S.BluetoothService.batteryPct >= 0 ? " " + S.BluetoothService.batteryPct + "%" : "") label: S.BluetoothService.device + (S.BluetoothService.batteryPct >= 0 ? " " + S.BluetoothService.batteryPct + "%" : "")
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
TapHandler { TapHandler {
onTapped: root._pinned = !root._pinned onTapped: {
M.FlyoutState.visible = false;
btLoader.active = true;
}
} }
} }
M.HoverPanel { required property var bar
id: hoverPanel
showPanel: root._showPanel
screen: QsWindow.window?.screen ?? null
anchorItem: root
accentColor: root.accentColor
panelNamespace: "nova-bluetooth"
panelTitle: "Bluetooth"
contentWidth: 250
titleActionsComponent: Component {
Item {
width: 20
height: 20
Text { LazyLoader {
anchors.centerIn: parent id: btLoader
text: "\uF011" active: false
color: S.BluetoothService.enabled ? hoverPanel.accentColor : S.Theme.base04 M.BluetoothMenu {
font.pixelSize: S.Theme.fontSize
font.family: S.Theme.iconFontFamily
Behavior on color {
ColorAnimation {
duration: 100
}
}
}
HoverHandler {
cursorShape: Qt.PointingHandCursor
}
TapHandler {
onTapped: S.BluetoothService.setPower(!S.BluetoothService.enabled)
}
}
}
onVisibleChanged: if (visible)
S.BluetoothService.refresh()
Connections {
target: S.BluetoothService
function onDevicesChanged() {
hoverPanel.keepOpen(500);
}
}
C.BluetoothApplet {
width: hoverPanel.contentWidth
accentColor: root.accentColor accentColor: root.accentColor
screen: QsWindow.window?.screen ?? null
anchorX: root.mapToGlobal(root.width / 2, 0).x - (QsWindow.window?.screen?.x ?? 0)
onDismissed: btLoader.active = false
} }
} }
} }

View file

@ -1,10 +1,45 @@
import QtQuick import QtQuick
import Quickshell
import "." as M
import "../services" as S import "../services" as S
Column { M.HoverPanel {
id: root id: menuWindow
required property color accentColor contentWidth: 250
panelNamespace: "nova-network"
panelTitle: "Wi-Fi"
titleActionsComponent: Component {
Item {
width: 20
height: 20
Text {
anchors.centerIn: parent
text: "\uF011"
color: S.NetworkService.wifiEnabled ? menuWindow.accentColor : S.Theme.base04
font.pixelSize: S.Theme.fontSize
font.family: S.Theme.iconFontFamily
Behavior on color {
ColorAnimation {
duration: 100
}
}
}
HoverHandler {
cursorShape: Qt.PointingHandCursor
}
TapHandler {
onTapped: S.NetworkService.setWifi(!S.NetworkService.wifiEnabled)
}
}
}
onVisibleChanged: if (visible)
S.NetworkService.refresh()
Repeater { Repeater {
model: S.NetworkService.networks model: S.NetworkService.networks
@ -14,7 +49,7 @@ Column {
required property var modelData required property var modelData
required property int index required property int index
width: root.width width: menuWindow.contentWidth
height: 32 height: 32
Rectangle { Rectangle {
@ -31,7 +66,7 @@ Column {
anchors.leftMargin: 12 anchors.leftMargin: 12
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
text: entry.modelData.isWifi ? "\uF1EB" : "\uDB80\uDE00" text: entry.modelData.isWifi ? "\uF1EB" : "\uDB80\uDE00"
color: entry.modelData.active ? root.accentColor : S.Theme.base05 color: entry.modelData.active ? menuWindow.accentColor : S.Theme.base05
font.pixelSize: S.Theme.fontSize + 1 font.pixelSize: S.Theme.fontSize + 1
font.family: S.Theme.iconFontFamily font.family: S.Theme.iconFontFamily
} }
@ -43,7 +78,7 @@ Column {
anchors.rightMargin: 4 anchors.rightMargin: 4
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
text: entry.modelData.name text: entry.modelData.name
color: entry.modelData.active ? root.accentColor : S.Theme.base05 color: entry.modelData.active ? menuWindow.accentColor : S.Theme.base05
font.pixelSize: S.Theme.fontSize font.pixelSize: S.Theme.fontSize
font.family: S.Theme.fontFamily font.family: S.Theme.fontFamily
font.bold: entry.modelData.active font.bold: entry.modelData.active
@ -72,6 +107,7 @@ Column {
S.NetworkService.disconnectNetwork(entry.modelData.uuid); S.NetworkService.disconnectNetwork(entry.modelData.uuid);
else else
S.NetworkService.connectNetwork(entry.modelData.uuid); S.NetworkService.connectNetwork(entry.modelData.uuid);
menuWindow.keepOpen(500);
} }
} }
} }
@ -79,7 +115,7 @@ Column {
Text { Text {
visible: S.NetworkService.networks.length === 0 visible: S.NetworkService.networks.length === 0
width: root.width width: menuWindow.contentWidth
height: 32 height: 32
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter

View file

@ -2,7 +2,6 @@ import QtQuick
import Quickshell import Quickshell
import "." as M import "." as M
import "../services" as S import "../services" as S
import "../applets" as C
M.BarSection { M.BarSection {
id: root id: root
@ -11,23 +10,6 @@ M.BarSection {
readonly property string state: S.NetworkService.state readonly property string state: S.NetworkService.state
property bool _pinned: false
readonly property bool _anyHover: root._hovered || hoverPanel.panelHovered
readonly property bool _showPanel: _anyHover || _pinned
on_AnyHoverChanged: {
if (_anyHover)
_unpinTimer.stop();
else if (_pinned)
_unpinTimer.start();
}
Timer {
id: _unpinTimer
interval: 500
onTriggered: root._pinned = false
}
M.BarIcon { M.BarIcon {
icon: { icon: {
if (root.state === "wifi") if (root.state === "wifi")
@ -40,71 +22,23 @@ M.BarSection {
} }
color: root.state === "disconnected" ? S.Theme.base08 : root.accentColor color: root.state === "disconnected" ? S.Theme.base08 : root.accentColor
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
TapHandler {
onTapped: root._pinned = !root._pinned
}
} }
M.BarLabel { M.BarLabel {
visible: root.state === "wifi" visible: root.state === "wifi"
label: S.NetworkService.essid label: S.NetworkService.essid
color: root.state === "disconnected" ? S.Theme.base08 : root.accentColor color: root.state === "disconnected" ? S.Theme.base08 : root.accentColor
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
TapHandler {
onTapped: root._pinned = !root._pinned
}
} }
M.HoverPanel { required property var bar
id: hoverPanel
showPanel: root._showPanel readonly property bool _anyHover: root._hovered || networkMenu.panelHovered
M.NetworkMenu {
id: networkMenu
showPanel: root._anyHover
screen: QsWindow.window?.screen ?? null screen: QsWindow.window?.screen ?? null
anchorItem: root anchorItem: root
accentColor: root.accentColor accentColor: root.accentColor
panelNamespace: "nova-network"
panelTitle: "Wi-Fi"
contentWidth: 250
titleActionsComponent: Component {
Item {
width: 20
height: 20
Text {
anchors.centerIn: parent
text: "\uF011"
color: S.NetworkService.wifiEnabled ? hoverPanel.accentColor : S.Theme.base04
font.pixelSize: S.Theme.fontSize
font.family: S.Theme.iconFontFamily
Behavior on color {
ColorAnimation {
duration: 100
}
}
}
HoverHandler {
cursorShape: Qt.PointingHandCursor
}
TapHandler {
onTapped: S.NetworkService.setWifi(!S.NetworkService.wifiEnabled)
}
}
}
onVisibleChanged: if (visible)
S.NetworkService.refresh()
Connections {
target: S.NetworkService
function onNetworksChanged() {
hoverPanel.keepOpen(500);
}
}
C.NetworkApplet {
width: hoverPanel.contentWidth
accentColor: root.accentColor
}
} }
} }

View file

@ -8,6 +8,7 @@ BarIcon 1.0 BarIcon.qml
BarLabel 1.0 BarLabel.qml BarLabel 1.0 BarLabel.qml
BarSection 1.0 BarSection.qml BarSection 1.0 BarSection.qml
BatteryModule 1.0 BatteryModule.qml BatteryModule 1.0 BatteryModule.qml
BluetoothMenu 1.0 BluetoothMenu.qml
BluetoothModule 1.0 BluetoothModule.qml BluetoothModule 1.0 BluetoothModule.qml
ClockModule 1.0 ClockModule.qml ClockModule 1.0 ClockModule.qml
CpuModule 1.0 CpuModule.qml CpuModule 1.0 CpuModule.qml
@ -18,6 +19,7 @@ HoverPanel 1.0 HoverPanel.qml
IdleInhibitorModule 1.0 IdleInhibitorModule.qml IdleInhibitorModule 1.0 IdleInhibitorModule.qml
MemoryModule 1.0 MemoryModule.qml MemoryModule 1.0 MemoryModule.qml
MprisModule 1.0 MprisModule.qml MprisModule 1.0 MprisModule.qml
NetworkMenu 1.0 NetworkMenu.qml
NetworkModule 1.0 NetworkModule.qml NetworkModule 1.0 NetworkModule.qml
NotifCard 1.0 NotifCard.qml NotifCard 1.0 NotifCard.qml
NotifCenter 1.0 NotifCenter.qml NotifCenter 1.0 NotifCenter.qml

View file

@ -26,11 +26,7 @@ QtObject {
_powerProc.running = true; _powerProc.running = true;
} }
// MAC address of device currently connecting/disconnecting
property string pendingMac: ""
function toggleDevice(mac, connect) { function toggleDevice(mac, connect) {
pendingMac = mac;
_toggleProc._action = connect ? "connect" : "disconnect"; _toggleProc._action = connect ? "connect" : "disconnect";
_toggleProc._mac = mac; _toggleProc._mac = mac;
_toggleProc.running = true; _toggleProc.running = true;
@ -124,9 +120,7 @@ QtObject {
property string _action: "" property string _action: ""
property string _mac: "" property string _mac: ""
command: ["bluetoothctl", _action, _mac] command: ["bluetoothctl", _action, _mac]
onRunningChanged: if (!running) { onRunningChanged: if (!running)
root.pendingMac = ""; root.refresh()
root.refresh();
}
} }
} }