From 5a90c7e3487e8a15e005abb5a516155106f6d62c Mon Sep 17 00:00:00 2001 From: DJ Griffin Date: Tue, 16 Apr 2024 17:33:36 -0400 Subject: [PATCH] Add initial shell based on GNOME Control Center code. --- src/meson.build | 2 + src/panel.c | 202 ++++++++++++++++++++++++++++++++++++++++++++++++ src/panel.h | 71 +++++++++++++++++ src/shell.c | 119 ++++++++++++++++++++++++++++ src/shell.h | 47 +++++++++++ 5 files changed, 441 insertions(+) create mode 100644 src/panel.c create mode 100644 src/panel.h create mode 100644 src/shell.c create mode 100644 src/shell.h diff --git a/src/meson.build b/src/meson.build index 23f138b..5f678b7 100644 --- a/src/meson.build +++ b/src/meson.build @@ -31,6 +31,8 @@ executable( 'app.c', 'sidebar.c', 'content.c', + 'panel.c', + 'shell.c', 'appwin.c', 'main.c', omp_resources, diff --git a/src/panel.c b/src/panel.c new file mode 100644 index 0000000..1ccfaee --- /dev/null +++ b/src/panel.c @@ -0,0 +1,202 @@ +#include "panel.h" + +#include +#include + +#include +#include + +typedef struct { + OMPShell* shell; + GCancellable* cancellable; + + gchar* subpage; +} OMPPanelPrivate; + +G_DEFINE_ABSTRACT_TYPE_WITH_CODE ( + OMPPanel, omp_panel, ADW_TYPE_NAVIGATION_PAGE, G_ADD_PRIVATE (OMPPanel) +) + +enum { PROP_0, PROP_SHELL, PROP_PARAMETERS, PROP_SUBPAGE, N_PROPS }; + +static GParamSpec* properties[N_PROPS]; + +/* GtkBuildable interface */ + +/* GObject overrides */ + +static void +omp_panel_set_property ( + GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec +) +{ + OMPPanelPrivate* priv = omp_panel_get_instance_private (OMP_PANEL (object)); + + switch (prop_id) { + case PROP_SHELL: + /* construct only property */ + priv->shell = g_value_get_object (value); + break; + + case PROP_PARAMETERS: { + g_autoptr (GVariant) v = NULL; + GVariant* parameters; + gsize n_parameters; + + parameters = g_value_get_variant (value); + + if (parameters == NULL) + return; + + n_parameters = g_variant_n_children (parameters); + if (n_parameters == 0) + return; + + g_variant_get_child (parameters, 0, "v", &v); + + if (g_variant_is_of_type (v, G_VARIANT_TYPE_STRING)) { + g_set_str (&priv->subpage, g_variant_get_string (v, NULL)); + g_object_notify_by_pspec (object, properties[PROP_SUBPAGE]); + } + else if (!g_variant_is_of_type (v, G_VARIANT_TYPE_DICTIONARY)) + g_warning ( + "Wrong type for the first argument GVariant, expected " + "'a{sv}' but got '%s'", + (gchar*)g_variant_get_type (v) + ); + else if (g_variant_n_children (v) > 0) + g_warning ("Ignoring additional flags"); + + if (n_parameters > 1) + g_warning ("Ignoring additional parameters"); + + break; + } + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +omp_panel_get_property ( + GObject* object, guint prop_id, GValue* value, GParamSpec* pspec +) +{ + OMPPanelPrivate* priv = omp_panel_get_instance_private (OMP_PANEL (object)); + + switch (prop_id) { + case PROP_SHELL: + g_value_set_object (value, priv->shell); + break; + + case PROP_SUBPAGE: + g_value_set_string (value, priv->subpage); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +omp_panel_finalize (GObject* object) +{ + OMPPanelPrivate* priv = omp_panel_get_instance_private (OMP_PANEL (object)); + + g_cancellable_cancel (priv->cancellable); + g_clear_object (&priv->cancellable); + g_clear_pointer (&priv->subpage, g_free); + + G_OBJECT_CLASS (omp_panel_parent_class)->finalize (object); +} + +static void +omp_panel_class_init (OMPPanelClass* klass) +{ + GObjectClass* object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = omp_panel_get_property; + object_class->set_property = omp_panel_set_property; + object_class->finalize = omp_panel_finalize; + + properties[PROP_SHELL] = g_param_spec_object ( + "shell", "Shell", "Shell the Panel resides in", OMP_TYPE_SHELL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS + ); + + properties[PROP_PARAMETERS] = g_param_spec_variant ( + "parameters", "Structured parameters", + "Additional parameters passed externally (ie. command line, D-Bus " + "activation)", + G_VARIANT_TYPE ("av"), NULL, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS + ); + + properties[PROP_SUBPAGE] = g_param_spec_string ( + "subpage", "Subpage parameters", + "Additional parameter extracted from the parameters property to launch " + "a panel subpage", + NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS + ); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +omp_panel_init (OMPPanel* panel) +{ +} + +/** + * omp_panel_get_shell: + * @panel: A #OMPPanel + * + * Get the shell that the panel resides in + * + * Returns: a #OMPShell + */ +OMPShell* +omp_panel_get_shell (OMPPanel* panel) +{ + OMPPanelPrivate* priv; + + g_return_val_if_fail (OMP_IS_PANEL (panel), NULL); + + priv = omp_panel_get_instance_private (panel); + + return priv->shell; +} + +const gchar* +omp_panel_get_help_uri (OMPPanel* panel) +{ + OMPPanelClass* class = OMP_PANEL_GET_CLASS (panel); + + if (class->get_help_uri) + return class->get_help_uri (panel); + + return NULL; +} + +GCancellable* +omp_panel_get_cancellable (OMPPanel* panel) +{ + OMPPanelPrivate* priv = omp_panel_get_instance_private (panel); + + g_return_val_if_fail (OMP_IS_PANEL (panel), NULL); + + if (priv->cancellable == NULL) + priv->cancellable = g_cancellable_new (); + + return priv->cancellable; +} + +void +omp_panel_deactivate (OMPPanel* panel) +{ + OMPPanelPrivate* priv = omp_panel_get_instance_private (panel); + + g_cancellable_cancel (priv->cancellable); +} diff --git a/src/panel.h b/src/panel.h new file mode 100644 index 0000000..5b95191 --- /dev/null +++ b/src/panel.h @@ -0,0 +1,71 @@ +#pragma once + +#include + +/** + * Utility macro used to register panels + * + * use: OMP_PANEL_REGISTER (PluginName, plugin_name) + */ +#define OMP_PANEL_REGISTER(PluginName, plugin_name) \ + G_DEFINE_TYPE (PluginName, plugin_name, OMP_TYPE_PANEL) + +/** + * OMPPanelStaticInitFunc: + * + * Function that statically allocates resources and initializes + * any data that the panel will make use of during runtime. + * + * If panels represent hardware that can potentially not exist, + * e.g. the Wi-Fi panel, these panels can use this function to + * show or hide themselves without needing to have an instance + * created and running. + */ +typedef void (*OMPPanelStaticInitFunc) (void); + +#define OMP_TYPE_PANEL (omp_panel_get_type ()) +G_DECLARE_DERIVABLE_TYPE (OMPPanel, omp_panel, OMP, PANEL, AdwNavigationPage) + +/** + * OMPPanelVisibility: + * + * @OMP_PANEL_HIDDEN: Panel is hidden from search and sidebar, and not + * reachable. + * @OMP_PANEL_VISIBLE_IN_SEARCH: Panel is hidden from main view, but can be + * aompessed from search. + * @OMP_PANEL_VISIBLE: Panel is visible everywhere. + */ +typedef enum { + OMP_PANEL_HIDDEN, + OMP_PANEL_VISIBLE_IN_SEARCH, + OMP_PANEL_VISIBLE, +} OMPPanelVisibility; + +/* omp-shell.h requires OMPPanel, so make sure it is defined first */ +#include "shell.h" + +G_BEGIN_DECLS + +/** + * OMPPanelClass: + * + * The contents of this struct are private and should not be aompessed directly. + */ +struct _OMPPanelClass { + /*< private >*/ + AdwNavigationPageClass parent_class; + + const gchar* (*get_help_uri) (OMPPanel* panel); +}; + +OMPShell* omp_panel_get_shell (OMPPanel* panel); + +GPermission* omp_panel_get_permission (OMPPanel* panel); + +const gchar* omp_panel_get_help_uri (OMPPanel* panel); + +GCancellable* omp_panel_get_cancellable (OMPPanel* panel); + +void omp_panel_deactivate (OMPPanel* panel); + +G_END_DECLS diff --git a/src/shell.c b/src/shell.c new file mode 100644 index 0000000..b6923ce --- /dev/null +++ b/src/shell.c @@ -0,0 +1,119 @@ +#include "shell.h" +#include "panel.h" + +G_DEFINE_INTERFACE (OMPShell, omp_shell, GTK_TYPE_WIDGET) + +static void +omp_shell_default_init (OMPShellInterface* iface) +{ + g_object_interface_install_property ( + iface, g_param_spec_object ( + "active-panel", "active panel", "The currently active Panel", + OMP_TYPE_PANEL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS + ) + ); +} + +/** + * omp_shell_get_active_panel: + * @shell: A #OMPShell + * + * Get the current active panel + * + * Returns: a #OMPPanel or NULL if no panel is active + */ +OMPPanel* +omp_shell_get_active_panel (OMPShell* shell) +{ + OMPPanel* panel = NULL; + + g_return_val_if_fail (OMP_IS_SHELL (shell), NULL); + + g_object_get (shell, "active-panel", &panel, NULL); + + return panel; +} + +/** + * omp_shell_set_active_panel: + * @shell: A #OMPShell + * @panel: A #OMPPanel + * + * Set the current active panel. If @panel is NULL, then the shell is returned + * to a state where no panel is being displayed (for example, the list of panels + * may be shown instead). + * + */ +void +omp_shell_set_active_panel (OMPShell* shell, OMPPanel* panel) +{ + g_return_if_fail (OMP_IS_SHELL (shell)); + g_return_if_fail (panel == NULL || OMP_IS_PANEL (panel)); + + g_object_set (shell, "active-panel", panel, NULL); +} + +/** + * omp_shell_set_active_panel_from_id: + * @shell: A #OMPShell + * @id: The ID of the panel to set as active + * @parameters: A #GVariant with additional parameters + * @error: A #GError + * + * Find a panel corresponding to the specified id and set it as active. + * + * Returns: #TRUE if the panel was found and set as the active panel + */ +gboolean +omp_shell_set_active_panel_from_id ( + OMPShell* shell, const gchar* id, GVariant* parameters, GError** error +) +{ + OMPShellInterface* iface; + + g_return_val_if_fail (OMP_IS_SHELL (shell), FALSE); + + iface = OMP_SHELL_GET_IFACE (shell); + + if (!iface->set_active_panel_from_id) { + g_warning ( + "Object of type \"%s\" does not implement required interface" + " method \"set_active_panel_from_id\",", + G_OBJECT_TYPE_NAME (shell) + ); + return FALSE; + } + else { + return iface->set_active_panel_from_id (shell, id, parameters, error); + } +} + +/** + * omp_shell_get_toplevel: + * @shell: A #OMPShell + * + * Gets the toplevel window of the shell. + * + * Returns: The #GtkWidget of the shell window, or #NULL on error. + */ +GtkWidget* +omp_shell_get_toplevel (OMPShell* shell) +{ + OMPShellInterface* iface; + + g_return_val_if_fail (OMP_IS_SHELL (shell), NULL); + + iface = OMP_SHELL_GET_IFACE (shell); + + if (iface->get_toplevel) { + return iface->get_toplevel (shell); + } + + g_warning ( + "Object of type \"%s\" does not implement required interface" + " method \"get_toplevel\",", + G_OBJECT_TYPE_NAME (shell) + ); + + return NULL; +} diff --git a/src/shell.h b/src/shell.h new file mode 100644 index 0000000..a00085e --- /dev/null +++ b/src/shell.h @@ -0,0 +1,47 @@ +#pragma once + +#include + +G_BEGIN_DECLS + +#define OMP_TYPE_SHELL (omp_shell_get_type ()) +#define OMP_SHELL(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), OMP_TYPE_SHELL, OMPShell)) +#define OMP_IS_SHELL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OMP_TYPE_SHELL)) +#define OMP_SHELL_GET_IFACE(obj) \ + (G_TYPE_INSTANCE_GET_INTERFACE ((obj), OMP_TYPE_SHELL, OMPShellInterface)) + +#define OMP_SHELL_PANEL_EXTENSION_POINT "open-music-player-1" + +typedef struct _OMPShell OMPShell; +typedef struct _OMPShellInterface OMPShellInterface; + +/* panel.h requires OMPShell, so make sure they are defined first */ +#include "panel.h" + +/** + * OMPShellInterface: + * @set_active_panel_from_id: virtual function to set the active panel from an + * id string + * + */ +struct _OMPShellInterface { + GTypeInterface g_iface; + + /* methods */ + gboolean (*set_active_panel_from_id) ( + OMPShell* shell, const gchar* id, GVariant* parameters, GError** error + ); + GtkWidget* (*get_toplevel) (OMPShell* shell); +}; + +GType omp_shell_get_type (void) G_GNUC_CONST; + +OMPPanel* omp_shell_get_active_panel (OMPShell* shell); +void omp_shell_set_active_panel (OMPShell* shell, OMPPanel* panel); +gboolean omp_shell_set_active_panel_from_id ( + OMPShell* shell, const gchar* id, GVariant* parameters, GError** error +); +GtkWidget* omp_shell_get_toplevel (OMPShell* shell); + +G_END_DECLS