diff --git a/lib/types/options.d.ts b/lib/types/options.d.ts index 51e7f76..5596376 100644 --- a/lib/types/options.d.ts +++ b/lib/types/options.d.ts @@ -215,3 +215,5 @@ export type ColorMapKey = keyof typeof defaultColorMap; export type ColorMapValue = (typeof defaultColorMap)[ColorMapKey]; export type ScalingPriority = 'gdk' | 'hyprland' | 'both'; + +export type BluetoothBatteryState = 'paired' | 'connected' | 'always'; diff --git a/modules/bar/network/index.ts b/modules/bar/network/index.ts index a6176f4..32debe9 100644 --- a/modules/bar/network/index.ts +++ b/modules/bar/network/index.ts @@ -13,7 +13,11 @@ const formatFrequency = (frequency: number): string => { }; const formatWifiInfo = (wifi: Wifi): string => { - return `Network: ${wifi.ssid === '' ? 'None' : wifi.ssid} \nSignal Strength: ${wifi.strength >= 0 ? wifi.strength : '--'}% \nFrequency: ${wifi.frequency >= 0 ? formatFrequency(wifi.frequency) : '--'}`; + const netSsid = wifi.ssid === '' ? 'None' : wifi.ssid; + const wifiStrength = wifi.strength >= 0 ? wifi.strength : '--'; + const wifiFreq = wifi.frequency >= 0 ? formatFrequency(wifi.frequency) : '--'; + + return `Network: ${netSsid} \nSignal Strength: ${wifiStrength}% \nFrequency: ${wifiFreq}`; }; const { diff --git a/modules/menus/bluetooth/devices/devicelist.ts b/modules/menus/bluetooth/devices/devicelist.ts index d701a81..af1a66b 100644 --- a/modules/menus/bluetooth/devices/devicelist.ts +++ b/modules/menus/bluetooth/devices/devicelist.ts @@ -1,12 +1,13 @@ -import { Bluetooth } from 'types/service/bluetooth.js'; +import { Bluetooth, BluetoothDevice } from 'types/service/bluetooth.js'; import Box from 'types/widgets/box.js'; import { connectedControls } from './connectedControls.js'; import { getBluetoothIcon } from '../utils.js'; import Gtk from 'types/@girs/gtk-3.0/gtk-3.0.js'; import { Attribute, Child } from 'lib/types/widget.js'; import options from 'options'; +import Label from 'types/widgets/label.js'; -const { showBattery, batteryIcon } = options.menus.bluetooth; +const { showBattery, batteryIcon, batteryState } = options.menus.bluetooth; const devices = (bluetooth: Bluetooth, self: Box): Box => { return self.hook(bluetooth, () => { @@ -65,6 +66,33 @@ const devices = (bluetooth: Bluetooth, self: Box): Box => { + return Widget.Label({ + hpack: 'start', + class_name: 'connection-status dim', + label: device.connected ? 'Connected' : 'Paired', + }); + }; + + const getBatteryInfo = (device: BluetoothDevice, batIcon: string): Gtk.Widget[] => { + if (typeof device.battery_percentage === 'number' && device.battery_percentage >= 0) { + return [ + Widget.Separator({ + class_name: 'menu-separator bluetooth-battery', + }), + Widget.Label({ + class_name: 'connection-status txt-icon', + label: `${batIcon}`, + }), + Widget.Label({ + class_name: 'connection-status battery', + label: `${device.battery_percentage}%`, + }), + ]; + } + return []; + }; + return (self.child = Widget.Box({ vertical: true, children: availableDevices.map((device) => { @@ -107,50 +135,24 @@ const devices = (bluetooth: Bluetooth, self: Box): Box { - if (!showBat) { - return [ - Widget.Label({ - hpack: 'start', - class_name: 'connection-status dim', - label: device.connected - ? 'Connected' - : 'Paired', - }), - ]; + [ + showBattery.bind('value'), + batteryIcon.bind('value'), + batteryState.bind('value'), + ], + (showBat, batIcon, batState) => { + if ( + !showBat || + (batState === 'paired' && !device.paired) || + (batState === 'connected' && !device.connected) + ) { + return [getConnectionStatusLabel(device)]; } return [ - Widget.Label({ - hpack: 'start', - class_name: 'connection-status dim', - label: device.connected - ? 'Connected' - : 'Paired', - }), + getConnectionStatusLabel(device), Widget.Box({ - children: - typeof device.battery_percentage === - 'number' && - device.battery_percentage >= 0 - ? [ - Widget.Separator({ - class_name: - 'menu-separator', - }), - Widget.Label({ - class_name: - 'connection-status txt-icon', - label: `${batIcon}`, - }), - Widget.Label({ - class_name: - 'connection-status battery', - label: `${device.battery_percentage}%`, - }), - ] - : [], + children: getBatteryInfo(device, batIcon), }), ]; }, diff --git a/options.ts b/options.ts index 5f04810..d86b368 100644 --- a/options.ts +++ b/options.ts @@ -5,6 +5,7 @@ import { ActiveWsIndicator, BarButtonStyles, BarLocation, + BluetoothBatteryState, NotificationAnchor, OSDAnchor, OSDOrientation, @@ -1057,6 +1058,7 @@ const options = mkOptions(OPTIONS, { transitionTime: opt(200), bluetooth: { showBattery: opt(false), + batteryState: opt('connected'), batteryIcon: opt('󰥉'), }, volume: { diff --git a/scss/style/menus/bluetooth.scss b/scss/style/menus/bluetooth.scss index 9ea32b7..1550d72 100644 --- a/scss/style/menus/bluetooth.scss +++ b/scss/style/menus/bluetooth.scss @@ -1,165 +1,197 @@ .menu-items-container.bluetooth * { - font-size: $font-size * $bar-menus-menu-bluetooth-scaling * 0.01; + font-size: $font-size * $bar-menus-menu-bluetooth-scaling * 0.01; } -@import "./menu.scss"; +@import './menu.scss'; .menu-items.bluetooth { - background: if($bar-menus-monochrome, $bar-menus-background, $bar-menus-menu-bluetooth-background-color); - border-color: if($bar-menus-monochrome, $bar-menus-border-color, $bar-menus-menu-bluetooth-border-color); - opacity: $bar-menus-opacity * 0.01; - font-size: $font-size * $bar-menus-menu-bluetooth-scaling * 0.01; + background: if($bar-menus-monochrome, $bar-menus-background, $bar-menus-menu-bluetooth-background-color); + border-color: if($bar-menus-monochrome, $bar-menus-border-color, $bar-menus-menu-bluetooth-border-color); + opacity: $bar-menus-opacity * 0.01; + font-size: $font-size * $bar-menus-menu-bluetooth-scaling * 0.01; } .menu-items-container.bluetooth { + min-width: 18em * $bar-menus-menu-bluetooth-scaling * 0.01; - min-width: 18em * $bar-menus-menu-bluetooth-scaling * 0.01; + font-size: 1.3em; - font-size: 1.3em; - - .menu-section-container { - margin: 1em 0em; - } - - .menu-label-container { - background: if($bar-menus-monochrome, $bar-menus-cards, $bar-menus-menu-bluetooth-card-color); - - .menu-label { - color: if($bar-menus-monochrome, $bar-menus-label, $bar-menus-menu-bluetooth-label-color); + .menu-section-container { + margin: 1em 0em; } - .controls-container { - margin: 0.5em 1em; + .menu-label-container { + background: if($bar-menus-monochrome, $bar-menus-cards, $bar-menus-menu-bluetooth-card-color); + + .menu-label { + color: if($bar-menus-monochrome, $bar-menus-label, $bar-menus-menu-bluetooth-label-color); + } + + .controls-container { + margin: 0.5em 1em; + } } - } + .menu-items-section { + background: if($bar-menus-monochrome, $bar-menus-cards, $bar-menus-menu-bluetooth-card-color); + min-height: 20em * $bar-menus-menu-bluetooth-scaling * 0.01; + font-size: 1em; + } - .menu-items-section { - background: if($bar-menus-monochrome, $bar-menus-cards, $bar-menus-menu-bluetooth-card-color); - min-height: 20em * $bar-menus-menu-bluetooth-scaling * 0.01; - font-size: 1em; - } + button { + margin-right: 0.5em; - button { - margin-right: 0.5em; + &.search { + image { + color: if( + $bar-menus-monochrome, + $bar-menus-iconbuttons-passive, + $bar-menus-menu-bluetooth-iconbutton-passive + ); + } - &.search { - image { + &:hover image { + color: if( + $bar-menus-monochrome, + $bar-menus-iconbuttons-active, + $bar-menus-menu-bluetooth-iconbutton-active + ); + } + + font-size: 0.8em; + margin-bottom: 0em; + } + + &:hover { + color: if( + $bar-menus-monochrome, + $bar-menus-iconbuttons-active, + $bar-menus-menu-bluetooth-iconbutton-active + ); + } + } + + .menu-icon-button.bluetooth { color: if($bar-menus-monochrome, $bar-menus-iconbuttons-passive, $bar-menus-menu-bluetooth-iconbutton-passive); - } - &:hover image { + &:hover { + color: if( + $bar-menus-monochrome, + $bar-menus-iconbuttons-active, + $bar-menus-menu-bluetooth-iconbutton-active + ); + } + } + + .bluetooth-element-item { + margin-bottom: 0.4em; + + &:hover { + .menu-button-icon, + .menu-button-name { + color: if( + $bar-menus-monochrome, + $bar-menus-iconbuttons-active, + $bar-menus-menu-bluetooth-iconbutton-active + ); + } + } + + image { + margin-right: 0em; + margin-top: 0em; + min-height: 1em; + min-width: 1em; + + &.active { + color: if($bar-menus-monochrome, $bar-menus-icons-active, $bar-menus-menu-bluetooth-icons-active); + } + } + + label { + color: if($bar-menus-monochrome, $bar-menus-text, $bar-menus-menu-bluetooth-text); + font-size: 1em; + } + + .menu-button-icon { + font-size: 1.5em; + color: if($bar-menus-monochrome, $bar-menus-icons-passive, $bar-menus-menu-bluetooth-icons-passive); + + &.active { + color: if($bar-menus-monochrome, $bar-menus-icons-active, $bar-menus-menu-bluetooth-icons-active); + } + } + + .connection-status { + font-size: 0.9em; + margin-left: 0.6rem; + color: if($bar-menus-monochrome, $bar-menus-text, $bar-menus-menu-bluetooth-text); + opacity: 0.4; + } + .battery { + opacity: 0.6; + } + } + + spinner { + min-height: 1.3em; + min-width: 1.3em; color: if($bar-menus-monochrome, $bar-menus-iconbuttons-active, $bar-menus-menu-bluetooth-iconbutton-active); - } - - font-size: 0.8em; - margin-bottom: 0em; } - &:hover { - color: if($bar-menus-monochrome, $bar-menus-iconbuttons-active, $bar-menus-menu-bluetooth-iconbutton-active); - } - } - - .menu-icon-button.bluetooth { - color: if($bar-menus-monochrome, $bar-menus-iconbuttons-passive, $bar-menus-menu-bluetooth-iconbutton-passive); - - &:hover { - color: if($bar-menus-monochrome, $bar-menus-iconbuttons-active, $bar-menus-menu-bluetooth-iconbutton-active); - } - } - - .bluetooth-element-item { - margin-bottom: 0.4em; - - &:hover { - - .menu-button-icon, - .menu-button-name { - color: if($bar-menus-monochrome, $bar-menus-iconbuttons-active, $bar-menus-menu-bluetooth-iconbutton-active); - } + .menu-separator { + margin: 0em 1em; } - image { - margin-right: 0em; - margin-top: 0.0em; - min-height: 1em; - min-width: 1em; + .menu-switch.bluetooth { + background-color: if( + $bar-menus-monochrome, + $bar-menus-switch-disabled, + $bar-menus-menu-bluetooth-switch-disabled + ); - &.active { - color: if($bar-menus-monochrome, $bar-menus-icons-active, $bar-menus-menu-bluetooth-icons-active); - } + &:checked { + background: if($bar-menus-monochrome, $bar-menus-switch-enabled, $bar-menus-menu-bluetooth-switch-enabled); + } + + slider { + background-color: if($bar-menus-monochrome, $bar-menus-switch-puck, $bar-menus-menu-bluetooth-switch-puck); + } + + &:hover { + trough { + background: if( + $bar-menus-monochrome, + $bar-menus-switch-disabled, + $bar-menus-menu-bluetooth-switch-disabled + ); + } + + slider { + background: if($bar-menus-monochrome, $bar-menus-switch-puck, $bar-menus-menu-bluetooth-switch-puck); + } + } + + &:active { + background: if($bar-menus-monochrome, $bar-menus-switch-enabled, $bar-menus-menu-bluetooth-switch-enabled); + } } - label { - color: if($bar-menus-monochrome, $bar-menus-text, $bar-menus-menu-bluetooth-text); - font-size: 1em; + .no-bluetooth-devices.dim, + .search-bluetooth-label.dim, + .bluetooth-disabled.dim { + &:last-child { + margin-bottom: 2em; + } + + color: if($bar-menus-monochrome, $bar-menus-text, $bar-menus-menu-bluetooth-text); + opacity: 0.5; } - - .menu-button-icon { - font-size: 1.5em; - color: if($bar-menus-monochrome, $bar-menus-icons-passive, $bar-menus-menu-bluetooth-icons-passive); - - &.active { - color: if($bar-menus-monochrome, $bar-menus-icons-active, $bar-menus-menu-bluetooth-icons-active); - } + .menu-separator.bluetooth-battery { + margin: 0.2em 0.5em; + min-width: 0.1em; } - - .connection-status { - font-size: 0.9em; - margin-left: 0.6rem; - color: if($bar-menus-monochrome, $bar-menus-text, $bar-menus-menu-bluetooth-text); - opacity: 0.4; + .connection-status.txt-icon { + margin-left: 0.2em; } - .battery{ - opacity: 0.6; - } - } - - spinner { - min-height: 1.3em; - min-width: 1.3em; - color: if($bar-menus-monochrome, $bar-menus-iconbuttons-active, $bar-menus-menu-bluetooth-iconbutton-active); - } - - .menu-separator { - margin: 0em 1em; - } - - .menu-switch.bluetooth { - background-color: if($bar-menus-monochrome, $bar-menus-switch-disabled, $bar-menus-menu-bluetooth-switch-disabled); - - &:checked { - background: if($bar-menus-monochrome, $bar-menus-switch-enabled, $bar-menus-menu-bluetooth-switch-enabled); - } - - slider { - background-color: if($bar-menus-monochrome, $bar-menus-switch-puck, $bar-menus-menu-bluetooth-switch-puck); - } - - &:hover { - trough { - background: if($bar-menus-monochrome, $bar-menus-switch-disabled, $bar-menus-menu-bluetooth-switch-disabled); - } - - slider { - background: if($bar-menus-monochrome, $bar-menus-switch-puck, $bar-menus-menu-bluetooth-switch-puck); - } - } - - &:active { - background: if($bar-menus-monochrome, $bar-menus-switch-enabled, $bar-menus-menu-bluetooth-switch-enabled); - } - } - - .no-bluetooth-devices.dim, - .search-bluetooth-label.dim, - .bluetooth-disabled.dim { - &:last-child { - margin-bottom: 2em; - } - - color: if($bar-menus-monochrome, $bar-menus-text, $bar-menus-menu-bluetooth-text); - opacity: 0.5; - } } diff --git a/widget/settings/pages/config/menus/bluetooth.ts b/widget/settings/pages/config/menus/bluetooth.ts index 7bfeb1c..191ed41 100644 --- a/widget/settings/pages/config/menus/bluetooth.ts +++ b/widget/settings/pages/config/menus/bluetooth.ts @@ -18,6 +18,12 @@ export const BluetoothMenuSettings = (): Scrollable => { title: 'Show Battery Percentage for Connected Devices (If Supported)', type: 'boolean', }), + Option({ + opt: options.menus.bluetooth.batteryState, + title: 'Show Battery When', + type: 'enum', + enums: ['connected', 'paired', 'always'], + }), Option({ opt: options.menus.bluetooth.batteryIcon, title: 'Battery Icon',