diff options
Diffstat (limited to 'examples/gtk3/js/simple-bar')
-rw-r--r-- | examples/gtk3/js/simple-bar/README.md | 12 | ||||
-rw-r--r-- | examples/gtk3/js/simple-bar/app.ts | 13 | ||||
-rw-r--r-- | examples/gtk3/js/simple-bar/style.scss | 106 | ||||
-rw-r--r-- | examples/gtk3/js/simple-bar/widget/Bar.tsx | 161 |
4 files changed, 292 insertions, 0 deletions
diff --git a/examples/gtk3/js/simple-bar/README.md b/examples/gtk3/js/simple-bar/README.md new file mode 100644 index 0000000..f92b20e --- /dev/null +++ b/examples/gtk3/js/simple-bar/README.md @@ -0,0 +1,12 @@ +# Simple Bar Example + + + +A simple bar for Hyprland using + +- [Battery library](https://aylur.github.io/astal/guide/libraries/battery). +- [Hyprland library](https://aylur.github.io/astal/guide/libraries/hyprland). +- [Mpris library](https://aylur.github.io/astal/guide/libraries/mpris). +- [Network library](https://aylur.github.io/astal/guide/libraries/network). +- [Tray library](https://aylur.github.io/astal/guide/libraries/tray). +- [WirePlumber library](https://aylur.github.io/astal/guide/libraries/wireplumber). diff --git a/examples/gtk3/js/simple-bar/app.ts b/examples/gtk3/js/simple-bar/app.ts new file mode 100644 index 0000000..4b7ea48 --- /dev/null +++ b/examples/gtk3/js/simple-bar/app.ts @@ -0,0 +1,13 @@ +import { App } from "astal/gtk3" +import style from "./style.scss" +import Bar from "./widget/Bar" + +App.start({ + css: style, + instanceName: "js", + requestHandler(request, res) { + print(request) + res("ok") + }, + main: () => App.get_monitors().map(Bar), +}) diff --git a/examples/gtk3/js/simple-bar/style.scss b/examples/gtk3/js/simple-bar/style.scss new file mode 100644 index 0000000..f5f771a --- /dev/null +++ b/examples/gtk3/js/simple-bar/style.scss @@ -0,0 +1,106 @@ +@use "sass:color"; + +$bg: #212223; +$fg: #f1f1f1; +$accent: #378DF7; +$radius: 7px; + +window.Bar { + border: none; + box-shadow: none; + background-color: $bg; + color: $fg; + font-size: 1.1em; + font-weight: bold; + + label { + margin: 0 8px; + } + + .Workspaces { + button { + all: unset; + background-color: transparent; + + &:hover label { + background-color: color.adjust($fg, $alpha: -0.84); + border-color: color.adjust($accent, $alpha: -0.8); + } + + &:active label { + background-color: color.adjust($fg, $alpha: -0.8) + } + } + + label { + transition: 200ms; + padding: 0 8px; + margin: 2px; + border-radius: $radius; + border: 1pt solid transparent; + } + + .focused label { + color: $accent; + border-color: $accent; + } + } + + .SysTray { + margin-right: 8px; + + button { + padding: 0 4px; + } + } + + .FocusedClient { + color: $accent; + } + + .Media .Cover { + min-height: 1.2em; + min-width: 1.2em; + border-radius: $radius; + background-position: center; + background-size: contain; + } + + .Battery label { + padding-left: 0; + margin-left: 0; + } + + .AudioSlider { + * { + all: unset; + } + + icon { + margin-right: .6em; + } + + & { + margin: 0 1em; + } + + trough { + background-color: color.adjust($fg, $alpha: -0.8); + border-radius: $radius; + } + + highlight { + background-color: $accent; + min-height: .8em; + border-radius: $radius; + } + + slider { + background-color: $fg; + border-radius: $radius; + min-height: 1em; + min-width: 1em; + margin: -.2em; + } + } +} diff --git a/examples/gtk3/js/simple-bar/widget/Bar.tsx b/examples/gtk3/js/simple-bar/widget/Bar.tsx new file mode 100644 index 0000000..6592f6a --- /dev/null +++ b/examples/gtk3/js/simple-bar/widget/Bar.tsx @@ -0,0 +1,161 @@ +import { App } from "astal/gtk3" +import { Variable, GLib, bind } from "astal" +import { Astal, Gtk, Gdk } from "astal/gtk3" +import Hyprland from "gi://AstalHyprland" +import Mpris from "gi://AstalMpris" +import Battery from "gi://AstalBattery" +import Wp from "gi://AstalWp" +import Network from "gi://AstalNetwork" +import Tray from "gi://AstalTray" + +function SysTray() { + const tray = Tray.get_default() + + return <box className="SysTray"> + {bind(tray, "items").as(items => items.map(item => ( + <menubutton + tooltipMarkup={bind(item, "tooltipMarkup")} + usePopover={false} + actionGroup={bind(item, "actionGroup").as(ag => ["dbusmenu", ag])} + menuModel={bind(item, "menuModel")}> + <icon gicon={bind(item, "gicon")} /> + </menubutton> + )))} + </box> +} + +function Wifi() { + const network = Network.get_default() + const wifi = bind(network, "wifi") + + return <box visible={wifi.as(Boolean)}> + {wifi.as(wifi => wifi && ( + <icon + tooltipText={bind(wifi, "ssid").as(String)} + className="Wifi" + icon={bind(wifi, "iconName")} + /> + ))} + </box> + +} + +function AudioSlider() { + const speaker = Wp.get_default()?.audio.defaultSpeaker! + + return <box className="AudioSlider" css="min-width: 140px"> + <icon icon={bind(speaker, "volumeIcon")} /> + <slider + hexpand + onDragged={({ value }) => speaker.volume = value} + value={bind(speaker, "volume")} + /> + </box> +} + +function BatteryLevel() { + const bat = Battery.get_default() + + return <box className="Battery" + visible={bind(bat, "isPresent")}> + <icon icon={bind(bat, "batteryIconName")} /> + <label label={bind(bat, "percentage").as(p => + `${Math.floor(p * 100)} %` + )} /> + </box> +} + +function Media() { + const mpris = Mpris.get_default() + + return <box className="Media"> + {bind(mpris, "players").as(ps => ps[0] ? ( + <box> + <box + className="Cover" + valign={Gtk.Align.CENTER} + css={bind(ps[0], "coverArt").as(cover => + `background-image: url('${cover}');` + )} + /> + <label + label={bind(ps[0], "title").as(() => + `${ps[0].title} - ${ps[0].artist}` + )} + /> + </box> + ) : ( + "Nothing Playing" + ))} + </box> +} + +function Workspaces() { + const hypr = Hyprland.get_default() + + return <box className="Workspaces"> + {bind(hypr, "workspaces").as(wss => wss + .filter(ws => !(ws.id >= -99 && ws.id <= -2)) // filter out special workspaces + .sort((a, b) => a.id - b.id) + .map(ws => ( + <button + className={bind(hypr, "focusedWorkspace").as(fw => + ws === fw ? "focused" : "")} + onClicked={() => ws.focus()}> + {ws.id} + </button> + )) + )} + </box> +} + +function FocusedClient() { + const hypr = Hyprland.get_default() + const focused = bind(hypr, "focusedClient") + + return <box + className="Focused" + visible={focused.as(Boolean)}> + {focused.as(client => ( + client && <label label={bind(client, "title").as(String)} /> + ))} + </box> +} + +function Time({ format = "%H:%M - %A %e." }) { + const time = Variable<string>("").poll(1000, () => + GLib.DateTime.new_now_local().format(format)!) + + return <label + className="Time" + onDestroy={() => time.drop()} + label={time()} + /> +} + +export default function Bar(monitor: Gdk.Monitor) { + const { TOP, LEFT, RIGHT } = Astal.WindowAnchor + + return <window + className="Bar" + gdkmonitor={monitor} + exclusivity={Astal.Exclusivity.EXCLUSIVE} + anchor={TOP | LEFT | RIGHT}> + <centerbox> + <box hexpand halign={Gtk.Align.START}> + <Workspaces /> + <FocusedClient /> + </box> + <box> + <Media /> + </box> + <box hexpand halign={Gtk.Align.END} > + <SysTray /> + <Wifi /> + <AudioSlider /> + <BatteryLevel /> + <Time /> + </box> + </centerbox> + </window> +} |