[gnome-calendar/wip/gbsneto/month-cell-subwindow: 3/4] month-view: introduce new overflow popover window
- From: Georges Basile Stavracas Neto <gbsneto src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-calendar/wip/gbsneto/month-cell-subwindow: 3/4] month-view: introduce new overflow popover window
- Date: Wed, 15 Nov 2017 04:33:48 +0000 (UTC)
commit eff50668d4f20187a28f628c944070870b33eda6
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date: Wed Nov 15 02:03:26 2017 -0200
month-view: introduce new overflow popover window
This ~almost~ implements a perfectly semantic transition (which
is kinda impossible now).
data/calendar.gresource.xml | 1 +
data/meson.build | 1 +
data/theme/gtk-styles.css | 14 +
data/ui/month-popover.ui | 99 +++++
data/ui/month-view.ui | 61 ---
src/meson.build | 2 +
src/views/gcal-month-popover.c | 865 ++++++++++++++++++++++++++++++++++++++++
src/views/gcal-month-popover.h | 50 +++
src/views/gcal-month-view.c | 136 ++-----
9 files changed, 1064 insertions(+), 165 deletions(-)
---
diff --git a/data/calendar.gresource.xml b/data/calendar.gresource.xml
index ccfe72d..1c11d23 100644
--- a/data/calendar.gresource.xml
+++ b/data/calendar.gresource.xml
@@ -8,6 +8,7 @@
<file alias="edit-dialog.ui" compressed="true" preprocess="xml-stripblanks">ui/edit-dialog.ui</file>
<file alias="event-widget.ui" compressed="true" preprocess="xml-stripblanks">ui/event-widget.ui</file>
<file alias="month-cell.ui" compressed="true" preprocess="xml-stripblanks">ui/month-cell.ui</file>
+ <file alias="month-popover.ui" compressed="true" preprocess="xml-stripblanks">ui/month-popover.ui</file>
<file alias="month-view.ui" compressed="true" preprocess="xml-stripblanks">ui/month-view.ui</file>
<file alias="multi-choice.ui" compressed="true" preprocess="xml-stripblanks">ui/multi-choice.ui</file>
<file alias="online-account-row.ui" compressed="true"
preprocess="xml-stripblanks">ui/online-account-row.ui</file>
diff --git a/data/meson.build b/data/meson.build
index baa50cb..f99ff60 100644
--- a/data/meson.build
+++ b/data/meson.build
@@ -55,6 +55,7 @@ resource_data = files(
'ui/help-overlay.ui',
'ui/menus.ui',
'ui/month-cell.ui',
+ 'ui/month-popover.ui',
'ui/month-view.ui',
'ui/multi-choice.ui',
'ui/quick-add-popover.ui',
diff --git a/data/theme/gtk-styles.css b/data/theme/gtk-styles.css
index 475589e..4cecb1e 100644
--- a/data/theme/gtk-styles.css
+++ b/data/theme/gtk-styles.css
@@ -465,3 +465,17 @@ monthcell button {
border-bottom: none;
border-right: none;
}
+
+/*
+ * 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;
+}
diff --git a/data/ui/month-popover.ui b/data/ui/month-popover.ui
new file mode 100644
index 0000000..3fc3116
--- /dev/null
+++ b/data/ui/month-popover.ui
@@ -0,0 +1,99 @@
+<?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>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">true</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+
+ <!-- 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>
+ </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>
+ <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>
+ </object>
+ </child>
+
+ </object>
+ </child>
+
+ <!-- Event list revealer -->
+ <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>
+ </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" />
+ </object>
+ </child>
+
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/data/ui/month-view.ui b/data/ui/month-view.ui
index 7b2aa44..afb6977 100644
--- a/data/ui/month-view.ui
+++ b/data/ui/month-view.ui
@@ -142,65 +142,4 @@
</object>
</child>
</template>
-
- <!-- Overflow popover -->
- <object class="GtkPopover" id="overflow_popover">
- <property name="can-focus">false</property>
- <property name="position">bottom</property>
- <signal name="drag-motion" handler="cancel_dnd_from_overflow_popover" object="GcalMonthView"
swapped="no" />
- <style>
- <class name="events" />
- </style>
-
- <child>
- <object class="GtkBox">
- <property name="visible">true</property>
- <property name="can-focus">false</property>
- <property name="spacing">6</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkLabel" id="popover_title">
- <property name="visible">true</property>
- <property name="can-focus">false</property>
- <property name="margin-top">6</property>
- <property name="margin-start">6</property>
- <property name="margin-end">6</property>
- <property name="xalign">0.0</property>
- <style>
- <class name="sidebar-header" />
- </style>
- </object>
- </child>
-
- <child>
- <object class="GtkScrolledWindow">
- <property name="visible">true</property>
- <property name="can-focus">false</property>
- <property name="hscrollbar-policy">never</property>
- <property name="vscrollbar-policy">automatic</property>
- <property name="max-content-height">400</property>
- <property name="propagate-natural-height">true</property>
- <child>
- <object class="GtkBox" id="events_box">
- <property name="visible">true</property>
- <property name="orientation">vertical</property>
- <property name="margin-start">2</property>
- <property name="margin-end">2</property>
- <property name="spacing">2</property>
- </object>
- </child>
- </object>
- </child>
-
- <child>
- <object class="GtkButton">
- <property name="visible">true</property>
- <property name="hexpand">true</property>
- <property name="label" translatable="yes">Add Event…</property>
- <signal name="clicked" handler="add_new_event_button_cb" object="GcalMonthView" swapped="no" />
- </object>
- </child>
- </object>
- </child>
- </object>
</interface>
diff --git a/src/meson.build b/src/meson.build
index b5a29e2..3b30e80 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -8,6 +8,7 @@ gcal_deps = [
libedataserver_dep,
libevolution_dep,
libecal_dep,
+ libdazzle_dep,
libsoup_dep,
libical_dep,
glib_dep,
@@ -19,6 +20,7 @@ gcal_deps = [
sources = files(
'views/gcal-month-cell.c',
+ 'views/gcal-month-popover.c',
'views/gcal-month-view.c',
'views/gcal-range-tree.c',
'views/gcal-view.c',
diff --git a/src/views/gcal-month-popover.c b/src/views/gcal-month-popover.c
new file mode 100644
index 0000000..cf0e836
--- /dev/null
+++ b/src/views/gcal-month-popover.c
@@ -0,0 +1,865 @@
+/* gcal-month-popover.c
+ *
+ * Copyright © 2017 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "GcalMonthPopover"
+
+#include "gcal-event-widget.h"
+#include "gcal-month-popover.h"
+#include "gcal-utils.h"
+
+#include <dazzle.h>
+
+#define RATIO_TO_RELATIVE 1.25
+
+struct _GcalMonthPopover
+{
+ GtkWindow parent;
+
+ GtkLabel *day_label;
+ GtkWidget *listbox;
+ GtkRevealer *revealer;
+ GtkWidget *scrolled_window;
+
+ GtkWidget *relative_to;
+ GtkWindow *transient_for;
+
+ GcalManager *manager;
+
+ 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;
+};
+
+static void event_activated_cb (GcalEventWidget *event_widget,
+ GcalMonthPopover *self);
+
+G_DEFINE_TYPE (GcalMonthPopover, gcal_month_popover, GTK_TYPE_WINDOW)
+
+enum
+{
+ PROP_0,
+ PROP_MANAGER,
+ PROP_RELATIVE_TO,
+ PROP_X,
+ PROP_Y,
+ N_PROPS
+};
+
+enum
+{
+ EVENT_ACTIVATED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+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 opacity */
+ 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);
+}
+
+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);
+
+ 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_autofree icaltimetype *start = NULL;
+ g_autofree icaltimetype *end = NULL;
+ g_autoptr (GDateTime) start_dt = NULL;
+ g_autoptr (GDateTime) end_dt = NULL;
+ g_autoptr (GList) events = NULL;
+ GList *l;
+
+ gtk_container_foreach (GTK_CONTAINER (self->listbox), (GtkCallback) gtk_widget_destroy, NULL);
+
+ if (!gtk_widget_get_realized (GTK_WIDGET (self)) ||
+ !gtk_widget_get_visible (GTK_WIDGET (self)) ||
+ !self->date)
+ {
+ return;
+ }
+
+ start_dt = g_date_time_new_local (g_date_time_get_year (self->date),
+ g_date_time_get_month (self->date),
+ g_date_time_get_day_of_month (self->date),
+ 0, 0, 0);
+ end_dt = g_date_time_add_days (start_dt, 1);
+
+ start = datetime_to_icaltime (start_dt);
+ end = datetime_to_icaltime (end_dt);
+
+ events = gcal_manager_get_events (self->manager, start, end);
+
+ for (l = events; l; l = l->next)
+ {
+ g_autoptr (GDateTime) event_start = NULL;
+ g_autoptr (GDateTime) event_end = NULL;
+ g_autoptr (GTimeZone) tz = NULL;
+ GtkWidget *event_widget;
+ GcalEvent *event;
+
+ event = l->data;
+
+ if (gcal_event_get_all_day (event))
+ tz = g_time_zone_new_utc ();
+ else
+ tz = g_time_zone_new_local ();
+
+ event_start = g_date_time_new (tz,
+ g_date_time_get_year (start_dt),
+ g_date_time_get_month (start_dt),
+ g_date_time_get_day_of_month (start_dt),
+ 0, 0, 0);
+
+ event_end = g_date_time_add_days (event_start, 1);
+
+ event_widget = gcal_event_widget_new (event);
+ 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);
+
+ g_signal_connect (event_widget,
+ "activate",
+ G_CALLBACK (event_activated_cb),
+ self);
+ }
+
+ gtk_widget_show_all (self->listbox);
+}
+
+
+/*
+ * Callbacks
+ */
+
+static void
+event_activated_cb (GcalEventWidget *event_widget,
+ GcalMonthPopover *self)
+{
+ g_signal_emit (self, signals[EVENT_ACTIVATED], 0, event_widget);
+}
+
+static void
+close_button_clicked_cb (GcalMonthPopover *self)
+{
+ gcal_month_popover_popdown (self);
+}
+
+static void
+new_event_button_clicked_cb (GtkWidget *button,
+ GcalMonthPopover *self)
+{
+ GActionMap *map;
+ GAction *action;
+
+ map = G_ACTION_MAP (g_application_get_default ());
+ action = g_action_map_lookup_action (map, "new");
+
+ g_action_activate (action, NULL);
+
+ //gcal_month_popover_popdown (self);
+}
+
+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)
+ {
+ dzl_animation_stop (self->opacity_animation);
+ dzl_clear_weak_pointer (&self->opacity_animation);
+ }
+
+ 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)
+{
+ GcalMonthPopover *self = (GcalMonthPopover *)widget;
+
+ if (self->transient_for)
+ gtk_window_group_remove_window (gtk_window_get_group (self->transient_for), GTK_WINDOW (self));
+
+ 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);
+
+ 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)
+ {
+ 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);
+
+ 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_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);
+}
+
+
+/*
+ * GObject overrides
+ */
+
+static void
+gcal_month_popover_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GcalMonthPopover *self = GCAL_MONTH_POPOVER (object);
+
+ switch (prop_id)
+ {
+ case PROP_MANAGER:
+ g_value_set_object (value, self->manager);
+ 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);
+ }
+}
+
+static void
+gcal_month_popover_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GcalMonthPopover *self = GCAL_MONTH_POPOVER (object);
+
+ switch (prop_id)
+ {
+ case PROP_MANAGER:
+ self->manager = 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_class_init (GcalMonthPopoverClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ 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;
+
+ properties [PROP_MANAGER] = g_param_spec_object ("manager",
+ "Manager",
+ "Manager",
+ GCAL_TYPE_MANAGER,
+ (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",
+ GCAL_TYPE_MONTH_POPOVER,
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 1,
+ GCAL_TYPE_EVENT_WIDGET);
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/calendar/month-popover.ui");
+
+ gtk_widget_class_bind_template_callback (widget_class, close_button_clicked_cb);
+ gtk_widget_class_bind_template_callback (widget_class, new_event_button_clicked_cb);
+
+ 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_set_css_name (widget_class, "monthpopover");
+}
+
+static void
+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);
+}
+
+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]);
+}
+
+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);
+ }
+
+ gtk_widget_show (GTK_WIDGET (self));
+
+ gtk_revealer_set_transition_duration (self->revealer, duration);
+ gtk_revealer_set_reveal_child (self->revealer, TRUE);
+
+ update_event_list (self);
+
+ gtk_widget_grab_focus (GTK_WIDGET (self));
+
+ /* Animations */
+ animate_opacity (self, 1.0);
+ reposition_popover (self, TRUE);
+}
+
+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);
+
+ animate_opacity (self, 0.0);
+ animate_position (self, 0.0, 0.0);
+ animate_size (self, alloc.width, alloc.height);
+}
+
+GDateTime*
+gcal_month_popover_get_date (GcalMonthPopover *self)
+{
+ g_return_val_if_fail (GCAL_IS_MONTH_POPOVER (self), NULL);
+
+ return self->date;
+}
+
+void
+gcal_month_popover_set_date (GcalMonthPopover *self,
+ GDateTime *date)
+{
+ g_return_if_fail (GCAL_IS_MONTH_POPOVER (self));
+
+ if (date && self->date && datetime_compare_date (self->date, date) == 0)
+ return;
+
+ gcal_clear_datetime (&self->date);
+
+ if (date)
+ {
+ g_autofree gchar *label;
+
+ self->date = g_date_time_ref (date);
+
+ label = g_strdup_printf ("%d", g_date_time_get_day_of_month (date));
+ gtk_label_set_label (self->day_label, label);
+ }
+
+ update_event_list (self);
+}
diff --git a/src/views/gcal-month-popover.h b/src/views/gcal-month-popover.h
new file mode 100644
index 0000000..83c7b45
--- /dev/null
+++ b/src/views/gcal-month-popover.h
@@ -0,0 +1,50 @@
+/* gcal-month-popover.h
+ *
+ * Copyright © 2017 Georges Basile Stavracas Neto <georges stavracas gmail com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GCAL_MONTH_POPOVER_H
+#define GCAL_MONTH_POPOVER_H
+
+#include "gcal-manager.h"
+
+#include <gtk/gtk.h>
+
+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)
+
+GtkWidget* gcal_month_popover_new (void);
+
+
+void gcal_month_popover_set_relative_to (GcalMonthPopover *self,
+ GtkWidget *relative_to);
+
+void gcal_month_popover_popup (GcalMonthPopover *self);
+
+void gcal_month_popover_popdown (GcalMonthPopover *self);
+
+GDateTime* gcal_month_popover_get_date (GcalMonthPopover *self);
+
+void gcal_month_popover_set_date (GcalMonthPopover *self,
+ GDateTime *date);
+
+G_END_DECLS
+
+#endif /* GCAL_MONTH_POPOVER_H */
+
diff --git a/src/views/gcal-month-view.c b/src/views/gcal-month-view.c
index daf246d..7387e4e 100644
--- a/src/views/gcal-month-view.c
+++ b/src/views/gcal-month-view.c
@@ -27,6 +27,7 @@
#include "gcal-debug.h"
#include "gcal-month-cell.h"
+#include "gcal-month-popover.h"
#include "gcal-month-view.h"
#include "gcal-utils.h"
#include "gcal-view.h"
@@ -41,9 +42,7 @@ struct _GcalMonthView
{
GtkContainer parent;
- GtkWidget *events_box;
- GtkWidget *overflow_popover;
- GtkWidget *popover_title;
+ GcalMonthPopover *overflow_popover;
GdkWindow *event_window;
@@ -185,11 +184,12 @@ cancel_selection (GcalMonthView *self)
}
static void
-event_activated (GcalEventWidget *widget,
- GcalMonthView *self)
+event_activated (GcalMonthView *self,
+ GcalEventWidget *widget,
+ gpointer unused)
{
cancel_selection (self);
- gtk_widget_hide (self->overflow_popover);
+ gcal_month_popover_popdown (self->overflow_popover);
g_signal_emit (self, signals[EVENT_ACTIVATED], 0, widget);
}
@@ -201,7 +201,7 @@ setup_child_widget (GcalMonthView *self,
if (!gtk_widget_get_parent (widget))
gtk_widget_set_parent (widget, GTK_WIDGET (self));
- g_signal_connect (widget, "activate", G_CALLBACK (event_activated), self);
+ g_signal_connect_swapped (widget, "activate", G_CALLBACK (event_activated), self);
g_signal_connect_swapped (widget, "hide", G_CALLBACK (gtk_widget_queue_resize), self);
g_signal_connect_swapped (widget, "show", G_CALLBACK (gtk_widget_queue_resize), self);
}
@@ -385,78 +385,15 @@ show_overflow_popover_cb (GcalMonthCell *cell,
GtkWidget *button,
GcalMonthView *self)
{
- g_autofree gchar *label_title = NULL;
- GtkWidget *child_widget;
- GDateTime *dt;
- GList *l;
- gint day, cell_nr;
-
- dt = gcal_month_cell_get_date (cell);
- day = g_date_time_get_day_of_month (dt);
- cell_nr = day + self->days_delay - 1;
-
- if (!g_hash_table_contains (self->overflow_cells, GINT_TO_POINTER (cell_nr)))
- {
- g_critical ("No overflow events for day %d. Something is wrong.", cell_nr);
- return;
- }
-
- /* Popover title */
- label_title = g_date_time_format (dt, _("%B %d"));
- gtk_label_set_text (GTK_LABEL (self->popover_title), label_title);
-
- /* Clean all the widgets */
- gtk_container_foreach (GTK_CONTAINER (self->events_box), (GtkCallback) gtk_widget_destroy, NULL);
-
- l = g_hash_table_lookup (self->overflow_cells, GINT_TO_POINTER (cell_nr));
-
- /* Setup the start & end dates of the events as the begin & end of day */
- for (; l != NULL; l = g_list_next (l))
- {
- g_autoptr (GDateTime) current_date = NULL;
- g_autoptr (GDateTime) dt_start = NULL;
- g_autoptr (GDateTime) dt_end = NULL;
- g_autoptr (GTimeZone) tz = NULL;
- GtkWidget *cloned_event;
- GcalEvent *event;
-
- event = gcal_event_widget_get_event (l->data);
-
- if (gcal_event_get_all_day (event))
- tz = g_time_zone_new_utc ();
- else
- tz = g_time_zone_new_local ();
-
- current_date = icaltime_to_datetime (self->date);
- dt_start = g_date_time_new (tz,
- g_date_time_get_year (current_date),
- g_date_time_get_month (current_date),
- day, 0, 0, 0);
-
- dt_end = g_date_time_add_days (dt_start, 1);
-
- cloned_event = gcal_event_widget_clone (l->data);
- gcal_event_widget_set_date_start (GCAL_EVENT_WIDGET (cloned_event), dt_start);
- gcal_event_widget_set_date_end (GCAL_EVENT_WIDGET (cloned_event), dt_end);
-
- gtk_container_add (GTK_CONTAINER (self->events_box), cloned_event);
- setup_child_widget (self, cloned_event);
- }
-
- gtk_popover_set_relative_to (GTK_POPOVER (self->overflow_popover), button);
-
- /* sizing hack */
- child_widget = gtk_bin_get_child (GTK_BIN (self->overflow_popover));
- gtk_widget_set_size_request (child_widget, 200, -1);
- gtk_widget_show_all (child_widget);
+ GcalMonthPopover *popover;
- g_object_set_data (G_OBJECT (self->overflow_popover),
- "selected-day",
- GINT_TO_POINTER (day));
-
- gtk_popover_popup (GTK_POPOVER (self->overflow_popover));
+ popover = GCAL_MONTH_POPOVER (self->overflow_popover);
cancel_selection (self);
+
+ 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);
}
static void
@@ -761,16 +698,6 @@ gcal_month_view_key_press (GtkWidget *widget,
}
static gboolean
-cancel_dnd_from_overflow_popover (GtkPopover *popover)
-{
- GCAL_ENTRY;
-
- gtk_popover_popdown (popover);
-
- GCAL_RETURN (FALSE);
-}
-
-static gboolean
gcal_month_view_scroll_event (GtkWidget *widget,
GdkEventScroll *scroll_event)
{
@@ -799,12 +726,12 @@ add_new_event_button_cb (GtkWidget *button,
gpointer user_data)
{
GcalMonthView *self;
- gint day;
GDateTime *start_date;
+ gint day;
self = GCAL_MONTH_VIEW (user_data);
- gtk_widget_hide (self->overflow_popover);
+ gcal_month_popover_popdown (self->overflow_popover);
day = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (self->overflow_popover), "selected-day"));
start_date = g_date_time_new_local (self->date->year, self->date->month, day, 0, 0, 0);
@@ -2045,7 +1972,7 @@ gcal_month_view_motion_notify_event (GtkWidget *widget,
}
else
{
- if (gtk_widget_is_visible (self->overflow_popover))
+ if (gtk_widget_is_visible (GTK_WIDGET (self->overflow_popover)))
GCAL_RETURN (GDK_EVENT_PROPAGATE);
}
@@ -2159,7 +2086,6 @@ gcal_month_view_class_init (GcalMonthViewClass *klass)
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/calendar/month-view.ui");
- gtk_widget_class_bind_template_child (widget_class, GcalMonthView, events_box);
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);
@@ -2168,14 +2094,13 @@ gcal_month_view_class_init (GcalMonthViewClass *klass)
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, overflow_popover);
- gtk_widget_class_bind_template_child (widget_class, GcalMonthView, popover_title);
gtk_widget_class_bind_template_child (widget_class, GcalMonthView, year_label);
gtk_widget_class_bind_template_callback (widget_class, add_new_event_button_cb);
- gtk_widget_class_bind_template_callback (widget_class, cancel_dnd_from_overflow_popover);
gtk_widget_class_set_css_name (widget_class, "calendar-view");
+
+ g_type_ensure (GCAL_TYPE_MONTH_POPOVER);
}
static void
@@ -2193,17 +2118,6 @@ gcal_month_view_init (GcalMonthView *self)
self->k = gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL;
- /*
- * Also set the overflow popover as a drop destination, so we can hide
- * it when the user starts dragging an event that is inside the overflow
- * list.
- */
- gtk_drag_dest_set (GTK_WIDGET (self->overflow_popover),
- 0,
- NULL,
- 0,
- GDK_ACTION_MOVE);
-
/* Weekday header labels */
self->weekday_label[0] = self->label_0;
self->weekday_label[1] = self->label_1;
@@ -2214,6 +2128,20 @@ gcal_month_view_init (GcalMonthView *self)
self->weekday_label[6] = self->label_6;
update_weekday_labels (self);
+
+ /* Overflow popover */
+ self->overflow_popover = (GcalMonthPopover*) gcal_month_popover_new ();
+
+ g_object_bind_property (self,
+ "manager",
+ self->overflow_popover,
+ "manager",
+ G_BINDING_DEFAULT);
+
+ g_signal_connect_swapped (self->overflow_popover,
+ "event-activated",
+ G_CALLBACK (event_activated),
+ self);
}
/* Public API */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]