From b3e146b310fa39fc82928baa1df2171edc03bd24 Mon Sep 17 00:00:00 2001 From: Kaushik Rawat <119434133+enchanteddev@users.noreply.github.com> Date: Fri, 11 Sep 2026 10:47:06 +0530 Subject: [PATCH] Island: Added option to choose the control center icon to be either Battery gauge or Connectivity (#3342) * Island: Added option to choose the control center icon to be either Battery gauge or Connectivity * Add Control Center to settings search index * island: keep connectivity dim opacity off the blink target DankBlink animates opacity and forces it back to 1.0 when it stops, which broke the icon's binding and left the disabled state fully opaque. --- quickshell/Common/SettingsData.qml | 6 + .../DankIsland/Activities/HomeCompact.qml | 187 +++++++++++++++++- .../DankIsland/DankIslandHostWindow.qml | 1 + .../Modules/DankIsland/IslandController.qml | 1 + quickshell/Modules/Settings/DankIslandTab.qml | 16 +- .../Settings/IslandHomeLayoutEditor.qml | 6 +- .../translations/settings_search_index.json | 19 ++ 7 files changed, 223 insertions(+), 13 deletions(-) diff --git a/quickshell/Common/SettingsData.qml b/quickshell/Common/SettingsData.qml index 4f17aeaeb..0e2988031 100644 --- a/quickshell/Common/SettingsData.qml +++ b/quickshell/Common/SettingsData.qml @@ -1017,6 +1017,7 @@ Singleton { "islandHomeClockDisplay": "both", "islandHomeVolumeDisplay": "both", "islandHomeBrightnessDisplay": "both", + "islandHomeStatusContent": "battery", "islandBatteryStyle": "solid", "islandSatellitesEnabled": true, "islandSatellitePosition": "edges", @@ -1046,6 +1047,11 @@ Singleton { return value === "time" || value === "date" || value === "both" ? value : "both"; } + function islandHomeStatusContent(bc) { + const value = islandSetting(bc, "islandHomeStatusContent"); + return value === "battery" || value === "connectivity" ? value : "battery"; + } + function islandEdge(bc) { return positionToSide(bc?.position ?? SettingsData.Position.Top) || "top"; } diff --git a/quickshell/Modules/DankIsland/Activities/HomeCompact.qml b/quickshell/Modules/DankIsland/Activities/HomeCompact.qml index de2109a02..56abab098 100644 --- a/quickshell/Modules/DankIsland/Activities/HomeCompact.qml +++ b/quickshell/Modules/DankIsland/Activities/HomeCompact.qml @@ -127,6 +127,61 @@ Item { DisplayService.setBrightness(next, root.brightnessDevice.id, true); } + function connectivityIconName(type) { + if (type === "wifi") { + if (!NetworkService.wifiAvailable || !NetworkService.networkAvailable) + return "wifi_off"; + if (NetworkService.wifiToggling) + return "sync"; + if (!NetworkService.wifiEnabled) + return "wifi_off"; + return NetworkService.wifiSignalIcon; + } + + if (!BluetoothService.available) + return "bluetooth_disabled"; + return BluetoothService.connected ? "bluetooth_connected" : "bluetooth"; + } + + function connectivityIconColor(type) { + if (type === "wifi") { + if (!NetworkService.wifiAvailable || !NetworkService.networkAvailable) + return Theme.surfaceTextMedium; + if (NetworkService.wifiToggling || NetworkService.isWifiConnecting || NetworkService.wifiConnected) + return Theme.primary; + if (!NetworkService.wifiEnabled) + return Theme.surfaceTextMedium; + return Theme.surfaceText; + } + + if (!BluetoothService.available || !BluetoothService.enabled) + return Theme.surfaceTextMedium; + return (BluetoothService.connected || BluetoothService.connecting) ? Theme.primary : Theme.surfaceText; + } + + function connectivityIconOpacity(type) { + if (type === "wifi") + return NetworkService.wifiAvailable && NetworkService.networkAvailable && (NetworkService.wifiEnabled || NetworkService.wifiToggling) ? 1 : 0.5; + return BluetoothService.available && BluetoothService.enabled ? 1 : 0.5; + } + + function connectivityBusy(type) { + return type === "wifi" ? (NetworkService.wifiToggling || NetworkService.isWifiConnecting) : BluetoothService.connecting; + } + + function toggleConnectivity(type) { + if (type === "wifi") { + if (!NetworkService.wifiAvailable || !NetworkService.networkAvailable || NetworkService.wifiToggling || NetworkService.isWifiConnecting) + return; + NetworkService.toggleWifiRadio(); + return; + } + + if (!BluetoothService.available || BluetoothService.connecting) + return; + BluetoothService.toggleBluetooth(); + } + function syncWeatherRef(wanted) { if (wanted === weatherRefHeld) return; @@ -197,7 +252,8 @@ Item { readonly property bool isNotifications: item.groupId === "notifications" readonly property bool isVolume: item.groupId === "volume" readonly property bool isBrightness: item.groupId === "brightness" - readonly property bool usesBattery: item.isStatus && BatteryService.batteryAvailable + readonly property bool usesConnectivity: item.isStatus && root.controller.homeStatusContent === "connectivity" + readonly property bool usesBattery: item.isStatus && !item.usesConnectivity && BatteryService.batteryAvailable width: { if (item.isMedia) @@ -209,7 +265,7 @@ Item { if (item.isVolume || item.isBrightness) return systemLevelRow.implicitWidth; if (item.isStatus) - return item.usesBattery ? batteryMeter.width : root.iconSize; + return item.usesConnectivity ? connectivityRow.implicitWidth : (item.usesBattery ? batteryMeter.width : root.iconSize); return clockRow.implicitWidth; } height: root.slotSize @@ -357,9 +413,57 @@ Item { levelColors: (root.controller.barConfig?.batteryColorMode ?? "theme") === "level" } + Row { + id: connectivityRow + + anchors.centerIn: parent + visible: item.usesConnectivity + spacing: Theme.spacingXXS + + Item { + width: root.iconSize + height: root.iconSize + opacity: root.connectivityIconOpacity("wifi") + + DankIcon { + id: wifiIcon + + anchors.centerIn: parent + name: root.connectivityIconName("wifi") + size: root.statusIconSize + color: root.connectivityIconColor("wifi") + + DankBlink { + target: wifiIcon + running: item.usesConnectivity && root.connectivityBusy("wifi") + } + } + } + + Item { + width: root.iconSize + height: root.iconSize + opacity: root.connectivityIconOpacity("bluetooth") + + DankIcon { + id: bluetoothIcon + + anchors.centerIn: parent + name: root.connectivityIconName("bluetooth") + size: root.statusIconSize + color: root.connectivityIconColor("bluetooth") + + DankBlink { + target: bluetoothIcon + running: item.usesConnectivity && root.connectivityBusy("bluetooth") + } + } + } + } + DankIcon { anchors.centerIn: parent - visible: item.isStatus && !item.usesBattery + visible: item.isStatus && !item.usesBattery && !item.usesConnectivity name: "tune" size: root.iconSize color: Theme.surfaceText @@ -375,7 +479,16 @@ Item { enabled: !item.isClock controller: root.controller acceptedButtons: Qt.LeftButton | Qt.MiddleButton - onClicked: (event) => { + onClicked: event => { + if (item.usesConnectivity) { + const type = event.x < item.leadPad + item.width / 2 ? "wifi" : "bluetooth"; + if (event.button === Qt.MiddleButton) { + root.toggleConnectivity(type); + return; + } + root.controller.requestControlCenter("", false); + return; + } if (event.button === Qt.MiddleButton) { if (item.isMedia && root.controller.mediaAvailable && MprisController.activePlayer?.canTogglePlaying) MprisController.activePlayer.togglePlaying(); @@ -385,7 +498,7 @@ Item { } return; } - root.activateGroup(item.groupId) + root.activateGroup(item.groupId); } onWheel: wheel => { if (!item.isVolume && !item.isBrightness) @@ -412,7 +525,8 @@ Item { readonly property bool isNotifications: item.groupId === "notifications" readonly property bool isVolume: item.groupId === "volume" readonly property bool isBrightness: item.groupId === "brightness" - readonly property bool usesBattery: item.isStatus && BatteryService.batteryAvailable + readonly property bool usesConnectivity: item.isStatus && root.controller.homeStatusContent === "connectivity" + readonly property bool usesBattery: item.isStatus && !item.usesConnectivity && BatteryService.batteryAvailable width: root.width height: stack.implicitHeight @@ -562,9 +676,55 @@ Item { levelColors: (root.controller.barConfig?.batteryColorMode ?? "theme") === "level" } + Column { + anchors.horizontalCenter: parent.horizontalCenter + visible: item.usesConnectivity + spacing: Theme.spacingXXS + + Item { + width: root.iconSize + height: root.iconSize + opacity: root.connectivityIconOpacity("wifi") + + DankIcon { + id: verticalWifiIcon + + anchors.centerIn: parent + name: root.connectivityIconName("wifi") + size: root.statusIconSize + color: root.connectivityIconColor("wifi") + + DankBlink { + target: verticalWifiIcon + running: item.usesConnectivity && root.connectivityBusy("wifi") + } + } + } + + Item { + width: root.iconSize + height: root.iconSize + opacity: root.connectivityIconOpacity("bluetooth") + + DankIcon { + id: verticalBluetoothIcon + + anchors.centerIn: parent + name: root.connectivityIconName("bluetooth") + size: root.statusIconSize + color: root.connectivityIconColor("bluetooth") + + DankBlink { + target: verticalBluetoothIcon + running: item.usesConnectivity && root.connectivityBusy("bluetooth") + } + } + } + } + DankIcon { anchors.horizontalCenter: parent.horizontalCenter - visible: item.isStatus && !item.usesBattery + visible: item.isStatus && !item.usesBattery && !item.usesConnectivity name: "tune" size: root.iconSize color: Theme.surfaceText @@ -581,7 +741,16 @@ Item { enabled: !item.isClock controller: root.controller acceptedButtons: Qt.LeftButton | Qt.MiddleButton - onClicked: (event) => { + onClicked: event => { + if (item.usesConnectivity) { + const type = event.y < item.leadPad + item.height / 2 ? "wifi" : "bluetooth"; + if (event.button === Qt.MiddleButton) { + root.toggleConnectivity(type); + return; + } + root.controller.requestControlCenter("", false); + return; + } if (event.button === Qt.MiddleButton) { if (item.isMedia && root.controller.mediaAvailable && MprisController.activePlayer?.canTogglePlaying) MprisController.activePlayer.togglePlaying(); @@ -591,7 +760,7 @@ Item { } return; } - root.activateGroup(item.groupId) + root.activateGroup(item.groupId); } onWheel: wheel => { if (!item.isVolume && !item.isBrightness) diff --git a/quickshell/Modules/DankIsland/DankIslandHostWindow.qml b/quickshell/Modules/DankIsland/DankIslandHostWindow.qml index 3e46cdc7d..36b8b4910 100644 --- a/quickshell/Modules/DankIsland/DankIslandHostWindow.qml +++ b/quickshell/Modules/DankIsland/DankIslandHostWindow.qml @@ -176,6 +176,7 @@ PanelWindow { compactThickness: root.compactThickness cornerRadius: Math.max(0, Math.min(64, root.setting("islandCornerRadius"))) homeCompactTight: root.setting("islandHomeCompactTight") + homeStatusContent: SettingsData.islandHomeStatusContent(root.barConfig) homeClockDisplay: SettingsData.islandClockDisplay(root.barConfig) homeVolumeDisplay: SettingsData.islandLevelDisplay(root.barConfig, "islandHomeVolumeDisplay") homeBrightnessDisplay: SettingsData.islandLevelDisplay(root.barConfig, "islandHomeBrightnessDisplay") diff --git a/quickshell/Modules/DankIsland/IslandController.qml b/quickshell/Modules/DankIsland/IslandController.qml index 4fedf8a38..364759c5f 100644 --- a/quickshell/Modules/DankIsland/IslandController.qml +++ b/quickshell/Modules/DankIsland/IslandController.qml @@ -281,6 +281,7 @@ QtObject { readonly property var homeLeftGroups: homeGroups("left") readonly property var homeRightGroups: homeGroups("right") + property string homeStatusContent: "battery" property string homeClockDisplay: "both" property string homeVolumeDisplay: "both" property string homeBrightnessDisplay: "both" diff --git a/quickshell/Modules/Settings/DankIslandTab.qml b/quickshell/Modules/Settings/DankIslandTab.qml index 8f86e6ee3..baa92e884 100644 --- a/quickshell/Modules/Settings/DankIslandTab.qml +++ b/quickshell/Modules/Settings/DankIslandTab.qml @@ -55,6 +55,7 @@ Item { } readonly property var clockDisplayValues: ["time", "date", "both"] readonly property var systemLevelDisplayValues: ["icon", "percentage", "both"] + readonly property var statusContentValues: ["battery", "connectivity"] readonly property var paletteValues: ["default", "bright", "dim"] readonly property var batteryStyleValues: ["solid", "outline", "ring"] readonly property var satellitePositionValues: ["island", "edges"] @@ -274,6 +275,19 @@ Item { } } + SettingsButtonGroupRow { + settingKey: "islandHomeStatusContent" + tags: ["island", "home", "compact", "status", "battery", "wifi", "bluetooth", "connectivity"] + text: I18n.tr("Control Center", "island settings: status group content row") + visible: SettingsData.islandHomeGroupEnabled(root.config, "status") + model: [I18n.tr("Battery", "island settings: status group battery content"), I18n.tr("Wi-Fi & Bluetooth", "island settings: status group connectivity content")] + currentIndex: root.valueIndex(root.statusContentValues, SettingsData.islandHomeStatusContent(root.config), "battery") + onSelectionChanged: (index, selected) => { + if (selected) + root.apply("islandHomeStatusContent", root.statusContentValues[index] ?? "battery"); + } + } + SettingsToggleRow { settingKey: "islandHomeCompactTight" tags: ["island", "home", "compact", "narrow", "width", "height", "clock"] @@ -379,7 +393,7 @@ Item { tags: ["island", "battery", "gauge", "solid", "outline", "ring", "circle", "appearance"] text: I18n.tr("Battery Style", "island settings: battery meter style row") description: I18n.tr("Solid or outlined material meter, or a circular gauge", "island settings: battery style description") - visible: BatteryService.batteryAvailable + visible: SettingsData.islandHomeGroupEnabled(root.config, "status") && BatteryService.batteryAvailable && SettingsData.islandHomeStatusContent(root.config) === "battery" model: [I18n.tr("Solid", "island settings: filled battery meter style"), I18n.tr("Outline", "island settings: outlined battery meter style"), I18n.tr("Circle", "island settings: circular battery meter style")] currentIndex: root.valueIndex(root.batteryStyleValues, root.setting("islandBatteryStyle"), "solid") onSelectionChanged: (index, selected) => { diff --git a/quickshell/Modules/Settings/IslandHomeLayoutEditor.qml b/quickshell/Modules/Settings/IslandHomeLayoutEditor.qml index 46943281f..f6faec886 100644 --- a/quickshell/Modules/Settings/IslandHomeLayoutEditor.qml +++ b/quickshell/Modules/Settings/IslandHomeLayoutEditor.qml @@ -35,9 +35,9 @@ Item { "description": SettingsData.weatherEnabled ? I18n.tr("Weather icon and temperature open the weather activity", "island settings: weather slot description") : I18n.tr("Enable weather in Time & Weather to show this shortcut", "island settings: weather slot disabled hint") }, "status": { - "icon": BatteryService.batteryAvailable ? "battery_full" : "tune", - "text": I18n.tr("Battery / Control Center", "island settings: battery or control center slot row"), - "description": BatteryService.batteryAvailable ? I18n.tr("Battery gauge opens Control Center", "island settings: status slot description with battery") : I18n.tr("Tools icon opens Control Center", "island settings: status slot description without battery") + "icon": "settings", + "text": I18n.tr("Control Center", "island settings: battery or control center slot row"), + "description": I18n.tr("Choose between battery gauge or connectivity icons", "island settings: control center slot description") }, "volume": { "icon": "volume_up", diff --git a/quickshell/translations/settings_search_index.json b/quickshell/translations/settings_search_index.json index 3fdd6e392..39616545b 100644 --- a/quickshell/translations/settings_search_index.json +++ b/quickshell/translations/settings_search_index.json @@ -12619,6 +12619,25 @@ "description": "Compact home clock pill in both width and height", "conditionKey": "islandEnabled" }, + { + "section": "islandHomeStatusContent", + "label": "Control Center", + "tabIndex": 46, + "category": "Island", + "keywords": [ + "battery", + "bluetooth", + "center", + "compact", + "connectivity", + "control", + "home", + "island", + "status", + "wifi" + ], + "conditionKey": "islandEnabled" + }, { "section": "islandCornerRadius", "label": "Corner Radius",