feat: add basic bar
This commit is contained in:
parent
864433e39a
commit
af86cc7beb
6 changed files with 170 additions and 54 deletions
6
app.ts
6
app.ts
|
@ -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),
|
||||
});
|
||||
|
|
18
flake.nix
18
flake.nix
|
@ -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;
|
||||
})
|
||||
];
|
||||
};
|
||||
|
|
64
style.scss
64
style.scss
|
@ -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;
|
||||
|
||||
>centerbox {
|
||||
background: $bg-color;
|
||||
border-radius: 10px;
|
||||
margin: 8px;
|
||||
}
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
background-color: $bg;
|
||||
color: $fg;
|
||||
font-size: 1.2em;
|
||||
padding: 0.5em 0;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"compilerOptions": {
|
||||
"experimentalDecorators": true,
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "astal/gtk3",
|
||||
"jsxImportSource": "astal/gtk4",
|
||||
"module": "ES2022",
|
||||
"moduleResolution": "Bundler",
|
||||
"strict": true,
|
||||
|
|
110
widget/Bar.tsx
110
widget/Bar.tsx
|
@ -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 <window
|
||||
className="Bar"
|
||||
gdkmonitor={gdkmonitor}
|
||||
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>
|
||||
</centerbox>
|
||||
</window>
|
||||
return (
|
||||
<box visible={wifi.as(Boolean)}>
|
||||
{wifi.as(
|
||||
(wifi) =>
|
||||
wifi && (
|
||||
<image cssClasses={["Wifi"]} iconName={bind(wifi, "iconName")} />
|
||||
),
|
||||
)}
|
||||
</box>
|
||||
);
|
||||
}
|
||||
|
||||
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}
|
||||
>
|
||||
<centerbox>
|
||||
<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
24
widget/Workspaces.tsx
Normal 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>
|
||||
);
|
||||
};
|
Loading…
Add table
Reference in a new issue