[gnome-calendar/gbsneto/gtk4: 10/21] month-view: Port to GTK4
- From: Georges Basile Stavracas Neto <gbsneto src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-calendar/gbsneto/gtk4: 10/21] month-view: Port to GTK4
- Date: Mon, 7 Feb 2022 22:45:37 +0000 (UTC)
commit 927c1a505e9147eb6b9ee80a994f3df28dde639a
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date: Sun Jan 23 21:03:42 2022 -0300
month-view: Port to GTK4
src/gui/views/gcal-month-cell.c | 142 +--
src/gui/views/gcal-month-cell.h | 4 +-
src/gui/views/gcal-month-cell.ui | 63 +-
src/gui/views/gcal-month-popover.c | 720 ++++-----------
src/gui/views/gcal-month-popover.h | 9 +-
src/gui/views/gcal-month-popover.ui | 79 +-
src/gui/views/gcal-month-view.c | 1738 +++++++++++++++++------------------
src/gui/views/gcal-month-view.h | 2 +-
src/gui/views/gcal-month-view.ui | 54 +-
src/gui/views/gcal-view.c | 2 +-
src/theme/Adwaita.css | 8 +-
src/utils/gcal-utils.c | 16 +-
src/utils/gcal-utils.h | 2 +-
13 files changed, 1093 insertions(+), 1746 deletions(-)
---
diff --git a/src/gui/views/gcal-month-cell.c b/src/gui/views/gcal-month-cell.c
index 5bb71a2a..c0b6cc3c 100644
--- a/src/gui/views/gcal-month-cell.c
+++ b/src/gui/views/gcal-month-cell.c
@@ -29,7 +29,7 @@
struct _GcalMonthCell
{
- GtkEventBox parent;
+ AdwBin parent;
GDateTime *date;
guint n_overflow;
@@ -46,16 +46,12 @@ struct _GcalMonthCell
gboolean different_month;
gboolean selected;
- /* internal fields */
- gboolean hovered : 1;
- gboolean pressed : 1;
-
GcalContext *context;
GcalWeatherInfo *weather_info;
};
-G_DEFINE_TYPE (GcalMonthCell, gcal_month_cell, GTK_TYPE_EVENT_BOX)
+G_DEFINE_TYPE (GcalMonthCell, gcal_month_cell, ADW_TYPE_BIN)
enum
{
@@ -82,52 +78,14 @@ static void
update_style_flags (GcalMonthCell *self)
{
g_autoptr (GDateTime) today = NULL;
- GtkStyleContext *context;
- GtkStateFlags flags;
- GtkWidget *widget;
-
- widget = GTK_WIDGET (self);
- flags = gtk_widget_get_state_flags (widget);
-
- if (self->hovered)
- flags |= GTK_STATE_FLAG_PRELIGHT;
- else
- flags &= ~GTK_STATE_FLAG_PRELIGHT;
-
- gtk_widget_set_state_flags (widget, flags, TRUE);
/* Today */
today = g_date_time_new_now_local ();
- context = gtk_widget_get_style_context (GTK_WIDGET (self));
if (gcal_date_time_compare_date (self->date, today) == 0)
- gtk_style_context_add_class (context, "today");
+ gtk_widget_add_css_class (GTK_WIDGET (self), "today");
else
- gtk_style_context_remove_class (context, "today");
-}
-
-static gboolean
-should_propagate_button_event (GtkWidget *widget,
- GdkEventButton *event_button)
-{
- GcalMonthCell *self;
- gdouble x, y;
- gint button_height, height;
-
- self = GCAL_MONTH_CELL (widget);
- height = gtk_widget_get_allocated_height (widget);
- button_height = gtk_widget_get_allocated_height (self->overflow_button);
-
- x = event_button->x;
- y = event_button->y;
-
- if (!gcal_translate_child_window_position (widget, event_button->window, x, y, &x, &y))
- return GDK_EVENT_PROPAGATE;
-
- if (self->n_overflow > 0 && y > height - button_height)
- return GDK_EVENT_STOP;
-
- return GDK_EVENT_PROPAGATE;
+ gtk_widget_remove_css_class (GTK_WIDGET (self), "today");
}
@@ -142,30 +100,6 @@ day_changed_cb (GcalClock *clock,
update_style_flags (self);
}
-static gboolean
-enter_notify_event_cb (GtkWidget *widget,
- GdkEventCrossing *event_crossing,
- GcalMonthCell *self)
-{
- self->hovered = TRUE;
-
- update_style_flags (self);
-
- return GDK_EVENT_PROPAGATE;
-}
-
-static gboolean
-leave_notify_event_cb (GtkWidget *widget,
- GdkEventCrossing *event_crossing,
- GcalMonthCell *self)
-{
- self->hovered = FALSE;
-
- update_style_flags (self);
-
- return GDK_EVENT_PROPAGATE;
-}
-
static void
overflow_button_clicked_cb (GtkWidget *button,
GcalMonthCell *self)
@@ -176,6 +110,9 @@ overflow_button_clicked_cb (GtkWidget *button,
/*
* GtkWidget overrides
*/
+
+#if 0 // TODO: DND
+
static gboolean
gcal_month_cell_drag_motion (GtkWidget *widget,
GdkDragContext *context,
@@ -297,51 +234,7 @@ gcal_month_cell_drag_leave (GtkWidget *widget,
gtk_drag_unhighlight (widget);
}
-static gboolean
-gcal_month_cell_button_press_event (GtkWidget *widget,
- GdkEventButton *event_button)
-{
- GcalMonthCell *self = GCAL_MONTH_CELL (widget);
-
- self->pressed = TRUE;
-
- return should_propagate_button_event (widget, event_button);
-}
-
-static gboolean
-gcal_month_cell_button_release_event (GtkWidget *widget,
- GdkEventButton *event_button)
-{
- GcalMonthCell *self = GCAL_MONTH_CELL (widget);
-
- if (!self->pressed)
- return GDK_EVENT_STOP;
-
- self->pressed = FALSE;
-
- return should_propagate_button_event (widget, event_button);
-}
-
-static gboolean
-gcal_month_cell_draw (GtkWidget *widget,
- cairo_t *cr)
-{
- GtkStyleContext *context;
- gint width, height;
-
- width = gtk_widget_get_allocated_width (widget);
- height = gtk_widget_get_allocated_height (widget);
- context = gtk_widget_get_style_context (widget);
-
- gtk_render_background (context, cr, 0, 0, width, height);
- gtk_render_frame (context, cr, 0, 0, width, height);
-
- if (gtk_widget_has_focus (widget))
- gtk_render_focus (context, cr, 0, 0, width, height);
-
- return GTK_WIDGET_CLASS (gcal_month_cell_parent_class)->draw (widget, cr);
-}
-
+#endif
/*
* GObject overrides
@@ -409,12 +302,11 @@ gcal_month_cell_class_init (GcalMonthCellClass *klass)
object_class->set_property = gcal_month_cell_set_property;
object_class->get_property = gcal_month_cell_get_property;
- widget_class->button_press_event = gcal_month_cell_button_press_event;
- widget_class->button_release_event = gcal_month_cell_button_release_event;
+#if 0 // TODO: DND
widget_class->drag_motion = gcal_month_cell_drag_motion;
widget_class->drag_drop = gcal_month_cell_drag_drop;
widget_class->drag_leave = gcal_month_cell_drag_leave;
- widget_class->draw = gcal_month_cell_draw;
+#endif
signals[SHOW_OVERFLOW] = g_signal_new ("show-overflow",
GCAL_TYPE_MONTH_CELL,
@@ -442,8 +334,6 @@ gcal_month_cell_class_init (GcalMonthCellClass *klass)
gtk_widget_class_bind_template_child (widget_class, GcalMonthCell, temp_label);
gtk_widget_class_bind_template_child (widget_class, GcalMonthCell, weather_icon);
- gtk_widget_class_bind_template_callback (widget_class, enter_notify_event_cb);
- gtk_widget_class_bind_template_callback (widget_class, leave_notify_event_cb);
gtk_widget_class_bind_template_callback (widget_class, overflow_button_clicked_cb);
gtk_widget_class_set_css_name (widget_class, "monthcell");
@@ -454,12 +344,14 @@ gcal_month_cell_init (GcalMonthCell *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
+#if 0 // TODO: DND
/* Setup the month cell as a drag n' drop destination */
gtk_drag_dest_set (GTK_WIDGET (self),
0,
NULL,
0,
GDK_ACTION_MOVE);
+#endif
}
GtkWidget*
@@ -533,7 +425,7 @@ gcal_month_cell_set_weather (GcalMonthCell *self,
icon_name = gcal_weather_info_get_icon_name (info);
temp_str = gcal_weather_info_get_temperature (info);
- gtk_image_set_from_icon_name (self->weather_icon, icon_name, GTK_ICON_SIZE_SMALL_TOOLBAR);
+ gtk_image_set_from_icon_name (self->weather_icon, icon_name);
gtk_label_set_text (self->temp_label, temp_str);
}
}
@@ -631,8 +523,8 @@ gcal_month_cell_get_content_space (GcalMonthCell *self)
g_return_val_if_fail (GCAL_IS_MONTH_CELL (self), -1);
context = gtk_widget_get_style_context (GTK_WIDGET (self));
- gtk_style_context_get_padding (context, gtk_style_context_get_state (context), &padding);
- gtk_style_context_get_border (context, gtk_style_context_get_state (context), &border);
+ gtk_style_context_get_padding (context, &padding);
+ gtk_style_context_get_border (context, &border);
return gtk_widget_get_allocated_height (GTK_WIDGET (self)) -
gcal_month_cell_get_header_height (self) -
@@ -650,8 +542,8 @@ gcal_month_cell_get_header_height (GcalMonthCell *self)
g_return_val_if_fail (GCAL_IS_MONTH_CELL (self), -1);
context = gtk_widget_get_style_context (GTK_WIDGET (self->header_box));
- gtk_style_context_get_padding (context, gtk_style_context_get_state (context), &padding);
- gtk_style_context_get_border (context, gtk_style_context_get_state (context), &border);
+ gtk_style_context_get_padding (context, &padding);
+ gtk_style_context_get_border (context, &border);
return gtk_widget_get_allocated_height (self->header_box) +
gtk_widget_get_margin_top (self->header_box) +
diff --git a/src/gui/views/gcal-month-cell.h b/src/gui/views/gcal-month-cell.h
index 6b1dcc83..09a6fe51 100644
--- a/src/gui/views/gcal-month-cell.h
+++ b/src/gui/views/gcal-month-cell.h
@@ -22,13 +22,13 @@
#include "gcal-context.h"
#include "gcal-weather-info.h"
-#include <gtk/gtk.h>
+#include <adwaita.h>
G_BEGIN_DECLS
#define GCAL_TYPE_MONTH_CELL (gcal_month_cell_get_type())
-G_DECLARE_FINAL_TYPE (GcalMonthCell, gcal_month_cell, GCAL, MONTH_CELL, GtkEventBox)
+G_DECLARE_FINAL_TYPE (GcalMonthCell, gcal_month_cell, GCAL, MONTH_CELL, AdwBin)
GtkWidget* gcal_month_cell_new (void);
diff --git a/src/gui/views/gcal-month-cell.ui b/src/gui/views/gcal-month-cell.ui
index 89904a41..46291a2c 100644
--- a/src/gui/views/gcal-month-cell.ui
+++ b/src/gui/views/gcal-month-cell.ui
@@ -1,28 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <template class="GcalMonthCell" parent="GtkEventBox">
- <property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="expand">True</property>
- <signal name="enter-notify-event" handler="enter_notify_event_cb" object="GcalMonthCell" swapped="no" />
- <signal name="leave-notify-event" handler="leave_notify_event_cb" object="GcalMonthCell" swapped="no" />
+ <template class="GcalMonthCell" parent="AdwBin">
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="focusable">True</property>
<child>
<object class="GtkOverlay" id="overlay">
- <property name="visible">True</property>
- <property name="can-focus">True</property>
<child type="overlay">
<object class="GtkBox" id="header_box">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
<property name="valign">start</property>
<property name="spacing">6</property>
- <property name="margin">6</property>
- <property name="margin-bottom">0</property>
+ <property name="margin-top">6</property>
+ <property name="margin-start">6</property>
+ <property name="margin-end">6</property>
<child>
<object class="GtkLabel" id="day_label">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
<style>
<class name="day-label" />
</style>
@@ -32,8 +25,6 @@
<!-- Weather forecast -->
<child>
<object class="GtkImage" id="weather_icon">
- <property name="can-focus">False</property>
- <property name="visible">True</property>
<property name="pixel_size">16</property>
<property name="margin-start">12</property>
<style>
@@ -44,8 +35,6 @@
<child>
<object class="GtkLabel" id="temp_label">
- <property name="visible">True</property>
- <property name="can-focus">True</property>
<style>
<class name="dim-label" />
<class name="temp-label" />
@@ -55,38 +44,18 @@
</object>
</child>
- <!-- HACK: in Gtk3, insensitive widgets receive no events. We "fix" this
- limitation by adding an always-sensitive GtkEventBox above the event,
- and when the event is insensitive, the event box's event window is
- above it -->
<child type="overlay">
- <object class="GtkEventBox" id="button_event_box">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <property name="above-child" bind-source="overflow_button" bind-property="sensitive"
bind-flags="default|invert-boolean|sync-create" />
- <signal name="enter-notify-event" handler="enter_notify_event_cb" object="GcalMonthCell"
swapped="no" />
- <signal name="leave-notify-event" handler="leave_notify_event_cb" object="GcalMonthCell"
swapped="no" />
+ <object class="GtkButton" id="overflow_button">
+ <property name="valign">end</property>
+ <signal name="clicked" handler="overflow_button_clicked_cb" object="GcalMonthCell" swapped="no"
/>
+ <style>
+ <class name="flat" />
+ </style>
<child>
- <object class="GtkButton" id="overflow_button">
- <property name="visible">True</property>
- <property name="can-focus">True</property>
- <property name="valign">end</property>
- <property name="receives-default">True</property>
- <signal name="clicked" handler="overflow_button_clicked_cb" object="GcalMonthCell"
swapped="no" />
- <style>
- <class name="flat" />
- </style>
+ <object class="GtkBox">
<child>
- <object class="GtkBox">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <child>
- <object class="GtkLabel" id="overflow_label">
- <property name="visible">True</property>
- <property name="can-focus">False</property>
- <property name="ellipsize">end</property>
- </object>
- </child>
+ <object class="GtkLabel" id="overflow_label">
+ <property name="ellipsize">end</property>
</object>
</child>
</object>
diff --git a/src/gui/views/gcal-month-popover.c b/src/gui/views/gcal-month-popover.c
index 05b044f5..42795e45 100644
--- a/src/gui/views/gcal-month-popover.c
+++ b/src/gui/views/gcal-month-popover.c
@@ -24,59 +24,41 @@
#include "gcal-month-popover.h"
#include "gcal-utils.h"
-#include <dazzle.h>
+#include <adwaita.h>
#define RATIO_TO_RELATIVE 1.25
#define MIN_WIDTH 250
struct _GcalMonthPopover
{
- GtkWindow parent;
+ GtkWidget parent;
GtkLabel *day_label;
- GtkWidget *listbox;
- GtkRevealer *revealer;
- GtkWidget *scrolled_window;
-
- GtkWidget *relative_to;
- GtkWindow *transient_for;
+ GtkListBox *listbox;
+ GtkWidget *main_box;
GcalContext *context;
- DzlAnimation *opacity_animation;
- DzlAnimation *position_animation;
- DzlAnimation *size_animation;
-
- gulong delete_event_handler;
- gulong configure_event_handler;
- gulong size_allocate_handler;
-
- /* Positioning flags - range from [0, 1] */
- gdouble x_ratio;
- gdouble y_ratio;
-
GDateTime *date;
+ AdwAnimation *animation;
};
static void event_activated_cb (GcalEventWidget *event_widget,
GcalMonthPopover *self);
-G_DEFINE_TYPE (GcalMonthPopover, gcal_month_popover, GTK_TYPE_WINDOW)
+G_DEFINE_TYPE (GcalMonthPopover, gcal_month_popover, GTK_TYPE_WIDGET)
enum
{
PROP_0,
PROP_CONTEXT,
- PROP_RELATIVE_TO,
- PROP_X,
- PROP_Y,
- N_PROPS
+ N_PROPS,
};
enum
{
EVENT_ACTIVATED,
- LAST_SIGNAL
+ LAST_SIGNAL,
};
static guint signals[LAST_SIGNAL] = { 0, };
@@ -87,227 +69,20 @@ static GParamSpec *properties [N_PROPS] = { NULL, };
* Auxiliary functions
*/
-static void
-adjust_margin (GcalMonthPopover *self,
- GdkRectangle *area)
-{
- GtkStyleContext *style_context;
- GtkBorder margin;
-
- style_context = gtk_widget_get_style_context (gtk_bin_get_child (GTK_BIN (self)));
- gtk_style_context_get_margin (style_context,
- gtk_style_context_get_state (style_context),
- &margin);
-
- area->x -= margin.right;
- area->y -= margin.top;
- area->width += margin.right + margin.left;
- area->height += margin.top + margin.bottom;
-}
-
-static void
-animate_opacity (GcalMonthPopover *self,
- gdouble target)
-{
- DzlAnimation *animation;
-
- if (self->opacity_animation)
- dzl_animation_stop (self->opacity_animation);
-
- /* Animate the opacity */
- animation = dzl_object_animate (self,
- DZL_ANIMATION_EASE_IN_OUT_CUBIC,
- 250,
- gtk_widget_get_frame_clock (GTK_WIDGET (self)),
- "opacity", target,
- NULL);
- dzl_set_weak_pointer (&self->opacity_animation, animation);
-}
-
-static void
-animate_position (GcalMonthPopover *self,
- gdouble x_ratio,
- gdouble y_ratio)
-{
- DzlAnimation *animation;
-
- if (self->position_animation)
- dzl_animation_stop (self->position_animation);
-
- /* Animate the opacity */
- animation = dzl_object_animate (self,
- DZL_ANIMATION_EASE_IN_OUT_CUBIC,
- 200,
- gtk_widget_get_frame_clock (GTK_WIDGET (self)),
- "x", x_ratio,
- "y", y_ratio,
- NULL);
- dzl_set_weak_pointer (&self->position_animation, animation);
-}
-
-static void
-animate_size (GcalMonthPopover *self,
- gint target_width,
- gint target_height)
-{
- DzlAnimation *animation;
-
- if (self->size_animation)
- dzl_animation_stop (self->size_animation);
-
- /* Animate the size */
- animation = dzl_object_animate (self,
- DZL_ANIMATION_EASE_IN_OUT_CUBIC,
- 200,
- gtk_widget_get_frame_clock (GTK_WIDGET (self)),
- "width-request", target_width,
- "height-request", target_height,
- NULL);
- dzl_set_weak_pointer (&self->size_animation, animation);
-}
-
-static void
-update_maximum_height (GcalMonthPopover *self)
-{
- GdkWindow *window;
- gint clip_height;
-
- clip_height = 3000;
- window = gtk_widget_get_window (GTK_WIDGET (self->relative_to));
-
- if (window)
- {
- GdkDisplay *display;
- GdkMonitor *monitor;
-
- display = gtk_widget_get_display (GTK_WIDGET (self));
- monitor = gdk_display_get_monitor_at_window (display, window);
-
- if (monitor)
- {
- GdkRectangle workarea;
-
- gdk_monitor_get_workarea (monitor, &workarea);
-
- clip_height = workarea.height / 2;
- }
- }
-
- g_object_set (self->scrolled_window,
- "max-content-height", clip_height,
- NULL);
-}
-
-static void
-update_position (GcalMonthPopover *self)
-{
- GtkAllocation alloc;
- GtkWidget *toplevel;
- GdkWindow *window;
- gint diff_w;
- gint diff_h;
- gint x;
- gint y;
-
- if (!gtk_widget_get_realized (GTK_WIDGET (self)))
- return;
-
- toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self->relative_to));
- window = gtk_widget_get_window (toplevel);
-
- gtk_widget_get_allocation (GTK_WIDGET (self->relative_to), &alloc);
-
- alloc.x = 0;
- alloc.y = 0;
-
- gtk_widget_translate_coordinates (GTK_WIDGET (self->relative_to), toplevel, 0, 0, &alloc.x, &alloc.y);
-
- gdk_window_get_position (window, &x, &y);
- alloc.x += x;
- alloc.y += y;
-
- adjust_margin (self, &alloc);
-
- diff_w = (RATIO_TO_RELATIVE - 1) * alloc.width * (1.0 - self->x_ratio);
- diff_h = (RATIO_TO_RELATIVE - 1) * alloc.height * (1.0 - self->y_ratio);
-
- /* Animate the margins, not the (x, y) position) */
- gtk_widget_set_margin_top (gtk_bin_get_child (GTK_BIN (self)), diff_h / 2);
-
- if (gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL)
- gtk_widget_set_margin_end (gtk_bin_get_child (GTK_BIN (self)), diff_w / 2);
- else
- gtk_widget_set_margin_start (gtk_bin_get_child (GTK_BIN (self)), diff_w / 2);
-}
-
-static void
-reposition_popover (GcalMonthPopover *self,
- gboolean animate)
-{
- GtkAllocation alloc;
- GtkWidget *toplevel;
- GdkWindow *window;
- gint diff_w;
- gint diff_h;
- gint x;
- gint y;
-
- if (!gtk_widget_get_realized (GTK_WIDGET (self)))
- return;
-
- toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self->relative_to));
- window = gtk_widget_get_window (toplevel);
-
- gtk_widget_get_allocation (GTK_WIDGET (self->relative_to), &alloc);
-
- alloc.x = 0;
- alloc.y = 0;
-
- gtk_widget_translate_coordinates (GTK_WIDGET (self->relative_to), toplevel, 0, 0, &alloc.x, &alloc.y);
-
- gdk_window_get_position (window, &x, &y);
- alloc.x += x;
- alloc.y += y;
-
- adjust_margin (self, &alloc);
- update_maximum_height (self);
-
- diff_w = (RATIO_TO_RELATIVE - 1) * alloc.width;
- diff_h = (RATIO_TO_RELATIVE - 1) * alloc.height;
-
- gtk_window_move (GTK_WINDOW (self), alloc.x - diff_w / 2, alloc.y - diff_h / 2);
-
- alloc.width = MAX (MIN_WIDTH, alloc.width);
-
- if (animate)
- {
- gtk_widget_set_size_request (GTK_WIDGET (self), alloc.width, alloc.height);
-
- animate_position (self, 1.0, 1.0);
- animate_size (self, alloc.width * RATIO_TO_RELATIVE, alloc.height * RATIO_TO_RELATIVE);
- }
- else
- {
- gtk_widget_set_size_request (GTK_WIDGET (self), alloc.width * RATIO_TO_RELATIVE, alloc.height *
RATIO_TO_RELATIVE);
- }
-}
-
static void
update_event_list (GcalMonthPopover *self)
{
g_autoptr (GDateTime) start_dt = NULL;
g_autoptr (GDateTime) end_dt = NULL;
g_autoptr (GPtrArray) events = NULL;
+ GtkWidget *child;
guint i;
- gtk_container_foreach (GTK_CONTAINER (self->listbox), (GtkCallback) gtk_widget_destroy, NULL);
+ while ((child = gtk_widget_get_first_child (GTK_WIDGET (self->listbox))) != NULL)
+ gtk_list_box_remove (self->listbox, child);
- if (!gtk_widget_get_realized (GTK_WIDGET (self)) ||
- !gtk_widget_get_visible (GTK_WIDGET (self)) ||
- !self->date)
- {
- return;
- }
+ if (!self->date)
+ return;
start_dt = g_date_time_new_local (g_date_time_get_year (self->date),
g_date_time_get_month (self->date),
@@ -324,6 +99,7 @@ update_event_list (GcalMonthPopover *self)
g_autoptr (GTimeZone) tz = NULL;
GtkWidget *event_widget;
GcalEvent *event;
+ GtkWidget *row;
event = g_ptr_array_index (events, i);
@@ -344,7 +120,11 @@ update_event_list (GcalMonthPopover *self)
gcal_event_widget_set_date_start (GCAL_EVENT_WIDGET (event_widget), event_start);
gcal_event_widget_set_date_end (GCAL_EVENT_WIDGET (event_widget), event_end);
- gtk_container_add (GTK_CONTAINER (self->listbox), event_widget);
+ row = gtk_list_box_row_new ();
+ gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), event_widget);
+ gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), FALSE);
+
+ gtk_list_box_append (self->listbox, row);
g_signal_connect_object (event_widget,
"activate",
@@ -352,8 +132,40 @@ update_event_list (GcalMonthPopover *self)
self,
0);
}
+}
+
+static inline gdouble
+lerp (gdouble from,
+ gdouble to,
+ gdouble progress)
+{
+ return from * (1.0 - progress) + to * progress;
+}
+
+static GskTransform*
+create_transform (GcalMonthPopover *self,
+ gint width,
+ gint height)
+{
+ graphene_point_t offset;
+ GskTransform *transform;
+ gdouble progress;
+ gdouble scale;
+
+ progress = adw_animation_get_value (self->animation);
+
+ transform = gsk_transform_new ();
+
+ graphene_point_init (&offset, width / 2.0, height / 2.0);
+ transform = gsk_transform_translate (transform, &offset);
- gtk_widget_show_all (self->listbox);
+ scale = lerp (0.75, 1.0, progress);
+ transform = gsk_transform_scale (transform, scale, scale);
+
+ graphene_point_init (&offset, -width / 2.0, -height / 2.0);
+ transform = gsk_transform_translate (transform, &offset);
+
+ return g_steal_pointer (&transform);
}
@@ -369,8 +181,8 @@ sort_func (GtkListBoxRow *a,
GcalEventWidget *event_a;
GcalEventWidget *event_b;
- event_a = GCAL_EVENT_WIDGET (gtk_bin_get_child (GTK_BIN (a)));
- event_b = GCAL_EVENT_WIDGET (gtk_bin_get_child (GTK_BIN (b)));
+ event_a = GCAL_EVENT_WIDGET (gtk_list_box_row_get_child (a));
+ event_b = GCAL_EVENT_WIDGET (gtk_list_box_row_get_child (b));
return gcal_event_widget_sort_events (event_a, event_b);
}
@@ -380,6 +192,24 @@ sort_func (GtkListBoxRow *a,
* Callbacks
*/
+static void
+animation_cb (gdouble value,
+ gpointer user_data)
+{
+ GcalMonthPopover *self = GCAL_MONTH_POPOVER (user_data);
+
+ gtk_widget_set_opacity (GTK_WIDGET (self->main_box), value);
+ gtk_widget_queue_allocate (GTK_WIDGET (self));
+}
+
+static void
+on_animation_done_cb (AdwAnimation *animation,
+ GcalMonthPopover *self)
+{
+ if (adw_animation_get_value (animation) == 0.0)
+ gtk_widget_hide (GTK_WIDGET (self));
+}
+
static void
event_activated_cb (GcalEventWidget *event_widget,
GcalMonthPopover *self)
@@ -397,197 +227,80 @@ static void
new_event_button_clicked_cb (GtkWidget *button,
GcalMonthPopover *self)
{
- GActionMap *map;
- GAction *action;
-
GCAL_ENTRY;
- map = G_ACTION_MAP (g_application_get_default ());
- action = g_action_map_lookup_action (map, "new");
-
gcal_month_popover_popdown (self);
-
- g_action_activate (action, NULL);
+ gtk_widget_activate_action (GTK_WIDGET (self), "win.new-event", NULL);
GCAL_EXIT;
}
-static void
-revealer_notify_child_revealed_cb (GcalMonthPopover *self,
- GParamSpec *pspec,
- GtkRevealer *revealer)
-{
- if (!gtk_revealer_get_reveal_child (self->revealer))
- gtk_widget_hide (GTK_WIDGET (self));
-}
-
-static gboolean
-transient_for_configure_event_cb (GcalMonthPopover *self,
- GdkEvent *event,
- GtkWindow *toplevel)
-{
- gtk_widget_hide (GTK_WIDGET (self));
- return FALSE;
-}
-
-static gboolean
-transient_for_delete_event_cb (GcalMonthPopover *self,
- GdkEvent *event,
- GtkWindow *toplevel)
-{
- gtk_widget_hide (GTK_WIDGET (self));
- return FALSE;
-}
-
-static void
-transient_for_size_allocate_cb (GcalMonthPopover *self,
- GtkAllocation *allocation,
- GtkWindow *toplevel)
-{
- reposition_popover (self, FALSE);
-}
-
/*
* GtkWidget overrides
*/
static void
-gcal_month_popover_destroy (GtkWidget *widget)
-{
- GcalMonthPopover *self = (GcalMonthPopover *)widget;
-
- if (self->transient_for)
- {
- g_signal_handler_disconnect (self->transient_for, self->size_allocate_handler);
- g_signal_handler_disconnect (self->transient_for, self->configure_event_handler);
- g_signal_handler_disconnect (self->transient_for, self->delete_event_handler);
-
- self->size_allocate_handler = 0;
- self->configure_event_handler = 0;
- self->delete_event_handler = 0;
-
- self->transient_for = NULL;
- }
-
- if (self->opacity_animation)
+gcal_month_popover_measure (GtkWidget *widget,
+ GtkOrientation orientation,
+ gint for_size,
+ gint *minimum,
+ gint *natural,
+ gint *minimum_baseline,
+ gint *natural_baseline)
+{
+ GtkWidget *child;
+
+ for (child = gtk_widget_get_first_child (widget);
+ child;
+ child = gtk_widget_get_next_sibling (child))
{
- dzl_animation_stop (self->opacity_animation);
- dzl_clear_weak_pointer (&self->opacity_animation);
+ gint child_min_baseline = -1;
+ gint child_nat_baseline = -1;
+ gint child_min = 0;
+ gint child_nat = 0;
+
+ if (!gtk_widget_should_layout (child))
+ continue;
+
+ gtk_widget_measure (child,
+ orientation,
+ for_size,
+ &child_min,
+ &child_nat,
+ &child_min_baseline,
+ &child_nat_baseline);
+
+ *minimum = MAX (*minimum, child_min);
+ *natural = MAX (*natural, child_nat);
+
+ if (child_min_baseline > -1)
+ *minimum_baseline = MAX (*minimum_baseline, child_min_baseline);
+ if (child_nat_baseline > -1)
+ *natural_baseline = MAX (*natural_baseline, child_nat_baseline);
}
-
- if (self->position_animation)
- {
- dzl_animation_stop (self->position_animation);
- dzl_clear_weak_pointer (&self->position_animation);
- }
-
- if (self->size_animation)
- {
- dzl_animation_stop (self->size_animation);
- dzl_clear_weak_pointer (&self->size_animation);
- }
-
- gcal_month_popover_set_relative_to (self, NULL);
-
- GTK_WIDGET_CLASS (gcal_month_popover_parent_class)->destroy (widget);
}
-
-static gboolean
-gcal_month_popover_focus_out_event (GtkWidget *widget,
- GdkEventFocus *event_focus)
-{
- gcal_month_popover_popdown (GCAL_MONTH_POPOVER (widget));
-
- return GTK_WIDGET_CLASS (gcal_month_popover_parent_class)->focus_out_event (widget, event_focus);
-}
-
static void
-gcal_month_popover_hide (GtkWidget *widget)
+gcal_month_popover_size_allocate (GtkWidget *widget,
+ gint width,
+ gint height,
+ gint baseline)
{
- GcalMonthPopover *self = (GcalMonthPopover *)widget;
-
- if (self->transient_for)
- gtk_window_group_remove_window (gtk_window_get_group (self->transient_for), GTK_WINDOW (self));
+ GcalMonthPopover *self = GCAL_MONTH_POPOVER (widget);
+ g_autoptr (GskTransform) transform = NULL;
+ GtkWidget *child;
- g_signal_handler_disconnect (self->transient_for, self->delete_event_handler);
- g_signal_handler_disconnect (self->transient_for, self->size_allocate_handler);
- g_signal_handler_disconnect (self->transient_for, self->configure_event_handler);
+ transform = create_transform (self, width, height);
- self->delete_event_handler = 0;
- self->size_allocate_handler = 0;
- self->configure_event_handler = 0;
-
- self->transient_for = NULL;
-
- gcal_month_popover_popdown (self);
-
- GTK_WIDGET_CLASS (gcal_month_popover_parent_class)->hide (widget);
-}
-
-static void
-gcal_month_popover_show (GtkWidget *widget)
-{
- GcalMonthPopover *self = (GcalMonthPopover *)widget;
-
- if (self->relative_to)
+ for (child = gtk_widget_get_first_child (widget);
+ child;
+ child = gtk_widget_get_next_sibling (child))
{
- GtkWidget *toplevel;
-
- toplevel = gtk_widget_get_ancestor (GTK_WIDGET (self->relative_to), GTK_TYPE_WINDOW);
-
- if (GTK_IS_WINDOW (toplevel))
- {
- self->transient_for = GTK_WINDOW (toplevel);
-
- gtk_window_group_add_window (gtk_window_get_group (self->transient_for), GTK_WINDOW (self));
-
- self->delete_event_handler =
- g_signal_connect_object (toplevel,
- "delete-event",
- G_CALLBACK (transient_for_delete_event_cb),
- self,
- G_CONNECT_SWAPPED);
-
- self->size_allocate_handler =
- g_signal_connect_object (toplevel,
- "size-allocate",
- G_CALLBACK (transient_for_size_allocate_cb),
- self,
- G_CONNECT_SWAPPED | G_CONNECT_AFTER);
+ if (!gtk_widget_should_layout (child))
+ continue;
- self->configure_event_handler =
- g_signal_connect_object (toplevel,
- "configure-event",
- G_CALLBACK (transient_for_configure_event_cb),
- self,
- G_CONNECT_SWAPPED);
-
- gtk_window_set_transient_for (GTK_WINDOW (self), self->transient_for);
-
- reposition_popover (self, FALSE);
- }
+ gtk_widget_allocate (child, width, height, baseline, gsk_transform_ref (transform));
}
-
- GTK_WIDGET_CLASS (gcal_month_popover_parent_class)->show (widget);
-}
-
-static void
-gcal_month_popover_realize (GtkWidget *widget)
-{
- GcalMonthPopover *self = (GcalMonthPopover *)widget;
- GdkScreen *screen;
- GdkVisual *visual;
-
- screen = gtk_widget_get_screen (widget);
- visual = gdk_screen_get_rgba_visual (screen);
-
- if (visual)
- gtk_widget_set_visual (widget, visual);
-
- GTK_WIDGET_CLASS (gcal_month_popover_parent_class)->realize (widget);
-
- reposition_popover (self, TRUE);
}
@@ -609,18 +322,6 @@ gcal_month_popover_get_property (GObject *object,
g_value_set_object (value, self->context);
break;
- case PROP_RELATIVE_TO:
- g_value_set_object (value, self->relative_to);
- break;
-
- case PROP_X:
- g_value_set_double (value, self->x_ratio);
- break;
-
- case PROP_Y:
- g_value_set_double (value, self->y_ratio);
- break;
-
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
@@ -640,40 +341,33 @@ gcal_month_popover_set_property (GObject *object,
self->context = g_value_dup_object (value);
break;
- case PROP_RELATIVE_TO:
- gcal_month_popover_set_relative_to (self, g_value_get_object (value));
- break;
-
- case PROP_X:
- self->x_ratio = g_value_get_double (value);
- update_position (self);
- break;
-
-
- case PROP_Y:
- self->y_ratio = g_value_get_double (value);
- update_position (self);
- break;
-
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
+static void
+gcal_month_popover_dispose (GObject *object)
+{
+ GcalMonthPopover *self = (GcalMonthPopover *)object;
+
+ g_clear_pointer (&self->main_box, gtk_widget_unparent);
+
+ G_OBJECT_CLASS (gcal_month_popover_parent_class)->dispose (object);
+}
+
static void
gcal_month_popover_class_init (GcalMonthPopoverClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ object_class->dispose = gcal_month_popover_dispose;
object_class->get_property = gcal_month_popover_get_property;
object_class->set_property = gcal_month_popover_set_property;
- widget_class->destroy = gcal_month_popover_destroy;
- widget_class->focus_out_event = gcal_month_popover_focus_out_event;
- widget_class->hide = gcal_month_popover_hide;
- widget_class->show = gcal_month_popover_show;
- widget_class->realize = gcal_month_popover_realize;
+ widget_class->measure = gcal_month_popover_measure;
+ widget_class->size_allocate = gcal_month_popover_size_allocate;
properties [PROP_CONTEXT] = g_param_spec_object ("context",
"Context",
@@ -681,28 +375,6 @@ gcal_month_popover_class_init (GcalMonthPopoverClass *klass)
GCAL_TYPE_CONTEXT,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY |
G_PARAM_STATIC_STRINGS);
- properties [PROP_RELATIVE_TO] = g_param_spec_object ("relative-to",
- "Relative To",
- "The widget to be relative to",
- GTK_TYPE_WIDGET,
- (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY |
G_PARAM_STATIC_STRINGS));
-
- properties [PROP_X] = g_param_spec_double ("x",
- "X Percent position",
- "X Percent position",
- 0.0,
- 1.0,
- 0.0,
- (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY |
G_PARAM_STATIC_STRINGS));
-
- properties [PROP_Y] = g_param_spec_double ("y",
- "Y Percent position",
- "Y Percent position",
- 0.0,
- 1.0,
- 0.0,
- (G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY |
G_PARAM_STATIC_STRINGS));
-
g_object_class_install_properties (object_class, N_PROPS, properties);
signals[EVENT_ACTIVATED] = g_signal_new ("event-activated",
@@ -720,8 +392,7 @@ gcal_month_popover_class_init (GcalMonthPopoverClass *klass)
gtk_widget_class_bind_template_child (widget_class, GcalMonthPopover, day_label);
gtk_widget_class_bind_template_child (widget_class, GcalMonthPopover, listbox);
- gtk_widget_class_bind_template_child (widget_class, GcalMonthPopover, revealer);
- gtk_widget_class_bind_template_child (widget_class, GcalMonthPopover, scrolled_window);
+ gtk_widget_class_bind_template_child (widget_class, GcalMonthPopover, main_box);
gtk_widget_class_set_css_name (widget_class, "monthpopover");
}
@@ -731,133 +402,52 @@ gcal_month_popover_init (GcalMonthPopover *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
- gtk_window_set_type_hint (GTK_WINDOW (self), GDK_WINDOW_TYPE_HINT_COMBO);
- gtk_window_set_decorated (GTK_WINDOW (self), FALSE);
- gtk_window_set_resizable (GTK_WINDOW (self), FALSE);
- gtk_window_set_skip_pager_hint (GTK_WINDOW (self), TRUE);
- gtk_window_set_skip_taskbar_hint (GTK_WINDOW (self), TRUE);
-
- g_signal_connect_object (self->revealer,
- "notify::child-revealed",
- G_CALLBACK (revealer_notify_child_revealed_cb),
- self,
- G_CONNECT_SWAPPED);
-
gtk_list_box_set_sort_func (GTK_LIST_BOX (self->listbox), sort_func, NULL, NULL);
+
+ self->animation = adw_timed_animation_new (GTK_WIDGET (self),
+ 0.0,
+ 1.0,
+ 250,
+ adw_callback_animation_target_new (animation_cb, self, NULL));
+ g_signal_connect (self->animation, "done", G_CALLBACK (on_animation_done_cb), self);
}
GtkWidget*
gcal_month_popover_new (void)
{
- return g_object_new (GCAL_TYPE_MONTH_POPOVER,
- "type", GTK_WINDOW_POPUP,
- NULL);
-}
-
-void
-gcal_month_popover_set_relative_to (GcalMonthPopover *self,
- GtkWidget *relative_to)
-{
- g_return_if_fail (GCAL_MONTH_POPOVER (self));
- g_return_if_fail (!relative_to || GTK_IS_WIDGET (relative_to));
-
- if (self->relative_to == relative_to)
- return;
-
- if (self->relative_to)
- {
- g_signal_handlers_disconnect_by_func (self->relative_to, gtk_widget_destroyed, &self->relative_to);
- self->relative_to = NULL;
- }
-
- if (relative_to)
- {
- self->relative_to = relative_to;
- g_signal_connect (self->relative_to,
- "destroy",
- G_CALLBACK (gtk_widget_destroyed),
- &self->relative_to);
- }
-
- g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_RELATIVE_TO]);
+ return g_object_new (GCAL_TYPE_MONTH_POPOVER, NULL);
}
void
gcal_month_popover_popup (GcalMonthPopover *self)
{
- gint duration = 250;
-
g_return_if_fail (GCAL_IS_MONTH_POPOVER (self));
- if (self->relative_to)
- {
- GtkAllocation alloc;
- GdkDisplay *display;
- GdkMonitor *monitor;
- GdkWindow *window;
- gint min_height;
- gint nat_height;
-
- display = gtk_widget_get_display (GTK_WIDGET (self->relative_to));
- window = gtk_widget_get_window (GTK_WIDGET (self->relative_to));
- monitor = gdk_display_get_monitor_at_window (display, window);
-
- gtk_widget_get_preferred_height (GTK_WIDGET (self), &min_height, &nat_height);
- gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
-
- duration = dzl_animation_calculate_duration (monitor, alloc.height, nat_height);
- }
+ GCAL_ENTRY;
gtk_widget_show (GTK_WIDGET (self));
- gtk_revealer_set_transition_duration (self->revealer, duration);
- gtk_revealer_set_reveal_child (self->revealer, TRUE);
+ adw_timed_animation_set_reverse (ADW_TIMED_ANIMATION (self->animation), FALSE);
+ adw_timed_animation_set_easing (ADW_TIMED_ANIMATION (self->animation), ADW_EASE_OUT_EXPO);
+ adw_animation_play (self->animation);
update_event_list (self);
- gtk_widget_grab_focus (GTK_WIDGET (self));
-
- /* Animations */
- animate_opacity (self, 1.0);
- reposition_popover (self, TRUE);
+ GCAL_EXIT;
}
void
gcal_month_popover_popdown (GcalMonthPopover *self)
{
- GtkAllocation alloc;
- GdkDisplay *display;
- GdkMonitor *monitor;
- GdkWindow *window;
- guint duration;
-
g_return_if_fail (GCAL_IS_MONTH_POPOVER (self));
- if (!gtk_widget_get_realized (GTK_WIDGET (self)))
- return;
-
- display = gtk_widget_get_display (GTK_WIDGET (self->relative_to));
- window = gtk_widget_get_window (GTK_WIDGET (self->relative_to));
- monitor = gdk_display_get_monitor_at_window (display, window);
-
- gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
-
- duration = dzl_animation_calculate_duration (monitor, alloc.height, 0);
-
- gtk_revealer_set_transition_duration (self->revealer, duration);
- gtk_revealer_set_reveal_child (self->revealer, FALSE);
-
- /* Animations */
- gtk_widget_get_allocation (GTK_WIDGET (self->relative_to), &alloc);
- gtk_widget_translate_coordinates (self->relative_to,
- gtk_widget_get_toplevel (self->relative_to), 0, 0, &alloc.x, &alloc.y);
- adjust_margin (self, &alloc);
+ GCAL_ENTRY;
- alloc.width = MAX (MIN_WIDTH, alloc.width);
+ adw_timed_animation_set_reverse (ADW_TIMED_ANIMATION (self->animation), TRUE);
+ adw_timed_animation_set_easing (ADW_TIMED_ANIMATION (self->animation), ADW_EASE_IN_EXPO);
+ adw_animation_play (self->animation);
- animate_opacity (self, 0.0);
- animate_position (self, 0.0, 0.0);
- animate_size (self, alloc.width, alloc.height);
+ GCAL_EXIT;
}
GDateTime*
diff --git a/src/gui/views/gcal-month-popover.h b/src/gui/views/gcal-month-popover.h
index 83c7b45d..1f15de1b 100644
--- a/src/gui/views/gcal-month-popover.h
+++ b/src/gui/views/gcal-month-popover.h
@@ -16,8 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef GCAL_MONTH_POPOVER_H
-#define GCAL_MONTH_POPOVER_H
+#pragma once
#include "gcal-manager.h"
@@ -26,8 +25,7 @@
G_BEGIN_DECLS
#define GCAL_TYPE_MONTH_POPOVER (gcal_month_popover_get_type())
-
-G_DECLARE_FINAL_TYPE (GcalMonthPopover, gcal_month_popover, GCAL, MONTH_POPOVER, GtkWindow)
+G_DECLARE_FINAL_TYPE (GcalMonthPopover, gcal_month_popover, GCAL, MONTH_POPOVER, GtkWidget)
GtkWidget* gcal_month_popover_new (void);
@@ -45,6 +43,3 @@ void gcal_month_popover_set_date (GcalMonthPopov
GDateTime *date);
G_END_DECLS
-
-#endif /* GCAL_MONTH_POPOVER_H */
-
diff --git a/src/gui/views/gcal-month-popover.ui b/src/gui/views/gcal-month-popover.ui
index 3433e05c..a613b678 100644
--- a/src/gui/views/gcal-month-popover.ui
+++ b/src/gui/views/gcal-month-popover.ui
@@ -1,95 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <template class="GcalMonthPopover" parent="GtkWindow">
- <property name="modal">false</property>
- <property name="opacity">0.0</property>
- <property name="destroy-with-parent">true</property>
+ <template class="GcalMonthPopover" parent="GtkWidget">
+ <property name="visible">False</property>
<child>
- <object class="GtkBox">
- <property name="visible">true</property>
+ <object class="GtkBox" id="main_box">
<property name="orientation">vertical</property>
<property name="spacing">12</property>
+ <style>
+ <class name="card" />
+ <class name="view" />
+ </style>
<!-- Day label & close button-->
<child>
<object class="GtkBox">
- <property name="visible">true</property>
<child>
<object class="GtkLabel" id="day_label">
- <property name="visible">true</property>
<property name="label">12</property>
<property name="xalign">0.0</property>
<property name="hexpand">true</property>
- <attributes>
- <attribute name="scale" value="2.25" />
- </attributes>
+ <style>
+ <class name="large-title" />
+ </style>
</object>
</child>
<child>
<object class="GtkButton">
- <property name="visible">true</property>
<property name="halign">end</property>
<property name="valign">start</property>
- <property name="relief">none</property>
+ <property name="icon-name">window-close-symbolic</property>
<signal name="clicked" handler="close_button_clicked_cb" object="GcalMonthPopover"
swapped="yes" />
- <child>
- <object class="GtkImage">
- <property name="visible">true</property>
- <property name="icon-name">window-close-symbolic</property>
- </object>
- </child>
+ <style>
+ <class name="flat" />
+ <class name="circular" />
+ </style>
</object>
</child>
</object>
</child>
- <!-- Event list revealer -->
+ <!-- Event list -->
<child>
- <object class="GtkRevealer" id="revealer">
- <property name="reveal-child">false</property>
- <property name="transition-duration">200</property>
- <property name="transition-type">slide-down</property>
- <property name="visible">true</property>
- <child>
- <object class="GtkBox" id="top_box">
- <property name="orientation">vertical</property>
- <property name="visible">true</property>
- <child>
- <object class="DzlElasticBin" id="elastic">
- <property name="visible">true</property>
- <child>
- <object class="GtkScrolledWindow" id="scrolled_window">
- <property name="hscrollbar-policy">never</property>
- <property name="propagate-natural-height">true</property>
- <property name="visible">true</property>
- <child>
- <object class="GtkListBox" id="listbox">
- <property name="visible">true</property>
- <property name="selection-mode">none</property>
- <style>
- <class name="background" />
- </style>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
+ <object class="GtkListBox" id="listbox">
+ <property name="vexpand">True</property>
+ <property name="selection-mode">none</property>
+ <style>
+ <class name="calendar-list" />
+ </style>
</object>
</child>
<!-- New Event button -->
<child>
<object class="GtkButton" id="new_event_button">
- <property name="visible">true</property>
- <property name="relief">none</property>
<property name="label" translatable="yes">New Event…</property>
<signal name="clicked" handler="new_event_button_clicked_cb" object="GcalMonthPopover"
swapped="no" />
+ <style>
+ <class name="flat" />
+ </style>
</object>
</child>
diff --git a/src/gui/views/gcal-month-view.c b/src/gui/views/gcal-month-view.c
index 18f52cdc..8e7892c9 100644
--- a/src/gui/views/gcal-month-view.c
+++ b/src/gui/views/gcal-month-view.c
@@ -44,21 +44,15 @@ typedef struct
struct _GcalMonthView
{
- GtkContainer parent;
+ GtkWidget parent;
- GcalMonthPopover *overflow_popover;
-
- GdkWindow *event_window;
+ struct {
+ GtkWidget *popover;
+ GtkWidget *relative_to;
+ } overflow;
/* Header widgets */
GtkWidget *header;
- GtkWidget *label_0;
- GtkWidget *label_1;
- GtkWidget *label_2;
- GtkWidget *label_3;
- GtkWidget *label_4;
- GtkWidget *label_5;
- GtkWidget *label_6;
GtkWidget *month_label;
GtkWidget *year_label;
GtkWidget *weekday_label[7];
@@ -67,6 +61,8 @@ struct _GcalMonthView
GtkWidget *grid;
GtkWidget *month_cell[6][7]; /* unowned */
+ GtkEventController *motion_controller;
+
/*
* Hash to keep children widgets (all of them, parent widgets and its parts if there's any),
* uuid as key and a list of all the instances of the event as value. Here, the first widget
@@ -75,13 +71,13 @@ struct _GcalMonthView
GHashTable *children;
/*
- * Hash containig single-cell events, day of the month, on month-view, month of the year on
+ * Hash containing single-cell events, day of the month, on month-view, month of the year on
* year-view as key anda list of the events that belongs to this cell
*/
GHashTable *single_cell_children;
/*
- * A sorted list containig multiday events. This one contains only parents events, to find out
+ * A sorted list containing multiday events. This one contains only parents events, to find out
* its parts @children will be used.
*/
GList *multi_cell_children;
@@ -122,7 +118,9 @@ struct _GcalMonthView
GDateTime *date;
GcalContext *context;
- gboolean pending_event_allocation;
+ gboolean needs_reallocation;
+ gint last_width;
+ gint last_height;
};
@@ -144,13 +142,15 @@ static void on_month_cell_show_overflow_popover_cb (GcalMonthCell
static void gcal_timeline_subscriber_interface_init (GcalTimelineSubscriberInterface *iface);
-G_DEFINE_TYPE_WITH_CODE (GcalMonthView, gcal_month_view, GTK_TYPE_CONTAINER,
+G_DEFINE_TYPE_WITH_CODE (GcalMonthView, gcal_month_view, GTK_TYPE_WIDGET,
G_IMPLEMENT_INTERFACE (GCAL_TYPE_VIEW, gcal_view_interface_init)
G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
gtk_buildable_interface_init)
G_IMPLEMENT_INTERFACE (GCAL_TYPE_TIMELINE_SUBSCRIBER,
gcal_timeline_subscriber_interface_init));
+static GtkBuildableIface *parent_buildable_iface;
+
enum
{
PROP_0,
@@ -167,6 +167,7 @@ enum
static inline void
cancel_selection (GcalMonthView *self)
{
+ gtk_event_controller_set_propagation_phase (self->motion_controller, GTK_PHASE_NONE);
g_clear_pointer (&self->start_mark_cell, g_date_time_unref);
g_clear_pointer (&self->end_mark_cell, g_date_time_unref);
}
@@ -176,7 +177,7 @@ activate_event (GcalMonthView *self,
GcalEventWidget *event_widget)
{
cancel_selection (self);
- gcal_month_popover_popdown (self->overflow_popover);
+ gcal_month_popover_popdown (GCAL_MONTH_POPOVER (self->overflow.popover));
g_signal_emit_by_name (self, "event-activated", event_widget);
}
@@ -186,7 +187,7 @@ setup_child_widget (GcalMonthView *self,
GtkWidget *widget)
{
if (!gtk_widget_get_parent (widget))
- gtk_widget_set_parent (widget, GTK_WIDGET (self));
+ gtk_widget_insert_before (widget, GTK_WIDGET (self), self->overflow.popover);
g_signal_connect_object (widget, "activate", G_CALLBACK (on_event_activated_cb), self, 0);
g_signal_connect_object (widget, "notify::visible", G_CALLBACK (on_event_widget_visibility_changed_cb),
self, 0);
@@ -195,8 +196,8 @@ setup_child_widget (GcalMonthView *self,
static gboolean
emit_create_event (GcalMonthView *self)
{
- GtkAllocation alloc;
GDateTime *end_dt,*start_dt;
+ GtkWidget *month_cell;
gboolean should_clear_end;
gdouble x, y;
gint cell;
@@ -237,11 +238,15 @@ emit_create_event (GcalMonthView *self)
/* Get the corresponding GcalMonthCell */
cell = g_date_time_get_day_of_month (self->end_mark_cell) + self->days_delay - 1;
+ month_cell = self->month_cell[cell / 7][cell % 7];
- gtk_widget_get_allocation (self->month_cell[cell / 7][cell % 7], &alloc);
+ x = gtk_widget_get_allocated_width (month_cell) / 2.0;
+ y = gtk_widget_get_allocated_height (month_cell) / 2.0;
- x = alloc.x + alloc.width / 2.0;
- y = alloc.y + alloc.height / 2.0;
+ gtk_widget_translate_coordinates (month_cell,
+ GTK_WIDGET (self),
+ x, y,
+ &x, &y);
g_signal_emit_by_name (self, "create-event", start_dt, end_dt, x, y);
@@ -257,35 +262,37 @@ get_month_cell_at_position (GcalMonthView *self,
gdouble y,
gint *cell)
{
- gint i;
+ GtkWidget *widget;
+ gint i = -1;
- if (y < gtk_widget_get_allocated_height (self->header))
- return NULL;
+ widget = gtk_widget_pick (GTK_WIDGET (self), x, y, GTK_PICK_INSENSITIVE);
+
+ if (!widget)
+ goto out;
+
+ widget = gtk_widget_get_ancestor (widget, GCAL_TYPE_MONTH_CELL);
+
+ if (!widget)
+ goto out;
for (i = 0; i < 42; i++)
{
- GtkAllocation alloc;
guint row, col;
row = i / 7;
col = i % 7;
- gtk_widget_get_allocation (self->month_cell[row][col], &alloc);
-
- if (x >= alloc.x && x < alloc.x + alloc.width &&
- y >= alloc.y && y < alloc.y + alloc.height)
- {
- if (cell)
- *cell = i;
-
- return self->month_cell[row][col];
- }
+ if (self->month_cell[row][col] == widget)
+ break;
}
+ g_assert (i >= 0 && i < 42);
+
+out:
if (cell)
- *cell = -1;
+ *cell = i;
- return NULL;
+ return widget;
}
static void
@@ -396,8 +403,12 @@ calculate_multiday_event_blocks (GcalMonthView *self,
/* Calculate the minimum event height */
context = gtk_widget_get_style_context (GTK_WIDGET (event_widget));
- gtk_style_context_get_margin (context, gtk_style_context_get_state (context), &margin);
- gtk_widget_get_preferred_height (GTK_WIDGET (event_widget), &minimum_height, NULL);
+ gtk_style_context_get_margin (context, &margin);
+ gtk_widget_measure (GTK_WIDGET (event_widget),
+ GTK_ORIENTATION_VERTICAL,
+ -1,
+ &minimum_height,
+ NULL, NULL, NULL);
minimum_height += margin.top + margin.bottom;
@@ -435,7 +446,6 @@ calculate_multiday_event_blocks (GcalMonthView *self,
event = gcal_event_widget_get_event (GCAL_EVENT_WIDGET (event_widget));
event_widget = gcal_event_widget_clone (GCAL_EVENT_WIDGET (event_widget));
- gtk_widget_show (event_widget);
setup_child_widget (self, event_widget);
aux = g_hash_table_lookup (self->children, gcal_event_get_uid (event));
@@ -465,25 +475,6 @@ calculate_multiday_event_blocks (GcalMonthView *self,
GCAL_RETURN (blocks);
}
-static void
-cleanup_overflow_information (GcalMonthView *self)
-{
- g_autoptr (GList) widgets = NULL;
- GList *aux = NULL;
- GList *l = NULL;
-
- /* Remove every widget' parts, but the master widget */
- widgets = g_hash_table_get_values (self->children);
-
- for (aux = widgets; aux; aux = g_list_next (aux))
- l = g_list_concat (l, g_list_copy (g_list_next (aux->data)));
-
- g_list_free_full (l, (GDestroyNotify) gtk_widget_destroy);
-
- /* Clean overflow information */
- g_hash_table_remove_all (self->overflow_cells);
-}
-
static void
remove_cell_border_and_padding (GtkWidget *cell,
gdouble *x,
@@ -496,8 +487,8 @@ remove_cell_border_and_padding (GtkWidget *cell,
gint header_height;
cell_context = gtk_widget_get_style_context (cell);
- gtk_style_context_get_border (cell_context, gtk_style_context_get_state (cell_context), &cell_border);
- gtk_style_context_get_padding (cell_context, gtk_style_context_get_state (cell_context), &cell_padding);
+ gtk_style_context_get_border (cell_context, &cell_border);
+ gtk_style_context_get_padding (cell_context, &cell_padding);
header_height = gcal_month_cell_get_header_height (GCAL_MONTH_CELL (cell));
@@ -565,7 +556,11 @@ get_real_event_widget_height (GtkWidget *widget)
{
gint min_height;
- gtk_widget_get_preferred_height (widget, &min_height, NULL);
+ gtk_widget_measure (GTK_WIDGET (widget),
+ GTK_ORIENTATION_VERTICAL,
+ -1,
+ &min_height,
+ NULL, NULL, NULL);
min_height += gtk_widget_get_margin_top (widget);
min_height += gtk_widget_get_margin_bottom (widget);
@@ -588,21 +583,23 @@ allocate_multiday_events (GcalMonthView *self,
gdouble *vertical_cell_space,
gdouble *size_left,
gint *events_at_day,
- gint *allocated_events_at_day)
+ gint *allocated_events_at_day,
+ gint baseline)
{
g_autoptr (GcalRange) month_range = NULL;
GtkAllocation event_allocation;
GtkBorder margin;
GList *l;
gboolean is_rtl;
+ gint header_height;
is_rtl = gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL;
month_range = gcal_timeline_subscriber_get_range (GCAL_TIMELINE_SUBSCRIBER (self));
+ header_height = gtk_widget_get_allocated_height (self->header);
for (l = self->multi_cell_children; l; l = g_list_next (l))
{
g_autoptr (GPtrArray) blocks = NULL;
- GtkStyleContext *child_context;
GtkWidget *child_widget;
GcalEvent *event;
gint block_idx;
@@ -613,7 +610,6 @@ allocate_multiday_events (GcalMonthView *self,
continue;
event = gcal_event_widget_get_event (l->data);
- child_context = gtk_widget_get_style_context (l->data);
if (!month_view_contains_event (month_range, event))
continue;
@@ -659,7 +655,6 @@ allocate_multiday_events (GcalMonthView *self,
day = cell - self->days_delay + 1;
child_widget = block->event_widget;
- child_context = gtk_widget_get_style_context (child_widget);
/* No space left, add to the overflow and continue */
if (!block->visible)
@@ -718,13 +713,11 @@ allocate_multiday_events (GcalMonthView *self,
gcal_event_widget_set_date_end (GCAL_EVENT_WIDGET (child_widget), dt_end);
/* Position and allocate the child widget */
- gtk_style_context_get_margin (gtk_widget_get_style_context (child_widget),
- gtk_style_context_get_state (child_context),
- &margin);
+ gtk_style_context_get_margin (gtk_widget_get_style_context (child_widget), &margin);
pos_x = first_cell_allocation.x + margin.left;
- pos_y = first_cell_allocation.y + margin.top;
+ pos_y = first_cell_allocation.y + margin.top + header_height;
width = last_cell_allocation.x + last_cell_allocation.width - first_cell_allocation.x;
remove_cell_border_and_padding (month_cell, &pos_x, &pos_y, &width);
@@ -740,7 +733,7 @@ allocate_multiday_events (GcalMonthView *self,
event_allocation.width = width - (margin.left + margin.right);
event_allocation.height = minimum_height;
- gtk_widget_size_allocate (child_widget, &event_allocation);
+ gtk_widget_size_allocate (child_widget, &event_allocation, baseline);
/* update size_left */
for (j = 0; j < length; j++)
@@ -757,7 +750,8 @@ allocate_single_day_events (GcalMonthView *self,
gdouble *vertical_cell_space,
gdouble *size_left,
gint *events_at_day,
- gint *allocated_events_at_day)
+ gint *allocated_events_at_day,
+ gint baseline)
{
g_autoptr (GcalRange) month_range = NULL;
GHashTableIter iter;
@@ -765,8 +759,10 @@ allocate_single_day_events (GcalMonthView *self,
GtkAllocation cell_allocation;
GtkBorder margin;
gpointer key, value;
+ gint header_height;
month_range = gcal_timeline_subscriber_get_range (GCAL_TIMELINE_SUBSCRIBER (self));
+ header_height = gtk_widget_get_allocated_height (self->header);
g_hash_table_iter_init (&iter, self->single_cell_children);
@@ -809,7 +805,7 @@ allocate_single_day_events (GcalMonthView *self,
child_context = gtk_widget_get_style_context (child_widget);
- gtk_style_context_get_margin (child_context, gtk_style_context_get_state (child_context), &margin);
+ gtk_style_context_get_margin (child_context, &margin);
minimum_height = get_real_event_widget_height (child_widget) + margin.top + margin.bottom;
/* Check for overflow */
@@ -841,7 +837,7 @@ allocate_single_day_events (GcalMonthView *self,
gtk_widget_set_child_visible (child_widget, TRUE);
pos_x = cell_allocation.x + margin.left;
- pos_y = cell_allocation.y + margin.top;
+ pos_y = cell_allocation.y + margin.top + header_height;
width = cell_allocation.width;
remove_cell_border_and_padding (month_cell, &pos_x, &pos_y, &width);
@@ -852,13 +848,67 @@ allocate_single_day_events (GcalMonthView *self,
event_allocation.height = minimum_height;
gtk_widget_set_child_visible (child_widget, TRUE);
- gtk_widget_size_allocate (child_widget, &event_allocation);
+ gtk_widget_size_allocate (child_widget, &event_allocation, baseline);
size_left[cell] -= minimum_height + margin.top + margin.bottom;
}
}
}
+static void
+allocate_overflow_popover (GcalMonthView *self,
+ gint width,
+ gint height,
+ gint baseline)
+{
+ GtkAllocation popover_allocation;
+ GtkAllocation allocation;
+ gint popover_min_width;
+ gint popover_nat_width;
+ gint popover_height;
+ gint popover_width;
+ gint header_height;
+
+ g_assert (self->overflow.relative_to != NULL);
+
+ header_height = gtk_widget_get_allocated_height (self->header);
+
+ gtk_widget_get_allocation (self->overflow.relative_to, &allocation);
+ allocation.y += header_height;
+
+ gtk_widget_measure (GTK_WIDGET (self->overflow.popover),
+ GTK_ORIENTATION_HORIZONTAL,
+ -1,
+ &popover_min_width,
+ &popover_nat_width,
+ NULL, NULL);
+
+ gtk_widget_measure (GTK_WIDGET (self->overflow.popover),
+ GTK_ORIENTATION_VERTICAL,
+ -1,
+ NULL,
+ &popover_height,
+ NULL, NULL);
+
+ popover_width = CLAMP (popover_nat_width, popover_min_width, allocation.width * 1.5);
+ popover_height = CLAMP (popover_height, allocation.height * 1.5, height);
+
+ popover_allocation = (GtkAllocation) {
+ .x = MAX (0, allocation.x - (popover_width - allocation.width) / 2.0),
+ .y = MAX (header_height, allocation.y - (popover_height - allocation.height) / 2.0),
+ .width = popover_width,
+ .height = popover_height,
+ };
+
+ if (popover_allocation.x + popover_allocation.width > width)
+ popover_allocation.x -= (popover_allocation.x + popover_allocation.width - width);
+
+ if (popover_allocation.y + popover_allocation.height > height)
+ popover_allocation.y -= (popover_allocation.y + popover_allocation.height - height);
+
+ gtk_widget_size_allocate (self->overflow.popover, &popover_allocation, baseline);
+}
+
static void
setup_header_widget (GcalMonthView *self,
GtkWidget *widget)
@@ -1080,1046 +1130,926 @@ update_weekday_labels (GcalMonthView *self)
}
}
-
-/*
- * Callbacks
- */
-
-static void
-add_new_event_button_cb (GtkWidget *button,
- gpointer user_data)
+static inline guint
+get_child_cell (GcalMonthView *self,
+ GcalEventWidget *child)
{
- GcalMonthView *self;
- GDateTime *start_date;
- gint day;
-
- self = GCAL_MONTH_VIEW (user_data);
-
- gcal_month_popover_popdown (self->overflow_popover);
+ GcalEvent *event;
+ GDateTime *dt;
+ gint cell;
- day = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (self->overflow_popover), "selected-day"));
- start_date = g_date_time_new_local (g_date_time_get_year (self->date),
- g_date_time_get_month (self->date),
- day, 0, 0, 0);
+ event = gcal_event_widget_get_event (child);
+ dt = gcal_event_get_date_start (event);
- g_signal_emit_by_name (GCAL_VIEW (user_data), "create-event-detailed", start_date, NULL);
+ /* Don't adjust the date when the event is all day */
+ if (gcal_event_get_all_day (event))
+ {
+ cell = g_date_time_get_day_of_month (dt);
+ }
+ else
+ {
+ dt = g_date_time_to_local (dt);
+ cell = g_date_time_get_day_of_month (dt);
- g_date_time_unref (start_date);
-}
+ g_clear_pointer (&dt, g_date_time_unref);
+ }
-static void
-on_event_activated_cb (GcalEventWidget *widget,
- GcalMonthView *self)
-{
- activate_event (self, widget);
+ return cell;
}
static void
-on_event_widget_visibility_changed_cb (GtkWidget *event_widget,
- GParamSpec *pspec,
- GcalMonthView *self)
+add_event_widget (GcalMonthView *self,
+ GtkWidget *widget)
{
- self->pending_event_allocation = TRUE;
- gtk_widget_queue_resize (GTK_WIDGET (self));
-}
+ GcalEvent *event;
+ const gchar *uuid;
+ GList *l = NULL;
-static void
-on_month_cell_show_overflow_popover_cb (GcalMonthCell *cell,
- GtkWidget *button,
- GcalMonthView *self)
-{
- GcalMonthPopover *popover;
+ g_return_if_fail (GCAL_IS_EVENT_WIDGET (widget));
+ g_return_if_fail (gtk_widget_get_parent (widget) == NULL);
- popover = GCAL_MONTH_POPOVER (self->overflow_popover);
+ event = gcal_event_widget_get_event (GCAL_EVENT_WIDGET (widget));
+ uuid = gcal_event_get_uid (event);
- cancel_selection (self);
+ /* inserting in all children hash */
+ if (g_hash_table_lookup (self->children, uuid) != NULL)
+ {
+ g_warning ("Event with uuid: %s already added", uuid);
+ g_object_unref (widget);
+ return;
+ }
- gcal_month_popover_set_relative_to (popover, GTK_WIDGET (cell));
- gcal_month_popover_set_date (popover, gcal_month_cell_get_date (cell));
- gcal_month_popover_popup (popover);
-}
+ l = g_list_append (l, widget);
+ g_hash_table_insert (self->children, g_strdup (uuid), l);
-static void
-on_month_popover_event_activated_cb (GcalMonthPopover *month_popover,
- GcalEventWidget *event_widget,
- GcalMonthView *self)
-{
- activate_event (self, event_widget);
-}
+ if (gcal_event_is_multiday (event))
+ {
+ self->multi_cell_children = g_list_insert_sorted (self->multi_cell_children,
+ widget,
+ (GCompareFunc) gcal_event_widget_sort_events);
+ }
+ else
+ {
+ guint cell_idx;
-static void
-on_weather_service_weather_changed_cb (GcalWeatherService *weather_service,
- GcalMonthView *self)
-{
- update_weather (self, TRUE);
-}
+ cell_idx = get_child_cell (self, GCAL_EVENT_WIDGET (widget));
+ l = g_hash_table_lookup (self->single_cell_children, GINT_TO_POINTER (cell_idx));
+ l = g_list_insert_sorted (l, widget, (GCompareFunc) gcal_event_widget_compare_by_start_date);
-/*
- * GcalView interface
- */
+ if (g_list_length (l) != 1)
+ g_hash_table_steal (self->single_cell_children, GINT_TO_POINTER (cell_idx));
-static GDateTime*
-gcal_month_view_get_date (GcalView *view)
-{
- GcalMonthView *self = GCAL_MONTH_VIEW (view);
+ g_hash_table_insert (self->single_cell_children, GINT_TO_POINTER (cell_idx), l);
+ }
- return self->date;
+ setup_child_widget (self, widget);
}
static void
-gcal_month_view_set_date (GcalView *view,
- GDateTime *date)
+remove_event_widget (GcalMonthView *self,
+ GtkWidget *widget)
{
- g_autofree gchar *new_date_string = NULL;
- GcalMonthView *self;
-
- GCAL_ENTRY;
+ GtkWidget *master_widget;
+ GcalEvent *event;
+ GList *l, *aux;
+ const gchar *uuid;
- self = GCAL_MONTH_VIEW (view);
+ g_return_if_fail (gtk_widget_get_parent (widget) == GTK_WIDGET (self));
- gcal_set_date_time (&self->date, date);
+ if (!GCAL_IS_EVENT_WIDGET (widget))
+ goto out;
- self->days_delay = (time_day_of_week (1, g_date_time_get_month (self->date) - 1, g_date_time_get_year
(self->date)) - self->first_weekday + 7) % 7;
- self->keyboard_cell = self->days_delay + (g_date_time_get_day_of_month (self->date) - 1);
+ event = gcal_event_widget_get_event (GCAL_EVENT_WIDGET (widget));
+ uuid = gcal_event_get_uid (event);
- new_date_string = g_date_time_format (date, "%x %X %z");
- GCAL_TRACE_MSG ("new date: %s", new_date_string);
+ l = g_hash_table_lookup (self->children, uuid);
- update_header_labels (self);
- update_month_cells (self);
+ if (l)
+ {
+ gtk_widget_unparent (widget);
- gcal_timeline_subscriber_range_changed (GCAL_TIMELINE_SUBSCRIBER (view));
+ master_widget = (GtkWidget*) l->data;
+ if (widget == master_widget)
+ {
+ if (g_list_find (self->multi_cell_children, widget) != NULL)
+ {
+ self->multi_cell_children = g_list_remove (self->multi_cell_children, widget);
- GCAL_EXIT;
-}
+ aux = g_list_next (l);
+ if (aux != NULL)
+ {
+ l->next = NULL;
+ aux->prev = NULL;
+ g_list_foreach (aux, (GFunc) gtk_widget_unparent, NULL);
+ g_list_free (aux);
+ }
+ }
+ else
+ {
+ GHashTableIter iter;
+ gpointer key, value;
-static void
-gcal_month_view_clear_marks (GcalView *view)
-{
- cancel_selection (GCAL_MONTH_VIEW (view));
- update_month_cells (GCAL_MONTH_VIEW (view));
+ /*
+ * When an event is changed, we can't rely on it's old date
+ * to remove the corresponding widget. Because of that, we have
+ * to iter through all the widgets to see which one matches
+ */
+ g_hash_table_iter_init (&iter, self->single_cell_children);
- gtk_widget_queue_allocate (GTK_WIDGET (view));
-}
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ gboolean should_break;
-static GList*
-gcal_month_view_get_children_by_uuid (GcalView *view,
- GcalRecurrenceModType mod,
- const gchar *uuid)
-{
- GHashTableIter iter;
- GcalMonthView *self;
- GList *children;
- GList *tmp;
+ should_break = FALSE;
- self = GCAL_MONTH_VIEW (view);
- children = NULL;
+ for (aux = value; aux != NULL; aux = aux->next)
+ {
+ if (aux->data == widget)
+ {
+ aux = g_list_remove (g_list_copy (value), widget);
- g_hash_table_iter_init (&iter, self->children);
+ /*
+ * If we removed the event and there's no event left for
+ * the day, remove the key from the table. If there are
+ * events for that day, replace the list.
+ */
+ if (!aux)
+ g_hash_table_remove (self->single_cell_children, key);
+ else
+ g_hash_table_replace (self->single_cell_children, key, aux);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &tmp))
- children = g_list_concat (children, g_list_copy (tmp));
+ should_break = TRUE;
- return filter_event_list_by_uid_and_modtype (children, mod, uuid);
-}
+ break;
+ }
+ }
-static GDateTime*
-gcal_month_view_get_next_date (GcalView *view)
-{
- GcalMonthView *self = GCAL_MONTH_VIEW (view);
-
- g_assert (self->date != NULL);
- return g_date_time_add_months (self->date, 1);
-}
+ if (should_break)
+ break;
+ }
+ }
+ }
+ l = g_list_remove (g_list_copy (l), widget);
-static GDateTime*
-gcal_month_view_get_previous_date (GcalView *view)
-{
- GcalMonthView *self = GCAL_MONTH_VIEW (view);
+ if (!l)
+ g_hash_table_remove (self->children, uuid);
+ else
+ g_hash_table_replace (self->children, g_strdup (uuid), l);
+ }
- g_assert (self->date != NULL);
- return g_date_time_add_months (self->date, -1);
+out:
+ gtk_widget_queue_resize (GTK_WIDGET (self));
}
static void
-gcal_view_interface_init (GcalViewInterface *iface)
+cleanup_overflow_information (GcalMonthView *self)
{
- iface->get_date = gcal_month_view_get_date;
- iface->set_date = gcal_month_view_set_date;
- iface->clear_marks = gcal_month_view_clear_marks;
- iface->get_children_by_uuid = gcal_month_view_get_children_by_uuid;
- iface->get_next_date = gcal_month_view_get_next_date;
- iface->get_previous_date = gcal_month_view_get_previous_date;
-}
-
+ g_autoptr (GList) widget_lists = NULL;
+ GList *l = NULL;
-/*
- * GtkBuildable interface
- */
+ /* Remove every widget' parts, but the master widget */
+ widget_lists = g_hash_table_get_values (self->children);
-static void
-gcal_month_view_add_child (GtkBuildable *buildable,
- GtkBuilder *builder,
- GObject *child,
- const gchar *type)
-{
- GcalMonthView *self = GCAL_MONTH_VIEW (buildable);
+ for (l = widget_lists; l; l = l->next)
+ {
+ g_autoptr (GList) widgets = g_list_copy (l->data);
+ GList *l2;
- if (type && strcmp (type, "header") == 0)
- setup_header_widget (self, GTK_WIDGET (child));
- else if (type && strcmp (type, "grid") == 0)
- setup_month_grid (self, GTK_WIDGET (child));
- else
- GTK_BUILDER_WARN_INVALID_CHILD_TYPE (buildable, type);
-}
+ for (l2 = widgets->next; l2; l2 = l2->next)
+ remove_event_widget (self, l2->data);
+ }
-static void
-gtk_buildable_interface_init (GtkBuildableIface *iface)
-{
- iface->add_child = gcal_month_view_add_child;
+ /* Clean overflow information */
+ g_hash_table_remove_all (self->overflow_cells);
}
/*
- * GcalTimelineSubscriber iface
+ * Callbacks
*/
-static GcalRange*
-gcal_month_view_get_range (GcalTimelineSubscriber *subscriber)
-{
- g_autoptr (GDateTime) month_start = NULL;
- g_autoptr (GDateTime) month_end = NULL;
- GcalMonthView *self;
-
- self = GCAL_MONTH_VIEW (subscriber);
- month_start = g_date_time_new_local (g_date_time_get_year (self->date),
- g_date_time_get_month (self->date),
- 1, 0, 0, 0);
- month_end = g_date_time_add_months (month_start, 1);
-
- return gcal_range_new (month_start, month_end, GCAL_RANGE_DEFAULT);
-}
-
static void
-gcal_month_view_add_event (GcalTimelineSubscriber *subscriber,
- GcalEvent *event)
+add_new_event_button_cb (GtkWidget *button,
+ gpointer user_data)
{
GcalMonthView *self;
- GcalCalendar *calendar;
- GtkWidget *event_widget;
+ GDateTime *start_date;
+ gint day;
- self = GCAL_MONTH_VIEW (subscriber);
- calendar = gcal_event_get_calendar (event);
+ self = GCAL_MONTH_VIEW (user_data);
- event_widget = gcal_event_widget_new (self->context, event);
- gcal_event_widget_set_read_only (GCAL_EVENT_WIDGET (event_widget), gcal_calendar_is_read_only (calendar));
+ gcal_month_popover_popdown (GCAL_MONTH_POPOVER (self->overflow.popover));
- gtk_widget_show (event_widget);
- gtk_container_add (GTK_CONTAINER (subscriber), event_widget);
+ day = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (self->overflow.popover), "selected-day"));
+ start_date = g_date_time_new_local (g_date_time_get_year (self->date),
+ g_date_time_get_month (self->date),
+ day, 0, 0, 0);
+
+ g_signal_emit_by_name (GCAL_VIEW (user_data), "create-event-detailed", start_date, NULL);
- self->pending_event_allocation = TRUE;
+ g_date_time_unref (start_date);
}
static void
-gcal_month_view_update_event (GcalTimelineSubscriber *subscriber,
- GcalEvent *event)
+on_click_gesture_pressed_cb (GtkGestureClick *click_gesture,
+ gint n_press,
+ gdouble x,
+ gdouble y,
+ GcalMonthView *self)
{
- GcalMonthView *self;
- GtkWidget *new_widget;
- GList *l;
+ gint days, clicked_cell;
GCAL_ENTRY;
- self = GCAL_MONTH_VIEW (subscriber);
+ days = self->days_delay + gcal_date_time_get_days_in_month (self->date);
- l = g_hash_table_lookup (self->children, gcal_event_get_uid (event));
+ get_month_cell_at_position (self, x, y, &clicked_cell);
- if (!l)
+ if (clicked_cell >= self->days_delay && clicked_cell < days)
{
- g_warning ("%s: Widget with uuid: %s not found in view: %s",
- G_STRFUNC,
- gcal_event_get_uid (event),
- gtk_widget_get_name (GTK_WIDGET (subscriber)));
- return;
- }
+ gtk_event_controller_set_propagation_phase (self->motion_controller, GTK_PHASE_BUBBLE);
- /* Destroy the old event widget (split event widgets will be destroyed too) */
- gtk_widget_destroy (l->data);
-
- /* Create and add the new event widget */
- new_widget = gcal_event_widget_new (self->context, event);
- gtk_widget_show (new_widget);
- gtk_container_add (GTK_CONTAINER (subscriber), new_widget);
+ g_clear_pointer (&self->start_mark_cell, g_date_time_unref);
- self->pending_event_allocation = TRUE;
+ self->keyboard_cell = clicked_cell;
+ self->start_mark_cell = g_date_time_new_local (g_date_time_get_year (self->date),
+ g_date_time_get_month (self->date),
+ self->keyboard_cell - self->days_delay + 1,
+ 0, 0, 0);
- GCAL_EXIT;
+ update_month_cells (self);
+ }
}
static void
-gcal_month_view_remove_event (GcalTimelineSubscriber *subscriber,
- GcalEvent *event)
+on_click_gesture_released_cb (GtkGestureClick *click_gesture,
+ gint n_press,
+ gdouble x,
+ gdouble y,
+ GcalMonthView *self)
{
- GcalMonthView *self;
- const gchar *uuid;
- GList *l;
+ gint days, current_day;
GCAL_ENTRY;
- self = GCAL_MONTH_VIEW (subscriber);
- uuid = gcal_event_get_uid (event);
- l = g_hash_table_lookup (self->children, uuid);
-
- if (!l)
- {
- g_warning ("%s: Widget with uuid: %s not found in view: %s",
- G_STRFUNC,
- uuid,
- gtk_widget_get_name (GTK_WIDGET (subscriber)));
- GCAL_RETURN ();
- }
-
- gtk_widget_destroy (l->data);
-
- self->pending_event_allocation = TRUE;
+ days = self->days_delay + gcal_date_time_get_days_in_month (self->date);
- GCAL_EXIT;
-}
+ get_month_cell_at_position (self, x, y, ¤t_day);
-static void
-gcal_timeline_subscriber_interface_init (GcalTimelineSubscriberInterface *iface)
-{
- iface->get_range = gcal_month_view_get_range;
- iface->add_event = gcal_month_view_add_event;
- iface->update_event = gcal_month_view_update_event;
- iface->remove_event = gcal_month_view_remove_event;
-}
+ if (current_day >= self->days_delay && current_day < days)
+ {
+ g_autoptr (GDateTime) new_active_date = NULL;
+ self->keyboard_cell = current_day;
+ new_active_date = g_date_time_new_local (g_date_time_get_year (self->date),
+ g_date_time_get_month (self->date),
+ current_day - self->days_delay + 1,
+ 0, 0, 0);
-/*
- * GtkContainer overrides
- */
+ gcal_set_date_time (&self->end_mark_cell, new_active_date);
+ gcal_set_date_time (&self->date, new_active_date);
-static inline guint
-get_child_cell (GcalMonthView *self,
- GcalEventWidget *child)
-{
- GcalEvent *event;
- GDateTime *dt;
- gint cell;
+ /* First, make sure to show the popover */
+ emit_create_event (self);
- event = gcal_event_widget_get_event (child);
- dt = gcal_event_get_date_start (event);
+ update_month_cells (self);
- /* Don't adjust the date when the event is all day */
- if (gcal_event_get_all_day (event))
- {
- cell = g_date_time_get_day_of_month (dt);
+ /* Then update the active date */
+ g_object_notify (G_OBJECT (self), "active-date");
}
else
{
- dt = g_date_time_to_local (dt);
- cell = g_date_time_get_day_of_month (dt);
-
- g_clear_pointer (&dt, g_date_time_unref);
+ /* If the button is released over an invalid cell, entirely cancel the selection */
+ cancel_selection (self);
+ gtk_widget_queue_resize (GTK_WIDGET (self));
}
- return cell;
+ gtk_event_controller_set_propagation_phase (self->motion_controller, GTK_PHASE_NONE);
}
static void
-gcal_month_view_add (GtkContainer *container,
- GtkWidget *widget)
+on_event_activated_cb (GcalEventWidget *widget,
+ GcalMonthView *self)
{
- GcalMonthView *self;
- GcalEvent *event;
- const gchar *uuid;
- GList *l = NULL;
+ activate_event (self, widget);
+}
- g_return_if_fail (GCAL_IS_EVENT_WIDGET (widget));
- g_return_if_fail (gtk_widget_get_parent (widget) == NULL);
+static void
+on_event_widget_visibility_changed_cb (GtkWidget *event_widget,
+ GParamSpec *pspec,
+ GcalMonthView *self)
+{
+ self->needs_reallocation = TRUE;
+}
- self = GCAL_MONTH_VIEW (container);
- event = gcal_event_widget_get_event (GCAL_EVENT_WIDGET (widget));
- uuid = gcal_event_get_uid (event);
+static gboolean
+on_key_controller_key_pressed_cb (GtkEventControllerKey *key_controller,
+ guint keyval,
+ guint keycode,
+ GdkModifierType state,
+ GcalMonthView *self)
+{
+ g_autoptr (GDateTime) new_date = NULL;
+ gboolean create_event;
+ gboolean selection;
+ gboolean valid_key;
+ gboolean is_ltr;
+ gint days_in_month;
+ gint min, max, diff, month_change, current_day;
+ gint row, col;
- /* inserting in all children hash */
- if (g_hash_table_lookup (self->children, uuid) != NULL)
- {
- g_warning ("Event with uuid: %s already added", uuid);
- gtk_widget_destroy (widget);
- return;
- }
+ GCAL_ENTRY;
- l = g_list_append (l, widget);
- g_hash_table_insert (self->children, g_strdup (uuid), l);
+ selection = state & GDK_SHIFT_MASK;
+ create_event = FALSE;
+ valid_key = FALSE;
+ diff = 0;
+ month_change = 0;
+ days_in_month = gcal_date_time_get_days_in_month (self->date);
+ current_day = self->keyboard_cell - self->days_delay + 1;
+ min = self->days_delay;
+ max = self->days_delay + days_in_month - 1;
+ is_ltr = gtk_widget_get_direction (GTK_WIDGET (self)) != GTK_TEXT_DIR_RTL;
- if (gcal_event_is_multiday (event))
+ /*
+ * If it's starting the selection right now, it should mark the current keyboard
+ * focused cell as the start, and then update the end mark after updating the
+ * focused cell.
+ */
+ if (selection && self->start_mark_cell == NULL)
{
- self->multi_cell_children = g_list_insert_sorted (self->multi_cell_children,
- widget,
- (GCompareFunc) gcal_event_widget_sort_events);
+ self->start_mark_cell = g_date_time_new_local (g_date_time_get_year (self->date),
+ g_date_time_get_month (self->date),
+ current_day,
+ 0, 0, 0);
}
- else
- {
- guint cell_idx;
- cell_idx = get_child_cell (self, GCAL_EVENT_WIDGET (widget));
+ switch (keyval)
+ {
+ case GDK_KEY_Up:
+ valid_key = TRUE;
+ diff = -7;
+ break;
- l = g_hash_table_lookup (self->single_cell_children, GINT_TO_POINTER (cell_idx));
- l = g_list_insert_sorted (l, widget, (GCompareFunc) gcal_event_widget_compare_by_start_date);
+ case GDK_KEY_Down:
+ valid_key = TRUE;
+ diff = 7;
+ break;
- if (g_list_length (l) != 1)
- g_hash_table_steal (self->single_cell_children, GINT_TO_POINTER (cell_idx));
+ case GDK_KEY_Left:
+ valid_key = TRUE;
+ diff = is_ltr ? -1 : 1;
+ break;
- g_hash_table_insert (self->single_cell_children, GINT_TO_POINTER (cell_idx), l);
- }
+ case GDK_KEY_Right:
+ valid_key = TRUE;
+ diff = is_ltr ? 1 : -1;
+ break;
- setup_child_widget (self, widget);
-}
+ case GDK_KEY_Return:
+ /*
+ * If it's not on the selection mode (i.e. shift is not pressed), we should
+ * simulate it by changing the start & end selected cells = keyboard cell.
+ */
+ if (!selection && !self->start_mark_cell && !self->end_mark_cell)
+ {
+ g_autoptr (GDateTime) new_mark = NULL;
-static void
-gcal_month_view_remove (GtkContainer *container,
- GtkWidget *widget)
-{
- GcalMonthView *self;
- GtkWidget *master_widget;
- GcalEvent *event;
- GList *l, *aux;
- const gchar *uuid;
+ new_mark = g_date_time_new_local (g_date_time_get_year (self->date),
+ g_date_time_get_month (self->date),
+ current_day,
+ 0, 0, 0);
+ self->start_mark_cell = g_object_ref (new_mark);
+ self->end_mark_cell = g_object_ref (new_mark);
+ }
- g_return_if_fail (gtk_widget_get_parent (widget) == GTK_WIDGET (container));
+ create_event = TRUE;
+ break;
- if (!GCAL_IS_EVENT_WIDGET (widget))
- goto out;
+ case GDK_KEY_Escape:
+ cancel_selection (self);
+ break;
- self = GCAL_MONTH_VIEW (container);
- event = gcal_event_widget_get_event (GCAL_EVENT_WIDGET (widget));
- uuid = gcal_event_get_uid (event);
+ default:
+ GCAL_RETURN (GDK_EVENT_PROPAGATE);
+ }
- l = g_hash_table_lookup (self->children, uuid);
+ g_message ("Keyboard cell: %d, diff: %d (min: %d, max: %d)", self->keyboard_cell, diff, min, max);
- if (l)
+ if (self->keyboard_cell + diff <= max && self->keyboard_cell + diff >= min)
{
- gtk_widget_unparent (widget);
-
- master_widget = (GtkWidget*) l->data;
- if (widget == master_widget)
- {
- if (g_list_find (self->multi_cell_children, widget) != NULL)
- {
- self->multi_cell_children = g_list_remove (self->multi_cell_children, widget);
-
- aux = g_list_next (l);
- if (aux != NULL)
- {
- l->next = NULL;
- aux->prev = NULL;
- g_list_foreach (aux, (GFunc) gtk_widget_unparent, NULL);
- g_list_free (aux);
- }
- }
- else
- {
- GHashTableIter iter;
- gpointer key, value;
-
- /*
- * When an event is changed, we can't rely on it's old date
- * to remove the corresponding widget. Because of that, we have
- * to iter through all the widgets to see which one matches
- */
- g_hash_table_iter_init (&iter, self->single_cell_children);
+ self->keyboard_cell += diff;
+ }
+ else
+ {
+ g_autoptr (GDateTime) new_month = NULL;
- while (g_hash_table_iter_next (&iter, &key, &value))
- {
- gboolean should_break;
+ month_change = self->keyboard_cell + diff > max ? 1 : -1;
+ new_month = g_date_time_add_months (self->date, month_change);
- should_break = FALSE;
+ self->days_delay = (time_day_of_week (1, g_date_time_get_month (new_month) - 1, g_date_time_get_year
(new_month)) - self->first_weekday + 7) % 7;
- for (aux = value; aux != NULL; aux = aux->next)
- {
- if (aux->data == widget)
- {
- aux = g_list_remove (g_list_copy (value), widget);
+ /*
+ * Set keyboard cell value to the sum or difference of days delay of successive
+ * month or last day of preceding month and overload value depending on
+ * month_change. Overload value is the equal to the deviation of the value
+ * of keboard_cell from the min or max value of the current month depending
+ * on the overload point.
+ */
+ if (month_change == 1)
+ self->keyboard_cell = self->days_delay + self->keyboard_cell + diff - max - 1;
+ else
+ self->keyboard_cell = self->days_delay + gcal_date_time_get_days_in_month (new_month) - min +
self->keyboard_cell + diff;
+ }
- /*
- * If we removed the event and there's no event left for
- * the day, remove the key from the table. If there are
- * events for that day, replace the list.
- */
- if (!aux)
- g_hash_table_remove (self->single_cell_children, key);
- else
- g_hash_table_replace (self->single_cell_children, key, aux);
+ /* Focus the selected month cell */
+ row = self->keyboard_cell / 7;
+ col = self->keyboard_cell % 7;
- should_break = TRUE;
+ gtk_widget_grab_focus (self->month_cell[row][col]);
- break;
- }
- }
+ /* Update the active date */
+ new_date = g_date_time_add_days (self->date, diff);
+ gcal_set_date_time (&self->date, new_date);
- if (should_break)
- break;
- }
- }
- }
+ /*
+ * We can only emit the :create-event signal ~after~ grabbing the focus, otherwise
+ * the popover is instantly hidden.
+ */
+ if (create_event)
+ emit_create_event (self);
- l = g_list_remove (g_list_copy (l), widget);
+ g_object_notify (G_OBJECT (self), "active-date");
- if (!l)
- g_hash_table_remove (self->children, uuid);
- else
- g_hash_table_replace (self->children, g_strdup (uuid), l);
+ if (selection)
+ {
+ self->end_mark_cell = g_date_time_new_local (g_date_time_get_year (self->date),
+ g_date_time_get_month (self->date),
+ current_day,
+ 0, 0, 0);
+ }
+ else if (!selection && valid_key)
+ {
+ /* Cancel selection if SHIFT is not pressed */
+ cancel_selection (self);
}
-out:
- gtk_widget_queue_resize (GTK_WIDGET (container));
+ GCAL_RETURN (GDK_EVENT_STOP);
}
static void
-gcal_month_view_forall (GtkContainer *container,
- gboolean include_internals,
- GtkCallback callback,
- gpointer callback_data)
+on_month_cell_show_overflow_popover_cb (GcalMonthCell *cell,
+ GtkWidget *button,
+ GcalMonthView *self)
{
- GcalMonthView *self;
- GList *l, *l2, *aux = NULL;
-
- self = GCAL_MONTH_VIEW (container);
-
- /* Header */
- if (self->header)
- (*callback) (self->header, callback_data);
+ GcalMonthPopover *popover;
- /* Grid */
- if (self->grid)
- (*callback) (self->grid, callback_data);
+ GCAL_ENTRY;
- /* Event widgets */
- l2 = g_hash_table_get_values (self->children);
+ popover = GCAL_MONTH_POPOVER (self->overflow.popover);
- for (l = l2; l != NULL; l = g_list_next (l))
- aux = g_list_concat (aux, g_list_reverse (g_list_copy (l->data)));
+ self->overflow.relative_to = GTK_WIDGET (cell);
- g_list_free (l2);
+ gcal_month_popover_set_date (popover, gcal_month_cell_get_date (cell));
+ gcal_month_popover_popup (popover);
- l = aux;
- while (aux)
- {
- GtkWidget *widget = (GtkWidget*) aux->data;
- aux = aux->next;
+ gcal_view_clear_marks (GCAL_VIEW (self));
- (*callback) (widget, callback_data);
- }
+ gcal_set_date_time (&self->date, gcal_month_cell_get_date (cell));
+ g_object_notify (G_OBJECT (self), "active-date");
- g_list_free (l);
+ GCAL_EXIT;
}
-
-/*
- * GtkWidget overrides
- */
-
static void
-gcal_month_view_realize (GtkWidget *widget)
+on_month_popover_event_activated_cb (GcalMonthPopover *month_popover,
+ GcalEventWidget *event_widget,
+ GcalMonthView *self)
{
- GcalMonthView *self;
- GdkWindow *parent_window;
- GdkWindowAttr attributes;
- gint attributes_mask;
- GtkAllocation allocation;
-
- self = GCAL_MONTH_VIEW (widget);
- gtk_widget_set_realized (widget, TRUE);
-
- parent_window = gtk_widget_get_parent_window (widget);
- gtk_widget_set_window (widget, g_object_ref (parent_window));
-
- gtk_widget_get_allocation (widget, &allocation);
-
- attributes.window_type = GDK_WINDOW_CHILD;
- attributes.wclass = GDK_INPUT_ONLY;
- attributes.x = allocation.x;
- attributes.y = allocation.y;
- attributes.width = allocation.width;
- attributes.height = allocation.height;
- attributes.event_mask = gtk_widget_get_events (widget);
- attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
- GDK_BUTTON_RELEASE_MASK |
- GDK_BUTTON1_MOTION_MASK |
- GDK_POINTER_MOTION_HINT_MASK |
- GDK_POINTER_MOTION_MASK |
- GDK_ENTER_NOTIFY_MASK |
- GDK_LEAVE_NOTIFY_MASK |
- GDK_SCROLL_MASK |
- GDK_SMOOTH_SCROLL_MASK);
- attributes_mask = GDK_WA_X | GDK_WA_Y;
-
- self->event_window = gdk_window_new (parent_window,
- &attributes,
- attributes_mask);
- gtk_widget_register_window (widget, self->event_window);
+ cancel_selection (self);
+ g_signal_emit_by_name (self, "event-activated", event_widget);
}
static void
-gcal_month_view_unrealize (GtkWidget *widget)
+on_motion_controller_motion_cb (GtkEventControllerMotion *motion_controller,
+ gdouble x,
+ gdouble y,
+ GcalMonthView *self)
{
- GcalMonthView *self = GCAL_MONTH_VIEW (widget);
+ gint days;
+ gint new_end_cell;
+
+ GCAL_ENTRY;
+
+ days = self->days_delay + gcal_date_time_get_days_in_month (self->date);
+
+ get_month_cell_at_position (self, x, y, &new_end_cell);
- if (self->event_window)
+ if (self->start_mark_cell)
{
- gtk_widget_unregister_window (widget, self->event_window);
- gdk_window_destroy (self->event_window);
- self->event_window = NULL;
+ if (new_end_cell < self->days_delay || new_end_cell >= days)
+ return;
+
+ /* Let the keyboard focus track the pointer */
+ self->keyboard_cell = new_end_cell;
+
+ g_clear_pointer (&self->end_mark_cell, g_date_time_unref);
+ self->end_mark_cell = g_date_time_new_local (g_date_time_get_year (self->date),
+ g_date_time_get_month (self->date),
+ new_end_cell - self->days_delay + 1,
+ 0, 0, 0);
+
+ update_month_cells (self);
}
- GTK_WIDGET_CLASS (gcal_month_view_parent_class)->unrealize (widget);
+ GCAL_EXIT;
}
-static void
-gcal_month_view_map (GtkWidget *widget)
+static gboolean
+on_scroll_controller_scrolled_cb (GtkEventControllerScroll *scroll_controller,
+ gdouble dx,
+ gdouble dy,
+ GcalMonthView *self)
{
- GcalMonthView *self = GCAL_MONTH_VIEW (widget);
+ g_autoptr (GDateTime) new_date = NULL;
+ GdkEvent *current_event;
+ gint diff;
+
+ current_event = gtk_event_controller_get_current_event (GTK_EVENT_CONTROLLER (scroll_controller));
+
+ if (!should_change_date_for_scroll (&self->scroll_value, current_event))
+ return GDK_EVENT_PROPAGATE;
- if (self->event_window)
- gdk_window_show (self->event_window);
+ diff = self->scroll_value > 0.0 ? 1.0 : -1.0;
+ new_date = g_date_time_add_months (self->date, diff);
- GTK_WIDGET_CLASS (gcal_month_view_parent_class)->map (widget);
+ gcal_set_date_time (&self->date, new_date);
+ self->scroll_value = 0.0;
+
+ g_object_notify (G_OBJECT (self), "active-date");
+
+ return GDK_EVENT_STOP;
}
+
static void
-gcal_month_view_unmap (GtkWidget *widget)
+on_weather_service_weather_changed_cb (GcalWeatherService *weather_service,
+ GcalMonthView *self)
{
- GcalMonthView *self = GCAL_MONTH_VIEW (widget);
+ update_weather (self, TRUE);
+}
+
+
+/*
+ * GcalView interface
+ */
- if (self->event_window)
- gdk_window_hide (self->event_window);
+static GDateTime*
+gcal_month_view_get_date (GcalView *view)
+{
+ GcalMonthView *self = GCAL_MONTH_VIEW (view);
- GTK_WIDGET_CLASS (gcal_month_view_parent_class)->unmap (widget);
+ return self->date;
}
static void
-gcal_month_view_size_allocate (GtkWidget *widget,
- GtkAllocation *allocation)
+gcal_month_view_set_date (GcalView *view,
+ GDateTime *date)
{
- GtkAllocation child_allocation;
- GtkAllocation old_alloc;
+ g_autofree gchar *new_date_string = NULL;
GcalMonthView *self;
- gint header_height;
- gint grid_height;
- gint i;
+ gboolean month_changed;
GCAL_ENTRY;
- self = GCAL_MONTH_VIEW (widget);
+ self = GCAL_MONTH_VIEW (view);
- /* Allocate the widget */
- gtk_widget_get_allocation (widget, &old_alloc);
- gtk_widget_set_allocation (widget, allocation);
+ month_changed = !self->date ||
+ !date ||
+ g_date_time_get_month (self->date) != g_date_time_get_month (date) ||
+ g_date_time_get_year (self->date) != g_date_time_get_year (date);
- if (gtk_widget_get_realized (widget))
- gdk_window_move_resize (self->event_window, allocation->x, allocation->y, allocation->width,
allocation->height);
+ gcal_set_date_time (&self->date, date);
- /* Header */
- gtk_widget_get_preferred_height (self->header, &header_height, NULL);
+ if (!month_changed)
+ GCAL_RETURN ();
- child_allocation.x = allocation->x;
- child_allocation.y = allocation->y;
- child_allocation.width = allocation->width;
- child_allocation.height = header_height;
+ self->days_delay = (time_day_of_week (1, g_date_time_get_month (self->date) - 1, g_date_time_get_year
(self->date)) - self->first_weekday + 7) % 7;
+ self->keyboard_cell = self->days_delay + (g_date_time_get_day_of_month (self->date) - 1);
- gtk_widget_size_allocate (self->header, &child_allocation);
+ new_date_string = g_date_time_format (date, "%x %X %z");
+ GCAL_TRACE_MSG ("new date: %s", new_date_string);
- /* Grid */
- gtk_widget_get_preferred_height (self->grid, &grid_height, NULL);
+ update_header_labels (self);
+ update_month_cells (self);
- child_allocation.x = allocation->x;
- child_allocation.y = allocation->y + header_height;
- child_allocation.width = allocation->width;
- child_allocation.height = MAX (allocation->height - header_height, grid_height);
+ gcal_timeline_subscriber_range_changed (GCAL_TIMELINE_SUBSCRIBER (view));
- gtk_widget_size_allocate (self->grid, &child_allocation);
+ GCAL_EXIT;
+}
- /*
- * At this point, the internal widgets (grid and header) already received the allocation they
- * asked for, and are happy. Now it comes the tricky part: when GTK is allocating sizes (here),
- * we cannot show or hide or add or remove widgets. Doing that can potentially trigger an infinite
- * allocation cycle, and this function will keep running forever nonstop, the CPU will melt, the
- * fans will scream and users will cry in anger.
- *
- * Thus, to avoid the infinite allocation cycle, we *only* update the event widgets when something
- * actually changed - either a new event widget was added (pending_event_allocation) or the size
- * of the Month view changed.
- *
- * The following code can be read as the pseudo-code:
- *
- * if (something changed)
- * recalculate and recreate event widgets;
- */
- if (self->pending_event_allocation ||
- allocation->width != old_alloc.width ||
- allocation->height != old_alloc.height)
- {
- gdouble vertical_cell_space [42];
- gdouble size_left [42];
- gint allocated_events_at_day [42] = { 0, };
- gint events_at_day [42] = { 0, };
+static void
+gcal_month_view_clear_marks (GcalView *view)
+{
+ cancel_selection (GCAL_MONTH_VIEW (view));
+ update_month_cells (GCAL_MONTH_VIEW (view));
- /* Remove every widget' parts, but the master widget */
- cleanup_overflow_information (self);
+ gtk_widget_queue_allocate (GTK_WIDGET (view));
+}
- /* Event widgets */
- count_events_per_day (self, events_at_day);
+static GList*
+gcal_month_view_get_children_by_uuid (GcalView *view,
+ GcalRecurrenceModType mod,
+ const gchar *uuid)
+{
+ return filter_children_by_uid_and_modtype (GTK_WIDGET (view), mod, uuid);
+}
- for (i = 0; i < 42; i++)
- {
- gint h = gcal_month_cell_get_content_space (GCAL_MONTH_CELL (self->month_cell[i / 7][i % 7]));
+static GDateTime*
+gcal_month_view_get_next_date (GcalView *view)
+{
+ GcalMonthView *self = GCAL_MONTH_VIEW (view);
- vertical_cell_space[i] = h;
- size_left[i] = h;
- }
+ g_assert (self->date != NULL);
+ return g_date_time_add_months (self->date, 1);
+}
- /* Allocate multidays events before single day events, as they have a higher priority */
- allocate_multiday_events (self, vertical_cell_space, size_left, events_at_day,
allocated_events_at_day);
- allocate_single_day_events (self, vertical_cell_space, size_left, events_at_day,
allocated_events_at_day);
- }
- queue_update_month_cells (self);
+static GDateTime*
+gcal_month_view_get_previous_date (GcalView *view)
+{
+ GcalMonthView *self = GCAL_MONTH_VIEW (view);
- self->pending_event_allocation = FALSE;
+ g_assert (self->date != NULL);
+ return g_date_time_add_months (self->date, -1);
+}
- GCAL_EXIT;
+static void
+gcal_view_interface_init (GcalViewInterface *iface)
+{
+ iface->get_date = gcal_month_view_get_date;
+ iface->set_date = gcal_month_view_set_date;
+ iface->clear_marks = gcal_month_view_clear_marks;
+ iface->get_children_by_uuid = gcal_month_view_get_children_by_uuid;
+ iface->get_next_date = gcal_month_view_get_next_date;
+ iface->get_previous_date = gcal_month_view_get_previous_date;
}
-static gboolean
-gcal_month_view_button_press (GtkWidget *widget,
- GdkEventButton *event)
+
+/*
+ * GtkBuildable interface
+ */
+
+static void
+gcal_month_view_add_child (GtkBuildable *buildable,
+ GtkBuilder *builder,
+ GObject *child,
+ const gchar *type)
{
- GcalMonthView *self;
- gdouble x, y;
- gint days, clicked_cell;
+ GcalMonthView *self = GCAL_MONTH_VIEW (buildable);
- GCAL_ENTRY;
+ if (type && strcmp (type, "header") == 0)
+ setup_header_widget (self, GTK_WIDGET (child));
+ else if (type && strcmp (type, "grid") == 0)
+ setup_month_grid (self, GTK_WIDGET (child));
+ else
+ parent_buildable_iface->add_child (buildable, builder, child, type);
+}
- self = GCAL_MONTH_VIEW (widget);
- days = self->days_delay + gcal_date_time_get_days_in_month (self->date);
+static void
+gtk_buildable_interface_init (GtkBuildableIface *iface)
+{
+ parent_buildable_iface = g_type_interface_peek_parent (iface);
- /* The event may have come from a child widget, so make it relative to the month view */
- if (!gcal_translate_child_window_position (widget, event->window, event->x, event->y, &x, &y))
- return GDK_EVENT_PROPAGATE;
+ iface->add_child = gcal_month_view_add_child;
+}
- get_month_cell_at_position (self, x, y, &clicked_cell);
- if (clicked_cell >= self->days_delay && clicked_cell < days)
- {
- g_clear_pointer (&self->start_mark_cell, g_date_time_unref);
+/*
+ * GcalTimelineSubscriber iface
+ */
- self->keyboard_cell = clicked_cell;
- self->start_mark_cell = g_date_time_new_local (g_date_time_get_year (self->date),
- g_date_time_get_month (self->date),
- self->keyboard_cell - self->days_delay + 1,
- 0, 0, 0);
+static GcalRange*
+gcal_month_view_get_range (GcalTimelineSubscriber *subscriber)
+{
+ g_autoptr (GDateTime) month_start = NULL;
+ g_autoptr (GDateTime) month_end = NULL;
+ GcalMonthView *self;
- update_month_cells (self);
- }
+ self = GCAL_MONTH_VIEW (subscriber);
+ month_start = g_date_time_new_local (g_date_time_get_year (self->date),
+ g_date_time_get_month (self->date),
+ 1, 0, 0, 0);
+ month_end = g_date_time_add_months (month_start, 1);
- GCAL_RETURN (GDK_EVENT_PROPAGATE);
+ return gcal_range_new (month_start, month_end, GCAL_RANGE_DEFAULT);
}
-static gboolean
-gcal_month_view_motion_notify_event (GtkWidget *widget,
- GdkEventMotion *event)
+static void
+gcal_month_view_add_event (GcalTimelineSubscriber *subscriber,
+ GcalEvent *event)
{
GcalMonthView *self;
- gdouble x, y;
- gint days;
- gint new_end_cell;
-
- GCAL_ENTRY;
+ GcalCalendar *calendar;
+ GtkWidget *event_widget;
- self = GCAL_MONTH_VIEW (widget);
- days = self->days_delay + gcal_date_time_get_days_in_month (self->date);
+ self = GCAL_MONTH_VIEW (subscriber);
+ calendar = gcal_event_get_calendar (event);
- if (!gcal_translate_child_window_position (widget, event->window, event->x, event->y, &x, &y))
- GCAL_RETURN (GDK_EVENT_PROPAGATE);
+ event_widget = gcal_event_widget_new (self->context, event);
+ gcal_event_widget_set_read_only (GCAL_EVENT_WIDGET (event_widget), gcal_calendar_is_read_only (calendar));
- get_month_cell_at_position (self, x, y, &new_end_cell);
+ add_event_widget (self, event_widget);
- if (self->start_mark_cell)
- {
- if (!(event->state & GDK_BUTTON_PRESS_MASK))
- GCAL_RETURN (GDK_EVENT_STOP);
+ self->needs_reallocation = TRUE;
+}
- if (new_end_cell < self->days_delay || new_end_cell >= days)
- GCAL_RETURN (GDK_EVENT_PROPAGATE);
+static void
+gcal_month_view_update_event (GcalTimelineSubscriber *subscriber,
+ GcalEvent *event)
+{
+ GcalMonthView *self;
+ GtkWidget *new_widget;
+ GList *l;
- /* Let the keyboard focus track the pointer */
- self->keyboard_cell = new_end_cell;
+ GCAL_ENTRY;
- g_clear_pointer (&self->end_mark_cell, g_date_time_unref);
- self->end_mark_cell = g_date_time_new_local (g_date_time_get_year (self->date),
- g_date_time_get_month (self->date),
- new_end_cell - self->days_delay + 1,
- 0, 0, 0);
+ self = GCAL_MONTH_VIEW (subscriber);
- update_month_cells (self);
+ l = g_hash_table_lookup (self->children, gcal_event_get_uid (event));
- GCAL_RETURN (GDK_EVENT_STOP);
- }
- else
+ if (!l)
{
- if (gtk_widget_is_visible (GTK_WIDGET (self->overflow_popover)))
- GCAL_RETURN (GDK_EVENT_PROPAGATE);
+ g_warning ("%s: Widget with uuid: %s not found in view: %s",
+ G_STRFUNC,
+ gcal_event_get_uid (event),
+ gtk_widget_get_name (GTK_WIDGET (subscriber)));
+ return;
}
- GCAL_RETURN (GDK_EVENT_PROPAGATE);
+ /* Destroy the old event widget (split event widgets will be destroyed too) */
+ remove_event_widget (self, l->data);
+
+ /* Create and add the new event widget */
+ new_widget = gcal_event_widget_new (self->context, event);
+ add_event_widget (self, new_widget);
+
+ self->needs_reallocation = TRUE;
+
+ GCAL_EXIT;
}
-static gboolean
-gcal_month_view_button_release (GtkWidget *widget,
- GdkEventButton *event)
+static void
+gcal_month_view_remove_event (GcalTimelineSubscriber *subscriber,
+ GcalEvent *event)
{
GcalMonthView *self;
- gdouble x, y;
- gint days, current_day;
+ const gchar *uuid;
+ GList *l;
GCAL_ENTRY;
- self = GCAL_MONTH_VIEW (widget);
- days = self->days_delay + gcal_date_time_get_days_in_month (self->date);
-
- if (!gcal_translate_child_window_position (widget, event->window, event->x, event->y, &x, &y))
- GCAL_RETURN (GDK_EVENT_PROPAGATE);
-
- get_month_cell_at_position (self, x, y, ¤t_day);
+ self = GCAL_MONTH_VIEW (subscriber);
+ uuid = gcal_event_get_uid (event);
+ l = g_hash_table_lookup (self->children, uuid);
- if (current_day >= self->days_delay && current_day < days)
+ if (!l)
{
- g_autoptr (GDateTime) new_active_date = NULL;
- gboolean valid;
-
- self->keyboard_cell = current_day;
- new_active_date = g_date_time_new_local (g_date_time_get_year (self->date),
- g_date_time_get_month (self->date),
- current_day - self->days_delay + 1,
- 0, 0, 0);
-
- gcal_set_date_time (&self->end_mark_cell, new_active_date);
- gcal_set_date_time (&self->date, new_active_date);
+ g_warning ("%s: Widget with uuid: %s not found in view: %s",
+ G_STRFUNC,
+ uuid,
+ gtk_widget_get_name (GTK_WIDGET (subscriber)));
+ GCAL_RETURN ();
+ }
- /* First, make sure to show the popover */
- valid = emit_create_event (self);
+ remove_event_widget (self, l->data);
- update_month_cells (self);
+ self->needs_reallocation = TRUE;
- /* Then update the active date */
- g_object_notify (G_OBJECT (self), "active-date");
+ GCAL_EXIT;
+}
- GCAL_RETURN (valid);
- }
- else
- {
- /* If the button is released over an invalid cell, entirely cancel the selection */
- cancel_selection (GCAL_MONTH_VIEW (widget));
+static void
+gcal_timeline_subscriber_interface_init (GcalTimelineSubscriberInterface *iface)
+{
+ iface->get_range = gcal_month_view_get_range;
+ iface->add_event = gcal_month_view_add_event;
+ iface->update_event = gcal_month_view_update_event;
+ iface->remove_event = gcal_month_view_remove_event;
+}
- gtk_widget_queue_resize (widget);
- GCAL_RETURN (GDK_EVENT_PROPAGATE);
- }
-}
+/*
+ * GtkWidget overrides
+ */
static void
gcal_month_view_direction_changed (GtkWidget *widget,
- GtkTextDirection previous_direction)
+ GtkTextDirection direction)
{
GcalMonthView *self = GCAL_MONTH_VIEW (widget);
- self->pending_event_allocation = TRUE;
-
- gtk_widget_queue_resize (widget);
+ self->needs_reallocation = TRUE;
}
-static gboolean
-gcal_month_view_key_press (GtkWidget *widget,
- GdkEventKey *event)
+static void
+gcal_month_view_size_allocate (GtkWidget *widget,
+ gint width,
+ gint height,
+ gint baseline)
{
+ GtkAllocation child_allocation;
GcalMonthView *self;
- gboolean create_event;
- gboolean selection;
- gboolean valid_key;
- gboolean is_ltr;
- gint days_in_month;
- gint min, max, diff, month_change, current_day;
- gint row, col;
+ gint header_height;
+ gint grid_height;
+ gint i;
- g_return_val_if_fail (GCAL_IS_MONTH_VIEW (widget), FALSE);
+ GCAL_ENTRY;
self = GCAL_MONTH_VIEW (widget);
- selection = event->state & GDK_SHIFT_MASK;
- create_event = FALSE;
- valid_key = FALSE;
- diff = 0;
- month_change = 0;
- days_in_month = gcal_date_time_get_days_in_month (self->date);
- current_day = self->keyboard_cell - self->days_delay + 1;
- min = self->days_delay;
- max = self->days_delay + days_in_month - 1;
- is_ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL;
- /*
- * If it's starting the selection right now, it should mark the current keyboard
- * focused cell as the start, and then update the end mark after updating the
- * focused cell.
- */
- if (selection && self->start_mark_cell == NULL)
- {
- self->start_mark_cell = g_date_time_new_local (g_date_time_get_year (self->date),
- g_date_time_get_month (self->date),
- current_day,
- 0, 0, 0);
- }
+ /* Header */
+ gtk_widget_measure (GTK_WIDGET (self->header),
+ GTK_ORIENTATION_VERTICAL,
+ -1,
+ &header_height,
+ NULL, NULL, NULL);
+
+ child_allocation.x = 0;
+ child_allocation.y = 0;
+ child_allocation.width = width;
+ child_allocation.height = header_height;
- switch (event->keyval)
- {
- case GDK_KEY_Up:
- valid_key = TRUE;
- diff = -7;
- break;
+ gtk_widget_size_allocate (self->header, &child_allocation, baseline);
- case GDK_KEY_Down:
- valid_key = TRUE;
- diff = 7;
- break;
+ /* Grid */
+ gtk_widget_measure (GTK_WIDGET (self->grid),
+ GTK_ORIENTATION_VERTICAL,
+ -1,
+ &grid_height,
+ NULL, NULL, NULL);
- case GDK_KEY_Left:
- valid_key = TRUE;
- diff = is_ltr ? -1 : 1;
- break;
+ child_allocation.x = 0;
+ child_allocation.y = header_height;
+ child_allocation.width = width;
+ child_allocation.height = MAX (height - header_height, grid_height);
- case GDK_KEY_Right:
- valid_key = TRUE;
- diff = is_ltr ? 1 : -1;
- break;
+ gtk_widget_size_allocate (self->grid, &child_allocation, baseline);
- case GDK_KEY_Return:
- /*
- * If it's not on the selection mode (i.e. shift is not pressed), we should
- * simulate it by changing the start & end selected cells = keyboard cell.
- */
- if (!selection && !self->start_mark_cell && !self->end_mark_cell)
- {
- g_autoptr (GDateTime) new_mark = NULL;
+ if (self->needs_reallocation || self->last_width != width || self->last_height != height)
+ {
+ gdouble vertical_cell_space [42];
+ gdouble size_left [42];
+ gint allocated_events_at_day [42] = { 0, };
+ gint events_at_day [42] = { 0, };
- new_mark = g_date_time_new_local (g_date_time_get_year (self->date),
- g_date_time_get_month (self->date),
- current_day,
- 0, 0, 0);
- self->start_mark_cell = g_object_ref (new_mark);
- self->end_mark_cell = g_object_ref (new_mark);
- }
+ /* Remove every widget' parts, but the master widget */
+ cleanup_overflow_information (self);
- create_event = TRUE;
- break;
+ /* Event widgets */
+ count_events_per_day (self, events_at_day);
- case GDK_KEY_Escape:
- cancel_selection (GCAL_MONTH_VIEW (widget));
- break;
+ for (i = 0; i < 42; i++)
+ {
+ gint h = gcal_month_cell_get_content_space (GCAL_MONTH_CELL (self->month_cell[i / 7][i % 7]));
- default:
- return GDK_EVENT_PROPAGATE;
- }
+ vertical_cell_space[i] = h;
+ size_left[i] = h;
+ }
- if (self->keyboard_cell + diff <= max && self->keyboard_cell + diff >= min)
- {
- self->keyboard_cell += diff;
+ allocate_multiday_events (self, vertical_cell_space, size_left, events_at_day,
allocated_events_at_day, baseline);
+ allocate_single_day_events (self, vertical_cell_space, size_left, events_at_day,
allocated_events_at_day, baseline);
}
else
{
- g_autoptr (GDateTime) new_month = NULL;
-
- month_change = self->keyboard_cell + diff > max ? 1 : -1;
- new_month = g_date_time_add_months (self->date, month_change);
-
- self->days_delay = (time_day_of_week (1, g_date_time_get_month (new_month) - 1, g_date_time_get_year
(new_month)) - self->first_weekday + 7) % 7;
-
- /*
- * Set keyboard cell value to the sum or difference of days delay of successive
- * month or last day of preceeding month and overload value depending on
- * month_change. Overload value is the equal to the deviation of the value
- * of keboard_cell from the min or max value of the current month depending
- * on the overload point.
- */
- if (month_change == 1)
- self->keyboard_cell = self->days_delay + self->keyboard_cell + diff - max - 1;
- else
- self->keyboard_cell = self->days_delay + gcal_date_time_get_days_in_month (new_month) - min +
self->keyboard_cell + diff;
- }
-
- /* Focus the selected month cell */
- row = self->keyboard_cell / 7;
- col = self->keyboard_cell % 7;
-
- gtk_widget_grab_focus (self->month_cell[row][col]);
+ GtkWidget *child;
- current_day = self->keyboard_cell - self->days_delay + 1;
- //self->date->day = current_day;
-
- /*
- * We can only emit the :create-event signal ~after~ grabbing the focus, otherwise
- * the popover is instantly hidden.
- */
- if (create_event)
- emit_create_event (self);
+ for (child = gtk_widget_get_first_child (widget);
+ child;
+ child = gtk_widget_get_next_sibling (child))
+ {
+ GtkAllocation allocation;
- g_object_notify (G_OBJECT (widget), "active-date");
+ if (!GCAL_IS_EVENT_WIDGET (child))
+ continue;
- if (selection)
- {
- self->end_mark_cell = g_date_time_new_local (g_date_time_get_year (self->date),
- g_date_time_get_month (self->date),
- current_day,
- 0, 0, 0);
- }
- else if (!selection && valid_key)
- {
- /* Cancel selection if SHIFT is not pressed */
- cancel_selection (GCAL_MONTH_VIEW (widget));
+ gtk_widget_get_allocation (child, &allocation);
+ gtk_widget_size_allocate (child, &allocation, baseline);
+ }
}
- return GDK_EVENT_STOP;
-}
-
-static gboolean
-gcal_month_view_scroll_event (GtkWidget *widget,
- GdkEventScroll *scroll_event)
-{
- GcalMonthView *self = GCAL_MONTH_VIEW (widget);
-
- /*
- * If we accumulated enough scrolling, change the month. Otherwise, we'd scroll
- * waaay too fast.
- */
- if (should_change_date_for_scroll (&self->scroll_value, scroll_event))
- {
- g_autoptr (GDateTime) new_date = NULL;
- gint diff;
-
- diff = self->scroll_value > 0 ? 1 : -1;
- new_date = g_date_time_add_months (self->date, diff);
-
- gcal_clear_date_time (&self->date);
- self->date = g_steal_pointer (&new_date);
- self->scroll_value = 0;
+ queue_update_month_cells (self);
- gtk_widget_queue_draw (widget);
+ if (gtk_widget_should_layout (self->overflow.popover))
+ allocate_overflow_popover (self, width, height, baseline);
- g_object_notify (G_OBJECT (widget), "active-date");
- }
+ self->last_width = width;
+ self->last_height = height;
+ self->needs_reallocation = FALSE;
- return GDK_EVENT_STOP;
+ GCAL_EXIT;
}
@@ -2200,69 +2130,75 @@ gcal_month_view_finalize (GObject *object)
GcalMonthView *self = GCAL_MONTH_VIEW (object);
gcal_clear_date_time (&self->date);
+ g_clear_object (&self->context);
+ g_clear_handle_id (&self->update_grid_id, g_source_remove);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (gcal_month_view_parent_class)->finalize (object);
+}
+
+static void
+gcal_month_view_dispose (GObject *object)
+{
+ GcalMonthView *self = GCAL_MONTH_VIEW (object);
+ GtkWidget *child;
+
+ GCAL_ENTRY;
+
+ g_clear_pointer (&self->overflow.popover, gtk_widget_unparent);
+ g_clear_pointer (&self->header, gtk_widget_unparent);
+ g_clear_pointer (&self->grid, gtk_widget_unparent);
+
+ while ((child = gtk_widget_get_first_child (GTK_WIDGET (self))) != NULL)
+ remove_event_widget (self, child);
+
g_clear_pointer (&self->children, g_hash_table_destroy);
g_clear_pointer (&self->single_cell_children, g_hash_table_destroy);
g_clear_pointer (&self->overflow_cells, g_hash_table_destroy);
g_clear_pointer (&self->multi_cell_children, g_list_free);
- g_clear_object (&self->context);
-
- if (self->update_grid_id > 0)
- {
- g_source_remove (self->update_grid_id);
- self->update_grid_id = 0;
- }
+ G_OBJECT_CLASS (gcal_month_view_parent_class)->dispose (object);
- /* Chain up to parent's finalize() method. */
- G_OBJECT_CLASS (gcal_month_view_parent_class)->finalize (object);
+ GCAL_EXIT;
}
static void
gcal_month_view_class_init (GcalMonthViewClass *klass)
{
- GObjectClass *object_class;
- GtkWidgetClass *widget_class;
- GtkContainerClass *container_class;
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);;
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
- object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = gcal_month_view_dispose;
+ object_class->finalize = gcal_month_view_finalize;
object_class->set_property = gcal_month_view_set_property;
object_class->get_property = gcal_month_view_get_property;
- object_class->finalize = gcal_month_view_finalize;
- widget_class = GTK_WIDGET_CLASS (klass);
- widget_class->realize = gcal_month_view_realize;
- widget_class->unrealize = gcal_month_view_unrealize;
- widget_class->map = gcal_month_view_map;
- widget_class->unmap = gcal_month_view_unmap;
- widget_class->size_allocate = gcal_month_view_size_allocate;
- widget_class->button_press_event = gcal_month_view_button_press;
- widget_class->motion_notify_event = gcal_month_view_motion_notify_event;
- widget_class->button_release_event = gcal_month_view_button_release;
widget_class->direction_changed = gcal_month_view_direction_changed;
- widget_class->key_press_event = gcal_month_view_key_press;
- widget_class->scroll_event = gcal_month_view_scroll_event;
-
- container_class = GTK_CONTAINER_CLASS (klass);
- container_class->add = gcal_month_view_add;
- container_class->remove = gcal_month_view_remove;
- container_class->forall = gcal_month_view_forall;
+ widget_class->size_allocate = gcal_month_view_size_allocate;
g_object_class_override_property (object_class, PROP_DATE, "active-date");
g_object_class_override_property (object_class, PROP_CONTEXT, "context");
gtk_widget_class_set_template_from_resource (widget_class,
"/org/gnome/calendar/ui/views/gcal-month-view.ui");
- gtk_widget_class_bind_template_child (widget_class, GcalMonthView, label_0);
- gtk_widget_class_bind_template_child (widget_class, GcalMonthView, label_1);
- gtk_widget_class_bind_template_child (widget_class, GcalMonthView, label_2);
- gtk_widget_class_bind_template_child (widget_class, GcalMonthView, label_3);
- gtk_widget_class_bind_template_child (widget_class, GcalMonthView, label_4);
- gtk_widget_class_bind_template_child (widget_class, GcalMonthView, label_5);
- gtk_widget_class_bind_template_child (widget_class, GcalMonthView, label_6);
gtk_widget_class_bind_template_child (widget_class, GcalMonthView, month_label);
+ gtk_widget_class_bind_template_child (widget_class, GcalMonthView, motion_controller);
gtk_widget_class_bind_template_child (widget_class, GcalMonthView, year_label);
+ gtk_widget_class_bind_template_child_full (widget_class, "label_0", FALSE, G_STRUCT_OFFSET (GcalMonthView,
weekday_label[0]));
+ gtk_widget_class_bind_template_child_full (widget_class, "label_1", FALSE, G_STRUCT_OFFSET (GcalMonthView,
weekday_label[1]));
+ gtk_widget_class_bind_template_child_full (widget_class, "label_2", FALSE, G_STRUCT_OFFSET (GcalMonthView,
weekday_label[2]));
+ gtk_widget_class_bind_template_child_full (widget_class, "label_3", FALSE, G_STRUCT_OFFSET (GcalMonthView,
weekday_label[3]));
+ gtk_widget_class_bind_template_child_full (widget_class, "label_4", FALSE, G_STRUCT_OFFSET (GcalMonthView,
weekday_label[4]));
+ gtk_widget_class_bind_template_child_full (widget_class, "label_5", FALSE, G_STRUCT_OFFSET (GcalMonthView,
weekday_label[5]));
+ gtk_widget_class_bind_template_child_full (widget_class, "label_6", FALSE, G_STRUCT_OFFSET (GcalMonthView,
weekday_label[6]));
+
gtk_widget_class_bind_template_callback (widget_class, add_new_event_button_cb);
+ gtk_widget_class_bind_template_callback (widget_class, on_click_gesture_pressed_cb);
+ gtk_widget_class_bind_template_callback (widget_class, on_click_gesture_released_cb);
+ gtk_widget_class_bind_template_callback (widget_class, on_key_controller_key_pressed_cb);
+ gtk_widget_class_bind_template_callback (widget_class, on_motion_controller_motion_cb);
+ gtk_widget_class_bind_template_callback (widget_class, on_scroll_controller_scrolled_cb);
gtk_widget_class_set_css_name (widget_class, "calendar-view");
@@ -2274,36 +2210,30 @@ gcal_month_view_init (GcalMonthView *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
- gtk_widget_set_has_window (GTK_WIDGET (self), FALSE);
-
+ self->last_width = -1;
+ self->last_height = -1;
self->children = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_list_free);
self->single_cell_children = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)
g_list_free);
self->overflow_cells = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)
g_list_free);
- self->pending_event_allocation = FALSE;
/* First weekday */
self->first_weekday = get_first_weekday ();
- /* Weekday header labels */
- self->weekday_label[0] = self->label_0;
- self->weekday_label[1] = self->label_1;
- self->weekday_label[2] = self->label_2;
- self->weekday_label[3] = self->label_3;
- self->weekday_label[4] = self->label_4;
- self->weekday_label[5] = self->label_5;
- self->weekday_label[6] = self->label_6;
-
update_weekday_labels (self);
/* Overflow popover */
- self->overflow_popover = (GcalMonthPopover*) gcal_month_popover_new ();
+ self->overflow.popover = gcal_month_popover_new ();
+ gtk_widget_set_parent (self->overflow.popover, GTK_WIDGET (self));
g_object_bind_property (self,
"context",
- self->overflow_popover,
+ self->overflow.popover,
"context",
G_BINDING_DEFAULT);
- g_signal_connect_object (self->overflow_popover, "event-activated", G_CALLBACK
(on_month_popover_event_activated_cb), self, 0);
+ g_signal_connect_object (self->overflow.popover,
+ "event-activated",
+ G_CALLBACK (on_month_popover_event_activated_cb),
+ self,
+ 0);
}
-
diff --git a/src/gui/views/gcal-month-view.h b/src/gui/views/gcal-month-view.h
index c560b72f..d5d5fb50 100644
--- a/src/gui/views/gcal-month-view.h
+++ b/src/gui/views/gcal-month-view.h
@@ -26,7 +26,7 @@ G_BEGIN_DECLS
#define GCAL_TYPE_MONTH_VIEW (gcal_month_view_get_type ())
-G_DECLARE_FINAL_TYPE (GcalMonthView, gcal_month_view, GCAL, MONTH_VIEW, GtkContainer)
+G_DECLARE_FINAL_TYPE (GcalMonthView, gcal_month_view, GCAL, MONTH_VIEW, GtkWidget)
G_END_DECLS
diff --git a/src/gui/views/gcal-month-view.ui b/src/gui/views/gcal-month-view.ui
index afb6977a..e043f446 100644
--- a/src/gui/views/gcal-month-view.ui
+++ b/src/gui/views/gcal-month-view.ui
@@ -1,20 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <template class="GcalMonthView" parent="GtkContainer">
+ <template class="GcalMonthView" parent="GtkWidget">
+
+ <child>
+ <object class="GtkGestureClick">
+ <property name="button">1</property>
+ <signal name="pressed" handler="on_click_gesture_pressed_cb" object="GcalMonthView" swapped="no" />
+ <signal name="released" handler="on_click_gesture_released_cb" object="GcalMonthView" swapped="no" />
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkEventControllerMotion" id="motion_controller">
+ <property name="propagation-phase">none</property>
+ <signal name="motion" handler="on_motion_controller_motion_cb" object="GcalMonthView" swapped="no" />
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkEventControllerKey">
+ <signal name="key-pressed" handler="on_key_controller_key_pressed_cb" object="GcalMonthView"
swapped="no" />
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkEventControllerScroll">
+ <property name="flags">vertical</property>
+ <signal name="scroll" handler="on_scroll_controller_scrolled_cb" object="GcalMonthView" swapped="no"
/>
+ </object>
+ </child>
<!-- Header -->
<child type="header">
<object class="GtkBox">
- <property name="visible">true</property>
- <property name="can_focus">false</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
- <property name="visible">true</property>
<child>
<object class="GtkLabel" id="month_label">
- <property name="visible">true</property>
- <property name="can_focus">false</property>
<property name="hexpand">true</property>
<property name="xalign">0.0</property>
<style>
@@ -24,8 +47,6 @@
</child>
<child>
<object class="GtkLabel" id="year_label">
- <property name="visible">true</property>
- <property name="can_focus">false</property>
<property name="hexpand">true</property>
<property name="xalign">1.0</property>
<style>
@@ -38,15 +59,12 @@
<child>
<object class="GtkBox">
- <property name="visible">true</property>
<property name="spacing">6</property>
<property name="margin-start">6</property>
<property name="margin-end">6</property>
<property name="margin-bottom">6</property>
<child>
<object class="GtkLabel" id="label_0">
- <property name="visible">true</property>
- <property name="can_focus">false</property>
<property name="hexpand">true</property>
<property name="xalign">0.0</property>
<style>
@@ -57,8 +75,6 @@
<child>
<object class="GtkLabel" id="label_1">
- <property name="visible">true</property>
- <property name="can_focus">false</property>
<property name="hexpand">true</property>
<property name="xalign">0.0</property>
<style>
@@ -69,8 +85,6 @@
<child>
<object class="GtkLabel" id="label_2">
- <property name="visible">true</property>
- <property name="can_focus">false</property>
<property name="hexpand">true</property>
<property name="xalign">0.0</property>
<style>
@@ -81,8 +95,6 @@
<child>
<object class="GtkLabel" id="label_3">
- <property name="visible">true</property>
- <property name="can_focus">false</property>
<property name="hexpand">true</property>
<property name="xalign">0.0</property>
<style>
@@ -93,8 +105,6 @@
<child>
<object class="GtkLabel" id="label_4">
- <property name="visible">true</property>
- <property name="can_focus">false</property>
<property name="hexpand">true</property>
<property name="xalign">0.0</property>
<style>
@@ -105,8 +115,6 @@
<child>
<object class="GtkLabel" id="label_5">
- <property name="visible">true</property>
- <property name="can_focus">false</property>
<property name="hexpand">true</property>
<property name="xalign">0.0</property>
<style>
@@ -117,8 +125,6 @@
<child>
<object class="GtkLabel" id="label_6">
- <property name="visible">true</property>
- <property name="can_focus">false</property>
<property name="hexpand">true</property>
<property name="xalign">0.0</property>
<style>
@@ -135,8 +141,6 @@
<!-- Grid -->
<child type="grid">
<object class="GtkGrid" id="grid">
- <property name="visible">true</property>
- <property name="can-focus">false</property>
<property name="row-homogeneous">true</property>
<property name="column-homogeneous">true</property>
</object>
diff --git a/src/gui/views/gcal-view.c b/src/gui/views/gcal-view.c
index 5defdbab..46e8f087 100644
--- a/src/gui/views/gcal-view.c
+++ b/src/gui/views/gcal-view.c
@@ -40,7 +40,7 @@ gcal_view_default_init (GcalViewInterface *iface)
g_object_interface_install_property (iface,
g_param_spec_boxed ("active-date",
"The active date",
- "The active/selecetd date in the view",
+ "The active/selected date in the view",
G_TYPE_DATE_TIME,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
diff --git a/src/theme/Adwaita.css b/src/theme/Adwaita.css
index fc1872af..456a98a2 100644
--- a/src/theme/Adwaita.css
+++ b/src/theme/Adwaita.css
@@ -16,7 +16,6 @@ label.secondary-label {
}
calendar-view {
- padding: 6px;
font-size: 10pt;
}
@@ -429,7 +428,6 @@ monthcell {
border: solid 1px alpha(@borders, 0.3);
border-width: 1px 0 0 1px;
background: transparent;
- padding: 1px 3px 0 3px;
transition: background-color 200ms;
}
@@ -474,14 +472,10 @@ monthcell button {
* Month popover
*/
-monthpopover { background: transparent; }
-
monthpopover > box {
margin: 4px;
- border: 1px solid @borders;
- box-shadow: 0px 0px 2px @wm_shadow;
padding: 12px;
- background: @theme_bg_color;
+ background-color: @popover_bg_color;
}
.notes-section box > textview {
diff --git a/src/utils/gcal-utils.c b/src/utils/gcal-utils.c
index c15da2e4..449e6255 100644
--- a/src/utils/gcal-utils.c
+++ b/src/utils/gcal-utils.c
@@ -877,7 +877,7 @@ get_alarm_trigger_minutes (GcalEvent *event,
/**
* should_change_date_for_scroll:
* @scroll_value: the current scroll value
- * @scroll_event: the #GdkEventScroll that is being parsed
+ * @scroll_event: the #GdkEvent that is being parsed
*
* Utility function to check if the date should change based
* on the scroll. The date is changed when the user scrolls
@@ -887,12 +887,14 @@ get_alarm_trigger_minutes (GcalEvent *event,
* Returns: %TRUE if the date should change, %FALSE otherwise.
*/
gboolean
-should_change_date_for_scroll (gdouble *scroll_value,
- GdkEventScroll *scroll_event)
+should_change_date_for_scroll (gdouble *scroll_value,
+ GdkEvent *scroll_event)
{
- gdouble delta_y;
+ gdouble dx, dy;
- switch (scroll_event->direction)
+ g_return_val_if_fail (gdk_event_get_event_type (scroll_event) == GDK_SCROLL, FALSE);
+
+ switch (gdk_scroll_event_get_direction (scroll_event))
{
case GDK_SCROLL_DOWN:
*scroll_value = SCROLL_HARDNESS;
@@ -903,8 +905,8 @@ should_change_date_for_scroll (gdouble *scroll_value,
break;
case GDK_SCROLL_SMOOTH:
- gdk_event_get_scroll_deltas ((GdkEvent*) scroll_event, NULL, &delta_y);
- *scroll_value += delta_y;
+ gdk_scroll_event_get_deltas (scroll_event, &dx, &dy);
+ *scroll_value += dy;
break;
/* Ignore horizontal scrolling for now */
diff --git a/src/utils/gcal-utils.h b/src/utils/gcal-utils.h
index bc0eb3b6..cf94f2e7 100644
--- a/src/utils/gcal-utils.h
+++ b/src/utils/gcal-utils.h
@@ -106,7 +106,7 @@ gint get_alarm_trigger_minutes (GcalEvent
ECalComponentAlarm *alarm);
gboolean should_change_date_for_scroll (gdouble *scroll_value,
- GdkEventScroll *scroll_event);
+ GdkEvent *scroll_event);
gboolean is_source_enabled (ESource *source);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]