[Astal] Add mode indicator

This commit is contained in:
2025-10-17 09:47:18 +02:00
parent 94a57be9f7
commit 47bbdd9f85
6 changed files with 282 additions and 219 deletions

View File

@@ -1,17 +1,24 @@
import { bind } from "astal";
import { execAsync } from "astal";
import AstalBattery from "gi://AstalBattery";
import AstalBluetooth from "gi://AstalBluetooth";
import AstalNetwork from "gi://AstalNetwork";
import AstalWp from "gi://AstalWp";
import { Gtk } from "astal/gtk4";
import Brightness from "../../../util/brightness";
import QuickActions from "../../QuickActions/QuickActions";
import AstalBattery from 'gi://AstalBattery';
import AstalBluetooth from 'gi://AstalBluetooth';
import AstalNetwork from 'gi://AstalNetwork';
import AstalWp from 'gi://AstalWp';
import Brightness from '../../../util/brightness';
import {
Gtk
} from 'astal/gtk4';
import QuickActions from '../../QuickActions/QuickActions';
import {
bind
} from 'astal';
import {
execAsync
} from 'astal';
const STATE = AstalNetwork.DeviceState;
const QuickView = () => {
const qa = QuickActions.QuickActions();
const showQuickActions = () => {
qa.popup();
};
@@ -19,14 +26,14 @@ const QuickView = () => {
return (
<button
onClicked={() => showQuickActions()}
cssClasses={["quick-action-button"]}
cssClasses={[ 'quick-action-button' ]}
child={
<box>
<BatteryWidget></BatteryWidget>
<Audio></Audio>
<BluetoothWidget></BluetoothWidget>
<NetworkWidget></NetworkWidget>
<image iconName={"system-shutdown-symbolic"}></image>
<image iconName={'system-shutdown-symbolic'}></image>
{qa}
</box>
}
@@ -40,37 +47,39 @@ const NetworkWidget = () => {
return (
<box>
<image
iconName={bind(network, "state").as(state => {
if (state === AstalNetwork.State.CONNECTING) {
return "chronometer-reset-symbolic";
iconName={bind( network, 'state' ).as( state => {
if ( state === AstalNetwork.State.CONNECTING ) {
return 'chronometer-reset-symbolic';
} else if (
state === AstalNetwork.State.CONNECTED_LOCAL ||
state === AstalNetwork.State.CONNECTED_SITE ||
state === AstalNetwork.State.CONNECTED_GLOBAL
state === AstalNetwork.State.CONNECTED_LOCAL
|| state === AstalNetwork.State.CONNECTED_SITE
|| state === AstalNetwork.State.CONNECTED_GLOBAL
) {
return "network-wired-activated-symbolic";
return 'network-wired-activated-symbolic';
} else {
return "paint-unknown-symbolic";
return 'paint-unknown-symbolic';
}
})}
cssClasses={["network-widget", "quick-view-symbol"]}
visible={bind(network.wifi, "state").as(
state => state !== STATE.ACTIVATED,
)}
} )}
cssClasses={[
'network-widget',
'quick-view-symbol'
]}
visible={bind( network.wifi, 'state' ).as( state => state !== STATE.ACTIVATED, )}
></image>
<image
iconName={bind(network.wifi, "state").as(state => {
if (state === STATE.ACTIVATED) {
iconName={bind( network.wifi, 'state' ).as( state => {
if ( state === STATE.ACTIVATED ) {
return network.wifi.iconName;
} else {
return "";
return '';
}
})}
tooltipText={bind(network.wifi, 'ssid')}
cssClasses={["network-widget", "quick-view-symbol"]}
visible={bind(network.wifi, "state").as(
state => state === STATE.ACTIVATED,
)}
} )}
tooltipText={bind( network.wifi, 'ssid' )}
cssClasses={[
'network-widget',
'quick-view-symbol'
]}
visible={bind( network.wifi, 'state' ).as( state => state === STATE.ACTIVATED, )}
></image>
</box>
);
@@ -78,44 +87,40 @@ const NetworkWidget = () => {
const BluetoothWidget = () => {
const bluetooth = AstalBluetooth.get_default();
const enabled = bind(bluetooth, "isPowered");
const connected = bind(bluetooth, "isConnected");
const enabled = bind( bluetooth, 'isPowered' );
const connected = bind( bluetooth, 'isConnected' );
// For each connected BT device, render status
return (
<box>
<box visible={enabled.as(e => e)}>
<box visible={enabled.as( e => e )}>
<image
iconName={"bluetooth-active-symbolic"}
visible={connected.as(c => c)}
iconName={'bluetooth-active-symbolic'}
visible={connected.as( c => c )}
></image>
<image
iconName={"bluetooth-disconnected-symbolic"}
visible={connected.as(c => !c)}
iconName={'bluetooth-disconnected-symbolic'}
visible={connected.as( c => !c )}
></image>
</box>
<image
iconName={"bluetooth-disabled-symbolic"}
visible={enabled.as(e => !e)}
iconName={'bluetooth-disabled-symbolic'}
visible={enabled.as( e => !e )}
></image>
<box>
{bind(bluetooth, "devices").as(devices => {
return devices.map(device => {
{bind( bluetooth, 'devices' ).as( devices => {
return devices.map( device => {
return (
<image
iconName={bind(device, "icon").as(
icon => icon,
)}
visible={bind(device, "connected")}
tooltipText={bind(device, "batteryPercentage").as(
n => {
return device.get_name() + ': ' + n + "%";
},
)}
iconName={bind( device, 'icon' ).as( icon => icon, )}
visible={bind( device, 'connected' )}
tooltipText={bind( device, 'batteryPercentage' ).as( n => {
return device.get_name() + ': ' + n + '%';
}, )}
></image>
);
});
})}
} );
} )}
</box>
</box>
);
@@ -123,21 +128,25 @@ const BluetoothWidget = () => {
let hasSentNotification = false;
const BatteryWidget = () => {
const battery = AstalBattery.get_default();
if (battery.get_is_present()) {
if ( battery.get_is_present() ) {
return (
<image
iconName={bind(battery, "batteryIconName").as(icon => icon)}
cssClasses={["quick-view-symbol"]}
tooltipText={bind(battery, 'percentage').as(p => {
const level = Math.round(p * 100)
iconName={bind( battery, 'batteryIconName' ).as( icon => icon )}
cssClasses={[ 'quick-view-symbol' ]}
tooltipText={bind( battery, 'percentage' ).as( p => {
const level = Math.round( p * 100 );
if ( level < 20 && !hasSentNotification ) {
hasSentNotification = true;
execAsync( 'bash -c "notify-send \'Battery level below 20%\'"' );
}
return `Battery Level: ${level}%`
})}
return `Battery Level: ${ level }%`;
} )}
></image>
);
} else {
@@ -148,14 +157,14 @@ const BatteryWidget = () => {
const BrightnessWidget = () => {
const brightness = Brightness.get_default();
const screen_brightness = bind(brightness, "screen");
const screen_brightness = bind( brightness, 'screen' );
return (
<box cssClasses={["quick-view-symbol"]}>
<image iconName={"brightness-high-symbolic"}></image>
<box cssClasses={[ 'quick-view-symbol' ]}>
<image iconName={'brightness-high-symbolic'}></image>
<label
label={screen_brightness.as(b => '' + Math.round(100 * b))}
visible={bind(brightness, "screenAvailable")}
label={screen_brightness.as( b => '' + Math.round( 100 * b ) )}
visible={bind( brightness, 'screenAvailable' )}
></label>
</box>
);
@@ -163,31 +172,27 @@ const BrightnessWidget = () => {
const Audio = () => {
const wireplumber = AstalWp.get_default();
if (wireplumber) {
if ( wireplumber ) {
return (
<box orientation={Gtk.Orientation.HORIZONTAL}>
<image
iconName={bind(wireplumber.defaultSpeaker, "volumeIcon").as(
icon => icon,
)}
cssClasses={["quick-view-symbol"]}
tooltipText={bind(wireplumber.defaultSpeaker, 'volume').as(v => Math.round(100 * v) + '%')}
iconName={bind( wireplumber.defaultSpeaker, 'volumeIcon' ).as( icon => icon, )}
cssClasses={[ 'quick-view-symbol' ]}
tooltipText={bind( wireplumber.defaultSpeaker, 'volume' ).as( v => Math.round( 100 * v ) + '%' )}
></image>
<image
iconName={bind(
wireplumber.defaultMicrophone,
"volumeIcon",
).as(icon => icon)}
cssClasses={["quick-view-symbol"]}
tooltipText={bind(wireplumber.defaultMicrophone, 'volume').as(v => Math.round(100 * v) + '%')}
iconName={bind( wireplumber.defaultMicrophone,
'volumeIcon', ).as( icon => icon )}
cssClasses={[ 'quick-view-symbol' ]}
tooltipText={bind( wireplumber.defaultMicrophone, 'volume' ).as( v => Math.round( 100 * v ) + '%' )}
></image>
</box>
);
} else {
print(
"[ WirePlumber ] Could not connect, Audio support in bar will be missing",
);
return <image iconName={"action-unavailable-symbolic"}></image>;
print( '[ WirePlumber ] Could not connect, Audio support in bar will be missing', );
return <image iconName={'action-unavailable-symbolic'}></image>;
}
};
@@ -195,4 +200,5 @@ const Audio = () => {
export default {
QuickView,
BrightnessWidget
};