1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-06-07 19:59:14 -04:00

feat: Blink WiFi/Bluetooth icons while connecting (#2448)

Pulses the WiFi and Bluetooth status icons while a connection is in
progress (lock screen, DankBar control center button, control center
compound pill). The pulse is implemented as a reusable Widgets/DankBlink
component, and the wifi-connecting condition is centralized as
NetworkService.isWifiConnecting.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Domen Kožar
2026-05-22 09:03:25 -04:00
committed by GitHub
parent aaff1ab61e
commit 7476a220b5
9 changed files with 108 additions and 3 deletions
@@ -301,12 +301,22 @@ Column {
property var widgetDef: root.model?.getWidgetForId(widgetData.id || "") property var widgetDef: root.model?.getWidgetForId(widgetData.id || "")
width: parent.width width: parent.width
height: 60 height: 60
iconBlinking: {
const id = widgetData.id || "";
if (id === "wifi")
return NetworkService.isWifiConnecting;
if (id === "bluetooth")
return BluetoothService.connecting;
return false;
}
iconName: { iconName: {
switch (widgetData.id || "") { switch (widgetData.id || "") {
case "wifi": case "wifi":
{ {
if (NetworkService.wifiToggling) if (NetworkService.wifiToggling)
return "sync"; return "sync";
if (NetworkService.isConnecting && !NetworkService.ethernetConnected)
return NetworkService.wifiSignalIcon;
const status = NetworkService.networkStatus; const status = NetworkService.networkStatus;
if (status === "ethernet") if (status === "ethernet")
@@ -360,6 +370,8 @@ Column {
{ {
if (NetworkService.wifiToggling) if (NetworkService.wifiToggling)
return NetworkService.wifiEnabled ? I18n.tr("Disabling WiFi...", "network status") : I18n.tr("Enabling WiFi...", "network status"); return NetworkService.wifiEnabled ? I18n.tr("Disabling WiFi...", "network status") : I18n.tr("Enabling WiFi...", "network status");
if (NetworkService.isConnecting && !NetworkService.ethernetConnected)
return NetworkService.connectingSSID || I18n.tr("Connecting...", "network status");
const status = NetworkService.networkStatus; const status = NetworkService.networkStatus;
if (status === "ethernet") if (status === "ethernet")
@@ -400,6 +412,8 @@ Column {
{ {
if (NetworkService.wifiToggling) if (NetworkService.wifiToggling)
return I18n.tr("Please wait...", "network status"); return I18n.tr("Please wait...", "network status");
if (NetworkService.isConnecting && !NetworkService.ethernetConnected)
return I18n.tr("Connecting...", "network status");
const status = NetworkService.networkStatus; const status = NetworkService.networkStatus;
if (status === "ethernet") if (status === "ethernet")
@@ -422,6 +436,8 @@ Column {
return I18n.tr("No adapters", "bluetooth status"); return I18n.tr("No adapters", "bluetooth status");
if (!BluetoothService.adapter || !BluetoothService.adapter.enabled) if (!BluetoothService.adapter || !BluetoothService.adapter.enabled)
return I18n.tr("Off", "bluetooth status"); return I18n.tr("Off", "bluetooth status");
if (BluetoothService.connecting)
return I18n.tr("Connecting...", "bluetooth status");
const primaryDevice = (() => { const primaryDevice = (() => {
if (!BluetoothService.adapter || !BluetoothService.adapter.devices) if (!BluetoothService.adapter || !BluetoothService.adapter.devices)
return null; return null;
@@ -10,6 +10,7 @@ Rectangle {
property string iconName: "" property string iconName: ""
property color iconColor: Theme.surfaceText property color iconColor: Theme.surfaceText
property bool iconBlinking: false
property string primaryText: "" property string primaryText: ""
property string secondaryText: "" property string secondaryText: ""
property bool expanded: false property bool expanded: false
@@ -109,10 +110,16 @@ Rectangle {
} }
DankIcon { DankIcon {
id: pillIcon
anchors.centerIn: parent anchors.centerIn: parent
name: iconName name: iconName
size: Theme.iconSize size: Theme.iconSize
color: isActive ? _tileIconActive : _tileIconInactive color: isActive ? _tileIconActive : _tileIconInactive
DankBlink {
target: pillIcon
running: root.iconBlinking
}
} }
DankRipple { DankRipple {
@@ -131,9 +131,19 @@ BasePill {
function getNetworkIconColor() { function getNetworkIconColor() {
if (NetworkService.wifiToggling) if (NetworkService.wifiToggling)
return Theme.primary; return Theme.primary;
if (NetworkService.isConnecting && !NetworkService.ethernetConnected)
return Theme.primary;
return NetworkService.networkStatus !== "disconnected" ? Theme.primary : Theme.surfaceText; return NetworkService.networkStatus !== "disconnected" ? Theme.primary : Theme.surfaceText;
} }
function getIconBlinking(id) {
if (id === "network")
return NetworkService.isWifiConnecting;
if (id === "bluetooth")
return BluetoothService.connecting;
return false;
}
function getVolumeIconName() { function getVolumeIconName() {
if (!AudioService.sink?.audio) if (!AudioService.sink?.audio)
return "volume_up"; return "volume_up";
@@ -485,6 +495,7 @@ BasePill {
} }
DankIcon { DankIcon {
id: vIconOnlyItem
anchors.centerIn: parent anchors.centerIn: parent
visible: !verticalGroupItem.modelData.composite visible: !verticalGroupItem.modelData.composite
name: { name: {
@@ -515,7 +526,7 @@ BasePill {
case "vpn": case "vpn":
return NetworkService.vpnConnected ? Theme.primary : Theme.surfaceText; return NetworkService.vpnConnected ? Theme.primary : Theme.surfaceText;
case "bluetooth": case "bluetooth":
return BluetoothService.connected ? Theme.primary : Theme.surfaceText; return (BluetoothService.connected || BluetoothService.connecting) ? Theme.primary : Theme.surfaceText;
case "battery": case "battery":
return root.getBatteryIconColor(); return root.getBatteryIconColor();
case "printer": case "printer":
@@ -524,6 +535,11 @@ BasePill {
return Theme.widgetIconColor; return Theme.widgetIconColor;
} }
} }
DankBlink {
target: vIconOnlyItem
running: root.getIconBlinking(verticalGroupItem.modelData.id)
}
} }
DankIcon { DankIcon {
@@ -687,7 +703,7 @@ BasePill {
case "vpn": case "vpn":
return NetworkService.vpnConnected ? Theme.primary : Theme.surfaceText; return NetworkService.vpnConnected ? Theme.primary : Theme.surfaceText;
case "bluetooth": case "bluetooth":
return BluetoothService.connected ? Theme.primary : Theme.surfaceText; return (BluetoothService.connected || BluetoothService.connecting) ? Theme.primary : Theme.surfaceText;
case "battery": case "battery":
return root.getBatteryIconColor(); return root.getBatteryIconColor();
case "printer": case "printer":
@@ -696,6 +712,11 @@ BasePill {
return Theme.widgetIconColor; return Theme.widgetIconColor;
} }
} }
DankBlink {
target: iconOnlyItem
running: root.getIconBlinking(horizontalGroupItem.modelData.id)
}
} }
Rectangle { Rectangle {
+13 -1
View File
@@ -1461,6 +1461,7 @@ Item {
} }
DankIcon { DankIcon {
id: lockNetworkIcon
name: { name: {
if (NetworkService.wifiToggling) if (NetworkService.wifiToggling)
return "sync"; return "sync";
@@ -1474,9 +1475,14 @@ Item {
} }
} }
size: Theme.iconSize - 2 size: Theme.iconSize - 2
color: NetworkService.networkStatus !== "disconnected" ? "white" : Qt.rgba(255, 255, 255, 0.5) color: (NetworkService.networkStatus !== "disconnected" || NetworkService.isConnecting) ? "white" : Qt.rgba(255, 255, 255, 0.5)
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
visible: NetworkService.networkAvailable visible: NetworkService.networkAvailable
DankBlink {
target: lockNetworkIcon
running: NetworkService.isWifiConnecting
}
} }
DankIcon { DankIcon {
@@ -1488,11 +1494,17 @@ Item {
} }
DankIcon { DankIcon {
id: lockBluetoothIcon
name: "bluetooth" name: "bluetooth"
size: Theme.iconSize - 2 size: Theme.iconSize - 2
color: "white" color: "white"
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
visible: BluetoothService.available && BluetoothService.enabled visible: BluetoothService.available && BluetoothService.enabled
DankBlink {
target: lockBluetoothIcon
running: BluetoothService.connecting
}
} }
DankIcon { DankIcon {
+14
View File
@@ -28,6 +28,20 @@ Singleton {
}); });
return isConnected; return isConnected;
} }
readonly property bool connecting: {
if (!adapter || !adapter.devices) {
return false;
}
let busy = false;
adapter.devices.values.forEach(dev => {
if (!dev)
return;
if (dev.pairing || dev.state === BluetoothDeviceState.Connecting)
busy = true;
});
return busy;
}
readonly property var pairedDevices: { readonly property var pairedDevices: {
if (!adapter || !adapter.devices) { if (!adapter || !adapter.devices) {
return []; return [];
@@ -41,6 +41,9 @@ Singleton {
property var savedConnections: [] property var savedConnections: []
property var ssidToConnectionName: ({}) property var ssidToConnectionName: ({})
property var wifiSignalIcon: { property var wifiSignalIcon: {
if (isConnecting) {
return "wifi";
}
if (!wifiConnected) { if (!wifiConnected) {
return "wifi_off"; return "wifi_off";
} }
@@ -99,6 +99,9 @@ Singleton {
} }
readonly property string wifiSignalIcon: { readonly property string wifiSignalIcon: {
if (isConnecting) {
return "wifi";
}
if (!wifiConnected || networkStatus !== "wifi") { if (!wifiConnected || networkStatus !== "wifi") {
return "wifi_off"; return "wifi_off";
} }
+1
View File
@@ -42,6 +42,7 @@ Singleton {
property string userPreference: activeService?.userPreference ?? "auto" property string userPreference: activeService?.userPreference ?? "auto"
property bool isConnecting: activeService?.isConnecting ?? false property bool isConnecting: activeService?.isConnecting ?? false
readonly property bool isWifiConnecting: isConnecting && !ethernetConnected && !wifiToggling
property string connectingSSID: activeService?.connectingSSID ?? "" property string connectingSSID: activeService?.connectingSSID ?? ""
property string connectionError: activeService?.connectionError ?? "" property string connectionError: activeService?.connectionError ?? ""
+28
View File
@@ -0,0 +1,28 @@
import QtQuick
SequentialAnimation {
id: root
property Item target
property real minOpacity: 0.3
property int pulseDuration: 600
loops: Animation.Infinite
NumberAnimation {
target: root.target
property: "opacity"
to: root.minOpacity
duration: root.pulseDuration
easing.type: Easing.InOutQuad
}
NumberAnimation {
target: root.target
property: "opacity"
to: 1.0
duration: root.pulseDuration
easing.type: Easing.InOutQuad
}
onStopped: if (root.target) root.target.opacity = 1.0
}