feat: add basic bar

This commit is contained in:
Anthony Rodriguez 2025-02-11 17:51:09 +01:00
parent 864433e39a
commit af86cc7beb
Signed by: nezia
SSH key fingerprint: SHA256:R/ue1eTzTHUoo77lJD/3fSUsyL4AwvcHImU5BAZai+8
6 changed files with 170 additions and 54 deletions

6
app.ts
View file

@ -1,10 +1,8 @@
import { App } from "astal/gtk3";
import { App } from "astal/gtk4";
import style from "./style.scss";
import Bar from "./widget/Bar";
App.start({
css: style,
main() {
App.get_monitors().map(Bar);
},
main: () => App.get_monitors().map(Bar),
});

View file

@ -17,6 +17,14 @@
}: let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
astalPkgs = with ags.packages.${system}; [
battery
hyprland
mpris
network
tray
wireplumber
];
in {
packages.${system} = {
default = ags.lib.bundle {
@ -24,12 +32,10 @@
src = ./.;
name = "coquille";
entry = "app.ts";
gtk4 = true;
# additional libraries and executables to add to gjs' runtime
extraPackages = [
# ags.packages.${system}.battery
# pkgs.fzf
];
extraPackages = astalPkgs;
};
};
@ -38,9 +44,7 @@
buildInputs = [
# includes astal3 astal4 astal-io by default
(ags.packages.${system}.default.override {
extraPackages = [
# cherry pick packages
];
extraPackages = astalPkgs;
})
];
};

View file

@ -1,20 +1,56 @@
// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gtk/theme/Adwaita/_colors-public.scss
$fg-color: #{"@theme_fg_color"};
$bg-color: #{"@theme_bg_color"};
@use "sass:color";
@use "sass:string";
@function gtkalpha($c, $a) {
@return string.unquote("alpha(#{$c},#{$a})");
}
// https://gitlab.gnome.org/GNOME/libadwaita/-/blob/main/src/stylesheet/_colors.scss
$bg: #{"@window_bg_color"};
$fg: #{"@window_fg_color"};
$accent: #{"@accent_color"};
$radius: 7px;
window.Bar {
background: transparent;
color: $fg-color;
font-weight: bold;
border: none;
box-shadow: none;
background-color: $bg;
color: $fg;
font-size: 1.2em;
padding: 0.5em 0;
>centerbox {
background: $bg-color;
border-radius: 10px;
margin: 8px;
centerbox {
min-height: 2.5em;
}
.Workspaces {
button {
border-radius: 8px;
margin: 2px;
all: unset;
min-width: 1.1rem;
min-height: 1.1rem;
background-color: transparent;
border-radius: 50%;
border: 2px solid $fg;
margin: 0.25rem 0.3rem;
&:hover {
border-color: gtkalpha($accent, 0.6);
}
}
.focused {
border-color: $accent;
background-color: $accent;
}
}
.Battery label {
padding-left: 0;
margin-left: 0;
}
.NotificationCenter {
&:hover {
background-color: gtkalpha($fg, 0.2);
}
}
}

View file

@ -3,7 +3,7 @@
"compilerOptions": {
"experimentalDecorators": true,
"jsx": "react-jsx",
"jsxImportSource": "astal/gtk3",
"jsxImportSource": "astal/gtk4",
"module": "ES2022",
"moduleResolution": "Bundler",
"strict": true,

View file

@ -1,31 +1,85 @@
import { App, Astal, Gtk, Gdk } from "astal/gtk3"
import { Variable } from "astal"
import { App, Astal, Gtk, Gdk } from "astal/gtk4";
import { Variable, GLib, bind } from "astal";
import Battery from "gi://AstalBattery";
import Wp from "gi://AstalWp";
import Network from "gi://AstalNetwork";
import { Workspaces } from "./Workspaces";
const time = Variable("").poll(1000, "date")
function Wifi() {
const network = Network.get_default();
const wifi = bind(network, "wifi");
export default function Bar(gdkmonitor: Gdk.Monitor) {
const { TOP, LEFT, RIGHT } = Astal.WindowAnchor
return (
<box visible={wifi.as(Boolean)}>
{wifi.as(
(wifi) =>
wifi && (
<image cssClasses={["Wifi"]} iconName={bind(wifi, "iconName")} />
),
)}
</box>
);
}
return <window
className="Bar"
gdkmonitor={gdkmonitor}
function Audio() {
const speaker = Wp.get_default()?.audio.defaultSpeaker!;
return (
<box cssClasses={["AudioSlider"]}>
<image iconName={bind(speaker, "volumeIcon")} />
</box>
);
}
function BatteryLevel() {
const bat = Battery.get_default();
return (
<box cssClasses={["Battery"]} visible={bind(bat, "isPresent")}>
<image iconName={bind(bat, "batteryIconName")} />
<label
label={bind(bat, "percentage").as((p) => `${Math.floor(p * 100)} %`)}
/>
</box>
);
}
function Time({ format = "%H:%M - %A %e." }) {
const time = Variable<string>("").poll(
1000,
() => GLib.DateTime.new_now_local().format(format)!,
);
return (
<label cssClasses={["Time"]} onDestroy={() => time.drop()} label={time()} />
);
}
export default function Bar(monitor: Gdk.Monitor) {
const { TOP, LEFT, RIGHT } = Astal.WindowAnchor;
return (
<window
visible
namespace={"bar"}
cssClasses={["Bar"]}
gdkmonitor={monitor}
exclusivity={Astal.Exclusivity.EXCLUSIVE}
anchor={TOP | LEFT | RIGHT}
application={App}>
>
<centerbox>
<button
onClicked="echo hello"
halign={Gtk.Align.CENTER}
>
Welcome to AGS!
</button>
<box />
<button
onClicked={() => print("hello")}
halign={Gtk.Align.CENTER}
>
<label label={time()} />
</button>
<box hexpand halign={Gtk.Align.START}>
<Workspaces />
</box>
<box>
<Time />
</box>
<box cssClasses={["NotificationCenter"]} hexpand halign={Gtk.Align.END}>
<Audio />
<Wifi />
<BatteryLevel />
</box>
</centerbox>
</window>
);
}

24
widget/Workspaces.tsx Normal file
View file

@ -0,0 +1,24 @@
import { bind } from "astal";
import Hyprland from "gi://AstalHyprland";
export const Workspaces = () => {
const hypr = Hyprland.get_default();
return (
<box cssClasses={["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
cssClasses={bind(hypr, "focusedWorkspace").as((fw) =>
ws === fw ? ["focused"] : [""],
)}
onClicked={() => ws.focus()}
></button>
)),
)}
</box>
);
};