From 66e7d9b6d77fc07123282417f996c68dcd6d1642 Mon Sep 17 00:00:00 2001 From: Hari Rana Date: Sun, 31 May 2026 06:15:27 -0400 Subject: [PATCH] event-editor/event-editor-section: Turn it into an abstract class Currently, GcalEventEditorSection is an interface that doesn't really do much apart from requiring a property and providing virtual methods. This proposed approach turns it into an abstract class, derived from AdwPreferencesGroup, which gives access to virtual methods as well as public methods. It comes with the same benefits of an interface, while sharing a lot of code that were duplicated in the previous commits. As a stopgap measure, it overrides several GtkWidget virtual methods to smoothly transition without causing the commits to break. These overrides will get removed in a later commit. Part-of: --- .../event-editor/gcal-event-editor-section.c | 319 +++++++++++++++--- .../event-editor/gcal-event-editor-section.h | 48 ++- 2 files changed, 300 insertions(+), 67 deletions(-) diff --git a/src/gui/event-editor/gcal-event-editor-section.c b/src/gui/event-editor/gcal-event-editor-section.c index 4fc11956..da1f4b3c 100644 --- a/src/gui/event-editor/gcal-event-editor-section.c +++ b/src/gui/event-editor/gcal-event-editor-section.c @@ -18,63 +18,300 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +#include "gcal-event-editor-dialog.h" #include "gcal-event-editor-section.h" -G_DEFINE_INTERFACE (GcalEventEditorSection, gcal_event_editor_section, ADW_TYPE_PREFERENCES_GROUP) +/** + * GcalEventEditorSection: + * + * An abstract class representing a section for an event editor dialog. + * + * Subclasses must override [vfunc@EventEditorSection.event_set_cb] to update widgets, and + * should call [method@EventEditorSection.get_event] to propagate changes. + */ + +typedef struct +{ + AdwPreferencesGroup parent_instance; + + GtkWidget *dialog; +} GcalEventEditorSectionPrivate; + +G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GcalEventEditorSection, gcal_event_editor_section, ADW_TYPE_PREFERENCES_GROUP) + +enum { + PROP_0, + PROP_DIALOG, + PROP_EVENT, + N_PROPS, +}; + +static GParamSpec *properties[N_PROPS]; + + +/* + * Callbacks + */ static void -gcal_event_editor_section_default_init (GcalEventEditorSectionInterface *iface) +on_event_set_cb (GcalEventEditorSection *self) +{ + GcalEvent *event; + + event = gcal_event_editor_section_get_event (self); + + GCAL_EVENT_EDITOR_SECTION_GET_CLASS (self)->event_set_cb (self, event); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_EVENT]); +} + + +/* + * GtkWidget overrides + */ + +static void +gcal_event_editor_section_root (GtkWidget *widget) +{ + GcalEventEditorSection *self = GCAL_EVENT_EDITOR_SECTION (widget); + GcalEventEditorSectionPrivate *priv = gcal_event_editor_section_get_instance_private (self); + GtkWidget *dialog; + + dialog = gtk_widget_get_ancestor (widget, GCAL_TYPE_EVENT_EDITOR_DIALOG); + + g_assert (GCAL_IS_EVENT_EDITOR_DIALOG (dialog)); + + g_set_object (&priv->dialog, dialog); + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DIALOG]); + + GTK_WIDGET_CLASS (gcal_event_editor_section_parent_class)->root (widget); +} + +static void +gcal_event_editor_section_map (GtkWidget *widget) +{ + GcalEventEditorSection *self = GCAL_EVENT_EDITOR_SECTION (widget); + GcalEvent *event; + + event = gcal_event_editor_section_get_event (self); + + GCAL_EVENT_EDITOR_SECTION_GET_CLASS (self)->event_set (self, event); + + GTK_WIDGET_CLASS (gcal_event_editor_section_parent_class)->map (widget); +} + +static void +gcal_event_editor_section_unmap (GtkWidget *widget) +{ + GcalEventEditorSection *self = GCAL_EVENT_EDITOR_SECTION (widget); + + GCAL_EVENT_EDITOR_SECTION_GET_CLASS (self)->event_set_cb (self, NULL); + + GTK_WIDGET_CLASS (gcal_event_editor_section_parent_class)->unmap (widget); +} + + +/* + * GObject overrides + */ + +static void +gcal_event_editor_section_constructed (GObject *object) +{ + g_assert (GCAL_EVENT_EDITOR_SECTION_GET_CLASS (object)->event_set_cb != NULL); + + G_OBJECT_CLASS (gcal_event_editor_section_parent_class)->constructed (object); +} + +static void +gcal_event_editor_section_dispose (GObject *object) +{ + GcalEventEditorSection *self = GCAL_EVENT_EDITOR_SECTION (object); + + gcal_event_editor_section_set_dialog (self, NULL); + + G_OBJECT_CLASS (gcal_event_editor_section_parent_class)->dispose (object); +} + +static void +gcal_event_editor_section_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GcalEventEditorSection *self = GCAL_EVENT_EDITOR_SECTION (object); + + switch (prop_id) + { + case PROP_DIALOG: + g_value_set_object (value, gcal_event_editor_section_get_dialog (self)); + break; + case PROP_EVENT: + g_value_set_object (value, gcal_event_editor_section_get_event (self)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gcal_event_editor_section_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GcalEventEditorSection *self = GCAL_EVENT_EDITOR_SECTION (object); + + switch (prop_id) + { + case PROP_DIALOG: + gcal_event_editor_section_set_dialog (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + + +/* + * Initialization + */ + +static void +gcal_event_editor_section_class_init (GcalEventEditorSectionClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->constructed = gcal_event_editor_section_constructed; + object_class->dispose = gcal_event_editor_section_dispose; + object_class->get_property = gcal_event_editor_section_get_property; + object_class->set_property = gcal_event_editor_section_set_property; + + widget_class->root = gcal_event_editor_section_root; + widget_class->map = gcal_event_editor_section_map; + widget_class->unmap = gcal_event_editor_section_unmap; + + klass->event_set_cb = NULL; + + /** + * GcalEventEditorSection:event: + * + * The #GcalEvent being edited. + */ + properties[PROP_EVENT] = + g_param_spec_object ("event", NULL, NULL, + GCAL_TYPE_EVENT, + G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + /** + * GcalEventEditorSection:dialog: + * + * The event editor dialog. + * + * The dialog must have the `event` property. + * Each time `event` is set, [vfunc@EventEditorSection.event_set] is invoked. + */ + properties[PROP_DIALOG] = + g_param_spec_object ("dialog", NULL, NULL, + GTK_TYPE_WIDGET, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, N_PROPS, properties); +} + +static void +gcal_event_editor_section_init (GcalEventEditorSection *self) { } + +/* + * Public methods + */ + /** - * gcal_event_editor_section_set_event: + * gcal_event_editor_section_get_event: * @self: a #GcalEventEditorSection - * @event: (nullable): a #GcalEvent * - * Sets the current event to be displayed / edited. + * Gets the event that is currently being edited. + * + * Returns: (nullable) (transfer none): a #GcalEvent + */ +GcalEvent * +gcal_event_editor_section_get_event (GcalEventEditorSection *self) +{ + GcalEventEditorSectionPrivate *priv; + GcalEvent *event; + + g_assert (GCAL_IS_EVENT_EDITOR_SECTION (self)); + + priv = gcal_event_editor_section_get_instance_private (self); + + g_object_get (priv->dialog, + "event", &event, + NULL); + + return event; +} + +/** + * gcal_event_editor_section_get_dialog: + * @self: a #GcalEventEditorSection + * + * Gets the dialog. + * + * Returns: (nullable) (transfer none): a dialog + */ +GtkWidget * +gcal_event_editor_section_get_dialog (GcalEventEditorSection *self) +{ + GcalEventEditorSectionPrivate *priv; + + g_assert (GCAL_IS_EVENT_EDITOR_SECTION (self)); + + priv = gcal_event_editor_section_get_instance_private (self); + + return priv->dialog; +} + +/** + * gcal_event_editor_section_set_dialog: + * @self: a #GcalEventEditorSection + * @dialog: (nullable) (transfer none): a dialog + * + * Sets the dialog. */ void -gcal_event_editor_section_set_event (GcalEventEditorSection *self, - GcalEvent *event, - GcalEventEditorFlags flags) +gcal_event_editor_section_set_dialog (GcalEventEditorSection *self, + GtkWidget *dialog) { - g_return_if_fail (GCAL_IS_EVENT_EDITOR_SECTION (self)); - g_return_if_fail (GCAL_EVENT_EDITOR_SECTION_GET_IFACE (self)->set_event); + GcalEventEditorSectionPrivate *priv; - GCAL_EVENT_EDITOR_SECTION_GET_IFACE (self)->set_event (self, event, flags); -} + g_assert (GCAL_IS_EVENT_EDITOR_SECTION (self)); + g_assert (dialog == NULL || GTK_IS_WIDGET (dialog)); -/** - * gcal_event_editor_section_apply: - * @self: a #GcalEventEditorSection - * - * Applies the changes to the event. - */ -void -gcal_event_editor_section_apply (GcalEventEditorSection *self) -{ - g_return_if_fail (GCAL_IS_EVENT_EDITOR_SECTION (self)); + priv = gcal_event_editor_section_get_instance_private (self); - if (GCAL_EVENT_EDITOR_SECTION_GET_IFACE (self)->apply) - GCAL_EVENT_EDITOR_SECTION_GET_IFACE (self)->apply (self); -} + if (priv->dialog == dialog) + return; -/** - * gcal_event_editor_section_changed: - * @self: a #GcalEventEditorSection - * - * Checks whether this section has any pending changes to the event. - * - * Returns: %TRUE if this section has pending changes - */ -gboolean -gcal_event_editor_section_changed (GcalEventEditorSection *self) -{ - g_return_val_if_fail (GCAL_IS_EVENT_EDITOR_SECTION (self), FALSE); + if (priv->dialog) + { + g_signal_handlers_disconnect_by_func (priv->dialog, on_event_set_cb, self); + g_object_unref (priv->dialog); + } - if (GCAL_EVENT_EDITOR_SECTION_GET_IFACE (self)->changed) - return GCAL_EVENT_EDITOR_SECTION_GET_IFACE (self)->changed (self); - else - return TRUE; + priv->dialog = dialog; + + if (priv->dialog) + { + g_signal_connect_object (priv->dialog, "notify::event", G_CALLBACK (on_event_set_cb), self, G_CONNECT_SWAPPED); + g_object_ref (priv->dialog); + } + + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DIALOG]); } diff --git a/src/gui/event-editor/gcal-event-editor-section.h b/src/gui/event-editor/gcal-event-editor-section.h index bc417060..5be9a2e7 100644 --- a/src/gui/event-editor/gcal-event-editor-section.h +++ b/src/gui/event-editor/gcal-event-editor-section.h @@ -20,41 +20,37 @@ #pragma once -#include "gcal-event.h" - #include +#include "gcal-event.h" + G_BEGIN_DECLS -#define GCAL_TYPE_EVENT_EDITOR_SECTION (gcal_event_editor_section_get_type ()) +#define GCAL_TYPE_EVENT_EDITOR_SECTION (gcal_event_editor_section_get_type()) -G_DECLARE_INTERFACE (GcalEventEditorSection, gcal_event_editor_section, GCAL, EVENT_EDITOR_SECTION, AdwPreferencesGroup) +G_DECLARE_DERIVABLE_TYPE (GcalEventEditorSection, gcal_event_editor_section, GCAL, EVENT_EDITOR_SECTION, AdwPreferencesGroup) -typedef enum +struct _GcalEventEditorSectionClass { - GCAL_EVENT_EDITOR_FLAG_NONE = 0, - GCAL_EVENT_EDITOR_FLAG_NEW_EVENT = 1 << 0, -} GcalEventEditorFlags; + AdwPreferencesGroupClass parent_class; -struct _GcalEventEditorSectionInterface -{ - GTypeInterface parent; - - void (*set_event) (GcalEventEditorSection *self, - GcalEvent *event, - GcalEventEditorFlags flags); - - void (*apply) (GcalEventEditorSection *self); - - gboolean (*changed) (GcalEventEditorSection *self); + /** + * GcalEventEditorSectionClass::event_set_cb: + * @self: a #GcalEventEditorSection + * @event: (nullable) (transfer none): a #GcalEvent + * + * Invoked when a new event has been set. + * + * Subclasses must implement this virtual method and update its children + * to reflect the new event. + */ + void (* event_set_cb) (GcalEventEditorSection *self, + GcalEvent *event); }; -void gcal_event_editor_section_set_event (GcalEventEditorSection *self, - GcalEvent *event, - GcalEventEditorFlags flags); - -void gcal_event_editor_section_apply (GcalEventEditorSection *self); - -gboolean gcal_event_editor_section_changed (GcalEventEditorSection *self); +GcalEvent *gcal_event_editor_section_get_event (GcalEventEditorSection *self); +GtkWidget *gcal_event_editor_section_get_dialog (GcalEventEditorSection *self); +void gcal_event_editor_section_set_dialog (GcalEventEditorSection *self, + GtkWidget *dialog); G_END_DECLS