[AGS] Bar progress
This commit is contained in:
parent
78e472beb8
commit
b2f1d8fd9e
@ -2,38 +2,24 @@ import { App } from "astal/gtk3"
|
|||||||
import style from "./style.scss"
|
import style from "./style.scss"
|
||||||
|
|
||||||
import notifications from "./components/notifications/handler";
|
import notifications from "./components/notifications/handler";
|
||||||
|
import Bar from "./components/bar/ui/Bar";
|
||||||
|
|
||||||
App.start({
|
App.start({
|
||||||
instanceName: "runner",
|
instanceName: "runner",
|
||||||
css: style,
|
css: style,
|
||||||
main() {
|
main() {
|
||||||
notifications.startNotificationHandler( 0, App.get_monitors()[0] )
|
notifications.startNotificationHandler( 0, App.get_monitors()[0] );
|
||||||
|
// TODO: Monitor handling
|
||||||
|
Bar.Bar( App.get_monitors()[0] );
|
||||||
},
|
},
|
||||||
requestHandler(request, res) {
|
requestHandler(request, res) {
|
||||||
const args = request.trimStart().split( ' ' );
|
const args = request.trimStart().split( ' ' );
|
||||||
|
|
||||||
// Notifications
|
// Notifications (TODO: Handle the arguments in the components themselves)
|
||||||
if ( args[ 0 ] === 'notifier' ) {
|
if ( args[ 0 ] === 'notifier' ) {
|
||||||
if ( args[ 1 ] == 'show' ) {
|
res( notifications.cliHandler( args ) );
|
||||||
notifications.openNotificationMenu( 0 );
|
|
||||||
res( 'Showing all open notifications' );
|
|
||||||
} else if ( args[ 1 ] == 'hide' ) {
|
|
||||||
notifications.closeNotificationMenu( 0 );
|
|
||||||
res( 'Hid all notifications' );
|
|
||||||
} else if ( args[ 1 ] == 'clear' ) {
|
|
||||||
notifications.clearAllNotifications( 0 );
|
|
||||||
res( 'Cleared all notifications' );
|
|
||||||
} else if ( args[ 1 ] == 'clear-newest' ) {
|
|
||||||
notifications.clearNewestNotifications( 0 );
|
|
||||||
res( 'Cleared newest notification' );
|
|
||||||
} else if ( args[ 1 ] == 'toggle' ) {
|
|
||||||
notifications.toggleNotificationMenu( 0 );
|
|
||||||
res( 'Toggled notifications' );
|
|
||||||
} else {
|
|
||||||
res( 'Unknown command!' );
|
|
||||||
}
|
|
||||||
} else if ( args[ 0 ] === 'bar' ) {
|
} else if ( args[ 0 ] === 'bar' ) {
|
||||||
|
res( Bar.cliHandler( args ) );
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -1,23 +1,36 @@
|
|||||||
import { createQuickActionsMenu } from "./QuickActions";
|
|
||||||
import { GLib } from "astal";
|
|
||||||
import { Astal, Gdk, Gtk } from "astal/gtk3";
|
import { Astal, Gdk, Gtk } from "astal/gtk3";
|
||||||
|
import Hyprland from "./modules/Hyprland";
|
||||||
|
import Calendar from "./modules/Calendar";
|
||||||
|
import QuickView from "./modules/QuickView";
|
||||||
|
|
||||||
const Bar = (gdkmonitor: Gdk.Monitor) => {
|
const Bar = (gdkmonitor: Gdk.Monitor) => {
|
||||||
|
const { TOP, LEFT, RIGHT } = Astal.WindowAnchor;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<window gdkmonitor={gdkmonitor}
|
<window gdkmonitor={gdkmonitor}
|
||||||
cssClasses={["Bar"]}>
|
cssClasses={["Bar"]}
|
||||||
|
exclusivity={Astal.Exclusivity.EXCLUSIVE}
|
||||||
|
anchor={TOP | LEFT | RIGHT}>
|
||||||
<box orientation={Gtk.Orientation.HORIZONTAL} spacing={10}>
|
<box orientation={Gtk.Orientation.HORIZONTAL} spacing={10}>
|
||||||
<box>
|
<box hexpand halign={Gtk.Align.START}>
|
||||||
|
<Calendar.Time />
|
||||||
|
<Hyprland.Workspace />
|
||||||
</box>
|
</box>
|
||||||
<label>{windowTitle}</label>
|
<Hyprland.ActiveWindow />
|
||||||
<box>
|
<box hexpand halign={Gtk.Align.END}>
|
||||||
<tray />
|
<Hyprland.SysTray />
|
||||||
<button icon="quickaction" menu={quickActionMenu} />
|
<QuickView.QuickView />
|
||||||
</box>
|
</box>
|
||||||
</box>
|
</box>
|
||||||
</window>
|
</window>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Bar;
|
const cliHandler = ( args: string[] ): string => {
|
||||||
|
return 'Not implemented';
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
Bar,
|
||||||
|
cliHandler
|
||||||
|
};
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
import { GLib, Variable } from "astal"
|
||||||
|
|
||||||
|
const Time = ({ format = "%a, %e.%m %H:%M:%S" }) => {
|
||||||
|
const time = Variable<string>("").poll(1000, () =>
|
||||||
|
GLib.DateTime.new_now_local().format(format)!)
|
||||||
|
|
||||||
|
return <label
|
||||||
|
className="Time"
|
||||||
|
onDestroy={() => time.drop()}
|
||||||
|
label={time()}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
Time
|
||||||
|
}
|
@ -19,7 +19,7 @@ const SysTray = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const HyprlandWorkspace = () => {
|
const Workspace = () => {
|
||||||
const hypr = AstalHyprland.get_default()
|
const hypr = AstalHyprland.get_default()
|
||||||
|
|
||||||
return <box className={"HyprlandWorkspaces"}>
|
return <box className={"HyprlandWorkspaces"}>
|
||||||
@ -39,7 +39,7 @@ const HyprlandWorkspace = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const HyprlandActiveWindow = () => {
|
const ActiveWindow = () => {
|
||||||
const hypr = AstalHyprland.get_default();
|
const hypr = AstalHyprland.get_default();
|
||||||
const focused = bind( hypr, "focusedClient" );
|
const focused = bind( hypr, "focusedClient" );
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ const HyprlandActiveWindow = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
HyprlandWorkspace,
|
Workspace,
|
||||||
HyprlandActiveWindow,
|
ActiveWindow,
|
||||||
SysTray
|
SysTray
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,106 @@
|
|||||||
|
import { bind } from "astal";
|
||||||
|
import AstalBattery from "gi://AstalBattery?version=0.1";
|
||||||
|
import AstalBluetooth from "gi://AstalBluetooth?version=0.1";
|
||||||
|
import AstalNetwork from "gi://AstalNetwork?version=0.1"
|
||||||
|
import AstalWp from "gi://AstalWp?version=0.1";
|
||||||
|
import Brightness from "../../util/brightness";
|
||||||
|
import { Gtk } from "astal/gtk3";
|
||||||
|
|
||||||
|
const QuickView = () => {
|
||||||
|
return <box>
|
||||||
|
<Audio></Audio>
|
||||||
|
<label label="QuickView"></label>
|
||||||
|
</box>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const NetworkWidget = () => {
|
||||||
|
const network = AstalNetwork.get_default();
|
||||||
|
const status = bind( network, "state" );
|
||||||
|
const wifiStrength = bind( network.wifi, 'strength' );
|
||||||
|
const states = {
|
||||||
|
"off": ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return <label label={""}></label>
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const BluetoothWidget = () => {
|
||||||
|
const bluetooth = AstalBluetooth.get_default();
|
||||||
|
const enabled = bind( bluetooth, "isPowered" );
|
||||||
|
const connected = bind( bluetooth, "isConnected" );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const BatteryWidget = () => {
|
||||||
|
const battery = AstalBattery.get_default();
|
||||||
|
if ( battery.get_is_present() ) {
|
||||||
|
const states = {
|
||||||
|
"100": "",
|
||||||
|
"90": "",
|
||||||
|
"80": "",
|
||||||
|
"70": "",
|
||||||
|
"60": "",
|
||||||
|
"50": "",
|
||||||
|
"40": "",
|
||||||
|
"30": "",
|
||||||
|
"20": "",
|
||||||
|
"10": "",
|
||||||
|
"critical": "",
|
||||||
|
"charging":"",
|
||||||
|
"plugged": " ",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Else, no battery available -> Don't show the widget
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const BrightnessWidget = () => {
|
||||||
|
// TODO: Finish (detect if there is a controllable screen)
|
||||||
|
const brightness = Brightness.get_default();
|
||||||
|
const screen_brightness = bind( brightness, "screen" );
|
||||||
|
|
||||||
|
return <label label={"🌣" + screen_brightness}></label>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const Audio = () => {
|
||||||
|
const wireplumber = AstalWp.get_default();
|
||||||
|
if ( wireplumber ) {
|
||||||
|
// With the states, split up the icons according to number of elements available
|
||||||
|
const speakerMuted = " ";
|
||||||
|
const speakersStates = [
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
""
|
||||||
|
]
|
||||||
|
const micStates = {
|
||||||
|
"on": " ",
|
||||||
|
"muted": " ",
|
||||||
|
}
|
||||||
|
const volume_speakers = bind( wireplumber.defaultSpeaker, 'volume' );
|
||||||
|
const muted_speakers = bind( wireplumber.defaultSpeaker, 'mute' );
|
||||||
|
const muted_mic = bind( wireplumber.defaultMicrophone, 'mute' );
|
||||||
|
|
||||||
|
return <box orientation={Gtk.Orientation.HORIZONTAL}>
|
||||||
|
<label label={micStates[ muted_mic ? 'muted' : 'on' ]}></label>
|
||||||
|
<label label={(muted_speakers ? speakerMuted : volume_speakers.as( v => {
|
||||||
|
if ( v === 0 ) return speakerMuted;
|
||||||
|
else if ( v <= 30 ) return speakersStates[ 0 ];
|
||||||
|
else if ( v <= 70 ) return speakersStates[ 1 ];
|
||||||
|
else return speakersStates[ 2 ];
|
||||||
|
} ) )}></label>
|
||||||
|
<label label={volume_speakers.as( v => { return "" + v } ) }></label>
|
||||||
|
<label label={wireplumber.default_speaker.get_name()}></label>
|
||||||
|
</box>
|
||||||
|
} else {
|
||||||
|
print( '[ WirePlumber ] Could not connect, Audio support in bar will be missing' );
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
QuickView
|
||||||
|
}
|
71
config/astal/components/bar/util/brightness.ts
Normal file
71
config/astal/components/bar/util/brightness.ts
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import GObject, { register, property } from "astal/gobject"
|
||||||
|
import { monitorFile, readFileAsync } from "astal/file"
|
||||||
|
import { exec, execAsync } from "astal/process"
|
||||||
|
|
||||||
|
const get = (args: string) => Number(exec(`brightnessctl ${args}`))
|
||||||
|
const screen = exec(`bash -c "ls -w1 /sys/class/backlight | head -1"`)
|
||||||
|
const kbd = exec(`bash -c "ls -w1 /sys/class/leds | head -1"`)
|
||||||
|
|
||||||
|
@register({ GTypeName: "Brightness" })
|
||||||
|
export default class Brightness extends GObject.Object {
|
||||||
|
static instance: Brightness
|
||||||
|
static get_default() {
|
||||||
|
if (!this.instance)
|
||||||
|
this.instance = new Brightness()
|
||||||
|
|
||||||
|
return this.instance
|
||||||
|
}
|
||||||
|
|
||||||
|
#kbdMax = get(`--device ${kbd} max`)
|
||||||
|
#kbd = get(`--device ${kbd} get`)
|
||||||
|
#screenMax = get("max")
|
||||||
|
#screen = get("get") / (get("max") || 1)
|
||||||
|
|
||||||
|
@property(Number)
|
||||||
|
get kbd() { return this.#kbd }
|
||||||
|
|
||||||
|
set kbd(value) {
|
||||||
|
if (value < 0 || value > this.#kbdMax)
|
||||||
|
return
|
||||||
|
|
||||||
|
execAsync(`brightnessctl -d ${kbd} s ${value} -q`).then(() => {
|
||||||
|
this.#kbd = value
|
||||||
|
this.notify("kbd")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@property(Number)
|
||||||
|
get screen() { return this.#screen }
|
||||||
|
|
||||||
|
set screen(percent) {
|
||||||
|
if (percent < 0)
|
||||||
|
percent = 0
|
||||||
|
|
||||||
|
if (percent > 1)
|
||||||
|
percent = 1
|
||||||
|
|
||||||
|
execAsync(`brightnessctl set ${Math.floor(percent * 100)}% -q`).then(() => {
|
||||||
|
this.#screen = percent
|
||||||
|
this.notify("screen")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
|
||||||
|
const screenPath = `/sys/class/backlight/${screen}/brightness`
|
||||||
|
const kbdPath = `/sys/class/leds/${kbd}/brightness`
|
||||||
|
|
||||||
|
monitorFile(screenPath, async f => {
|
||||||
|
const v = await readFileAsync(f)
|
||||||
|
this.#screen = Number(v) / this.#screenMax
|
||||||
|
this.notify("screen")
|
||||||
|
})
|
||||||
|
|
||||||
|
monitorFile(kbdPath, async f => {
|
||||||
|
const v = await readFileAsync(f)
|
||||||
|
this.#kbd = Number(v) / this.#kbdMax
|
||||||
|
this.notify("kbd")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -1,68 +0,0 @@
|
|||||||
import AstalHyprland from "gi://AstalHyprland";
|
|
||||||
|
|
||||||
const getAvailableWorkspaces = (): number[] => {
|
|
||||||
const workspaces: number[] = [];
|
|
||||||
AstalHyprland.get_default().get_workspaces().forEach( val => {
|
|
||||||
workspaces.push( val.get_id() );
|
|
||||||
} )
|
|
||||||
return workspaces;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getCurrentWorkspaceID = (): number => {
|
|
||||||
return AstalHyprland.get_default().get_focused_workspace().get_id();
|
|
||||||
}
|
|
||||||
|
|
||||||
const getCurrentWindowTitle = (): string => {
|
|
||||||
return AstalHyprland.get_default().get_focused_client().get_title();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the workspace ID of a window by its address
|
|
||||||
* @param address - The address of the window
|
|
||||||
* @returns The workspace ID
|
|
||||||
*/
|
|
||||||
const getWorkspaceIDOfWindowByAddress = ( address: string ): number => {
|
|
||||||
AstalHyprland.get_default().get_clients().forEach( client => {
|
|
||||||
if ( client.get_address() === address ) {
|
|
||||||
return client.get_workspace().get_id();
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
interface HyprlandSubscriptions {
|
|
||||||
[key: string]: ( data: string ) => void;
|
|
||||||
}
|
|
||||||
const hooks: HyprlandSubscriptions = {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add an event listener for Hyprland events.
|
|
||||||
* @param event - A hyprland IPC event. See https://wiki.hyprland.org/IPC/. Useful events include: urgent, windowtitlev2, workspace, createworkspacev2, destroyworkspacev2, activewindowv2
|
|
||||||
* @param cb - The callback function
|
|
||||||
*/
|
|
||||||
const subscribeToUpdates = ( event: string, cb: ( data: string ) => void ): void => {
|
|
||||||
hooks[ event ] = cb;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listen to events. Must be called at some point if events are to be listened for
|
|
||||||
*/
|
|
||||||
const listen = () => {
|
|
||||||
AstalHyprland.get_default().connect( "event", ( name: string, data: string ) => {
|
|
||||||
if ( hooks[ name ] ) {
|
|
||||||
hooks[ name ]( data );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export default {
|
|
||||||
getAvailableWorkspaces,
|
|
||||||
getCurrentWorkspaceID,
|
|
||||||
getCurrentWindowTitle,
|
|
||||||
getWorkspaceIDOfWindowByAddress,
|
|
||||||
subscribeToUpdates,
|
|
||||||
listen
|
|
||||||
}
|
|
@ -195,12 +195,29 @@ const startNotificationHandler = (id: number, gdkmonitor: Gdk.Monitor) => {
|
|||||||
</window>
|
</window>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cliHandler = ( args: string[] ): string => {
|
||||||
|
if ( args[ 1 ] == 'show' ) {
|
||||||
|
openNotificationMenu( 0 );
|
||||||
|
return 'Showing all open notifications';
|
||||||
|
} else if ( args[ 1 ] == 'hide' ) {
|
||||||
|
closeNotificationMenu( 0 );
|
||||||
|
return 'Hid all notifications';
|
||||||
|
} else if ( args[ 1 ] == 'clear' ) {
|
||||||
|
clearAllNotifications( 0 );
|
||||||
|
return 'Cleared all notifications';
|
||||||
|
} else if ( args[ 1 ] == 'clear-newest' ) {
|
||||||
|
clearNewestNotifications( 0 );
|
||||||
|
return 'Cleared newest notification';
|
||||||
|
} else if ( args[ 1 ] == 'toggle' ) {
|
||||||
|
toggleNotificationMenu( 0 );
|
||||||
|
return 'Toggled notifications';
|
||||||
|
} else {
|
||||||
|
return 'Unknown command!';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
startNotificationHandler,
|
startNotificationHandler,
|
||||||
openNotificationMenu,
|
cliHandler
|
||||||
closeNotificationMenu,
|
|
||||||
clearAllNotifications,
|
|
||||||
clearNewestNotifications,
|
|
||||||
toggleNotificationMenu
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user