diff options
Diffstat (limited to 'examples/gtk4/simple-bar/vala/src')
-rw-r--r-- | examples/gtk4/simple-bar/vala/src/gresource.xml | 7 | ||||
-rw-r--r-- | examples/gtk4/simple-bar/vala/src/main.scss | 1 | ||||
-rw-r--r-- | examples/gtk4/simple-bar/vala/src/meson.build | 67 | ||||
-rw-r--r-- | examples/gtk4/simple-bar/vala/src/scss/Bar.scss | 19 | ||||
-rw-r--r-- | examples/gtk4/simple-bar/vala/src/ui/Bar.blp | 84 | ||||
-rw-r--r-- | examples/gtk4/simple-bar/vala/src/vala/App.vala | 45 | ||||
-rw-r--r-- | examples/gtk4/simple-bar/vala/src/vala/Bar.vala | 186 |
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); + } + } +} |