summaryrefslogtreecommitdiff
path: root/examples/gtk4/simple-bar/vala/src
diff options
context:
space:
mode:
Diffstat (limited to 'examples/gtk4/simple-bar/vala/src')
-rw-r--r--examples/gtk4/simple-bar/vala/src/gresource.xml7
-rw-r--r--examples/gtk4/simple-bar/vala/src/main.scss1
-rw-r--r--examples/gtk4/simple-bar/vala/src/meson.build67
-rw-r--r--examples/gtk4/simple-bar/vala/src/scss/Bar.scss19
-rw-r--r--examples/gtk4/simple-bar/vala/src/ui/Bar.blp84
-rw-r--r--examples/gtk4/simple-bar/vala/src/vala/App.vala45
-rw-r--r--examples/gtk4/simple-bar/vala/src/vala/Bar.vala186
7 files changed, 409 insertions, 0 deletions
diff --git a/examples/gtk4/simple-bar/vala/src/gresource.xml b/examples/gtk4/simple-bar/vala/src/gresource.xml
new file mode 100644
index 0000000..abc50e5
--- /dev/null
+++ b/examples/gtk4/simple-bar/vala/src/gresource.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/">
+ <file>main.css</file>
+ <file>ui/Bar.ui</file>
+ </gresource>
+</gresources>
diff --git a/examples/gtk4/simple-bar/vala/src/main.scss b/examples/gtk4/simple-bar/vala/src/main.scss
new file mode 100644
index 0000000..c37695a
--- /dev/null
+++ b/examples/gtk4/simple-bar/vala/src/main.scss
@@ -0,0 +1 @@
+@use "./scss/Bar.scss";
diff --git a/examples/gtk4/simple-bar/vala/src/meson.build b/examples/gtk4/simple-bar/vala/src/meson.build
new file mode 100644
index 0000000..a11da46
--- /dev/null
+++ b/examples/gtk4/simple-bar/vala/src/meson.build
@@ -0,0 +1,67 @@
+pkgdatadir = get_option('prefix') / get_option('datadir')
+bindir = get_option('prefix') / get_option('bindir')
+blp = find_program('blueprint-compiler', required: true)
+sass = find_program('sass', required: true)
+
+dependencies = [
+ dependency('gtk4-layer-shell-0'),
+ dependency('astal-io-0.1'),
+ dependency('glib-2.0'),
+ dependency('astal-4-4.0'),
+ dependency('astal-battery-0.1'),
+ dependency('astal-wireplumber-0.1'),
+ dependency('astal-network-0.1'),
+ dependency('libnm'),
+ dependency('astal-mpris-0.1'),
+ dependency('astal-power-profiles-0.1'),
+ dependency('astal-tray-0.1'),
+ dependency('astal-bluetooth-0.1'),
+]
+
+blueprint_sources = files(
+ 'ui/Bar.blp',
+)
+
+vala_sources = files(
+ 'vala/Bar.vala',
+ 'vala/App.vala',
+)
+
+# transplie blueprints
+ui = custom_target(
+ 'blueprint',
+ input: blueprint_sources,
+ output: '.',
+ command: [
+ blp,
+ 'batch-compile',
+ '@OUTPUT@',
+ '@CURRENT_SOURCE_DIR@',
+ '@INPUT@',
+ ],
+)
+
+# bundle scss files
+css = custom_target(
+ 'scss',
+ input: files('main.scss'),
+ command: [sass, '@INPUT@', '@OUTPUT@'],
+ output: ['main.css'],
+)
+
+# compiling data files into a binary
+resource = import('gnome').compile_resources(
+ 'data',
+ files('gresource.xml'),
+ dependencies: [ui, css],
+ source_dir: meson.current_build_dir(),
+)
+
+executable(
+ meson.project_name(),
+ dependencies: dependencies,
+ sources: [vala_sources, resource],
+ link_args: ['-lm'], # Link math library
+ install: true,
+ install_dir: bindir,
+)
diff --git a/examples/gtk4/simple-bar/vala/src/scss/Bar.scss b/examples/gtk4/simple-bar/vala/src/scss/Bar.scss
new file mode 100644
index 0000000..86ea856
--- /dev/null
+++ b/examples/gtk4/simple-bar/vala/src/scss/Bar.scss
@@ -0,0 +1,19 @@
+// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-4-16/gtk/theme/Default/_colors-public.scss
+$fg-color: #{"@theme_fg_color"};
+$bg-color: #{"@theme_bg_color"};
+
+window.Bar {
+ > box {
+ background: $bg-color;
+ color: $fg-color;
+ font-weight: bold;
+ }
+
+ button {
+ min-height: 0;
+ min-width: 0;
+ border-radius: 8px;
+ margin: 4px;
+ padding: 4px 8px;
+ }
+}
diff --git a/examples/gtk4/simple-bar/vala/src/ui/Bar.blp b/examples/gtk4/simple-bar/vala/src/ui/Bar.blp
new file mode 100644
index 0000000..6e401e7
--- /dev/null
+++ b/examples/gtk4/simple-bar/vala/src/ui/Bar.blp
@@ -0,0 +1,84 @@
+using Gtk 4.0;
+using Astal 4.0;
+
+template $Bar: Astal.Window {
+ CenterBox centerbox {
+ start-widget: Box {
+ MenuButton {
+ Label {
+ label: bind template.clock;
+ }
+
+ popover: Popover popover {
+ Calendar calendar {
+ show-day-names: true;
+ show-heading: true;
+ show-week-numbers: true;
+ }
+ };
+ }
+ };
+
+ center-widget: Box {
+ Box {
+ visible: bind template.mpris-visible;
+
+ Image {
+ file: bind template.mpris-art;
+ }
+
+ Label {
+ label: bind template.mpris-label;
+ }
+ }
+ };
+
+ end-widget: Box {
+ spacing: 4;
+
+ Image {
+ visible: bind template.bluetooth-visible;
+ icon-name: "bluetooth-symbolic";
+ }
+
+ Image {
+ icon-name: bind template.power-profile-icon;
+ }
+
+ Image {
+ icon-name: bind template.network-icon;
+ }
+
+ Box {
+ Image {
+ icon-name: bind template.volume-icon;
+ }
+
+ Scale {
+ width-request: 100;
+ change-value => $change_volume();
+
+ adjustment: Adjustment {
+ value: bind template.volume;
+ lower: 0;
+ upper: 1;
+ };
+ }
+ }
+
+ Box {
+ Image {
+ icon-name: bind template.battery-icon;
+ }
+
+ Label {
+ label: bind template.battery-label;
+ }
+ }
+
+ Box traybox {
+ spacing: 4;
+ }
+ };
+ }
+}
diff --git a/examples/gtk4/simple-bar/vala/src/vala/App.vala b/examples/gtk4/simple-bar/vala/src/vala/App.vala
new file mode 100644
index 0000000..20be55d
--- /dev/null
+++ b/examples/gtk4/simple-bar/vala/src/vala/App.vala
@@ -0,0 +1,45 @@
+class App : Astal.Application {
+ static App instance;
+
+ // this is where request handlers can be implemented
+ // that will be used to handle `astal` cli invocations
+ public override void request(string request, GLib.SocketConnection conn) {
+ print(@"incoming request: $request\n");
+
+ AstalIO.write_sock.begin(conn, "response", null);
+ }
+
+ // this is the method that will be invoked on `app.run()`
+ // this is where everything should be initialized and instantiated
+ public override void activate() {
+ this.apply_css("resource:///main.css", false);
+ this.add_window(new Bar());
+ }
+
+ // entry point of our app
+ static int main(string[] argv) {
+ App.instance = new App() { instance_name = "simple-bar" };
+
+ try {
+ // `app.acquire_socket()` needed for the request API to work
+ App.instance.acquire_socket();
+
+ // if it succeeds we can run the app
+ return App.instance.run(null);
+ } catch (Error _) {
+ // if it throws an error it means there is already an instance
+ // with `instance_name` running, so we just send a request instead
+ try {
+ var response = AstalIO.send_request(
+ "simple-bar",
+ string.joinv(" ", argv[1:])
+ );
+ print(@"$response\n");
+ return 0;
+ } catch (Error err) {
+ printerr(err.message);
+ return 1;
+ }
+ }
+ }
+}
diff --git a/examples/gtk4/simple-bar/vala/src/vala/Bar.vala b/examples/gtk4/simple-bar/vala/src/vala/Bar.vala
new file mode 100644
index 0000000..010b97e
--- /dev/null
+++ b/examples/gtk4/simple-bar/vala/src/vala/Bar.vala
@@ -0,0 +1,186 @@
+[GtkTemplate(ui="/ui/Bar.ui")]
+class Bar : Astal.Window {
+ public string clock { get; set; }
+ public string volume_icon { get; set; }
+ public string battery_visible { get; set; }
+ public string battery_label { get; set; }
+ public string battery_icon { get; set; }
+ public double volume { get; set; }
+ public string network_icon { get; set; }
+ public bool mpris_visible { get; set; }
+ public string mpris_label { get; set; }
+ public string mpris_art { get; set; }
+ public string power_profile_icon { get; set; }
+ public bool bluetooth_visible { get; set; }
+
+ AstalIO.Time timer;
+ AstalMpris.Player player;
+ HashTable<string, TrayButton> tray_items;
+
+ [GtkChild] unowned Gtk.Popover popover;
+ [GtkChild] unowned Gtk.Calendar calendar;
+ [GtkChild] unowned Gtk.Box traybox;
+
+ public Bar() {
+ anchor = TOP | LEFT | RIGHT;
+ exclusivity = EXCLUSIVE;
+ add_css_class("Bar");
+ present();
+
+ // clock
+ timer = AstalIO.Time.interval(1000, null);
+ timer.now.connect(() => {
+ clock = new DateTime.now_local().format("%H:%M:%S");
+ });
+
+ // everytime popover is opened, select current day
+ popover.notify["visible"].connect(() => {
+ if (popover.visible) {
+ calendar.select_day(new DateTime.now_local());
+ }
+ });
+
+ // network
+ var nw = AstalNetwork.get_default();
+ Binding networkBinding = null;
+
+ nw.bind_property(
+ "primary",
+ this,
+ "network-icon",
+ BindingFlags.SYNC_CREATE,
+ (_, primary) => {
+ if (networkBinding != null) networkBinding.unbind();
+
+ switch (primary.get_enum()) {
+ case AstalNetwork.Primary.WIRED:
+ networkBinding = nw.wired.bind_property(
+ "icon-name",
+ this,
+ "network-icon",
+ BindingFlags.SYNC_CREATE
+ );
+ return false;
+
+ case AstalNetwork.Primary.WIFI:
+ networkBinding = nw.wifi.bind_property(
+ "icon-name",
+ this,
+ "network-icon",
+ BindingFlags.SYNC_CREATE
+ );
+ return false;
+
+ default:
+ network_icon = "network-idle-symbolic";
+ return false;
+ }
+ },
+ null
+ );
+
+ // battery
+ var bat = AstalBattery.get_default();
+ bat.bind_property("is-present", this, "battery-visible", BindingFlags.SYNC_CREATE);
+ bat.bind_property("icon-name", this, "battery-icon", BindingFlags.SYNC_CREATE);
+ bat.bind_property("percentage", this, "battery-label", BindingFlags.SYNC_CREATE, (_, src, ref target) => {
+ target.set_string(@"$(Math.floor(bat.percentage * 100))%");
+ return true;
+ }, null);
+
+ // volume
+ var speaker = AstalWp.get_default().get_default_speaker();
+ speaker.bind_property("volume-icon", this, "volume-icon", BindingFlags.SYNC_CREATE);
+ speaker.bind_property("volume", this, "volume", BindingFlags.SYNC_CREATE);
+
+ // mpris
+ player = new AstalMpris.Player("spotify");
+ player.bind_property("available", this, "mpris-visible", BindingFlags.SYNC_CREATE);
+ player.bind_property("cover-art", this, "mpris-art", BindingFlags.SYNC_CREATE);
+ player.bind_property("metadata", this, "mpris-label", BindingFlags.SYNC_CREATE, (_, src, ref target) => {
+ if (player.title == null || player.artist == null) {
+ return false;
+ }
+ target.set_string(@"$(player.artist) - $(player.title)");
+ return true;
+ }, null);
+
+ // powerprofiles
+ var powerprofile = AstalPowerProfiles.get_default();
+ powerprofile.bind_property("icon-name", this, "power-profile-icon", BindingFlags.SYNC_CREATE);
+
+ // tray
+ var tray = AstalTray.get_default();
+ tray_items = new HashTable<string, TrayButton>(str_hash, str_equal);
+ tray.item_added.connect(on_tray_item_added);
+ tray.item_removed.connect(on_tray_item_removed);
+
+ // bluetooth
+ var bt = AstalBluetooth.get_default();
+ bt.bind_property("is-connected", this, "bluetooth-visible", BindingFlags.SYNC_CREATE);
+ }
+
+ void on_tray_item_added(AstalTray.Tray tray, string id) {
+ var button = new TrayButton(id);
+ tray_items.set(id, button);
+ traybox.append(button);
+ }
+
+ void on_tray_item_removed(string id) {
+ var button = tray_items.get(id);
+ traybox.remove(button);
+ tray_items.remove(id);
+ }
+
+ [GtkCallback]
+ bool change_volume(Gtk.Range scale, Gtk.ScrollType type, double value) {
+ AstalWp.get_default().get_default_speaker().set_volume(value);
+ return true;
+ }
+
+ public override void dispose() {
+ var tray = AstalTray.get_default();
+ tray.item_added.disconnect(on_tray_item_added);
+ tray.item_removed.disconnect(on_tray_item_removed);
+
+ foreach (var button in tray_items.get_values()) {
+ button.dispose();
+ }
+
+ timer.cancel();
+ timer.dispose();
+ player.dispose();
+ base.dispose();
+ }
+
+ class TrayButton : Astal.Bin {
+ AstalTray.TrayItem item;
+ Gtk.Popover popover;
+ Gtk.Image image;
+
+ public TrayButton(string id) {
+ var tray = AstalTray.get_default();
+ item = tray.get_item(id);
+
+ image = new Gtk.Image();
+ popover = new Gtk.PopoverMenu.from_model(item.menu_model);
+
+ child = new Gtk.MenuButton() {
+ child = image,
+ popover = popover,
+ };
+
+ item.bind_property("gicon", image, "gicon", BindingFlags.SYNC_CREATE);
+ popover.insert_action_group("dbusmenu", item.action_group);
+ item.notify["action-group"].connect(on_action_group);
+ }
+
+ void on_action_group() {
+ popover.insert_action_group("dbusmenu", item.action_group);
+ }
+
+ public override void dispose() {
+ item.notify.disconnect(on_action_group);
+ }
+ }
+}