From 9fab13452a26ed55c01047d4225f699f43bba20d Mon Sep 17 00:00:00 2001 From: Aylur Date: Mon, 14 Oct 2024 16:45:03 +0000 Subject: feat: Astal3 --- lib/astal/gtk3/src/application.vala | 217 ++++++++++++++++++++ lib/astal/gtk3/src/config.vala.in | 6 + lib/astal/gtk3/src/idle-inhibit.c | 114 +++++++++++ lib/astal/gtk3/src/idle-inhibit.h | 22 ++ lib/astal/gtk3/src/meson.build | 120 +++++++++++ lib/astal/gtk3/src/vapi/AstalInhibitManager.vapi | 13 ++ lib/astal/gtk3/src/widget/box.vala | 50 +++++ lib/astal/gtk3/src/widget/button.vala | 99 +++++++++ lib/astal/gtk3/src/widget/centerbox.vala | 52 +++++ lib/astal/gtk3/src/widget/circularprogress.vala | 180 +++++++++++++++++ lib/astal/gtk3/src/widget/eventbox.vala | 64 ++++++ lib/astal/gtk3/src/widget/icon.vala | 105 ++++++++++ lib/astal/gtk3/src/widget/label.vala | 18 ++ lib/astal/gtk3/src/widget/levelbar.vala | 13 ++ lib/astal/gtk3/src/widget/overlay.vala | 57 ++++++ lib/astal/gtk3/src/widget/scrollable.vala | 40 ++++ lib/astal/gtk3/src/widget/slider.vala | 71 +++++++ lib/astal/gtk3/src/widget/stack.vala | 26 +++ lib/astal/gtk3/src/widget/widget.vala | 157 +++++++++++++++ lib/astal/gtk3/src/widget/window.vala | 246 +++++++++++++++++++++++ 20 files changed, 1670 insertions(+) create mode 100644 lib/astal/gtk3/src/application.vala create mode 100644 lib/astal/gtk3/src/config.vala.in create mode 100644 lib/astal/gtk3/src/idle-inhibit.c create mode 100644 lib/astal/gtk3/src/idle-inhibit.h create mode 100644 lib/astal/gtk3/src/meson.build create mode 100644 lib/astal/gtk3/src/vapi/AstalInhibitManager.vapi create mode 100644 lib/astal/gtk3/src/widget/box.vala create mode 100644 lib/astal/gtk3/src/widget/button.vala create mode 100644 lib/astal/gtk3/src/widget/centerbox.vala create mode 100644 lib/astal/gtk3/src/widget/circularprogress.vala create mode 100644 lib/astal/gtk3/src/widget/eventbox.vala create mode 100644 lib/astal/gtk3/src/widget/icon.vala create mode 100644 lib/astal/gtk3/src/widget/label.vala create mode 100644 lib/astal/gtk3/src/widget/levelbar.vala create mode 100644 lib/astal/gtk3/src/widget/overlay.vala create mode 100644 lib/astal/gtk3/src/widget/scrollable.vala create mode 100644 lib/astal/gtk3/src/widget/slider.vala create mode 100644 lib/astal/gtk3/src/widget/stack.vala create mode 100644 lib/astal/gtk3/src/widget/widget.vala create mode 100644 lib/astal/gtk3/src/widget/window.vala (limited to 'lib/astal/gtk3/src') diff --git a/lib/astal/gtk3/src/application.vala b/lib/astal/gtk3/src/application.vala new file mode 100644 index 0000000..8539aa0 --- /dev/null +++ b/lib/astal/gtk3/src/application.vala @@ -0,0 +1,217 @@ +[DBus (name="io.Astal.Application")] +public class Astal.Application : Gtk.Application, AstalIO.Application { + private List css_providers = new List(); + private SocketService service; + private DBusConnection conn; + private string socket_path { get; set; } + private string _instance_name; + + [DBus (visible=false)] + public signal void monitor_added(Gdk.Monitor monitor); + + [DBus (visible=false)] + public signal void monitor_removed(Gdk.Monitor monitor); + + [DBus (visible=false)] + public signal void window_toggled(Gtk.Window window); + + [DBus (visible=false)] + public List monitors { + owned get { + var display = Gdk.Display.get_default(); + var list = new List(); + for (var i = 0; i <= display.get_n_monitors(); ++i) { + var mon = display.get_monitor(i); + if (mon != null) { + list.append(mon); + } + } + return list; + } + } + + [DBus (visible=false)] + public string instance_name { + owned get { return _instance_name; } + construct set { + application_id = "io.Astal." + value; + _instance_name = value; + } + } + + [DBus (visible=false)] + public List windows { + get { return get_windows(); } + } + + [DBus (visible=false)] + public Gtk.Settings settings { + get { return Gtk.Settings.get_default(); } + } + + [DBus (visible=false)] + public Gdk.Screen screen { + get { return Gdk.Screen.get_default(); } + } + + [DBus (visible=false)] + public string gtk_theme { + owned get { return settings.gtk_theme_name; } + set { settings.gtk_theme_name = value; } + } + + [DBus (visible=false)] + public string icon_theme { + owned get { return settings.gtk_icon_theme_name; } + set { settings.gtk_icon_theme_name = value; } + } + + [DBus (visible=false)] + public string cursor_theme { + owned get { return settings.gtk_cursor_theme_name; } + set { settings.gtk_cursor_theme_name = value; } + } + + [DBus (visible=false)] + public void reset_css() { + foreach(var provider in css_providers) { + Gtk.StyleContext.remove_provider_for_screen(screen, provider); + } + css_providers = new List(); + } + + public void inspector() throws DBusError, IOError { + Gtk.Window.set_interactive_debugging(true); + } + + [DBus (visible=false)] + public Gtk.Window? get_window(string name) { + foreach(var win in windows) { + if (win.name == name) + return win; + } + + critical("no window with name \"%s\"".printf(name)); + return null; + } + + public void toggle_window(string window) throws Error { + var win = get_window(window); + if (win != null) { + win.visible = !win.visible; + } else { + throw new IOError.FAILED("window not found"); + } + } + + [DBus (visible=false)] + public void apply_css(string style, bool reset = false) { + var provider = new Gtk.CssProvider(); + + if (reset) + reset_css(); + + try { + if (FileUtils.test(style, FileTest.EXISTS)) + provider.load_from_path(style); + else + provider.load_from_data(style); + } catch (Error err) { + critical(err.message); + } + + Gtk.StyleContext.add_provider_for_screen( + screen, provider, Gtk.STYLE_PROVIDER_PRIORITY_USER); + + css_providers.append(provider); + } + + [DBus (visible=false)] + public void add_icons(string? path) { + if (path != null) { + Gtk.IconTheme.get_default().prepend_search_path(path); + } + } + + [DBus (visible=false)] + public virtual void request(string msg, SocketConnection conn) { + AstalIO.write_sock.begin(conn, @"missing response implementation on $application_id"); + } + + /** + * should be called before `run()` + * the return value indicates if instance is already running + */ + [DBus (visible=false)] + public void acquire_socket() { + try { + service = AstalIO.acquire_socket(this); + + Bus.own_name( + BusType.SESSION, + "io.Astal." + instance_name, + BusNameOwnerFlags.NONE, + (conn) => { + try { + this.conn = conn; + conn.register_object("/io/Astal/Application", this); + } catch (Error err) { + critical(err.message); + } + }, + () => {}, + () => {} + ); + } catch (Error err) { + critical("could not acquire socket %s\n", application_id); + critical(err.message); + } + } + + public new void quit() throws DBusError, IOError { + if (service != null) { + if (FileUtils.test(socket_path, GLib.FileTest.EXISTS)){ + try { + File.new_for_path(socket_path).delete(null); + } catch (Error err) { + warning(err.message); + } + } + } + + base.quit(); + } + + construct { + if (instance_name == null) + instance_name = "astal"; + + activate.connect(() => { + var display = Gdk.Display.get_default(); + display.monitor_added.connect((mon) => { + monitor_added(mon); + notify_property("monitors"); + }); + display.monitor_removed.connect((mon) => { + monitor_removed(mon); + notify_property("monitors"); + }); + }); + + window_added.connect((window) => { + ulong id1, id2; + id1 = window.notify["visible"].connect(() => window_toggled(window)); + id2 = window_removed.connect((removed) => { + if (removed == window) { + window.disconnect(id1); + this.disconnect(id2); + } + }); + }); + + shutdown.connect(() => { try { quit(); } catch(Error err) {} }); + Unix.signal_add(1, () => { try { quit(); } catch(Error err) {} }, Priority.HIGH); + Unix.signal_add(2, () => { try { quit(); } catch(Error err) {} }, Priority.HIGH); + Unix.signal_add(15, () => { try { quit(); } catch(Error err) {} }, Priority.HIGH); + } +} diff --git a/lib/astal/gtk3/src/config.vala.in b/lib/astal/gtk3/src/config.vala.in new file mode 100644 index 0000000..88bfe9c --- /dev/null +++ b/lib/astal/gtk3/src/config.vala.in @@ -0,0 +1,6 @@ +namespace Astal { + public const int MAJOR_VERSION = @MAJOR_VERSION@; + public const int MINOR_VERSION = @MINOR_VERSION@; + public const int MICRO_VERSION = @MICRO_VERSION@; + public const string VERSION = "@VERSION@"; +} diff --git a/lib/astal/gtk3/src/idle-inhibit.c b/lib/astal/gtk3/src/idle-inhibit.c new file mode 100644 index 0000000..48f2471 --- /dev/null +++ b/lib/astal/gtk3/src/idle-inhibit.c @@ -0,0 +1,114 @@ +#include "idle-inhibit.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "idle-inhibit-unstable-v1-client.h" + +struct _AstalInhibitManager { + GObject parent_instance; +}; + +typedef struct { + gboolean init; + struct wl_registry* wl_registry; + struct wl_display* display; + struct zwp_idle_inhibit_manager_v1* idle_inhibit_manager; +} AstalInhibitManagerPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(AstalInhibitManager, astal_inhibit_manager, G_TYPE_OBJECT) + +AstalInhibitor* astal_inhibit_manager_inhibit(AstalInhibitManager* self, GtkWindow* window) { + AstalInhibitManagerPrivate* priv = astal_inhibit_manager_get_instance_private(self); + g_assert_true(priv->init); + GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window)); + struct wl_surface* surface = gdk_wayland_window_get_wl_surface(gdk_window); + return zwp_idle_inhibit_manager_v1_create_inhibitor(priv->idle_inhibit_manager, surface); +} + +static void global_registry_handler(void* data, struct wl_registry* registry, uint32_t id, + const char* interface, uint32_t version) { + AstalInhibitManager* self = ASTAL_INHIBIT_MANAGER(data); + AstalInhibitManagerPrivate* priv = astal_inhibit_manager_get_instance_private(self); + + if (strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name) == 0) { + priv->idle_inhibit_manager = + wl_registry_bind(registry, id, &zwp_idle_inhibit_manager_v1_interface, 1); + } +} + +static void global_registry_remover(void* data, struct wl_registry* registry, uint32_t id) { + // neither inhibit_manager nor inhibitor is going to be removed by the compositor, so we don't + // need do anything here. +} + +static const struct wl_registry_listener registry_listener = {global_registry_handler, + global_registry_remover}; + +static gboolean astal_inhibit_manager_wayland_init(AstalInhibitManager* self) { + AstalInhibitManagerPrivate* priv = astal_inhibit_manager_get_instance_private(self); + + if (priv->init) return TRUE; + + GdkDisplay* gdk_display = gdk_display_get_default(); + priv->display = gdk_wayland_display_get_wl_display(gdk_display); + + priv->wl_registry = wl_display_get_registry(priv->display); + wl_registry_add_listener(priv->wl_registry, ®istry_listener, self); + + wl_display_roundtrip(priv->display); + + if (priv->idle_inhibit_manager == NULL) { + g_critical("Can not connect idle inhibitor protocol"); + return FALSE; + } + + priv->init = TRUE; + return TRUE; +} + +AstalInhibitManager* astal_inhibit_manager_get_default() { + static AstalInhibitManager* self = NULL; + + if (self == NULL) { + self = g_object_new(ASTAL_TYPE_INHIBIT_MANAGER, NULL); + if (!astal_inhibit_manager_wayland_init(self)) { + g_object_unref(self); + self = NULL; + } + } + + return self; +} + +static void astal_inhibit_manager_init(AstalInhibitManager* self) { + AstalInhibitManagerPrivate* priv = astal_inhibit_manager_get_instance_private(self); + priv->init = FALSE; + priv->display = NULL; + priv->wl_registry = NULL; + priv->idle_inhibit_manager = NULL; +} + +static void astal_inhibit_manager_finalize(GObject* object) { + AstalInhibitManager* self = ASTAL_INHIBIT_MANAGER(object); + AstalInhibitManagerPrivate* priv = astal_inhibit_manager_get_instance_private(self); + + if (priv->display != NULL) wl_display_roundtrip(priv->display); + + if (priv->wl_registry != NULL) wl_registry_destroy(priv->wl_registry); + if (priv->idle_inhibit_manager != NULL) + zwp_idle_inhibit_manager_v1_destroy(priv->idle_inhibit_manager); + + G_OBJECT_CLASS(astal_inhibit_manager_parent_class)->finalize(object); +} + +static void astal_inhibit_manager_class_init(AstalInhibitManagerClass* class) { + GObjectClass* object_class = G_OBJECT_CLASS(class); + object_class->finalize = astal_inhibit_manager_finalize; +} diff --git a/lib/astal/gtk3/src/idle-inhibit.h b/lib/astal/gtk3/src/idle-inhibit.h new file mode 100644 index 0000000..5e9a3ab --- /dev/null +++ b/lib/astal/gtk3/src/idle-inhibit.h @@ -0,0 +1,22 @@ +#ifndef ASTAL_IDLE_INHIBITOR_H +#define ASTAL_IDLE_INHIBITOR_H + +#include +#include + +#include "idle-inhibit-unstable-v1-client.h" + +G_BEGIN_DECLS + +#define ASTAL_TYPE_INHIBIT_MANAGER (astal_inhibit_manager_get_type()) + +G_DECLARE_FINAL_TYPE(AstalInhibitManager, astal_inhibit_manager, ASTAL, INHIBIT_MANAGER, GObject) + +typedef struct zwp_idle_inhibitor_v1 AstalInhibitor; + +AstalInhibitManager* astal_inhibit_manager_get_default(); +AstalInhibitor* astal_inhibit_manager_inhibit(AstalInhibitManager* self, GtkWindow* window); + +G_END_DECLS + +#endif // !ASTAL_IDLE_INHIBITOR_H diff --git a/lib/astal/gtk3/src/meson.build b/lib/astal/gtk3/src/meson.build new file mode 100644 index 0000000..c8c7df2 --- /dev/null +++ b/lib/astal/gtk3/src/meson.build @@ -0,0 +1,120 @@ +version_split = meson.project_version().split('.') +api_version = version_split[0] + '.' + version_split[1] +gir = 'Astal-' + api_version + '.gir' +typelib = 'Astal-' + api_version + '.typelib' + +vapi_dir = meson.current_source_dir() / 'vapi' +add_project_arguments(['--vapidir', vapi_dir], language: 'vala') + +config = configure_file( + input: 'config.vala.in', + output: 'config.vala', + configuration: { + 'VERSION': meson.project_version(), + 'MAJOR_VERSION': version_split[0], + 'MINOR_VERSION': version_split[1], + 'MICRO_VERSION': version_split[2], + }, +) + +pkgconfig_deps = [ + dependency('astal-io-0.1'), + dependency('glib-2.0'), + dependency('gio-unix-2.0'), + dependency('gobject-2.0'), + dependency('gio-2.0'), + dependency('gtk+-3.0'), + dependency('gdk-pixbuf-2.0'), + dependency('gtk-layer-shell-0'), + dependency('wayland-client'), +] + +deps = pkgconfig_deps + meson.get_compiler('c').find_library('m') + +wayland_protos = dependency('wayland-protocols') +wayland_scanner = find_program('wayland-scanner') + +wl_protocol_dir = wayland_protos.get_variable(pkgconfig: 'pkgdatadir') + +gen_client_header = generator( + wayland_scanner, + output: ['@BASENAME@-client.h'], + arguments: ['-c', 'client-header', '@INPUT@', '@BUILD_DIR@/@BASENAME@-client.h'], +) + +gen_private_code = generator( + wayland_scanner, + output: ['@BASENAME@.c'], + arguments: ['-c', 'private-code', '@INPUT@', '@BUILD_DIR@/@BASENAME@.c'], +) + +protocols = [ + join_paths(wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'), +] + +client_protocol_srcs = [] + +foreach protocol : protocols + client_header = gen_client_header.process(protocol) + code = gen_private_code.process(protocol) + client_protocol_srcs += [client_header, code] +endforeach + +sources = [ + config, + 'widget/box.vala', + 'widget/button.vala', + 'widget/centerbox.vala', + 'widget/circularprogress.vala', + 'widget/eventbox.vala', + 'widget/icon.vala', + 'widget/label.vala', + 'widget/levelbar.vala', + 'widget/overlay.vala', + 'widget/scrollable.vala', + 'widget/slider.vala', + 'widget/stack.vala', + 'widget/widget.vala', + 'widget/window.vala', + 'application.vala', + 'idle-inhibit.h', + 'idle-inhibit.c', +] + client_protocol_srcs + +lib = library( + meson.project_name(), + sources, + dependencies: deps, + vala_args: ['--pkg', 'AstalInhibitManager'], + vala_header: meson.project_name() + '.h', + vala_vapi: meson.project_name() + '-' + api_version + '.vapi', + vala_gir: gir, + version: meson.project_version(), + install: true, + install_dir: [true, true, true, true], +) + +import('pkgconfig').generate( + lib, + name: meson.project_name(), + filebase: meson.project_name() + '-' + api_version, + version: meson.project_version(), + subdirs: meson.project_name(), + requires: pkgconfig_deps, + install_dir: libdir / 'pkgconfig', +) + +custom_target( + typelib, + command: [ + find_program('g-ir-compiler'), + '--output', '@OUTPUT@', + '--shared-library', libdir / '@PLAINNAME@', + meson.current_build_dir() / gir, + ], + input: lib, + output: typelib, + depends: lib, + install: true, + install_dir: libdir / 'girepository-1.0', +) diff --git a/lib/astal/gtk3/src/vapi/AstalInhibitManager.vapi b/lib/astal/gtk3/src/vapi/AstalInhibitManager.vapi new file mode 100644 index 0000000..6232a3c --- /dev/null +++ b/lib/astal/gtk3/src/vapi/AstalInhibitManager.vapi @@ -0,0 +1,13 @@ +[CCode (cprefix = "Astal", gir_namespace = "Astal", lower_case_cprefix = "astal_")] +namespace Astal { + [CCode (cheader_filename = "idle-inhibit.h", type_id = "astal_idle_inhibit_manager_get_type()")] + public class InhibitManager : GLib.Object { + public static unowned InhibitManager? get_default(); + public Inhibitor inhibit (Gtk.Window window); + } + + [CCode (cheader_filename = "idle-inhibit.h", free_function = "zwp_idle_inhibitor_v1_destroy")] + [Compact] + public class Inhibitor { + } +} diff --git a/lib/astal/gtk3/src/widget/box.vala b/lib/astal/gtk3/src/widget/box.vala new file mode 100644 index 0000000..d23a799 --- /dev/null +++ b/lib/astal/gtk3/src/widget/box.vala @@ -0,0 +1,50 @@ +public class Astal.Box : Gtk.Box { + [CCode (notify = false)] + public bool vertical { + get { return orientation == Gtk.Orientation.VERTICAL; } + set { orientation = value ? Gtk.Orientation.VERTICAL : Gtk.Orientation.HORIZONTAL; } + } + + public List children { + set { _set_children(value); } + owned get { return get_children(); } + } + + public new Gtk.Widget child { + owned get { return _get_child(); } + set { _set_child(value); } + } + + construct { + notify["orientation"].connect(() => { + notify_property("vertical"); + }); + } + + private void _set_child(Gtk.Widget child) { + var list = new List(); + list.append(child); + _set_children(list); + } + + private Gtk.Widget? _get_child() { + foreach(var child in get_children()) + return child; + + return null; + } + + private void _set_children(List arr) { + foreach(var child in get_children()) { + remove(child); + } + + foreach(var child in arr) + add(child); + } + + public Box(bool vertical, List children) { + this.vertical = vertical; + _set_children(children); + } +} diff --git a/lib/astal/gtk3/src/widget/button.vala b/lib/astal/gtk3/src/widget/button.vala new file mode 100644 index 0000000..bc10577 --- /dev/null +++ b/lib/astal/gtk3/src/widget/button.vala @@ -0,0 +1,99 @@ +public class Astal.Button : Gtk.Button { + public signal void hover (HoverEvent event); + public signal void hover_lost (HoverEvent event); + public signal void click (ClickEvent event); + public signal void click_release (ClickEvent event); + public signal void scroll (ScrollEvent event); + + construct { + add_events(Gdk.EventMask.SCROLL_MASK); + add_events(Gdk.EventMask.SMOOTH_SCROLL_MASK); + + enter_notify_event.connect((self, event) => { + hover(HoverEvent(event) { lost = false }); + }); + + leave_notify_event.connect((self, event) => { + hover_lost(HoverEvent(event) { lost = true }); + }); + + button_press_event.connect((event) => { + click(ClickEvent(event) { release = false }); + }); + + button_release_event.connect((event) => { + click_release(ClickEvent(event) { release = true }); + }); + + scroll_event.connect((event) => { + scroll(ScrollEvent(event)); + }); + } +} + +public enum Astal.MouseButton { + PRIMARY = 1, + MIDDLE = 2, + SECONDARY = 3, + BACK = 4, + FORWARD = 5, +} + +// these structs are here because gjs converts every event +// into a union Gdk.Event, which cannot be destructured +// and are not as convinent to work with as a struct +public struct Astal.ClickEvent { + bool release; + uint time; + double x; + double y; + Gdk.ModifierType modifier; + MouseButton button; + + public ClickEvent(Gdk.EventButton event) { + this.time = event.time; + this.x = event.x; + this.y = event.y; + this.button = (MouseButton)event.button; + this.modifier = event.state; + } +} + +public struct Astal.HoverEvent { + bool lost; + uint time; + double x; + double y; + Gdk.ModifierType modifier; + Gdk.CrossingMode mode; + Gdk.NotifyType detail; + + public HoverEvent(Gdk.EventCrossing event) { + this.time = event.time; + this.x = event.x; + this.y = event.y; + this.modifier = event.state; + this.mode = event.mode; + this.detail = event.detail; + } +} + +public struct Astal.ScrollEvent { + uint time; + double x; + double y; + Gdk.ModifierType modifier; + Gdk.ScrollDirection direction; + double delta_x; + double delta_y; + + public ScrollEvent(Gdk.EventScroll event) { + this.time = event.time; + this.x = event.x; + this.y = event.y; + this.modifier = event.state; + this.direction = event.direction; + this.delta_x = event.delta_x; + this.delta_y = event.delta_y; + } +} diff --git a/lib/astal/gtk3/src/widget/centerbox.vala b/lib/astal/gtk3/src/widget/centerbox.vala new file mode 100644 index 0000000..89bf50b --- /dev/null +++ b/lib/astal/gtk3/src/widget/centerbox.vala @@ -0,0 +1,52 @@ +public class Astal.CenterBox : Gtk.Box { + [CCode (notify = false)] + public bool vertical { + get { return orientation == Gtk.Orientation.VERTICAL; } + set { orientation = value ? Gtk.Orientation.VERTICAL : Gtk.Orientation.HORIZONTAL; } + } + + construct { + notify["orientation"].connect(() => { + notify_property("vertical"); + }); + } + + static construct { + set_css_name("centerbox"); + } + + private Gtk.Widget _start_widget; + public Gtk.Widget start_widget { + get { return _start_widget; } + set { + if (_start_widget != null) + remove(_start_widget); + + if (value != null) + pack_start(value, true, true, 0); + } + } + + private Gtk.Widget _end_widget; + public Gtk.Widget end_widget { + get { return _end_widget; } + set { + if (_end_widget != null) + remove(_end_widget); + + if (value != null) + pack_end(value, true, true, 0); + } + } + + public Gtk.Widget center_widget { + get { return get_center_widget(); } + set { + if (center_widget != null) + remove(center_widget); + + if (value != null) + set_center_widget(value); + } + } +} diff --git a/lib/astal/gtk3/src/widget/circularprogress.vala b/lib/astal/gtk3/src/widget/circularprogress.vala new file mode 100644 index 0000000..dd7c97b --- /dev/null +++ b/lib/astal/gtk3/src/widget/circularprogress.vala @@ -0,0 +1,180 @@ +public class Astal.CircularProgress : Gtk.Bin { + public double start_at { get; set; } + public double end_at { get; set; } + public double value { get; set; } + public bool inverted { get; set; } + public bool rounded { get; set; } + + construct { + notify["start-at"].connect(queue_draw); + notify["end-at"].connect(queue_draw); + notify["value"].connect(queue_draw); + notify["inverted"].connect(queue_draw); + notify["rounded"].connect(queue_draw); + notify["child"].connect(queue_draw); + } + + static construct { + set_css_name("circular-progress"); + } + + public override void get_preferred_height(out int minh, out int nath) { + var val = get_style_context().get_property("min-height", Gtk.StateFlags.NORMAL); + if (val.get_int() <= 0) { + minh = 40; + nath = 40; + } + + minh = val.get_int(); + nath = val.get_int(); + } + + public override void get_preferred_width(out int minw, out int natw) { + var val = get_style_context().get_property("min-width", Gtk.StateFlags.NORMAL); + if (val.get_int() <= 0) { + minw = 40; + natw = 40; + } + + minw = val.get_int(); + natw = val.get_int(); + } + + private double to_radian(double percentage) { + percentage = Math.floor(percentage * 100); + return (percentage / 100) * (2 * Math.PI); + } + + private bool is_full_circle(double start, double end, double epsilon = 1e-10) { + // Ensure that start and end are between 0 and 1 + start = (start % 1 + 1) % 1; + end = (end % 1 + 1) % 1; + + // Check if the difference between start and end is close to 1 + return Math.fabs(start - end) <= epsilon; + } + + private double scale_arc_value(double start, double end, double value) { + // Ensure that start and end are between 0 and 1 + start = (start % 1 + 1) % 1; + end = (end % 1 + 1) % 1; + + // Calculate the length of the arc + var arc_length = end - start; + if (arc_length < 0) + arc_length += 1; // Adjust for circular representation + + // Calculate the position on the arc based on the percentage value + var scaled = arc_length + value; + + // Ensure the position is between 0 and 1 + return (scaled % 1 + 1) % 1; + } + + private double min(double[] arr) { + double min = arr[0]; + foreach(var i in arr) + if (min > i) min = i; + return min; + } + + private double max(double[] arr) { + double max = arr[0]; + foreach(var i in arr) + if (max < i) max = i; + return max; + } + + public override bool draw(Cairo.Context cr) { + Gtk.Allocation allocation; + get_allocation(out allocation); + + var styles = get_style_context(); + var width = allocation.width; + var height = allocation.height; + var thickness = styles.get_property("font-size", Gtk.StateFlags.NORMAL).get_double(); + var margin = styles.get_margin(Gtk.StateFlags.NORMAL); + var fg = styles.get_color(Gtk.StateFlags.NORMAL); + var bg = styles.get_background_color(Gtk.StateFlags.NORMAL); + + var bg_stroke = thickness + min({margin.bottom, margin.top, margin.left, margin.right}); + var fg_stroke = thickness; + var radius = min({width, height}) / 2.0 - max({bg_stroke, fg_stroke}) / 2.0; + var center_x = width / 2; + var center_y = height / 2; + + var start_background = to_radian(start_at); + var end_background = to_radian(end_at); + var ranged_value = value + start_at; + + var is_circle = is_full_circle(this.start_at, this.end_at); + + if (is_circle) { + // Redefine end_draw in radius to create an accurate full circle + end_background = start_background + 2 * Math.PI; + ranged_value = to_radian(value); + } else { + // Range the value for the arc shape + ranged_value = to_radian(scale_arc_value( + start_at, + end_at, + value + )); + } + + double start_progress, end_progress; + + if (inverted) { + start_progress = end_background - ranged_value; + end_progress = end_background; + } else { + start_progress = start_background; + end_progress = start_background + ranged_value; + } + + // Draw background + cr.set_source_rgba(bg.red, bg.green, bg.blue, bg.alpha); + cr.arc(center_x, center_y, radius, start_background, end_background); + cr.set_line_width(bg_stroke); + cr.stroke(); + + // Draw rounded background ends + if (rounded) { + var start_x = center_x + Math.cos(start_background) * radius; + var start_y = center_y + Math.sin(start_background) * radius; + var end_x = center_x + Math.cos(end_background) * radius; + var end_y = center_y + Math.sin(end_background) * radius; + cr.set_line_width(0); + cr.arc(start_x, start_y, bg_stroke / 2, 0, 0 - 0.01); + cr.fill(); + cr.arc(end_x, end_y, bg_stroke / 2, 0, 0 - 0.01); + cr.fill(); + } + + // Draw progress + cr.set_source_rgba(fg.red, fg.green, fg.blue, fg.alpha); + cr.arc(center_x, center_y, radius, start_progress, end_progress); + cr.set_line_width(fg_stroke); + cr.stroke(); + + // Draw rounded progress ends + if (rounded) { + var start_x = center_x + Math.cos(start_progress) * radius; + var start_y = center_y + Math.sin(start_progress) * radius; + var end_x = center_x + Math.cos(end_progress) * radius; + var end_y = center_y + Math.sin(end_progress) * radius; + cr.set_line_width(0); + cr.arc(start_x, start_y, fg_stroke / 2, 0, 0 - 0.01); + cr.fill(); + cr.arc(end_x, end_y, fg_stroke / 2, 0, 0 - 0.01); + cr.fill(); + } + + if (get_child() != null) { + get_child().size_allocate(allocation); + propagate_draw(get_child(), cr); + } + + return true; + } +} diff --git a/lib/astal/gtk3/src/widget/eventbox.vala b/lib/astal/gtk3/src/widget/eventbox.vala new file mode 100644 index 0000000..611da2a --- /dev/null +++ b/lib/astal/gtk3/src/widget/eventbox.vala @@ -0,0 +1,64 @@ +public class Astal.EventBox : Gtk.EventBox { + public signal void hover (HoverEvent event); + public signal void hover_lost (HoverEvent event); + public signal void click (ClickEvent event); + public signal void click_release (ClickEvent event); + public signal void scroll (ScrollEvent event); + public signal void motion (MotionEvent event); + + static construct { + set_css_name("eventbox"); + } + + construct { + add_events(Gdk.EventMask.SCROLL_MASK); + add_events(Gdk.EventMask.SMOOTH_SCROLL_MASK); + add_events(Gdk.EventMask.POINTER_MOTION_MASK); + + enter_notify_event.connect((self, event) => { + if (event.window == self.get_window() && + event.detail != Gdk.NotifyType.INFERIOR) { + this.set_state_flags(Gtk.StateFlags.PRELIGHT, false); + hover(HoverEvent(event) { lost = false }); + } + }); + + leave_notify_event.connect((self, event) => { + if (event.window == self.get_window() && + event.detail != Gdk.NotifyType.INFERIOR) { + this.unset_state_flags(Gtk.StateFlags.PRELIGHT); + hover_lost(HoverEvent(event) { lost = true }); + } + }); + + button_press_event.connect((event) => { + click(ClickEvent(event) { release = false }); + }); + + button_release_event.connect((event) => { + click_release(ClickEvent(event) { release = true }); + }); + + scroll_event.connect((event) => { + scroll(ScrollEvent(event)); + }); + + motion_notify_event.connect((event) => { + motion(MotionEvent(event)); + }); + } +} + +public struct Astal.MotionEvent { + uint time; + double x; + double y; + Gdk.ModifierType modifier; + + public MotionEvent(Gdk.EventMotion event) { + this.time = event.time; + this.x = event.x; + this.y = event.y; + this.modifier = event.state; + } +} diff --git a/lib/astal/gtk3/src/widget/icon.vala b/lib/astal/gtk3/src/widget/icon.vala new file mode 100644 index 0000000..f2d59a2 --- /dev/null +++ b/lib/astal/gtk3/src/widget/icon.vala @@ -0,0 +1,105 @@ +public class Astal.Icon : Gtk.Image { + private IconType type = IconType.NAMED; + private double size { get; set; default = 14; } + + public new Gdk.Pixbuf pixbuf { get; set; } + public string icon { get; set; default = ""; } + public GLib.Icon g_icon {get; set;} + + public static Gtk.IconInfo? lookup_icon(string icon) { + var theme = Gtk.IconTheme.get_default(); + return theme.lookup_icon(icon, 16, Gtk.IconLookupFlags.USE_BUILTIN); + } + + private async void display_icon() { + switch(type) { + case IconType.NAMED: + icon_name = icon; + pixel_size = (int)size; + break; + case IconType.FILE: + try { + var file = File.new_for_path(icon); + var stream = yield file.read_async(); + var pb = yield new Gdk.Pixbuf.from_stream_at_scale_async( + stream, + (int)size * scale_factor, + (int)size * scale_factor, + true, + null + ); + var cs = Gdk.cairo_surface_create_from_pixbuf(pb, 0, this.get_window()); + set_from_surface(cs); + } catch (Error err) { + printerr(err.message); + } + break; + case IconType.PIXBUF: + var pb_scaled = pixbuf.scale_simple( + (int)size * scale_factor, + (int)size * scale_factor, + Gdk.InterpType.BILINEAR + ); + if (pb_scaled != null) { + var cs = Gdk.cairo_surface_create_from_pixbuf(pb_scaled, 0, this.get_window()); + set_from_surface(cs); + } + break; + case IconType.GICON: + pixel_size = (int)size; + gicon = g_icon; + break; + + } + } + + static construct { + set_css_name("icon"); + } + + construct { + notify["icon"].connect(() => { + if(FileUtils.test(icon, GLib.FileTest.EXISTS)) + type = IconType.FILE; + else if (lookup_icon(icon) != null) + type = IconType.NAMED; + else { + type = IconType.NAMED; + warning("cannot assign %s as icon, "+ + "it is not a file nor a named icon", icon); + } + display_icon.begin(); + }); + + notify["pixbuf"].connect(() => { + type = IconType.PIXBUF; + display_icon.begin(); + }); + + notify["g-icon"].connect(() => { + type = IconType.GICON; + display_icon.begin(); + }); + + size_allocate.connect(() => { + size = get_style_context() + .get_property("font-size", Gtk.StateFlags.NORMAL).get_double(); + + display_icon.begin(); + }); + + get_style_context().changed.connect(() => { + size = get_style_context() + .get_property("font-size", Gtk.StateFlags.NORMAL).get_double(); + + display_icon.begin(); + }); + } +} + +private enum Astal.IconType { + NAMED, + FILE, + PIXBUF, + GICON, +} diff --git a/lib/astal/gtk3/src/widget/label.vala b/lib/astal/gtk3/src/widget/label.vala new file mode 100644 index 0000000..4063b6f --- /dev/null +++ b/lib/astal/gtk3/src/widget/label.vala @@ -0,0 +1,18 @@ +using Pango; + +public class Astal.Label : Gtk.Label { + public bool truncate { + set { ellipsize = value ? EllipsizeMode.END : EllipsizeMode.NONE; } + get { return ellipsize == EllipsizeMode.END; } + } + + public new bool justify_fill { + set { justify = value ? Gtk.Justification.FILL : Gtk.Justification.LEFT; } + get { return justify == Gtk.Justification.FILL; } + } + + construct { + notify["ellipsize"].connect(() => notify_property("truncate")); + notify["justify"].connect(() => notify_property("justify_fill")); + } +} diff --git a/lib/astal/gtk3/src/widget/levelbar.vala b/lib/astal/gtk3/src/widget/levelbar.vala new file mode 100644 index 0000000..9b61957 --- /dev/null +++ b/lib/astal/gtk3/src/widget/levelbar.vala @@ -0,0 +1,13 @@ +public class Astal.LevelBar : Gtk.LevelBar { + [CCode (notify = false)] + public bool vertical { + get { return orientation == Gtk.Orientation.VERTICAL; } + set { orientation = value ? Gtk.Orientation.VERTICAL : Gtk.Orientation.HORIZONTAL; } + } + + construct { + notify["orientation"].connect(() => { + notify_property("vertical"); + }); + } +} diff --git a/lib/astal/gtk3/src/widget/overlay.vala b/lib/astal/gtk3/src/widget/overlay.vala new file mode 100644 index 0000000..603ee66 --- /dev/null +++ b/lib/astal/gtk3/src/widget/overlay.vala @@ -0,0 +1,57 @@ +public class Astal.Overlay : Gtk.Overlay { + public bool pass_through { get; set; } + + public Gtk.Widget? overlay { + get { return overlays.nth_data(0); } + set { + foreach (var ch in get_children()) { + if (ch != child) + remove(ch); + } + + if (value != null) + add_overlay(value); + } + } + + public List overlays { + owned get { return get_children(); } + set { + foreach (var ch in get_children()) { + if (ch != child) + remove(ch); + } + + foreach (var ch in value) + add_overlay(ch); + } + } + + public new Gtk.Widget? child { + get { return get_child(); } + set { + var ch = get_child(); + if (ch != null) + remove(ch); + + if (value != null) + add(value); + } + } + + construct { + notify["pass-through"].connect(() => { + update_pass_through(); + }); + } + + private void update_pass_through() { + foreach (var child in get_children()) + set_overlay_pass_through(child, pass_through); + } + + public new void add_overlay(Gtk.Widget widget) { + base.add_overlay(widget); + set_overlay_pass_through(widget, pass_through); + } +} diff --git a/lib/astal/gtk3/src/widget/scrollable.vala b/lib/astal/gtk3/src/widget/scrollable.vala new file mode 100644 index 0000000..57afb6e --- /dev/null +++ b/lib/astal/gtk3/src/widget/scrollable.vala @@ -0,0 +1,40 @@ +public class Astal.Scrollable : Gtk.ScrolledWindow { + private Gtk.PolicyType _hscroll = Gtk.PolicyType.AUTOMATIC; + private Gtk.PolicyType _vscroll = Gtk.PolicyType.AUTOMATIC; + + public Gtk.PolicyType hscroll { + get { return _hscroll; } + set { + _hscroll = value; + set_policy(value, vscroll); + } + } + + public Gtk.PolicyType vscroll { + get { return _vscroll; } + set { + _vscroll = value; + set_policy(hscroll, value); + } + } + + static construct { + set_css_name("scrollable"); + } + + construct { + if (hadjustment != null) + hadjustment = new Gtk.Adjustment(0,0,0,0,0,0); + + if (vadjustment != null) + vadjustment = new Gtk.Adjustment(0,0,0,0,0,0); + } + + public new Gtk.Widget get_child() { + var ch = base.get_child(); + if (ch is Gtk.Viewport) { + return ch.get_child(); + } + return ch; + } +} diff --git a/lib/astal/gtk3/src/widget/slider.vala b/lib/astal/gtk3/src/widget/slider.vala new file mode 100644 index 0000000..466275b --- /dev/null +++ b/lib/astal/gtk3/src/widget/slider.vala @@ -0,0 +1,71 @@ +public class Astal.Slider : Gtk.Scale { + [CCode (notify = false)] + public bool vertical { + get { return orientation == Gtk.Orientation.VERTICAL; } + set { orientation = value ? Gtk.Orientation.VERTICAL : Gtk.Orientation.HORIZONTAL; } + } + + // emitted when the user drags the slider + public signal void dragged (); + + construct { + draw_value = false; + + if (adjustment == null) + adjustment = new Gtk.Adjustment(0,0,0,0,0,0); + + if (max == 0 && min == 0) { + max = 1; + } + + if (step == 0) { + step = 0.05; + } + + notify["orientation"].connect(() => { + notify_property("vertical"); + }); + + button_press_event.connect(() => { dragging = true; }); + key_press_event.connect(() => { dragging = true; }); + button_release_event.connect(() => { dragging = false; }); + key_release_event.connect(() => { dragging = false; }); + scroll_event.connect((event) => { + dragging = true; + if (event.delta_y > 0) + value -= step; + else + value += step; + dragging = false; + }); + + value_changed.connect(() => { + if (dragging) + dragged(); + }); + } + + public bool dragging { get; private set; } + + public double value { + get { return adjustment.value; } + set { if (!dragging) adjustment.value = value; } + } + + public double min { + get { return adjustment.lower; } + set { adjustment.lower = value; } + } + + public double max { + get { return adjustment.upper; } + set { adjustment.upper = value; } + } + + public double step { + get { return adjustment.step_increment; } + set { adjustment.step_increment = value; } + } + + // TODO: marks +} diff --git a/lib/astal/gtk3/src/widget/stack.vala b/lib/astal/gtk3/src/widget/stack.vala new file mode 100644 index 0000000..02f9959 --- /dev/null +++ b/lib/astal/gtk3/src/widget/stack.vala @@ -0,0 +1,26 @@ +public class Astal.Stack : Gtk.Stack { + public string shown { + get { return visible_child_name; } + set { visible_child_name = value; } + } + + public List children { + set { _set_children(value); } + owned get { return get_children(); } + } + + private void _set_children(List arr) { + foreach(var child in get_children()) { + remove(child); + } + + var i = 0; + foreach(var child in arr) { + if (child.name != null) { + add_named(child, child.name); + } else { + add_named(child, (++i).to_string()); + } + } + } +} diff --git a/lib/astal/gtk3/src/widget/widget.vala b/lib/astal/gtk3/src/widget/widget.vala new file mode 100644 index 0000000..2506bc8 --- /dev/null +++ b/lib/astal/gtk3/src/widget/widget.vala @@ -0,0 +1,157 @@ +namespace Astal { +private class Css { + private static HashTable _providers; + public static HashTable providers { + get { + if (_providers == null) { + _providers = new HashTable( + (w) => (uint)w, + (a, b) => a == b); + } + + return _providers; + } + } +} + +private void remove_provider(Gtk.Widget widget) { + var providers = Css.providers; + + if (providers.contains(widget)) { + var p = providers.get(widget); + widget.get_style_context().remove_provider(p); + providers.remove(widget); + p.dispose(); + } +} + +public void widget_set_css(Gtk.Widget widget, string css) { + var providers = Css.providers; + + if (providers.contains(widget)) { + remove_provider(widget); + } else { + widget.destroy.connect(() => { + remove_provider(widget); + }); + } + + var style = !css.contains("{") || !css.contains("}") + ? "* { ".concat(css, "}") : css; + + var p = new Gtk.CssProvider(); + widget.get_style_context() + .add_provider(p, Gtk.STYLE_PROVIDER_PRIORITY_USER); + + try { + p.load_from_data(style, style.length); + providers.set(widget, p); + } catch (Error err) { + warning(err.message); + } +} + +public string widget_get_css(Gtk.Widget widget) { + var providers = Css.providers; + + if (providers.contains(widget)) + return providers.get(widget).to_string(); + + return ""; +} + +public void widget_set_class_names(Gtk.Widget widget, string[] class_names) { + foreach (var name in widget_get_class_names(widget)) + widget_toggle_class_name(widget, name, false); + + foreach (var name in class_names) + widget_toggle_class_name(widget, name, true); +} + +public List widget_get_class_names(Gtk.Widget widget) { + return widget.get_style_context().list_classes(); +} + +public void widget_toggle_class_name( + Gtk.Widget widget, + string class_name, + bool condition = true +) { + var c = widget.get_style_context(); + if (condition) + c.add_class(class_name); + else + c.remove_class(class_name); +} + +private class Cursor { + private static HashTable _cursors; + public static HashTable cursors { + get { + if (_cursors == null) { + _cursors = new HashTable( + (w) => (uint)w, + (a, b) => a == b); + } + return _cursors; + } + } +} + +private void widget_setup_cursor(Gtk.Widget widget) { + widget.add_events(Gdk.EventMask.ENTER_NOTIFY_MASK); + widget.add_events(Gdk.EventMask.LEAVE_NOTIFY_MASK); + widget.enter_notify_event.connect(() => { + widget.get_window().set_cursor( + new Gdk.Cursor.from_name( + Gdk.Display.get_default(), + Cursor.cursors.get(widget))); + return false; + }); + widget.leave_notify_event.connect(() => { + widget.get_window().set_cursor( + new Gdk.Cursor.from_name( + Gdk.Display.get_default(), + "default")); + return false; + }); + widget.destroy.connect(() => { + if (Cursor.cursors.contains(widget)) + Cursor.cursors.remove(widget); + }); +} + +public void widget_set_cursor(Gtk.Widget widget, string cursor) { + if (!Cursor.cursors.contains(widget)) + widget_setup_cursor(widget); + + Cursor.cursors.set(widget, cursor); +} + +public string widget_get_cursor(Gtk.Widget widget) { + return Cursor.cursors.get(widget); +} + +private class ClickThrough { + private static HashTable _click_through; + public static HashTable click_through { + get { + if (_click_through == null) { + _click_through = new HashTable( + (w) => (uint)w, + (a, b) => a == b); + } + return _click_through; + } + } +} + +public void widget_set_click_through(Gtk.Widget widget, bool click_through) { + ClickThrough.click_through.set(widget, click_through); + widget.input_shape_combine_region(click_through ? new Cairo.Region() : null); +} + +public bool widget_get_click_through(Gtk.Widget widget) { + return ClickThrough.click_through.get(widget); +} +} diff --git a/lib/astal/gtk3/src/widget/window.vala b/lib/astal/gtk3/src/widget/window.vala new file mode 100644 index 0000000..946e766 --- /dev/null +++ b/lib/astal/gtk3/src/widget/window.vala @@ -0,0 +1,246 @@ +using GtkLayerShell; + +public enum Astal.WindowAnchor { + NONE = 0, + TOP = 1, + RIGHT = 2, + LEFT = 4, + BOTTOM = 8, +} + +public enum Astal.Exclusivity { + NORMAL, + EXCLUSIVE, + IGNORE, +} + +public enum Astal.Layer { + BACKGROUND = 0, // GtkLayerShell.Layer.BACKGROUND + BOTTOM = 1, // GtkLayerShell.Layer.BOTTOM + TOP = 2, // GtkLayerShell.Layer.TOP + OVERLAY = 3, // GtkLayerShell.Layer.OVERLAY +} + +public enum Astal.Keymode { + NONE = 0, // GtkLayerShell.KeyboardMode.NONE + ON_DEMAND = 1, // GtkLayerShell.KeyboardMode.ON_DEMAND + EXCLUSIVE = 2, // GtkLayerShell.KeyboardMode.EXCLUSIVE +} + +public class Astal.Window : Gtk.Window { + private static bool check(string action) { + if (!is_supported()) { + critical(@"can not $action on window: layer shell not supported"); + print("tip: running from an xwayland terminal can cause this, for example VsCode"); + return true; + } + return false; + } + + private InhibitManager? inhibit_manager; + private Inhibitor? inhibitor; + + construct { + if (check("initialize layer shell")) + return; + + height_request = 1; + width_request = 1; + init_for_window(this); + inhibit_manager = InhibitManager.get_default(); + } + + public bool inhibit { + set { + if (inhibit_manager == null) { + return; + } + if (value && inhibitor == null) { + inhibitor = inhibit_manager.inhibit(this); + } + else if (!value && inhibitor != null) { + inhibitor = null; + } + } + get { + return inhibitor != null; + } + } + + public override void show() { + base.show(); + if(inhibit) { + inhibitor = inhibit_manager.inhibit(this); + } + } + + public string namespace { + get { return get_namespace(this); } + set { set_namespace(this, value); } + } + + public int anchor { + set { + if (check("set anchor")) + return; + + set_anchor(this, Edge.TOP, WindowAnchor.TOP in value); + set_anchor(this, Edge.BOTTOM, WindowAnchor.BOTTOM in value); + set_anchor(this, Edge.LEFT, WindowAnchor.LEFT in value); + set_anchor(this, Edge.RIGHT, WindowAnchor.RIGHT in value); + } + get { + var a = WindowAnchor.NONE; + if (get_anchor(this, Edge.TOP)) + a = a | WindowAnchor.TOP; + + if (get_anchor(this, Edge.RIGHT)) + a = a | WindowAnchor.RIGHT; + + if (get_anchor(this, Edge.LEFT)) + a = a | WindowAnchor.LEFT; + + if (get_anchor(this, Edge.BOTTOM)) + a = a | WindowAnchor.BOTTOM; + + return a; + } + } + + public Exclusivity exclusivity { + set { + if (check("set exclusivity")) + return; + + switch (value) { + case Exclusivity.NORMAL: + set_exclusive_zone(this, 0); + break; + case Exclusivity.EXCLUSIVE: + auto_exclusive_zone_enable(this); + break; + case Exclusivity.IGNORE: + set_exclusive_zone(this, -1); + break; + } + } + get { + if (auto_exclusive_zone_is_enabled(this)) + return Exclusivity.EXCLUSIVE; + + if (get_exclusive_zone(this) == -1) + return Exclusivity.IGNORE; + + return Exclusivity.NORMAL; + } + } + + public Layer layer { + get { return (Layer)get_layer(this); } + set { + if (check("set layer")) + return; + + set_layer(this, (GtkLayerShell.Layer)value); + } + } + + public Keymode keymode { + get { return (Keymode)get_keyboard_mode(this); } + set { + if (check("set keymode")) + return; + + set_keyboard_mode(this, (GtkLayerShell.KeyboardMode)value); + } + } + + public Gdk.Monitor gdkmonitor { + get { return get_monitor(this); } + set { + if (check("set gdkmonitor")) + return; + + set_monitor (this, value); + } + } + + public new int margin_top { + get { return GtkLayerShell.get_margin(this, Edge.TOP); } + set { + if (check("set margin_top")) + return; + + GtkLayerShell.set_margin(this, Edge.TOP, value); + } + } + + public new int margin_bottom { + get { return GtkLayerShell.get_margin(this, Edge.BOTTOM); } + set { + if (check("set margin_bottom")) + return; + + GtkLayerShell.set_margin(this, Edge.BOTTOM, value); + } + } + + public new int margin_left { + get { return GtkLayerShell.get_margin(this, Edge.LEFT); } + set { + if (check("set margin_left")) + return; + + GtkLayerShell.set_margin(this, Edge.LEFT, value); + } + } + + public new int margin_right { + get { return GtkLayerShell.get_margin(this, Edge.RIGHT); } + set { + if (check("set margin_right")) + return; + + GtkLayerShell.set_margin(this, Edge.RIGHT, value); + } + } + + public new int margin { + set { + if (check("set margin")) + return; + + margin_top = value; + margin_right = value; + margin_bottom = value; + margin_left = value; + } + } + + /** + * CAUTION: the id might not be the same mapped by the compositor + * to reset and let the compositor map it pass a negative number + */ + public int monitor { + set { + if (check("set monitor")) + return; + + if (value < 0) + set_monitor(this, (Gdk.Monitor)null); + + var m = Gdk.Display.get_default().get_monitor(value); + set_monitor(this, m); + } + get { + var m = get_monitor(this); + var d = Gdk.Display.get_default(); + for (var i = 0; i < d.get_n_monitors(); ++i) { + if (m == d.get_monitor(i)) + return i; + } + + return -1; + } + } +} -- cgit v1.2.3 From 2f71cd4c08bb4514efe43533e6a5d03535204c29 Mon Sep 17 00:00:00 2001 From: Aylur Date: Tue, 15 Oct 2024 01:26:32 +0200 Subject: refactor lua and gjs lib --- lib/astal/gtk3/src/application.vala | 68 +++++++++++++++---------------------- 1 file changed, 27 insertions(+), 41 deletions(-) (limited to 'lib/astal/gtk3/src') diff --git a/lib/astal/gtk3/src/application.vala b/lib/astal/gtk3/src/application.vala index 8539aa0..2255333 100644 --- a/lib/astal/gtk3/src/application.vala +++ b/lib/astal/gtk3/src/application.vala @@ -3,8 +3,9 @@ public class Astal.Application : Gtk.Application, AstalIO.Application { private List css_providers = new List(); private SocketService service; private DBusConnection conn; - private string socket_path { get; set; } - private string _instance_name; + private string _instance_name = "astal"; + + public string socket_path { get; private set; } [DBus (visible=false)] public signal void monitor_added(Gdk.Monitor monitor); @@ -34,8 +35,8 @@ public class Astal.Application : Gtk.Application, AstalIO.Application { public string instance_name { owned get { return _instance_name; } construct set { - application_id = "io.Astal." + value; - _instance_name = value; + _instance_name = value != null ? value : "astal"; + application_id = @"io.Astal.$_instance_name"; } } @@ -138,54 +139,39 @@ public class Astal.Application : Gtk.Application, AstalIO.Application { AstalIO.write_sock.begin(conn, @"missing response implementation on $application_id"); } - /** - * should be called before `run()` - * the return value indicates if instance is already running - */ [DBus (visible=false)] - public void acquire_socket() { - try { - service = AstalIO.acquire_socket(this); - - Bus.own_name( - BusType.SESSION, - "io.Astal." + instance_name, - BusNameOwnerFlags.NONE, - (conn) => { - try { - this.conn = conn; - conn.register_object("/io/Astal/Application", this); - } catch (Error err) { - critical(err.message); - } - }, - () => {}, - () => {} - ); - } catch (Error err) { - critical("could not acquire socket %s\n", application_id); - critical(err.message); - } - } + public void acquire_socket() throws Error { + string path; + service = AstalIO.acquire_socket(this, out path); + socket_path = path; - public new void quit() throws DBusError, IOError { - if (service != null) { - if (FileUtils.test(socket_path, GLib.FileTest.EXISTS)){ + Bus.own_name( + BusType.SESSION, + application_id, + BusNameOwnerFlags.NONE, + (conn) => { try { - File.new_for_path(socket_path).delete(null); + this.conn = conn; + conn.register_object("/io/Astal/Application", this); } catch (Error err) { - warning(err.message); + critical(err.message); } - } + }, + () => {}, + () => {} + ); + } + + public new void quit() throws DBusError, IOError { + if (service != null) { + service.stop(); + service.close(); } base.quit(); } construct { - if (instance_name == null) - instance_name = "astal"; - activate.connect(() => { var display = Gdk.Display.get_default(); display.monitor_added.connect((mon) => { -- cgit v1.2.3 From dde50e333a237efb4ddaf522398dedf1a69999d1 Mon Sep 17 00:00:00 2001 From: Aylur Date: Tue, 15 Oct 2024 11:47:26 +0000 Subject: docs: update references flake --- lib/astal/gtk3/src/application.vala | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'lib/astal/gtk3/src') diff --git a/lib/astal/gtk3/src/application.vala b/lib/astal/gtk3/src/application.vala index 2255333..1210d88 100644 --- a/lib/astal/gtk3/src/application.vala +++ b/lib/astal/gtk3/src/application.vala @@ -4,8 +4,7 @@ public class Astal.Application : Gtk.Application, AstalIO.Application { private SocketService service; private DBusConnection conn; private string _instance_name = "astal"; - - public string socket_path { get; private set; } + private string socket_path { get; private set; } [DBus (visible=false)] public signal void monitor_added(Gdk.Monitor monitor); @@ -45,13 +44,11 @@ public class Astal.Application : Gtk.Application, AstalIO.Application { get { return get_windows(); } } - [DBus (visible=false)] - public Gtk.Settings settings { + private Gtk.Settings settings { get { return Gtk.Settings.get_default(); } } - [DBus (visible=false)] - public Gdk.Screen screen { + private Gdk.Screen screen { get { return Gdk.Screen.get_default(); } } -- cgit v1.2.3 From bfb7e27c1f9d099fcc457379f0317b1cea43fc37 Mon Sep 17 00:00:00 2001 From: Aylur Date: Sun, 20 Oct 2024 22:49:44 +0000 Subject: fix #51 keymode enum --- lib/astal/gtk3/src/widget/window.vala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/astal/gtk3/src') diff --git a/lib/astal/gtk3/src/widget/window.vala b/lib/astal/gtk3/src/widget/window.vala index 946e766..2690365 100644 --- a/lib/astal/gtk3/src/widget/window.vala +++ b/lib/astal/gtk3/src/widget/window.vala @@ -23,8 +23,8 @@ public enum Astal.Layer { public enum Astal.Keymode { NONE = 0, // GtkLayerShell.KeyboardMode.NONE - ON_DEMAND = 1, // GtkLayerShell.KeyboardMode.ON_DEMAND - EXCLUSIVE = 2, // GtkLayerShell.KeyboardMode.EXCLUSIVE + EXCLUSIVE = 1, // GtkLayerShell.KeyboardMode.EXCLUSIVE + ON_DEMAND = 2, // GtkLayerShell.KeyboardMode.ON_DEMAND } public class Astal.Window : Gtk.Window { -- cgit v1.2.3 From 306e64998c1bf1fb997c1098ae92d6edfef31cd2 Mon Sep 17 00:00:00 2001 From: Aylur Date: Wed, 23 Oct 2024 20:37:32 +0000 Subject: docs: astal3 and io comments --- lib/astal/gtk3/src/application.vala | 65 ++++++++++++++++++++++++ lib/astal/gtk3/src/meson.build | 57 +++++++++++++++------ lib/astal/gtk3/src/vapi/AstalInhibitManager.vapi | 5 +- lib/astal/gtk3/src/widget/box.vala | 3 ++ lib/astal/gtk3/src/widget/button.vala | 18 +++++-- lib/astal/gtk3/src/widget/centerbox.vala | 3 ++ lib/astal/gtk3/src/widget/circularprogress.vala | 26 ++++++++++ lib/astal/gtk3/src/widget/eventbox.vala | 9 ++++ lib/astal/gtk3/src/widget/icon.vala | 12 ++++- lib/astal/gtk3/src/widget/label.vala | 6 +++ lib/astal/gtk3/src/widget/levelbar.vala | 3 ++ lib/astal/gtk3/src/widget/overlay.vala | 8 +++ lib/astal/gtk3/src/widget/scrollable.vala | 8 +++ lib/astal/gtk3/src/widget/slider.vala | 27 +++++++++- lib/astal/gtk3/src/widget/stack.vala | 14 +++++ lib/astal/gtk3/src/widget/window.vala | 50 +++++++++++++++++- 16 files changed, 287 insertions(+), 27 deletions(-) (limited to 'lib/astal/gtk3/src') diff --git a/lib/astal/gtk3/src/application.vala b/lib/astal/gtk3/src/application.vala index 1210d88..82ee797 100644 --- a/lib/astal/gtk3/src/application.vala +++ b/lib/astal/gtk3/src/application.vala @@ -6,15 +6,28 @@ public class Astal.Application : Gtk.Application, AstalIO.Application { private string _instance_name = "astal"; private string socket_path { get; private set; } + /** + * Emitted when a new monitor is added to [class@Gdk.Display]. + */ [DBus (visible=false)] public signal void monitor_added(Gdk.Monitor monitor); + /** + * Emitted when a monitor is disconnected from [class@Gdk.Display]. + */ [DBus (visible=false)] public signal void monitor_removed(Gdk.Monitor monitor); + /** + * Emitted when a window that has been added using + * [method@Gtk.Application.add_window] changes its visibility . + */ [DBus (visible=false)] public signal void window_toggled(Gtk.Window window); + /** + * Get all monitors from [class@Gdk.Display]. + */ [DBus (visible=false)] public List monitors { owned get { @@ -30,6 +43,11 @@ public class Astal.Application : Gtk.Application, AstalIO.Application { } } + /** + * A unique instance name. + * + * This is the identifier used by the AstalIO package and the CLI. + */ [DBus (visible=false)] public string instance_name { owned get { return _instance_name; } @@ -39,6 +57,9 @@ public class Astal.Application : Gtk.Application, AstalIO.Application { } } + /** + * Windows that has been added to this app using [method@Gtk.Application.add_window]. + */ [DBus (visible=false)] public List windows { get { return get_windows(); } @@ -52,24 +73,36 @@ public class Astal.Application : Gtk.Application, AstalIO.Application { get { return Gdk.Screen.get_default(); } } + /** + * Shortcut for [property@Gtk.Settings:gtk_theme_name] + */ [DBus (visible=false)] public string gtk_theme { owned get { return settings.gtk_theme_name; } set { settings.gtk_theme_name = value; } } + /** + * Shortcut for [property@Gtk.Settings:gtk_icon_theme_name] + */ [DBus (visible=false)] public string icon_theme { owned get { return settings.gtk_icon_theme_name; } set { settings.gtk_icon_theme_name = value; } } + /** + * Shortcut for [property@Gtk.Settings:gtk_cursor_theme_name] + */ [DBus (visible=false)] public string cursor_theme { owned get { return settings.gtk_cursor_theme_name; } set { settings.gtk_cursor_theme_name = value; } } + /** + * Remove all [class@Gtk.StyleContext] providers. + */ [DBus (visible=false)] public void reset_css() { foreach(var provider in css_providers) { @@ -78,10 +111,17 @@ public class Astal.Application : Gtk.Application, AstalIO.Application { css_providers = new List(); } + /** + * Shortcut for [func@Gtk.Window.set_interactive_debugging]. + */ public void inspector() throws DBusError, IOError { Gtk.Window.set_interactive_debugging(true); } + /** + * Get a window by its [property@Gtk.Widget:name] that has been added to this app + * using [method@Gtk.Application.add_window]. + */ [DBus (visible=false)] public Gtk.Window? get_window(string name) { foreach(var win in windows) { @@ -93,6 +133,10 @@ public class Astal.Application : Gtk.Application, AstalIO.Application { return null; } + /** + * Toggle the visibility of a window by its [property@Gtk.Widget:name] + * that has been added to this app using [method@Gtk.Application.add_window]. + */ public void toggle_window(string window) throws Error { var win = get_window(window); if (win != null) { @@ -102,6 +146,11 @@ public class Astal.Application : Gtk.Application, AstalIO.Application { } } + /** + * Add a new [class@Gtk.StyleContext] provider. + * + * @param style Css string or a path to a css file. + */ [DBus (visible=false)] public void apply_css(string style, bool reset = false) { var provider = new Gtk.CssProvider(); @@ -124,6 +173,9 @@ public class Astal.Application : Gtk.Application, AstalIO.Application { css_providers.append(provider); } + /** + * Shortcut for [method@Gtk.IconTheme.prepend_search_path]. + */ [DBus (visible=false)] public void add_icons(string? path) { if (path != null) { @@ -131,11 +183,21 @@ public class Astal.Application : Gtk.Application, AstalIO.Application { } } + /** + * Handler for an incoming request. + * + * @param msg Body of the message + * @param conn The connection which expects the response. + */ [DBus (visible=false)] public virtual void request(string msg, SocketConnection conn) { AstalIO.write_sock.begin(conn, @"missing response implementation on $application_id"); } + /** + * Attempt to acquire the astal socket for this app identified by its [property@AstalIO.Application:instance_name]. + * If the socket is in use by another app with the same name an [error@AstalIO.AppError.NAME_OCCUPIED] is thrown. + */ [DBus (visible=false)] public void acquire_socket() throws Error { string path; @@ -159,6 +221,9 @@ public class Astal.Application : Gtk.Application, AstalIO.Application { ); } + /** + * Quit and stop the socket if it was acquired. + */ public new void quit() throws DBusError, IOError { if (service != null) { service.stop(); diff --git a/lib/astal/gtk3/src/meson.build b/lib/astal/gtk3/src/meson.build index c8c7df2..bf8f72a 100644 --- a/lib/astal/gtk3/src/meson.build +++ b/lib/astal/gtk3/src/meson.build @@ -60,8 +60,7 @@ foreach protocol : protocols client_protocol_srcs += [client_header, code] endforeach -sources = [ - config, +vala_sources = [config] + files( 'widget/box.vala', 'widget/button.vala', 'widget/centerbox.vala', @@ -77,31 +76,47 @@ sources = [ 'widget/widget.vala', 'widget/window.vala', 'application.vala', - 'idle-inhibit.h', 'idle-inhibit.c', -] + client_protocol_srcs +) + +sources = vala_sources + client_protocol_srcs + files( + 'idle-inhibit.h', +) lib = library( meson.project_name(), sources, dependencies: deps, - vala_args: ['--pkg', 'AstalInhibitManager'], + vala_args: ['--vapi-comments', '--pkg', 'AstalInhibitManager'], vala_header: meson.project_name() + '.h', vala_vapi: meson.project_name() + '-' + api_version + '.vapi', - vala_gir: gir, version: meson.project_version(), install: true, - install_dir: [true, true, true, true], + install_dir: [true, true, true], ) -import('pkgconfig').generate( - lib, - name: meson.project_name(), - filebase: meson.project_name() + '-' + api_version, - version: meson.project_version(), - subdirs: meson.project_name(), - requires: pkgconfig_deps, - install_dir: libdir / 'pkgconfig', +pkgs = [] +foreach dep : pkgconfig_deps + pkgs += ['--pkg=' + dep.name()] +endforeach + +gir_tgt = custom_target( + gir, + command: [ + find_program('python3'), + girpy, + meson.project_name(), + gir + ':src/' + gir, + ] + + pkgs + + vala_sources + + [meson.project_source_root() / 'src' / 'vapi' / 'AstalInhibitManager.vapi'], + + input: sources, + depends: lib, + output: gir, + install: true, + install_dir: get_option('datadir') / 'gir-1.0', ) custom_target( @@ -114,7 +129,17 @@ custom_target( ], input: lib, output: typelib, - depends: lib, + depends: [lib, gir_tgt], install: true, install_dir: libdir / 'girepository-1.0', ) + +import('pkgconfig').generate( + lib, + name: meson.project_name(), + filebase: meson.project_name() + '-' + api_version, + version: meson.project_version(), + subdirs: meson.project_name(), + requires: pkgconfig_deps, + install_dir: libdir / 'pkgconfig', +) diff --git a/lib/astal/gtk3/src/vapi/AstalInhibitManager.vapi b/lib/astal/gtk3/src/vapi/AstalInhibitManager.vapi index 6232a3c..b2b3b34 100644 --- a/lib/astal/gtk3/src/vapi/AstalInhibitManager.vapi +++ b/lib/astal/gtk3/src/vapi/AstalInhibitManager.vapi @@ -5,9 +5,8 @@ namespace Astal { public static unowned InhibitManager? get_default(); public Inhibitor inhibit (Gtk.Window window); } - + [CCode (cheader_filename = "idle-inhibit.h", free_function = "zwp_idle_inhibitor_v1_destroy")] [Compact] - public class Inhibitor { - } + public class Inhibitor { } } diff --git a/lib/astal/gtk3/src/widget/box.vala b/lib/astal/gtk3/src/widget/box.vala index d23a799..d049161 100644 --- a/lib/astal/gtk3/src/widget/box.vala +++ b/lib/astal/gtk3/src/widget/box.vala @@ -1,4 +1,7 @@ public class Astal.Box : Gtk.Box { + /** + * Corresponds to [property@Gtk.Orientable :orientation]. + */ [CCode (notify = false)] public bool vertical { get { return orientation == Gtk.Orientation.VERTICAL; } diff --git a/lib/astal/gtk3/src/widget/button.vala b/lib/astal/gtk3/src/widget/button.vala index bc10577..2d3095a 100644 --- a/lib/astal/gtk3/src/widget/button.vala +++ b/lib/astal/gtk3/src/widget/button.vala @@ -1,3 +1,9 @@ +/** + * This button has no extra functionality on top if its base [class@Gtk.Button] class. + * + * The purpose of this Button subclass is to have a destructable + * struct as the argument in GJS event handlers. + */ public class Astal.Button : Gtk.Button { public signal void hover (HoverEvent event); public signal void hover_lost (HoverEvent event); @@ -39,9 +45,9 @@ public enum Astal.MouseButton { FORWARD = 5, } -// these structs are here because gjs converts every event -// into a union Gdk.Event, which cannot be destructured -// and are not as convinent to work with as a struct +/** + * Struct for [struct@Gdk.EventButton] + */ public struct Astal.ClickEvent { bool release; uint time; @@ -59,6 +65,9 @@ public struct Astal.ClickEvent { } } +/** + * Struct for [struct@Gdk.EventCrossing] + */ public struct Astal.HoverEvent { bool lost; uint time; @@ -78,6 +87,9 @@ public struct Astal.HoverEvent { } } +/** + * Struct for [struct@Gdk.EventScroll] + */ public struct Astal.ScrollEvent { uint time; double x; diff --git a/lib/astal/gtk3/src/widget/centerbox.vala b/lib/astal/gtk3/src/widget/centerbox.vala index 89bf50b..d74a2c4 100644 --- a/lib/astal/gtk3/src/widget/centerbox.vala +++ b/lib/astal/gtk3/src/widget/centerbox.vala @@ -1,4 +1,7 @@ public class Astal.CenterBox : Gtk.Box { + /** + * Corresponds to [property@Gtk.Orientable :orientation]. + */ [CCode (notify = false)] public bool vertical { get { return orientation == Gtk.Orientation.VERTICAL; } diff --git a/lib/astal/gtk3/src/widget/circularprogress.vala b/lib/astal/gtk3/src/widget/circularprogress.vala index dd7c97b..a3ecdf1 100644 --- a/lib/astal/gtk3/src/widget/circularprogress.vala +++ b/lib/astal/gtk3/src/widget/circularprogress.vala @@ -1,8 +1,34 @@ +/** + * CircularProgress is a subclass of [class@Gtk.Bin] which provides a circular progress bar + * with customizable properties such as starting and ending points, + * progress value, and visual features like rounded ends and inversion of progress direction. + */ public class Astal.CircularProgress : Gtk.Bin { + /** + * The starting point of the progress circle, + * where 0 represents 3 o'clock position or 0° degrees and 1 represents 360°. + */ public double start_at { get; set; } + + /** + * The cutoff point of the background color of the progress circle. + */ public double end_at { get; set; } + + /** + * The value which determines the arc of the drawn foreground color. + * Should be a value between 0 and 1. + */ public double value { get; set; } + + /** + * Inverts the progress direction, making it draw counterclockwise. + */ public bool inverted { get; set; } + + /** + * Renders rounded ends at both the start and the end of the progress bar. + */ public bool rounded { get; set; } construct { diff --git a/lib/astal/gtk3/src/widget/eventbox.vala b/lib/astal/gtk3/src/widget/eventbox.vala index 611da2a..0b588e9 100644 --- a/lib/astal/gtk3/src/widget/eventbox.vala +++ b/lib/astal/gtk3/src/widget/eventbox.vala @@ -1,3 +1,9 @@ +/** + * EventBox is a [class@Gtk.EventBox] subclass which is meant to fix an issue with its + * [signal@Gtk.Widget::enter_notify_event] and [signal@Gtk.Widget::leave_notify_event] when nesting EventBoxes + * + * Its css selector is `eventbox`. + */ public class Astal.EventBox : Gtk.EventBox { public signal void hover (HoverEvent event); public signal void hover_lost (HoverEvent event); @@ -49,6 +55,9 @@ public class Astal.EventBox : Gtk.EventBox { } } +/** + * Struct for [struct@Gdk.EventMotion] + */ public struct Astal.MotionEvent { uint time; double x; diff --git a/lib/astal/gtk3/src/widget/icon.vala b/lib/astal/gtk3/src/widget/icon.vala index f2d59a2..9a20359 100644 --- a/lib/astal/gtk3/src/widget/icon.vala +++ b/lib/astal/gtk3/src/widget/icon.vala @@ -1,10 +1,20 @@ +/** + * [class@Gtk.Image] subclass meant to be used only for icons. + * + * It's size is calculated from `font-size` css property. + * Its css selector is `icon`. + */ public class Astal.Icon : Gtk.Image { private IconType type = IconType.NAMED; private double size { get; set; default = 14; } public new Gdk.Pixbuf pixbuf { get; set; } + public GLib.Icon g_icon { get; set; } + + /** + * Either a named icon or a path to a file. + */ public string icon { get; set; default = ""; } - public GLib.Icon g_icon {get; set;} public static Gtk.IconInfo? lookup_icon(string icon) { var theme = Gtk.IconTheme.get_default(); diff --git a/lib/astal/gtk3/src/widget/label.vala b/lib/astal/gtk3/src/widget/label.vala index 4063b6f..899cba9 100644 --- a/lib/astal/gtk3/src/widget/label.vala +++ b/lib/astal/gtk3/src/widget/label.vala @@ -1,11 +1,17 @@ using Pango; public class Astal.Label : Gtk.Label { + /** + * Shortcut for setting [property@Gtk.Label:ellipsize] to [enum@Pango.EllipsizeMode.END] + */ public bool truncate { set { ellipsize = value ? EllipsizeMode.END : EllipsizeMode.NONE; } get { return ellipsize == EllipsizeMode.END; } } + /** + * Shortcut for setting [property@Gtk.Label:justify] to [enum@Gtk.Justification.FILL] + */ public new bool justify_fill { set { justify = value ? Gtk.Justification.FILL : Gtk.Justification.LEFT; } get { return justify == Gtk.Justification.FILL; } diff --git a/lib/astal/gtk3/src/widget/levelbar.vala b/lib/astal/gtk3/src/widget/levelbar.vala index 9b61957..3e98afb 100644 --- a/lib/astal/gtk3/src/widget/levelbar.vala +++ b/lib/astal/gtk3/src/widget/levelbar.vala @@ -1,4 +1,7 @@ public class Astal.LevelBar : Gtk.LevelBar { + /** + * Corresponds to [property@Gtk.Orientable :orientation]. + */ [CCode (notify = false)] public bool vertical { get { return orientation == Gtk.Orientation.VERTICAL; } diff --git a/lib/astal/gtk3/src/widget/overlay.vala b/lib/astal/gtk3/src/widget/overlay.vala index 603ee66..ed5f03b 100644 --- a/lib/astal/gtk3/src/widget/overlay.vala +++ b/lib/astal/gtk3/src/widget/overlay.vala @@ -1,6 +1,11 @@ public class Astal.Overlay : Gtk.Overlay { public bool pass_through { get; set; } + /** + * First [property@Astal.Overlay:overlays] element. + * + * WARNING: setting this value will remove every overlay but the first. + */ public Gtk.Widget? overlay { get { return overlays.nth_data(0); } set { @@ -14,6 +19,9 @@ public class Astal.Overlay : Gtk.Overlay { } } + /** + * Sets the overlays of this Overlay. [method@Gtk.Overlay.add_overlay]. + */ public List overlays { owned get { return get_children(); } set { diff --git a/lib/astal/gtk3/src/widget/scrollable.vala b/lib/astal/gtk3/src/widget/scrollable.vala index 57afb6e..57a440c 100644 --- a/lib/astal/gtk3/src/widget/scrollable.vala +++ b/lib/astal/gtk3/src/widget/scrollable.vala @@ -1,3 +1,11 @@ +/** + * Subclass of [class@Gtk.ScrolledWindow] which has its policy default to + * [enum@Gtk.PolicyType.AUTOMATIC]. + * + * Its css selector is `scrollable`. + * Its child getter returns the child of the inner + * [class@Gtk.Viewport], instead of the viewport. + */ public class Astal.Scrollable : Gtk.ScrolledWindow { private Gtk.PolicyType _hscroll = Gtk.PolicyType.AUTOMATIC; private Gtk.PolicyType _vscroll = Gtk.PolicyType.AUTOMATIC; diff --git a/lib/astal/gtk3/src/widget/slider.vala b/lib/astal/gtk3/src/widget/slider.vala index 466275b..97cfb69 100644 --- a/lib/astal/gtk3/src/widget/slider.vala +++ b/lib/astal/gtk3/src/widget/slider.vala @@ -1,12 +1,20 @@ +/** + * Subclass of [class@Gtk.Scale] which adds a signal and property for the drag state. + */ public class Astal.Slider : Gtk.Scale { + /** + * Corresponds to [property@Gtk.Orientable :orientation]. + */ [CCode (notify = false)] public bool vertical { get { return orientation == Gtk.Orientation.VERTICAL; } set { orientation = value ? Gtk.Orientation.VERTICAL : Gtk.Orientation.HORIZONTAL; } } - // emitted when the user drags the slider - public signal void dragged (); + /** + * Emitted when the user drags the slider or uses keyboard arrows and its value changes. + */ + public signal void dragged(); construct { draw_value = false; @@ -45,23 +53,38 @@ public class Astal.Slider : Gtk.Scale { }); } + /** + * `true` when the user drags the slider or uses keyboard arrows. + */ public bool dragging { get; private set; } + /** + * Value of this slider. Defaults to `0`. + */ public double value { get { return adjustment.value; } set { if (!dragging) adjustment.value = value; } } + /** + * Minimum possible value of this slider. Defaults to `0`. + */ public double min { get { return adjustment.lower; } set { adjustment.lower = value; } } + /** + * Maximum possible value of this slider. Defaults to `1`. + */ public double max { get { return adjustment.upper; } set { adjustment.upper = value; } } + /** + * Size of step increments. Defaults to `0.05`. + */ public double step { get { return adjustment.step_increment; } set { adjustment.step_increment = value; } diff --git a/lib/astal/gtk3/src/widget/stack.vala b/lib/astal/gtk3/src/widget/stack.vala index 02f9959..4e856a6 100644 --- a/lib/astal/gtk3/src/widget/stack.vala +++ b/lib/astal/gtk3/src/widget/stack.vala @@ -1,4 +1,12 @@ +/** + * Subclass of [class@Gtk.Stack] that has a children setter which + * invokes [method@Gt.Stack.add_named] with the child's [property@Gtk.Widget:name] property. + */ public class Astal.Stack : Gtk.Stack { + /** + * Same as [property@Gtk.Stack:visible-child-name]. + */ + [CCode (notify = false)] public string shown { get { return visible_child_name; } set { visible_child_name = value; } @@ -23,4 +31,10 @@ public class Astal.Stack : Gtk.Stack { } } } + + construct { + notify["visible_child_name"].connect(() => { + notify_property("shown"); + }); + } } diff --git a/lib/astal/gtk3/src/widget/window.vala b/lib/astal/gtk3/src/widget/window.vala index 2690365..e513242 100644 --- a/lib/astal/gtk3/src/widget/window.vala +++ b/lib/astal/gtk3/src/widget/window.vala @@ -10,7 +10,13 @@ public enum Astal.WindowAnchor { public enum Astal.Exclusivity { NORMAL, + /** + * Request the compositor to allocate space for this window. + */ EXCLUSIVE, + /** + * Request the compositor to stack layers on top of each other. + */ IGNORE, } @@ -22,11 +28,23 @@ public enum Astal.Layer { } public enum Astal.Keymode { + /** + * Window should not receive keyboard events. + */ NONE = 0, // GtkLayerShell.KeyboardMode.NONE + /** + * Window should have exclusive focus if it is on the top or overlay layer. + */ EXCLUSIVE = 1, // GtkLayerShell.KeyboardMode.EXCLUSIVE + /** + * Focus and Unfocues the window as needed. + */ ON_DEMAND = 2, // GtkLayerShell.KeyboardMode.ON_DEMAND } +/** + * Subclass of [class@Gtk.Window] which integrates GtkLayerShell as class fields. + */ public class Astal.Window : Gtk.Window { private static bool check(string action) { if (!is_supported()) { @@ -44,12 +62,18 @@ public class Astal.Window : Gtk.Window { if (check("initialize layer shell")) return; + // If the window has no size allocatoted when it gets mapped. + // It won't show up later either when it size changes by adding children. height_request = 1; width_request = 1; + init_for_window(this); inhibit_manager = InhibitManager.get_default(); } + /** + * When `true` it will permit inhibiting the idle behavior such as screen blanking, locking, and screensaving. + */ public bool inhibit { set { if (inhibit_manager == null) { @@ -74,11 +98,20 @@ public class Astal.Window : Gtk.Window { } } + /** + * Namespace of this window. This can be used to target the layer in compositor rules. + */ public string namespace { get { return get_namespace(this); } set { set_namespace(this, value); } } + /** + * Edges to anchor the window to. + * + * If two perpendicular edges are anchored, the surface will be anchored to that corner. + * If two opposite edges are anchored, the window will be stretched across the screen in that direction. + */ public int anchor { set { if (check("set anchor")) @@ -107,6 +140,9 @@ public class Astal.Window : Gtk.Window { } } + /** + * Exclusivity of this window. + */ public Exclusivity exclusivity { set { if (check("set exclusivity")) @@ -135,6 +171,9 @@ public class Astal.Window : Gtk.Window { } } + /** + * Which layer to appear this window on. + */ public Layer layer { get { return (Layer)get_layer(this); } set { @@ -145,6 +184,9 @@ public class Astal.Window : Gtk.Window { } } + /** + * Keyboard mode of this window. + */ public Keymode keymode { get { return (Keymode)get_keyboard_mode(this); } set { @@ -155,6 +197,9 @@ public class Astal.Window : Gtk.Window { } } + /** + * Which monitor to appear this window on. + */ public Gdk.Monitor gdkmonitor { get { return get_monitor(this); } set { @@ -218,8 +263,9 @@ public class Astal.Window : Gtk.Window { } /** - * CAUTION: the id might not be the same mapped by the compositor - * to reset and let the compositor map it pass a negative number + * Which monitor to appear this window on. + * + * CAUTION: the id might not be the same mapped by the compositor. */ public int monitor { set { -- cgit v1.2.3 From b56e169214b93fbb866401b60744a5152e278220 Mon Sep 17 00:00:00 2001 From: Aylur Date: Sun, 27 Oct 2024 23:36:06 +0000 Subject: fix(astal3): windowanchor annotation --- lib/astal/gtk3/src/widget/window.vala | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'lib/astal/gtk3/src') diff --git a/lib/astal/gtk3/src/widget/window.vala b/lib/astal/gtk3/src/widget/window.vala index e513242..9287200 100644 --- a/lib/astal/gtk3/src/widget/window.vala +++ b/lib/astal/gtk3/src/widget/window.vala @@ -1,11 +1,12 @@ using GtkLayerShell; +[Flags] public enum Astal.WindowAnchor { - NONE = 0, - TOP = 1, - RIGHT = 2, - LEFT = 4, - BOTTOM = 8, + NONE, + TOP, + RIGHT, + LEFT, + BOTTOM, } public enum Astal.Exclusivity { @@ -112,7 +113,7 @@ public class Astal.Window : Gtk.Window { * If two perpendicular edges are anchored, the surface will be anchored to that corner. * If two opposite edges are anchored, the window will be stretched across the screen in that direction. */ - public int anchor { + public WindowAnchor anchor { set { if (check("set anchor")) return; -- cgit v1.2.3