summaryrefslogtreecommitdiff
path: root/examples/gtk3/vala/simple-bar
diff options
context:
space:
mode:
Diffstat (limited to 'examples/gtk3/vala/simple-bar')
-rw-r--r--examples/gtk3/vala/simple-bar/README.md13
-rw-r--r--examples/gtk3/vala/simple-bar/app.in.vala30
-rw-r--r--examples/gtk3/vala/simple-bar/flake.nix42
-rw-r--r--examples/gtk3/vala/simple-bar/meson.build45
-rw-r--r--examples/gtk3/vala/simple-bar/style.scss106
-rw-r--r--examples/gtk3/vala/simple-bar/widget/Bar.vala253
6 files changed, 489 insertions, 0 deletions
diff --git a/examples/gtk3/vala/simple-bar/README.md b/examples/gtk3/vala/simple-bar/README.md
new file mode 100644
index 0000000..48cc27c
--- /dev/null
+++ b/examples/gtk3/vala/simple-bar/README.md
@@ -0,0 +1,13 @@
+# Simple Bar Example
+
+![simple-bar](https://github.com/user-attachments/assets/a306c864-56b7-44c4-8820-81f424f32b9b)
+
+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).
+- [dart-sass](https://sass-lang.com/dart-sass/) as the css precompiler
diff --git a/examples/gtk3/vala/simple-bar/app.in.vala b/examples/gtk3/vala/simple-bar/app.in.vala
new file mode 100644
index 0000000..b04a1fa
--- /dev/null
+++ b/examples/gtk3/vala/simple-bar/app.in.vala
@@ -0,0 +1,30 @@
+class App : Astal.Application {
+ public static App instance;
+
+ public override void request (string msg, SocketConnection conn) {
+ print(@"$msg\n");
+ AstalIO.write_sock.begin(conn, "ok");
+ }
+
+ public override void activate () {
+ foreach (var mon in this.monitors)
+ add_window(new Bar(mon));
+
+ apply_css("@STYLE@");
+ }
+
+ public static void main(string[] args) {
+ var instance_name = "vala";
+
+ App.instance = new App() {
+ instance_name = instance_name
+ };
+
+ try {
+ App.instance.acquire_socket();
+ App.instance.run(null);
+ } catch (Error err) {
+ print(AstalIO.send_message(instance_name, string.joinv(" ", args)));
+ }
+ }
+}
diff --git a/examples/gtk3/vala/simple-bar/flake.nix b/examples/gtk3/vala/simple-bar/flake.nix
new file mode 100644
index 0000000..d13c649
--- /dev/null
+++ b/examples/gtk3/vala/simple-bar/flake.nix
@@ -0,0 +1,42 @@
+{
+ inputs = {
+ nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
+ astal.url = "github:aylur/astal";
+ };
+
+ outputs = {
+ self,
+ nixpkgs,
+ astal,
+ }: let
+ system = "x86_64-linux";
+ pkgs = nixpkgs.legacyPackages.${system};
+ in {
+ packages.${system} = {
+ default = pkgs.stdenv.mkDerivation {
+ name = "simple-bar";
+ src = ./.;
+
+ nativeBuildInputs = with pkgs; [
+ meson
+ ninja
+ pkg-config
+ vala
+ gobject-introspection
+ dart-sass
+ ];
+
+ buildInputs = [
+ astal.packages.${system}.io
+ astal.packages.${system}.astal3
+ astal.packages.${system}.battery
+ astal.packages.${system}.wireplumber
+ astal.packages.${system}.network
+ astal.packages.${system}.tray
+ astal.packages.${system}.mpris
+ astal.packages.${system}.hyprland
+ ];
+ };
+ };
+ };
+}
diff --git a/examples/gtk3/vala/simple-bar/meson.build b/examples/gtk3/vala/simple-bar/meson.build
new file mode 100644
index 0000000..5a0ef4c
--- /dev/null
+++ b/examples/gtk3/vala/simple-bar/meson.build
@@ -0,0 +1,45 @@
+project('simple-bar', 'vala', 'c')
+
+bindir = get_option('prefix') / get_option('bindir')
+libdir = get_option('prefix') / get_option('libdir')
+
+pkgconfig_deps = [
+ dependency('glib-2.0'),
+ dependency('gobject-2.0'),
+ dependency('gtk+-3.0'),
+ dependency('libnm'),
+ dependency('astal-io-0.1'),
+ dependency('astal-3.0'),
+ dependency('astal-battery-0.1'),
+ dependency('astal-wireplumber-0.1'),
+ dependency('astal-network-0.1'),
+ dependency('astal-tray-0.1'),
+ dependency('astal-mpris-0.1'),
+ dependency('astal-hyprland-0.1'),
+]
+
+# needed for GLib.Math
+deps = pkgconfig_deps + meson.get_compiler('c').find_library('m')
+
+main = configure_file(
+ input: 'app.in.vala',
+ output: 'app.vala',
+ configuration: {
+ 'STYLE': run_command(
+ find_program('sass'),
+ meson.project_source_root() / 'style.scss',
+ ).stdout(),
+ },
+)
+
+sources = files(
+ 'widget/Bar.vala',
+)
+
+executable(
+ 'simple-bar',
+ [sources, main],
+ dependencies: deps,
+ install: true,
+ install_dir: bindir,
+)
diff --git a/examples/gtk3/vala/simple-bar/style.scss b/examples/gtk3/vala/simple-bar/style.scss
new file mode 100644
index 0000000..f5f771a
--- /dev/null
+++ b/examples/gtk3/vala/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/vala/simple-bar/widget/Bar.vala b/examples/gtk3/vala/simple-bar/widget/Bar.vala
new file mode 100644
index 0000000..28b32ef
--- /dev/null
+++ b/examples/gtk3/vala/simple-bar/widget/Bar.vala
@@ -0,0 +1,253 @@
+class Workspaces : Gtk.Box {
+ AstalHyprland.Hyprland hypr = AstalHyprland.get_default();
+ public Workspaces() {
+ Astal.widget_set_class_names(this, {"Workspaces"});
+ hypr.notify["workspaces"].connect(sync);
+ sync();
+ }
+
+ void sync() {
+ foreach (var child in get_children())
+ child.destroy();
+
+ foreach (var ws in hypr.workspaces) {
+ // filter out special workspaces
+ if (!(ws.id >= -99 && ws.id <= -2)) {
+ add(button(ws));
+ }
+ }
+ }
+
+ Gtk.Button button(AstalHyprland.Workspace ws) {
+ var btn = new Gtk.Button() {
+ visible = true,
+ label = ws.id.to_string()
+ };
+
+ hypr.notify["focused-workspace"].connect(() => {
+ var focused = hypr.focused_workspace == ws;
+ if (focused) {
+ Astal.widget_set_class_names(btn, {"focused"});
+ } else {
+ Astal.widget_set_class_names(btn, {});
+ }
+ });
+
+ btn.clicked.connect(ws.focus);
+ return btn;
+ }
+}
+
+class FocusedClient : Gtk.Box {
+ public FocusedClient() {
+ Astal.widget_set_class_names(this, {"Focused"});
+ AstalHyprland.get_default().notify["focused-client"].connect(sync);
+ sync();
+ }
+
+ void sync() {
+ foreach (var child in get_children())
+ child.destroy();
+
+ var client = AstalHyprland.get_default().focused_client;
+ if (client == null)
+ return;
+
+ var label = new Gtk.Label(client.title) { visible = true };
+ client.bind_property("title", label, "label", BindingFlags.SYNC_CREATE);
+ add(label);
+ }
+}
+
+class Media : Gtk.Box {
+ AstalMpris.Mpris mpris = AstalMpris.get_default();
+
+ public Media() {
+ Astal.widget_set_class_names(this, {"Media"});
+ mpris.notify["players"].connect(sync);
+ sync();
+ }
+
+ void sync() {
+ foreach (var child in get_children())
+ child.destroy();
+
+ if (mpris.players.length() == 0) {
+ add(new Gtk.Label("Nothing Playing"));
+ return;
+ }
+
+ var player = mpris.players.nth_data(0);
+ var label = new Gtk.Label(null);
+ var cover = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0) {
+ valign = Gtk.Align.CENTER
+ };
+
+ Astal.widget_set_class_names(cover, {"Cover"});
+ player.bind_property("title", label, "label", BindingFlags.SYNC_CREATE, (_, src, ref trgt) => {
+ var title = player.title;
+ var artist = player.artist;
+ trgt.set_string(@"$artist - $title");
+ return true;
+ });
+
+ var id = player.notify["cover-art"].connect(() => {
+ var art = player.cover_art;
+ Astal.widget_set_css(cover, @"background-image: url('$art')");
+ });
+
+ cover.destroy.connect(() => player.disconnect(id));
+ add(cover);
+ add(label);
+ }
+}
+
+class SysTray : Gtk.Box {
+ HashTable<string, Gtk.Widget> items = new HashTable<string, Gtk.Widget>(str_hash, str_equal);
+ AstalTray.Tray tray = AstalTray.get_default();
+
+ public SysTray() {
+ Astal.widget_set_class_names(this, { "SysTray" });
+ tray.item_added.connect(add_item);
+ tray.item_removed.connect(remove_item);
+ }
+
+ void add_item(string id) {
+ if (items.contains(id))
+ return;
+
+ var item = tray.get_item(id);
+ var btn = new Gtk.MenuButton() { use_popover = false, visible = true };
+ var icon = new Astal.Icon() { visible = true };
+
+ item.bind_property("tooltip-markup", btn, "tooltip-markup", BindingFlags.SYNC_CREATE);
+ item.bind_property("gicon", icon, "gicon", BindingFlags.SYNC_CREATE);
+ item.bind_property("menu-model", btn, "menu-model", BindingFlags.SYNC_CREATE);
+ btn.insert_action_group("dbusmenu", item.action_group);
+ item.notify["action-group"].connect(() => {
+ btn.insert_action_group("dbusmenu", item.action_group);
+ });
+
+ btn.add(icon);
+ add(btn);
+ items.set(id, btn);
+ }
+
+ void remove_item(string id) {
+ if (items.contains(id)) {
+ items.remove(id);
+ }
+ }
+}
+
+class Wifi : Astal.Icon {
+ public Wifi() {
+ Astal.widget_set_class_names(this, {"Wifi"});
+ var wifi = AstalNetwork.get_default().get_wifi();
+ if (wifi != null) {
+ wifi.bind_property("ssid", this, "tooltip-text", BindingFlags.SYNC_CREATE);
+ wifi.bind_property("icon-name", this, "icon", BindingFlags.SYNC_CREATE);
+ }
+ }
+}
+
+class AudioSlider : Gtk.Box {
+ Astal.Icon icon = new Astal.Icon();
+ Astal.Slider slider = new Astal.Slider() { hexpand = true };
+
+ public AudioSlider() {
+ add(icon);
+ add(slider);
+ Astal.widget_set_class_names(this, {"AudioSlider"});
+ Astal.widget_set_css(this, "min-width: 140px");
+
+ var speaker = AstalWp.get_default().audio.default_speaker;
+ speaker.bind_property("volume-icon", icon, "icon", BindingFlags.SYNC_CREATE);
+ speaker.bind_property("volume", slider, "value", BindingFlags.SYNC_CREATE);
+ slider.dragged.connect(() => speaker.volume = slider.value);
+ }
+}
+
+class Battery : Gtk.Box {
+ Astal.Icon icon = new Astal.Icon();
+ Astal.Label label = new Astal.Label();
+
+ public Battery() {
+ add(icon);
+ add(label);
+ Astal.widget_set_class_names(this, {"Battery"});
+
+ var bat = AstalBattery.get_default();
+ bat.bind_property("is-present", this, "visible", BindingFlags.SYNC_CREATE);
+ bat.bind_property("battery-icon-name", icon, "icon", BindingFlags.SYNC_CREATE);
+ bat.bind_property("percentage", label, "label", BindingFlags.SYNC_CREATE, (_, src, ref trgt) => {
+ var p = Math.floor(src.get_double() * 100);
+ trgt.set_string(@"$p%");
+ return true;
+ });
+ }
+}
+
+class Time : Astal.Label {
+ string format;
+ AstalIO.Time interval;
+
+ void sync() {
+ label = new DateTime.now_local().format(format);
+ }
+
+ public Time(string format = "%H:%M - %A %e.") {
+ this.format = format;
+ interval = AstalIO.Time.interval(1000, null);
+ interval.now.connect(sync);
+ destroy.connect(interval.cancel);
+ Astal.widget_set_class_names(this, {"Time"});
+ }
+}
+
+class Left : Gtk.Box {
+ public Left() {
+ Object(hexpand: true, halign: Gtk.Align.START);
+ add(new Workspaces());
+ add(new FocusedClient());
+ }
+}
+
+class Center : Gtk.Box {
+ public Center() {
+ add(new Media());
+ }
+}
+
+class Right : Gtk.Box {
+ public Right() {
+ Object(hexpand: true, halign: Gtk.Align.END);
+ add(new SysTray());
+ add(new Wifi());
+ add(new AudioSlider());
+ add(new Battery());
+ add(new Time());
+ }
+}
+
+class Bar : Astal.Window {
+ public Bar(Gdk.Monitor monitor) {
+ Object(
+ anchor: Astal.WindowAnchor.TOP
+ | Astal.WindowAnchor.LEFT
+ | Astal.WindowAnchor.RIGHT,
+ exclusivity: Astal.Exclusivity.EXCLUSIVE,
+ gdkmonitor: monitor
+ );
+
+ Astal.widget_set_class_names(this, {"Bar"});
+
+ add(new Astal.CenterBox() {
+ start_widget = new Left(),
+ center_widget = new Center(),
+ end_widget = new Right(),
+ });
+
+ show_all();
+ }
+}