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: <https://gitlab.gnome.org/GNOME/gnome-calendar/-/merge_requests/711>
This commit is contained in:
Hari Rana
2026-06-26 08:23:08 -04:00
parent da9b28cf93
commit 66e7d9b6d7
2 changed files with 300 additions and 67 deletions
+278 -41
View File
@@ -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]);
}
@@ -20,41 +20,37 @@
#pragma once
#include "gcal-event.h"
#include <adwaita.h>
#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