diff --git a/src/gui/views/gcal-week-grid.c b/src/gui/views/gcal-week-grid.c index 8df2a5e4..3c9236a3 100644 --- a/src/gui/views/gcal-week-grid.c +++ b/src/gui/views/gcal-week-grid.c @@ -25,6 +25,7 @@ #include "gcal-debug.h" #include "gcal-week-grid.h" #include "gcal-week-view.h" +#include "gcal-week-view-common.h" #include "gcal-utils.h" #include "gcal-view.h" #include "gcal-event-widget.h" @@ -34,12 +35,6 @@ #include #include -static const double dashed [] = -{ - 5.0, - 6.0 -}; - typedef struct { GtkWidget *widget; @@ -48,10 +43,11 @@ typedef struct struct _GcalWeekGrid { - GtkContainer parent; + GtkWidget parent; GtkWidget *hours_sidebar; - GdkWindow *event_window; + GtkWidget *now_strip; + GtkEventController *motion_controller; GDateTime *active_date; @@ -68,7 +64,7 @@ struct _GcalWeekGrid GcalContext *context; }; -G_DEFINE_TYPE (GcalWeekGrid, gcal_week_grid, GTK_TYPE_CONTAINER); +G_DEFINE_TYPE (GcalWeekGrid, gcal_week_grid, GTK_TYPE_WIDGET); enum @@ -79,7 +75,10 @@ enum static guint signals[LAST_SIGNAL] = { 0, }; -/* ChildData methods */ +/* + * Auxiliary methods + */ + static ChildData* child_data_new (GtkWidget *widget, GcalEvent *event) @@ -93,31 +92,19 @@ child_data_new (GtkWidget *widget, return data; } -/* Event activation methods */ static void -on_event_widget_activated (GcalEventWidget *widget, - GcalWeekGrid *self) +child_data_free (gpointer data) { - g_signal_emit (self, signals[EVENT_ACTIVATED], 0, widget); + ChildData *child_data = data; + + if (!child_data) + return; + + g_clear_pointer (&child_data->widget, gtk_widget_unparent); + g_clear_object (&child_data->event); + g_free (child_data); } - -static inline void -setup_event_widget (GcalWeekGrid *self, - GtkWidget *widget) -{ - g_signal_connect (widget, "activate", G_CALLBACK (on_event_widget_activated), self); -} - -static inline void -destroy_event_widget (GcalWeekGrid *self, - GtkWidget *widget) -{ - g_signal_handlers_disconnect_by_func (widget, on_event_widget_activated, self); - gtk_widget_destroy (widget); -} - -/* Auxiliary methods */ static inline gint uint16_compare (gconstpointer a, gconstpointer b) @@ -187,55 +174,175 @@ count_overlaps_at_range (GcalRangeTree *self, return counter; } +/* + * Callbacks + */ + +static void +on_click_gesture_pressed_cb (GtkGestureClick *click_gesture, + gint n_press, + gdouble x, + gdouble y, + GcalWeekGrid *self) +{ + GtkAllocation alloc; + gdouble minute_height; + gint column_width; + gint column; + gint minute; + + gtk_widget_get_allocation (GTK_WIDGET (self), &alloc); + minute_height = (gdouble) alloc.height / MINUTES_PER_DAY; + column_width = floor (alloc.width / 7); + column = (gint) x / column_width; + minute = y / minute_height; + minute = minute - (minute % 30); + + self->selection_start = (column * MINUTES_PER_DAY + minute) / 30; + self->selection_end = self->selection_start; + + gtk_widget_queue_draw (GTK_WIDGET (self)); + + gtk_event_controller_set_propagation_phase (self->motion_controller, GTK_PHASE_BUBBLE); +} + +static void +on_event_widget_activated_cb (GcalEventWidget *widget, + GcalWeekGrid *self) +{ + g_signal_emit (self, signals[EVENT_ACTIVATED], 0, widget); +} + +static void +on_motion_controller_motion_cb (GtkEventControllerMotion *motion_controller, + gdouble x, + gdouble y, + GcalWeekGrid *self) +{ + GtkAllocation alloc; + gdouble minute_height; + gint column; + gint minute; + + gtk_widget_get_allocation (GTK_WIDGET (self), &alloc); + minute_height = (gdouble) alloc.height / MINUTES_PER_DAY; + column = self->selection_start * 30 / MINUTES_PER_DAY; + minute = y / minute_height; + minute = minute - (minute % 30); + + self->selection_end = (column * MINUTES_PER_DAY + minute) / 30; + + gtk_widget_queue_draw (GTK_WIDGET (self)); +} + +static void +on_click_gesture_released_cb (GtkGestureClick *click_gesture, + gint n_press, + gdouble x, + gdouble y, + GcalWeekGrid *self) +{ + g_autoptr (GDateTime) week_start = NULL; + g_autoptr (GDateTime) start = NULL; + g_autoptr (GDateTime) end = NULL; + GtkAllocation alloc; + GtkWidget *weekview; + gboolean ltr; + gdouble minute_height; + gdouble local_x; + gdouble local_y; + gdouble out_x; + gdouble out_y; + gint column; + gint minute; + gint start_cell; + gint end_cell; + + ltr = gtk_widget_get_direction (GTK_WIDGET (self)) != GTK_TEXT_DIR_RTL; + + gtk_widget_get_allocation (GTK_WIDGET (self), &alloc); + minute_height = (gdouble) alloc.height / MINUTES_PER_DAY; + column = self->selection_start * 30 / MINUTES_PER_DAY; + minute = y / minute_height; + minute = minute - (minute % 30); + + self->selection_end = (column * MINUTES_PER_DAY + minute) / 30; + + start_cell = self->selection_start; + end_cell = self->selection_end; + + if (start_cell > end_cell) + { + start_cell = start_cell + end_cell; + end_cell = start_cell - end_cell; + start_cell = start_cell - end_cell; + } + + gtk_widget_queue_draw (GTK_WIDGET (self)); + + /* Fake the week view's event so we can control the X and Y values */ + weekview = gtk_widget_get_ancestor (GTK_WIDGET (self), GCAL_TYPE_WEEK_VIEW); + week_start = gcal_date_time_get_start_of_week (self->active_date); + + if (ltr) + { + start = g_date_time_add_minutes (week_start, start_cell * 30); + end = g_date_time_add_minutes (week_start, (end_cell + 1) * 30); + } + else + { + guint rtl_start_cell, rtl_end_cell, rtl_column; + + /* Fix the minute */ + rtl_column = 6 - column; + rtl_start_cell = start_cell + (rtl_column - column) * 48; + rtl_end_cell = (rtl_column * MINUTES_PER_DAY + minute) / 30; + + start = g_date_time_add_minutes (week_start, rtl_start_cell * 30); + end = g_date_time_add_minutes (week_start, (rtl_end_cell + 1) * 30); + } + + local_x = round ((column + 0.5) * (alloc.width / 7.0)); + local_y = (minute + 15) * minute_height; + + gtk_widget_translate_coordinates (GTK_WIDGET (self), + weekview, + local_x, + local_y, + &out_x, + &out_y); + + g_signal_emit_by_name (weekview, + "create-event", + start, + end, + out_x, + out_y); + + gtk_event_controller_set_propagation_phase (self->motion_controller, GTK_PHASE_NONE); +} + +static void +gcal_week_grid_dispose (GObject *object) +{ + GcalWeekGrid *self = GCAL_WEEK_GRID (object); + + g_clear_pointer (&self->events, gcal_range_tree_unref); + g_clear_pointer (&self->now_strip, gtk_widget_unparent); + + G_OBJECT_CLASS (gcal_week_grid_parent_class)->dispose (object); +} + static void gcal_week_grid_finalize (GObject *object) { GcalWeekGrid *self = GCAL_WEEK_GRID (object); - g_clear_pointer (&self->events, gcal_range_tree_unref); gcal_clear_date_time (&self->active_date); G_OBJECT_CLASS (gcal_week_grid_parent_class)->finalize (object); } -static void -gcal_week_grid_add (GtkContainer *container, - GtkWidget *widget) -{ - if (!gtk_widget_get_parent (widget)) - gtk_widget_set_parent (widget, GTK_WIDGET (container)); -} - -static void -gcal_week_grid_remove (GtkContainer *container, - GtkWidget *widget) -{ -} - -static void -gcal_week_grid_forall (GtkContainer *container, - gboolean include_internals, - GtkCallback callback, - gpointer callback_data) -{ - GcalWeekGrid *self; - GPtrArray *widgets_data; - guint i; - - self = GCAL_WEEK_GRID (container); - widgets_data = gcal_range_tree_get_all_data (self->events); - - for (i = 0; i < widgets_data->len; i++) - { - ChildData *data; - - data = g_ptr_array_index (widgets_data, i); - callback (data->widget, callback_data); - } - - g_clear_pointer (&widgets_data, g_ptr_array_unref); -} - static void gcal_week_grid_get_property (GObject *object, guint prop_id, @@ -254,91 +361,6 @@ gcal_week_grid_set_property (GObject *object, G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } -static void -gcal_week_grid_realize (GtkWidget *widget) -{ - GcalWeekGrid *self; - GdkWindow *parent_window; - GdkWindowAttr attributes; - gint attributes_mask; - GtkAllocation allocation; - - self = GCAL_WEEK_GRID (widget); - parent_window = gtk_widget_get_parent_window (widget); - - gtk_widget_set_realized (widget, TRUE); - gtk_widget_set_window (widget, parent_window); - 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); -} - -static void -gcal_week_grid_unrealize (GtkWidget *widget) -{ - GcalWeekGrid *self; - - self = GCAL_WEEK_GRID (widget); - - if (self->event_window) - { - gtk_widget_unregister_window (widget, self->event_window); - gdk_window_destroy (self->event_window); - self->event_window = NULL; - } - - GTK_WIDGET_CLASS (gcal_week_grid_parent_class)->unrealize (widget); -} - -static void -gcal_week_grid_map (GtkWidget *widget) -{ - GcalWeekGrid *self; - - self = GCAL_WEEK_GRID (widget); - - if (self->event_window) - gdk_window_show (self->event_window); - - GTK_WIDGET_CLASS (gcal_week_grid_parent_class)->map (widget); -} - -static void -gcal_week_grid_unmap (GtkWidget *widget) -{ - GcalWeekGrid *self; - - self = GCAL_WEEK_GRID (widget); - - if (self->event_window) - gdk_window_hide (self->event_window); - - GTK_WIDGET_CLASS (gcal_week_grid_parent_class)->unmap (widget); -} - static inline gint get_today_column (GcalWeekGrid *self) { @@ -357,40 +379,95 @@ get_today_column (GcalWeekGrid *self) return days_diff; } -static gboolean -gcal_week_grid_draw (GtkWidget *widget, - cairo_t *cr) +static void +gcal_week_grid_measure (GtkWidget *widget, + GtkOrientation orientation, + gint for_size, + gint *minimum, + gint *natural, + gint *minimum_baseline, + gint *natural_baseline) { + g_autoptr (PangoLayout) layout = NULL; + PangoFontDescription *font_desc; + GtkStyleContext *context; + PangoContext *pango_context; + GtkBorder padding; + gint hours_12_height; + gint hours_24_height; + gint cell_height; + gint height; + + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + if (minimum) + *minimum = 1; + if (natural) + *natural = 1; + return; + } + + context = gtk_widget_get_style_context (widget); + + gtk_style_context_save (context); + gtk_style_context_add_class (context, "hours"); + + gtk_style_context_get_padding (context, &padding); + + pango_context = gtk_widget_get_pango_context (widget); + font_desc = pango_context_get_font_description (pango_context); + + layout = pango_layout_new (pango_context); + pango_layout_set_font_description (layout, font_desc); + + pango_layout_set_text (layout, _("00 AM"), -1); + pango_layout_get_pixel_size (layout, NULL, &hours_12_height); + + pango_layout_set_text (layout, _("00:00"), -1); + pango_layout_get_pixel_size (layout, NULL, &hours_24_height); + + cell_height = MAX (hours_12_height, hours_24_height) + padding.top + padding.bottom; + height = cell_height * 48; + + gtk_style_context_restore (context); + + /* Report the height */ + if (minimum) + *minimum = height; + + if (natural) + *natural = height; +} + +static void +gcal_week_grid_snapshot (GtkWidget *widget, + GtkSnapshot *snapshot) +{ + g_autoptr (GPtrArray) widgets = NULL; GtkStyleContext *context; GtkStateFlags state; GcalWeekGrid *self; GtkBorder padding; GdkRGBA color; - - gboolean ltr; gdouble minutes_height; gdouble x, column_width; - gint i, width, height, today_column; + guint i; + gint width, height; self = GCAL_WEEK_GRID (widget); context = gtk_widget_get_style_context (widget); state = gtk_widget_get_state_flags (widget); - ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL; gtk_style_context_save (context); gtk_style_context_add_class (context, "lines"); - gtk_style_context_get_color (context, state, &color); - gtk_style_context_get_padding (context, state, &padding); + gtk_style_context_get_color (context, &color); + gtk_style_context_get_padding (context, &padding); - gdk_cairo_set_source_rgba (cr, &color); - - width = gtk_widget_get_allocated_width (widget); - height = gtk_widget_get_allocated_height (widget); + width = gtk_widget_get_width (widget); + height = gtk_widget_get_height (widget); column_width = width / 7.0; minutes_height = (gdouble) height / MINUTES_PER_DAY; - cairo_set_line_width (cr, 0.65); - /* First, draw the selection */ if (self->selection_start != -1 && self->selection_end != -1) { @@ -418,12 +495,12 @@ gcal_week_grid_draw (GtkWidget *widget, gtk_style_context_save (context); gtk_style_context_set_state (context, state | GTK_STATE_FLAG_SELECTED); - gtk_render_background (context, - cr, - ALIGNED (x), - round ((start * 30 % MINUTES_PER_DAY) * minutes_height), - column_width, - selection_height); + gtk_snapshot_render_background (snapshot, + context, + ALIGNED (x), + round ((start * 30 % MINUTES_PER_DAY) * minutes_height), + column_width, + selection_height); gtk_style_context_restore (context); } @@ -438,93 +515,40 @@ gcal_week_grid_draw (GtkWidget *widget, column = self->dnd_cell / (MINUTES_PER_DAY / 30); row = self->dnd_cell - column * 48; - gtk_render_background (context, - cr, - column * column_width, - row * cell_height, - column_width, - cell_height); - } - - /* Vertical lines */ - for (i = 0; i < 7; i++) - { - if (ltr) - x = column_width * i; - else - x = width - column_width * i; - - cairo_move_to (cr, ALIGNED (x), 0); - cairo_rel_line_to (cr, 0, height); - } - - /* Horizontal lines */ - for (i = 1; i < 24; i++) - { - cairo_move_to (cr, 0, ALIGNED ((height / 24.0) * i)); - cairo_rel_line_to (cr, width, 0); - } - - cairo_stroke (cr); - - /* Dashed lines between the vertical lines */ - cairo_set_dash (cr, dashed, 2, 0); - - for (i = 0; i < 24; i++) - { - cairo_move_to (cr, 0, ALIGNED ((height / 24.0) * i + (height / 48.0))); - cairo_rel_line_to (cr, width, 0); - } - - cairo_stroke (cr); - - gtk_style_context_restore (context); - - GTK_WIDGET_CLASS (gcal_week_grid_parent_class)->draw (widget, cr); - - /* Today column */ - today_column = get_today_column (GCAL_WEEK_GRID (widget)); - - if (today_column != -1) - { - g_autoptr (GDateTime) now = NULL; - GtkBorder margin; - gdouble strip_width; - guint minutes_from_midnight; - gint min_stip_height; - - now = g_date_time_new_now_local (); - minutes_from_midnight = g_date_time_get_hour (now) * 60 + g_date_time_get_minute (now); - gtk_style_context_save (context); - gtk_style_context_add_class (context, "now-strip"); + gtk_style_context_add_class (context, "dnd"); - gtk_style_context_get (context, state, "min-height", &min_stip_height, NULL); - gtk_style_context_get_margin (context, state, &margin); - - strip_width = column_width - margin.left - margin.right; - - if (ltr) - x = today_column * column_width + margin.left; - else - x = width - (today_column * column_width + margin.right) - strip_width; - - gtk_render_background (context, - cr, - x, - round (minutes_from_midnight * ((gdouble) height / MINUTES_PER_DAY) + margin.top), - strip_width, - MAX (1, min_stip_height - margin.top - margin.bottom)); + gtk_snapshot_render_background (snapshot, + context, + column * column_width, + row * cell_height, + column_width, + cell_height); gtk_style_context_restore (context); } - return FALSE; + gcal_week_view_common_snapshot_hour_lines (widget, snapshot, GTK_ORIENTATION_HORIZONTAL, &color, width, height); + gcal_week_view_common_snapshot_hour_lines (widget, snapshot, GTK_ORIENTATION_VERTICAL, &color, width, height); + + gtk_style_context_restore (context); + + widgets = gcal_range_tree_get_all_data (self->events); + for (i = 0; i < widgets->len; i++) + { + ChildData *child_data = g_ptr_array_index (widgets, i); + + gtk_widget_snapshot_child (widget, child_data->widget, snapshot); + } + + gtk_widget_snapshot_child (widget, self->now_strip, snapshot); } static void -gcal_week_grid_size_allocate (GtkWidget *widget, - GtkAllocation *allocation) +gcal_week_grid_size_allocate (GtkWidget *widget, + gint width, + gint height, + gint baseline) { GcalWeekGrid *self = GCAL_WEEK_GRID (widget); g_autoptr (GDateTime) week_start = NULL; @@ -534,25 +558,13 @@ gcal_week_grid_size_allocate (GtkWidget *widget, gdouble column_width; guint x, y; guint i; - - /* Allocate the widget */ - gtk_widget_set_allocation (widget, allocation); + gint today_column; ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL; - if (gtk_widget_get_realized (widget)) - { - gdk_window_move_resize (self->event_window, - allocation->x, - allocation->y, - allocation->width, - allocation->height); - } - - /* Preliminary calculations */ - minutes_height = (gdouble) allocation->height / MINUTES_PER_DAY; - column_width = (gdouble) allocation->width / 7.0; + minutes_height = (gdouble) height / MINUTES_PER_DAY; + column_width = (gdouble) width / 7.0; /* Temporary range tree to hold positioned events' indexes */ overlaps = gcal_range_tree_new (); @@ -578,26 +590,27 @@ gcal_week_grid_size_allocate (GtkWidget *widget, { g_autoptr (GDateTime) event_start = NULL; g_autoptr (GDateTime) event_end = NULL; - GtkStyleContext *context; GtkAllocation child_allocation; GtkWidget *event_widget; GcalRange *event_range; ChildData *data; - GtkBorder margin; guint64 events_at_range; gint event_minutes; gint natural_height; gint widget_index; gint offset; - gint height; - gint width; + gint event_height; + gint event_width; data = g_ptr_array_index (widgets_data, j); event_widget = data->widget; + + if (!gtk_widget_should_layout (event_widget)) + continue; + event_range = gcal_event_get_range (data->event); event_start = g_date_time_to_local (gcal_event_get_date_start (data->event)); event_end = g_date_time_to_local (gcal_event_get_date_end (data->event)); - context = gtk_widget_get_style_context (event_widget); /* The total number of events available in this range */ events_at_range = count_overlaps_at_range (self->events, event_range); @@ -606,36 +619,26 @@ gcal_week_grid_size_allocate (GtkWidget *widget, widget_index = get_event_index (overlaps, event_range); /* Gtk complains about that */ - gtk_widget_get_preferred_height (event_widget, NULL, &natural_height); - - /* Consider the margins of the child */ - gtk_style_context_get_margin (context, - gtk_style_context_get_state (context), - &margin); + gtk_widget_measure (event_widget, GTK_ORIENTATION_VERTICAL, -1, NULL, &natural_height, NULL, NULL); event_minutes = g_date_time_difference (event_end, event_start) / G_TIME_SPAN_MINUTE; - width = column_width / events_at_range - margin.left - margin.right; - height = event_minutes * minutes_height - margin.top - margin.bottom; - offset = (width + margin.left + margin.right) * widget_index; - y = (g_date_time_get_hour (event_start) * 60 + g_date_time_get_minute (event_start)) * minutes_height + margin.top; + event_width = column_width / events_at_range; + event_height = event_minutes * minutes_height; + offset = event_width * widget_index; + y = (g_date_time_get_hour (event_start) * 60 + g_date_time_get_minute (event_start)) * minutes_height; if (ltr) - x = column_width * i + offset + allocation->x + margin.left + 1; + x = column_width * i + offset + 1; else - x = allocation->width - width - (column_width * i + offset + allocation->x + margin.left + 1); - - /* TODO: find a better way to handle line widths */ - height -= 2; - width -= 1; - y += 2; + x = width - event_width - (column_width * i + offset + x + 1); /* Setup the child position and size */ child_allocation.x = x; child_allocation.y = y; - child_allocation.width = width; - child_allocation.height = height; + child_allocation.width = event_width; + child_allocation.height = event_height; - gtk_widget_size_allocate (event_widget, &child_allocation); + gtk_widget_size_allocate (event_widget, &child_allocation, baseline); /* * Add the current event to the temporary overlaps tree so we have a way to @@ -648,202 +651,45 @@ gcal_week_grid_size_allocate (GtkWidget *widget, } g_clear_pointer (&overlaps, gcal_range_tree_unref); -} -static void -gcal_week_grid_get_preferred_height (GtkWidget *widget, - gint *minimum_height, - gint *natural_height) -{ - GtkStyleContext *context; - GtkStateFlags state; - GtkBorder padding; + /* Today column */ + today_column = get_today_column (GCAL_WEEK_GRID (widget)); - PangoLayout *layout; - PangoFontDescription *font_desc; + gtk_widget_set_child_visible (self->now_strip, today_column != -1); - gint hours_12_height, hours_24_height, cell_height, height; - - context = gtk_widget_get_style_context (widget); - state = gtk_style_context_get_state (context); - - gtk_style_context_save (context); - gtk_style_context_add_class (context, "hours"); - - gtk_style_context_get (context, state, - "font", &font_desc, - NULL); - gtk_style_context_get_padding (context, state, &padding); - - layout = pango_layout_new (gtk_widget_get_pango_context (widget)); - pango_layout_set_font_description (layout, font_desc); - - pango_layout_set_text (layout, _("00 AM"), -1); - pango_layout_get_pixel_size (layout, NULL, &hours_12_height); - - pango_layout_set_text (layout, _("00:00"), -1); - pango_layout_get_pixel_size (layout, NULL, &hours_24_height); - - cell_height = MAX (hours_12_height, hours_24_height) + padding.top + padding.bottom; - height = cell_height * 48; - - gtk_style_context_restore (context); - - pango_font_description_free (font_desc); - g_object_unref (layout); - - /* Report the height */ - if (minimum_height) - *minimum_height = height; - - if (natural_height) - *natural_height = height; -} - -static gboolean -gcal_week_grid_button_press (GtkWidget *widget, - GdkEventButton *event_button) -{ - GcalWeekGrid *self; - GtkAllocation alloc; - gdouble minute_height; - gint column_width; - gint column; - gint minute; - - self = GCAL_WEEK_GRID (widget); - - gtk_widget_get_allocation (widget, &alloc); - minute_height = (gdouble) alloc.height / MINUTES_PER_DAY; - column_width = floor (alloc.width / 7); - column = (gint) event_button->x / column_width; - minute = event_button->y / minute_height; - minute = minute - (minute % 30); - - self->selection_start = (column * MINUTES_PER_DAY + minute) / 30; - self->selection_end = self->selection_start; - - gtk_widget_queue_draw (widget); - - return GDK_EVENT_PROPAGATE; -} - -static gboolean -gcal_week_grid_motion_notify_event (GtkWidget *widget, - GdkEventMotion *event) -{ - GcalWeekGrid *self; - GtkAllocation alloc; - gdouble minute_height; - gint column; - gint minute; - - if (!(event->state & GDK_BUTTON_PRESS_MASK)) - return GDK_EVENT_PROPAGATE; - - self = GCAL_WEEK_GRID (widget); - - gtk_widget_get_allocation (widget, &alloc); - minute_height = (gdouble) alloc.height / MINUTES_PER_DAY; - column = self->selection_start * 30 / MINUTES_PER_DAY; - minute = event->y / minute_height; - minute = minute - (minute % 30); - - self->selection_end = (column * MINUTES_PER_DAY + minute) / 30; - - gtk_widget_queue_draw (widget); - - return GDK_EVENT_STOP; -} - -static gboolean -gcal_week_grid_button_release (GtkWidget *widget, - GdkEventButton *event) -{ - GcalWeekGrid *self; - GtkAllocation alloc; - GDateTime *week_start; - GDateTime *start, *end; - GtkWidget *weekview; - gboolean ltr; - gdouble minute_height; - gdouble x, y; - gint column; - gint minute; - gint start_cell; - gint end_cell; - gint out_x; - gint out_y; - - self = GCAL_WEEK_GRID (widget); - ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL; - - gtk_widget_get_allocation (widget, &alloc); - minute_height = (gdouble) alloc.height / MINUTES_PER_DAY; - column = self->selection_start * 30 / MINUTES_PER_DAY; - minute = event->y / minute_height; - minute = minute - (minute % 30); - - self->selection_end = (column * MINUTES_PER_DAY + minute) / 30; - - start_cell = self->selection_start; - end_cell = self->selection_end; - - if (start_cell > end_cell) + if (today_column != -1) { - start_cell = start_cell + end_cell; - end_cell = start_cell - end_cell; - start_cell = start_cell - end_cell; + g_autoptr (GDateTime) now = NULL; + GtkAllocation allocation; + guint minutes_from_midnight; + gint now_strip_height; + + now = g_date_time_new_now_local (); + minutes_from_midnight = g_date_time_get_hour (now) * 60 + g_date_time_get_minute (now); + + gtk_widget_measure (self->now_strip, + GTK_ORIENTATION_VERTICAL, + -1, + &now_strip_height, + NULL, + NULL, + NULL); + + if (ltr) + x = today_column * column_width; + else + x = width - (today_column * column_width) - column_width; + + allocation.x = x; + allocation.y = round (minutes_from_midnight * ((gdouble) height / MINUTES_PER_DAY)); + allocation.width = column_width; + allocation.height = MAX (1, now_strip_height); + + gtk_widget_size_allocate (self->now_strip, &allocation, baseline); } - - gtk_widget_queue_draw (widget); - - /* Fake the week view's event so we can control the X and Y values */ - weekview = gtk_widget_get_ancestor (widget, GCAL_TYPE_WEEK_VIEW); - week_start = gcal_date_time_get_start_of_week (self->active_date); - - if (ltr) - { - start = g_date_time_add_minutes (week_start, start_cell * 30); - end = g_date_time_add_minutes (week_start, (end_cell + 1) * 30); - } - else - { - guint rtl_start_cell, rtl_end_cell, rtl_column; - - /* Fix the minute */ - rtl_column = 6 - column; - rtl_start_cell = start_cell + (rtl_column - column) * 48; - rtl_end_cell = (rtl_column * MINUTES_PER_DAY + minute) / 30; - - start = g_date_time_add_minutes (week_start, rtl_start_cell * 30); - end = g_date_time_add_minutes (week_start, (rtl_end_cell + 1) * 30); - } - - x = round ((column + 0.5) * (alloc.width / 7.0)); - y = (minute + 15) * minute_height; - - gtk_widget_translate_coordinates (widget, - weekview, - x, - y, - &out_x, - &out_y); - - g_signal_emit_by_name (weekview, - "create-event", - start, - end, - (gdouble) out_x, - (gdouble) out_y); - - gcal_clear_date_time (&week_start); - gcal_clear_date_time (&start); - gcal_clear_date_time (&end); - - return GDK_EVENT_STOP; } +#if 0 // TODO: DND static gint get_dnd_cell (GtkWidget *widget, gint x, @@ -995,35 +841,22 @@ gcal_week_grid_drag_leave (GtkWidget *widget, gtk_widget_queue_draw (widget); } +#endif static void gcal_week_grid_class_init (GcalWeekGridClass *klass) { - GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); - container_class->add = gcal_week_grid_add; - container_class->remove = gcal_week_grid_remove; - container_class->forall = gcal_week_grid_forall; - + object_class->dispose = gcal_week_grid_dispose; object_class->finalize = gcal_week_grid_finalize; object_class->get_property = gcal_week_grid_get_property; object_class->set_property = gcal_week_grid_set_property; - widget_class->draw = gcal_week_grid_draw; + widget_class->measure = gcal_week_grid_measure; widget_class->size_allocate = gcal_week_grid_size_allocate; - widget_class->realize = gcal_week_grid_realize; - widget_class->unrealize = gcal_week_grid_unrealize; - widget_class->map = gcal_week_grid_map; - widget_class->unmap = gcal_week_grid_unmap; - widget_class->get_preferred_height = gcal_week_grid_get_preferred_height; - widget_class->button_press_event = gcal_week_grid_button_press; - widget_class->motion_notify_event = gcal_week_grid_motion_notify_event; - widget_class->button_release_event = gcal_week_grid_button_release; - widget_class->drag_motion = gcal_week_grid_drag_motion; - widget_class->drag_leave = gcal_week_grid_drag_leave; - widget_class->drag_drop = gcal_week_grid_drag_drop; + widget_class->snapshot = gcal_week_grid_snapshot; signals[EVENT_ACTIVATED] = g_signal_new ("event-activated", GCAL_TYPE_WEEK_GRID, @@ -1039,20 +872,38 @@ gcal_week_grid_class_init (GcalWeekGridClass *klass) static void gcal_week_grid_init (GcalWeekGrid *self) { - gtk_widget_set_has_window (GTK_WIDGET (self), FALSE); + GtkGesture *click_gesture; self->selection_start = -1; self->selection_end = -1; self->dnd_cell = -1; - self->events = gcal_range_tree_new (); + self->events = gcal_range_tree_new_with_free_func (child_data_free); + self->now_strip = adw_bin_new (); + gtk_widget_add_css_class (self->now_strip, "now-strip"); + gtk_widget_set_can_target (self->now_strip, FALSE); + gtk_widget_set_parent (self->now_strip, GTK_WIDGET (self)); + + click_gesture = gtk_gesture_click_new (); + gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (click_gesture), GDK_BUTTON_PRIMARY); + g_signal_connect (click_gesture, "pressed", G_CALLBACK (on_click_gesture_pressed_cb), self); + g_signal_connect (click_gesture, "released", G_CALLBACK (on_click_gesture_released_cb), self); + gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (click_gesture)); + + self->motion_controller = gtk_event_controller_motion_new (); + g_signal_connect (self->motion_controller, "motion", G_CALLBACK (on_motion_controller_motion_cb), self); + gtk_event_controller_set_propagation_phase (self->motion_controller, GTK_PHASE_NONE); + gtk_widget_add_controller (GTK_WIDGET (self), self->motion_controller); + +#if 0 // TODO: DND /* Setup the week view as a drag n' drop destination */ gtk_drag_dest_set (GTK_WIDGET (self), 0, NULL, 0, GDK_ACTION_MOVE); +#endif } /* Public API */ @@ -1066,8 +917,8 @@ gcal_week_grid_set_context (GcalWeekGrid *self, g_signal_connect_object (gcal_context_get_clock (context), "minute-changed", - G_CALLBACK (gtk_widget_queue_draw), - self, + G_CALLBACK (gtk_widget_queue_resize), + self->now_strip, G_CONNECT_SWAPPED); } @@ -1091,10 +942,9 @@ gcal_week_grid_add_event (GcalWeekGrid *self, gcal_event_get_range (event), child_data_new (widget, event)); - setup_event_widget (self, widget); - gtk_widget_show (widget); + g_signal_connect (widget, "activate", G_CALLBACK (on_event_widget_activated_cb), self); - gtk_container_add (GTK_CONTAINER (self), widget); + gtk_widget_set_parent (widget, GTK_WIDGET (self)); } void @@ -1120,10 +970,7 @@ gcal_week_grid_remove_event (GcalWeekGrid *self, continue; gcal_range_tree_remove_range (self->events, gcal_event_get_range (event), data); - destroy_event_widget (self, data->widget); gtk_widget_queue_allocate (GTK_WIDGET (self)); - g_object_unref (event); - g_free (data); } g_clear_pointer (&widgets, g_ptr_array_unref); @@ -1134,29 +981,13 @@ gcal_week_grid_get_children_by_uuid (GcalWeekGrid *self, GcalRecurrenceModType mod, const gchar *uid) { - GPtrArray *widgets; - GList *events; - GList *result; - guint i; + g_autoptr (GList) result = NULL; GCAL_ENTRY; - events = NULL; - result = NULL; - widgets = gcal_range_tree_get_all_data (self->events); + result = filter_children_by_uid_and_modtype (GTK_WIDGET (self), mod, uid); - for (i = 0; widgets && i < widgets->len; i++) - { - ChildData *data = g_ptr_array_index (widgets, i); - events = g_list_prepend (events, data->widget); - } - - result = filter_event_list_by_uid_and_modtype (events, mod, uid); - - g_clear_pointer (&widgets, g_ptr_array_unref); - g_clear_pointer (&events, g_list_free); - - GCAL_RETURN (result); + GCAL_RETURN (g_steal_pointer (&result)); } void diff --git a/src/gui/views/gcal-week-grid.h b/src/gui/views/gcal-week-grid.h index f21487c0..299ebe4d 100644 --- a/src/gui/views/gcal-week-grid.h +++ b/src/gui/views/gcal-week-grid.h @@ -28,7 +28,7 @@ G_BEGIN_DECLS #define GCAL_TYPE_WEEK_GRID (gcal_week_grid_get_type()) -G_DECLARE_FINAL_TYPE (GcalWeekGrid, gcal_week_grid, GCAL, WEEK_GRID, GtkContainer) +G_DECLARE_FINAL_TYPE (GcalWeekGrid, gcal_week_grid, GCAL, WEEK_GRID, GtkWidget) void gcal_week_grid_set_context (GcalWeekGrid *week_grid, GcalContext *context); diff --git a/src/gui/views/gcal-week-header.c b/src/gui/views/gcal-week-header.c index e722bdb6..e026b736 100644 --- a/src/gui/views/gcal-week-header.c +++ b/src/gui/views/gcal-week-header.c @@ -30,13 +30,12 @@ #include "gcal-view.h" #include "gcal-week-header.h" #include "gcal-week-view.h" +#include "gcal-week-view-common.h" #include #include #include -#define COLUMN_PADDING 6 - /* WeatherInfoDay: * @winfo: (nullable): Holds weather information for this week-day. All other fields are only valid if this one is not %NULL. * @icon_buf: (nullable): Buffered weather icon. @@ -52,22 +51,29 @@ typedef struct { GcalWeatherInfo *winfo; /* owned */ - GdkPixbuf *icon_buf; /* owned */ + GtkIconPaintable *icon_buf; /* owned */ } WeatherInfoDay; +typedef struct +{ + GtkWidget *day_number_label; + GtkWidget *weekday_name_label; +} WeekdayHeader; + struct _GcalWeekHeader { - GtkGrid parent; + GtkBox parent; - GtkWidget *grid; + GtkGrid *grid; GtkWidget *month_label; GtkWidget *week_label; GtkWidget *year_label; GtkWidget *scrolledwindow; - GtkWidget *expand_button; + GtkButton *expand_button; GtkWidget *expand_button_box; - GtkWidget *expand_button_image; GtkWidget *header_labels_box; + GtkEventController *motion_controller; + GtkBox *weekdays_box; GcalContext *context; @@ -78,6 +84,7 @@ struct _GcalWeekHeader */ GList *events[7]; GtkWidget *overflow_label[7]; + WeekdayHeader weekday_header[7]; gint first_weekday; @@ -113,7 +120,7 @@ enum static guint signals[LAST_SIGNAL] = { 0, }; -G_DEFINE_TYPE (GcalWeekHeader, gcal_week_header, GTK_TYPE_GRID); +G_DEFINE_TYPE (GcalWeekHeader, gcal_week_header, GTK_TYPE_BOX); /* WeatherInfoDay methods */ @@ -128,11 +135,8 @@ wid_clear (WeatherInfoDay *wid) { g_return_if_fail (wid != NULL); - if (wid->winfo != NULL) - g_clear_object (&wid->winfo); - - if (wid->icon_buf != NULL) - g_clear_object (&wid->icon_buf); + g_clear_object (&wid->winfo); + g_clear_object (&wid->icon_buf); } static gint @@ -229,62 +233,63 @@ destroy_event_widget (GcalWeekHeader *self, GtkWidget *widget) { g_signal_handlers_disconnect_by_func (widget, on_event_widget_activated, self); - gtk_widget_destroy (widget); + gtk_grid_remove (self->grid, widget); } /* Auxiliary methods */ -static gboolean -on_button_pressed (GcalWeekHeader *self, - GdkEventButton *event, - GtkWidget *widget) +static void +on_button_pressed (GtkGestureClick *click_gesture, + gint n_press, + gdouble x, + gdouble y, + GcalWeekHeader *self) { gboolean ltr; gdouble column_width; gint column; gint width; - ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL; - width = gtk_widget_get_allocated_width (widget); + ltr = gtk_widget_get_direction (GTK_WIDGET (self)) != GTK_TEXT_DIR_RTL; + width = gtk_widget_get_allocated_width (self->scrolledwindow); column_width = width / 7.0; - column = ltr ? (event->x / column_width) : (7 - event->x / column_width); + column = ltr ? (x / column_width) : (7 - x / column_width); self->selection_start = column; self->selection_end = column; gtk_widget_queue_draw (GTK_WIDGET (self)); - return GDK_EVENT_PROPAGATE; + gtk_event_controller_set_propagation_phase (self->motion_controller, + GTK_PHASE_BUBBLE); } -static gboolean -on_motion_notify (GcalWeekHeader *self, - GdkEventMotion *event, - GtkWidget *widget) +static void +on_motion_notify (GtkEventControllerMotion *motion_event, + gdouble x, + gdouble y, + GcalWeekHeader *self) { gboolean ltr; gdouble column_width; gint column; gint width; - if (!(event->state & GDK_BUTTON_PRESS_MASK)) - return GDK_EVENT_PROPAGATE; - - ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL; - width = gtk_widget_get_allocated_width (widget); + ltr = gtk_widget_get_direction (GTK_WIDGET (self)) != GTK_TEXT_DIR_RTL; + width = gtk_widget_get_allocated_width (self->scrolledwindow); column_width = width / 7.0; - column = ltr ? (event->x / column_width) : (7 - event->x / column_width); + column = ltr ? (x / column_width) : (7 - x / column_width); self->selection_end = column; gtk_widget_queue_draw (GTK_WIDGET (self)); - - return GDK_EVENT_STOP; } -static gboolean -on_button_released (GcalWeekHeader *self, - GdkEventButton *event, - GtkWidget *widget) +static void +on_button_released (GtkGestureClick *click_gesture, + gint n_press, + gdouble x, + gdouble y, + GcalWeekHeader *self) { g_autoptr (GDateTime) selection_start = NULL; g_autoptr (GDateTime) selection_end = NULL; @@ -292,23 +297,23 @@ on_button_released (GcalWeekHeader *self, GtkWidget *weekview; gboolean ltr; gdouble column_width; - gint out_x, out_y; + gdouble out_x, out_y; gint column; gint width; gint start; gint end; - ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL; - width = gtk_widget_get_allocated_width (widget); + ltr = gtk_widget_get_direction (GTK_WIDGET (self)) != GTK_TEXT_DIR_RTL; + width = gtk_widget_get_allocated_width (self->scrolledwindow); column_width = width / 7.0; - column = ltr ? (event->x / column_width) : (7 - event->x / column_width); + column = ltr ? (x / column_width) : (7 - x / column_width); self->selection_end = column; gtk_widget_queue_draw (GTK_WIDGET (self)); /* Fake the week view's event so we can control the X and Y values */ - weekview = gtk_widget_get_ancestor (widget, GCAL_TYPE_WEEK_VIEW); + weekview = gtk_widget_get_ancestor (GTK_WIDGET (self), GCAL_TYPE_WEEK_VIEW); start = self->selection_start; end = self->selection_end; @@ -327,7 +332,7 @@ on_button_released (GcalWeekHeader *self, out_x = ltr ? (column_width * (column + 0.5)) : (width - column_width * (column + 0.5)); /* Translate X... */ - gtk_widget_translate_coordinates (widget, weekview, out_x, 0, &out_x, NULL); + gtk_widget_translate_coordinates (self->scrolledwindow, weekview, out_x, 0, &out_x, NULL); /* And Y */ gtk_widget_translate_coordinates (GTK_WIDGET (self), @@ -341,10 +346,11 @@ on_button_released (GcalWeekHeader *self, "create-event", selection_start, selection_end, - (gdouble) out_x, - (gdouble) out_y); + out_x, + out_y); - return GDK_EVENT_STOP; + gtk_event_controller_set_propagation_phase (self->motion_controller, + GTK_PHASE_NONE); } static void @@ -464,15 +470,11 @@ update_overflow (GcalWeekHeader *self) text = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "Other event", "Other %d events", n_events - 2), n_events - 2); - /* Show the button if not visible yet */ - if (!gtk_widget_get_visible (self->expand_button)) - gtk_widget_show (self->expand_button); - /* TODO: use a button and show an overflow popover */ if (!label) { label = gtk_label_new (""); - gtk_grid_attach (GTK_GRID (self->grid), + gtk_grid_attach (self->grid, label, i, 3, @@ -480,8 +482,6 @@ update_overflow (GcalWeekHeader *self) 1); self->overflow_label[i] = label; - - gtk_widget_show (label); } gtk_label_set_label (GTK_LABEL (label), text); @@ -489,12 +489,12 @@ update_overflow (GcalWeekHeader *self) } else if (label) { - gtk_widget_destroy (label); + gtk_grid_remove (self->grid, label); self->overflow_label[i] = NULL; } } - gtk_widget_set_visible (self->expand_button, show_expand); + gtk_widget_set_visible (GTK_WIDGET (self->expand_button), show_expand); } static void @@ -502,6 +502,8 @@ merge_events (GcalWeekHeader *self, GtkWidget *event, GtkWidget *to_be_removed) { + GtkLayoutManager *layout_manager; + GtkLayoutChild *layout_child; GDateTime *end_date; gint deleted_width, current_width; @@ -510,23 +512,18 @@ merge_events (GcalWeekHeader *self, gcal_event_widget_set_date_end (GCAL_EVENT_WIDGET (event), end_date); /* Retrieve the current sizes */ - gtk_container_child_get (GTK_CONTAINER (self->grid), - to_be_removed, - "width", &deleted_width, - NULL); + layout_manager = gtk_widget_get_layout_manager (GTK_WIDGET (self->grid)); + layout_child = gtk_layout_manager_get_layout_child (layout_manager, to_be_removed); + deleted_width = gtk_grid_layout_child_get_column_span (GTK_GRID_LAYOUT_CHILD (layout_child)); - gtk_container_child_get (GTK_CONTAINER (self->grid), - event, - "width", ¤t_width, - NULL); + layout_child = gtk_layout_manager_get_layout_child (layout_manager, event); + current_width = gtk_grid_layout_child_get_column_span (GTK_GRID_LAYOUT_CHILD (layout_child)); destroy_event_widget (self, to_be_removed); /* Update the event's size */ - gtk_container_child_set (GTK_CONTAINER (self->grid), - event, - "width", current_width + deleted_width, - NULL); + gtk_grid_layout_child_set_column_span (GTK_GRID_LAYOUT_CHILD (layout_child), + current_width + deleted_width); } static void @@ -587,8 +584,8 @@ check_mergeable_events (GcalWeekHeader *self) { GtkWidget *current_widget, *to_be_removed; - current_widget = gtk_grid_get_child_at (GTK_GRID (self->grid), weekday + i, index + 1); - to_be_removed = gtk_grid_get_child_at (GTK_GRID (self->grid), weekday + i + 1, index + 1); + current_widget = gtk_grid_get_child_at (self->grid, weekday + i, index + 1); + to_be_removed = gtk_grid_get_child_at (self->grid, weekday + i + 1, index + 1); /* * We don't want to merge: @@ -622,6 +619,8 @@ split_event_widget_at_column (GcalWeekHeader *self, GtkWidget *widget, gint column) { + GtkLayoutManager *layout_manager; + GtkLayoutChild *layout_child; GDateTime *week_start, *column_date, *end_column_date; gboolean create_before; gboolean create_after; @@ -634,12 +633,11 @@ split_event_widget_at_column (GcalWeekHeader *self, column_date = g_date_time_add_days (week_start, column); end_column_date = g_date_time_add_days (column_date, 1); - gtk_container_child_get (GTK_CONTAINER (self->grid), - widget, - "top_attach", &top_attach, - "left_attach", &left_attach, - "width", &old_width, - NULL); + layout_manager = gtk_widget_get_layout_manager (GTK_WIDGET (self->grid)); + layout_child = gtk_layout_manager_get_layout_child (layout_manager, widget); + top_attach = gtk_grid_layout_child_get_row (GTK_GRID_LAYOUT_CHILD (layout_child)); + left_attach = gtk_grid_layout_child_get_column (GTK_GRID_LAYOUT_CHILD (layout_child)); + old_width = gtk_grid_layout_child_get_column_span (GTK_GRID_LAYOUT_CHILD (layout_child)); create_before = column > 0 && left_attach < column; create_after = column < 6 && old_width > 1 && left_attach + old_width > column + 1; @@ -653,7 +651,7 @@ split_event_widget_at_column (GcalWeekHeader *self, setup_event_widget (self, widget_before); - gtk_grid_attach (GTK_GRID (self->grid), + gtk_grid_attach (self->grid, widget_before, left_attach, top_attach, @@ -667,11 +665,8 @@ split_event_widget_at_column (GcalWeekHeader *self, left_attach = column; /* Update the current event position, size and start date */ - gtk_container_child_set (GTK_CONTAINER (self->grid), - widget, - "left_attach", left_attach, - "width", new_width, - NULL); + gtk_grid_layout_child_set_column (GTK_GRID_LAYOUT_CHILD (layout_child), left_attach); + gtk_grid_layout_child_set_column_span (GTK_GRID_LAYOUT_CHILD (layout_child), new_width); gcal_event_widget_set_date_start (GCAL_EVENT_WIDGET (widget), column_date); gtk_widget_set_visible (widget, is_event_visible (self, left_attach, top_attach - 1)); @@ -691,7 +686,7 @@ split_event_widget_at_column (GcalWeekHeader *self, setup_event_widget (self, widget_after); - gtk_grid_attach (GTK_GRID (self->grid), + gtk_grid_attach (self->grid, widget_after, column + 1, top_attach, @@ -701,10 +696,7 @@ split_event_widget_at_column (GcalWeekHeader *self, new_width = column - left_attach + 1; /* Only update the current widget's width */ - gtk_container_child_set (GTK_CONTAINER (self->grid), - widget, - "width", new_width, - NULL); + gtk_grid_layout_child_set_column_span (GTK_GRID_LAYOUT_CHILD (layout_child), new_width); gtk_widget_set_visible (widget_after, is_event_visible (self, column + 1, top_attach - 1)); } @@ -720,42 +712,39 @@ move_events_at_column (GcalWeekHeader *self, gint column, gint start_at) { - GList *children, *l; + GtkLayoutManager *layout_manager; + GtkWidget *child; - children = gtk_container_get_children (GTK_CONTAINER (self->grid)); + layout_manager = gtk_widget_get_layout_manager (GTK_WIDGET (self->grid)); /* First, lets find the widgets at this column */ - for (l = children; l != NULL; l = l->next) + for (child = gtk_widget_get_first_child (GTK_WIDGET (self->grid)); + child; + child = gtk_widget_get_next_sibling (child)) { gint top_attach, left_attach, width; + GtkLayoutChild *layout_child; /* Get the widget's current position... */ - gtk_container_child_get (GTK_CONTAINER (self->grid), - l->data, - "top_attach", &top_attach, - "left_attach", &left_attach, - "width", &width, - NULL); + layout_child = gtk_layout_manager_get_layout_child (layout_manager, child); + top_attach = gtk_grid_layout_child_get_row (GTK_GRID_LAYOUT_CHILD (layout_child)); + left_attach = gtk_grid_layout_child_get_column (GTK_GRID_LAYOUT_CHILD (layout_child)); + width = gtk_grid_layout_child_get_column_span (GTK_GRID_LAYOUT_CHILD (layout_child)); - if (left_attach != column || start_at > top_attach - 1 || !GCAL_IS_EVENT_WIDGET (l->data)) + if (left_attach != column || start_at > top_attach - 1 || !GCAL_IS_EVENT_WIDGET (child)) continue; /* If this is a multiday event, break it */ if (width > 1) - split_event_widget_at_column (self, l->data, column); + split_event_widget_at_column (self, child, column); top_attach = top_attach + (direction == DOWN ? 1 : -1); /* And move it to position + 1 */ - gtk_container_child_set (GTK_CONTAINER (self->grid), - l->data, - "top_attach", top_attach, - NULL); + gtk_grid_layout_child_set_row (GTK_GRID_LAYOUT_CHILD (layout_child), top_attach); - gtk_widget_set_visible (l->data, is_event_visible (self, left_attach, top_attach - 1)); + gtk_widget_set_visible (child, is_event_visible (self, left_attach, top_attach - 1)); } - - g_clear_pointer (&children, g_list_free); } static void @@ -771,7 +760,7 @@ apply_overflow_at_weekday (GcalWeekHeader *self, if (self->expanded || self->overflow_label[weekday] || g_list_length (self->events[weekday]) < 4) return; - child = gtk_grid_get_child_at (GTK_GRID (self->grid), weekday, 3); + child = gtk_grid_get_child_at (self->grid, weekday, 3); split_event_widget_at_column (self, child, weekday); gtk_widget_hide (child); @@ -785,6 +774,7 @@ add_event_to_grid (GcalWeekHeader *self, { g_autoptr (GDateTime) week_start = NULL; g_autoptr (GDateTime) week_end = NULL; + GtkLayoutManager *layout_manager; GtkWidget *widget; gboolean is_visible, was_visible; gint position; @@ -802,7 +792,7 @@ add_event_to_grid (GcalWeekHeader *self, widget = gcal_event_widget_new (self->context, event); setup_event_widget (self, widget); - gtk_grid_attach (GTK_GRID (self->grid), + gtk_grid_attach (self->grid, widget, start, position + 1, @@ -827,6 +817,8 @@ add_event_to_grid (GcalWeekHeader *self, gcal_event_widget_set_date_start (GCAL_EVENT_WIDGET (widget), week_start); gcal_event_widget_set_date_end (GCAL_EVENT_WIDGET (widget), week_end); + layout_manager = gtk_widget_get_layout_manager (GTK_WIDGET (self->grid)); + /* * In addition to moving the current column's events below, multiday * events must also move the events from ~all~ the columns it spans @@ -847,10 +839,11 @@ add_event_to_grid (GcalWeekHeader *self, /* Add the event to the grid */ if (new_position == position && was_visible == is_visible) { - gtk_container_child_set (GTK_CONTAINER (self->grid), - widget, - "width", i - start + 1, - NULL); + GtkLayoutChild *layout_child; + + layout_child = gtk_layout_manager_get_layout_child (layout_manager, widget); + gtk_grid_layout_child_set_column_span (GTK_GRID_LAYOUT_CHILD (layout_child), + i - start + 1); } else { @@ -861,7 +854,7 @@ add_event_to_grid (GcalWeekHeader *self, cloned_widget = gcal_event_widget_clone (GCAL_EVENT_WIDGET (widget)); setup_event_widget (self, cloned_widget); - gtk_grid_attach (GTK_GRID (self->grid), + gtk_grid_attach (self->grid, cloned_widget, i, new_position + 1, @@ -971,6 +964,8 @@ update_title (GcalWeekHeader *self) { GDateTime *week_start, *week_end, *week_mid; gchar *year_label, *month_label, *week_label; + gint today_column; + gint i; if(!self->active_date) return; @@ -1007,6 +1002,44 @@ update_title (GcalWeekHeader *self) gtk_label_set_label (GTK_LABEL (self->week_label), week_label); gtk_label_set_label (GTK_LABEL (self->year_label), year_label); + today_column = get_today_column (self); + + for (i = 0; i < 7; i++) + { + g_autoptr (GDateTime) day = NULL; + g_autofree gchar *weekday_date = NULL; + g_autofree gchar *weekday_abv = NULL; + g_autofree gchar *weekday = NULL; + WeekdayHeader *header; + gint n_day; + + day = g_date_time_add_days (week_start, i); + n_day = g_date_time_get_day_of_month (day); + + if (n_day > g_date_get_days_in_month (g_date_time_get_month (week_start), g_date_time_get_year (week_start))) + n_day = n_day - g_date_get_days_in_month (g_date_time_get_month (week_start), g_date_time_get_year (week_start)); + + header = &self->weekday_header[i]; + + if (i == today_column) + { + gtk_widget_add_css_class (header->weekday_name_label, "accent"); + gtk_widget_add_css_class (header->day_number_label, "accent"); + } + else + { + gtk_widget_remove_css_class (header->weekday_name_label, "accent"); + gtk_widget_remove_css_class (header->day_number_label, "accent"); + } + + weekday_date = g_strdup_printf ("%d", n_day); + gtk_label_set_label (GTK_LABEL (header->day_number_label), weekday_date); + + weekday = g_date_time_format (day, "%a"); + weekday_abv = g_utf8_strup (weekday, -1); + gtk_label_set_label (GTK_LABEL (header->weekday_name_label), weekday_abv); + } + g_clear_pointer (&week_start, g_date_time_unref); g_clear_pointer (&week_end, g_date_time_unref); g_clear_pointer (&week_mid, g_date_time_unref); @@ -1018,9 +1051,8 @@ update_title (GcalWeekHeader *self) static void header_collapse (GcalWeekHeader *self) { - GList *children, *l; - - children = gtk_container_get_children (GTK_CONTAINER (self->grid)); + GtkLayoutManager *layout_manager; + GtkWidget *child; self->expanded = FALSE; @@ -1028,36 +1060,38 @@ header_collapse (GcalWeekHeader *self) GTK_POLICY_NEVER, GTK_POLICY_NEVER); gtk_scrolled_window_set_max_content_height (GTK_SCROLLED_WINDOW (self->scrolledwindow), -1); - gtk_image_set_from_icon_name (GTK_IMAGE (self->expand_button_image), "go-down-symbolic", 4); + gtk_button_set_icon_name (self->expand_button, "go-down-symbolic"); - for (l = children; l != NULL; l = l->next) + layout_manager = gtk_widget_get_layout_manager (GTK_WIDGET (self->grid)); + + for (child = gtk_widget_get_first_child (GTK_WIDGET (self->grid)); + child; + child = gtk_widget_get_next_sibling (child)) { + GtkLayoutChild *layout_child; gint top_attach, left_attach; - gtk_container_child_get (GTK_CONTAINER (self->grid), - l->data, - "top-attach", &top_attach, - "left_attach", &left_attach, - NULL); + layout_child = gtk_layout_manager_get_layout_child (layout_manager, child); + left_attach = gtk_grid_layout_child_get_column (GTK_GRID_LAYOUT_CHILD (layout_child)); + top_attach = gtk_grid_layout_child_get_row (GTK_GRID_LAYOUT_CHILD (layout_child)); - gtk_widget_set_visible (l->data, is_event_visible (self, left_attach, top_attach - 1)); + gtk_widget_set_visible (child, is_event_visible (self, left_attach, top_attach - 1)); apply_overflow_at_weekday (self, left_attach); } update_overflow (self); - - g_clear_pointer (&children, g_list_free); } static void header_expand (GcalWeekHeader *self) { + GtkLayoutManager *layout_manager; GtkWidget *week_view; - GList *children, *l; + GtkWidget *child; - children = gtk_container_get_children (GTK_CONTAINER (self->grid)); week_view = gtk_widget_get_ancestor (GTK_WIDGET (self), GCAL_TYPE_WEEK_VIEW); + layout_manager = gtk_widget_get_layout_manager (GTK_WIDGET (self->grid)); self->expanded = TRUE; @@ -1069,33 +1103,35 @@ header_expand (GcalWeekHeader *self) gtk_scrolled_window_set_max_content_height (GTK_SCROLLED_WINDOW (self->scrolledwindow), gtk_widget_get_allocated_height (week_view) / 2); - gtk_image_set_from_icon_name (GTK_IMAGE (self->expand_button_image), "go-up-symbolic", 4); + gtk_button_set_icon_name (self->expand_button, "go-up-symbolic"); - for (l = children; l != NULL; l = l->next) + child = gtk_widget_get_first_child (GTK_WIDGET (self->grid)); + while (child) { + GtkWidget *next = gtk_widget_get_next_sibling (child); + /* Remove any remaining labels */ - if (GTK_IS_LABEL (l->data)) + if (GTK_IS_LABEL (child)) { - gint left_attach; + GtkLayoutChild *layout_child; + gint column; - gtk_container_child_get (GTK_CONTAINER (self->grid), - l->data, - "left_attach", &left_attach, - NULL); + layout_child = gtk_layout_manager_get_layout_child (layout_manager, child); + column = gtk_grid_layout_child_get_column (GTK_GRID_LAYOUT_CHILD (layout_child)); - self->overflow_label[left_attach] = NULL; - gtk_widget_destroy (l->data); + self->overflow_label[column] = NULL; + gtk_grid_remove (self->grid, child); } else { - gtk_widget_show (l->data); + gtk_widget_show (child); } + + child = next; } /* Merge events that were broken because of the overflow label */ check_mergeable_events (self); - - g_clear_pointer (&children, g_list_free); } static void @@ -1109,105 +1145,26 @@ on_expand_action_activated (GcalWeekHeader *self, } /* Drawing area content and size */ -static gdouble -get_weekday_names_height (GtkWidget *widget) -{ - PangoFontDescription *font_desc; - GtkStyleContext* context; - GtkStateFlags state_flags; - PangoLayout *layout; - GtkBorder padding; - gint final_height; - gint font_height; - - context = gtk_widget_get_style_context (widget); - state_flags = gtk_style_context_get_state (context); - - layout = gtk_widget_create_pango_layout (widget, "A"); - - gtk_style_context_save (context); - gtk_style_context_add_class (context, "week-dates"); - gtk_style_context_get (context, state_flags, "font", &font_desc, NULL); - gtk_style_context_get_padding (context, state_flags, &padding); - - pango_layout_set_font_description (layout, font_desc); - pango_layout_get_pixel_size (layout, NULL, &font_height); - - pango_font_description_free (font_desc); - gtk_style_context_restore (context); - - final_height = padding.top + font_height + padding.bottom; - - gtk_style_context_save (context); - gtk_style_context_add_class (context, "week-names"); - gtk_style_context_get (context, state_flags, "font", &font_desc, NULL); - gtk_style_context_get_padding (context, state_flags, &padding); - - pango_layout_set_font_description (layout, font_desc); - pango_layout_get_pixel_size (layout, NULL, &font_height); - - pango_font_description_free (font_desc); - gtk_style_context_restore (context); - - final_height += padding.top + font_height + padding.bottom; - - return final_height; -} static void -gcal_week_header_finalize (GObject *object) -{ - GcalWeekHeader *self = GCAL_WEEK_HEADER (object); - gint i; - - gcal_clear_date_time (&self->active_date); - - for (i = 0; i < 7; i++) - g_list_free (self->events[i]); - - for (i = 0; i < G_N_ELEMENTS (self->weather_infos); i++) - wid_clear (&self->weather_infos[i]); - - G_OBJECT_CLASS (gcal_week_header_parent_class)->finalize (object); -} - -static void -gcal_week_header_size_allocate (GtkWidget *widget, - GtkAllocation *alloc) -{ - GcalWeekHeader *self = GCAL_WEEK_HEADER (widget); - gint min_header_height; - - min_header_height = get_weekday_names_height (widget); - - gtk_widget_set_margin_top (self->scrolledwindow, min_header_height); - - GTK_WIDGET_CLASS (gcal_week_header_parent_class)->size_allocate (widget, alloc); -} - -static gboolean -gcal_week_header_draw (GtkWidget *widget, - cairo_t *cr) +gcal_week_header_snapshot (GtkWidget *widget, + GtkSnapshot *snapshot) { GtkStyleContext *context; GcalWeekHeader *self; GtkStateFlags state; GdkRGBA color; GtkAllocation alloc; - GtkBorder padding; - GDateTime *week_start, *week_end; - - PangoLayout *layout; - PangoFontDescription *bold_font; - - gdouble cell_width; - gint i, day_abv_font_height, current_cell, today_column; - gint start_x, start_y; - gboolean ltr; - - cairo_save (cr); + gdouble cell_width; + gint current_cell; + gint start_x; + gint start_y; + gint height; + gint width; + gint x; + gint y; /* Fonts and colour selection */ self = GCAL_WEEK_HEADER (widget); @@ -1218,40 +1175,32 @@ gcal_week_header_draw (GtkWidget *widget, start_x = ltr ? gtk_widget_get_allocated_width (self->expand_button_box) : 0; start_y = gtk_widget_get_allocated_height (self->header_labels_box); - gtk_style_context_get_padding (context, state, &padding); gtk_widget_get_allocation (widget, &alloc); if (!ltr) alloc.width -= gtk_widget_get_allocated_width (self->expand_button_box); - gtk_style_context_get_color (context, state, &color); - gdk_cairo_set_source_rgba (cr, &color); - - layout = pango_cairo_create_layout (cr); - gtk_style_context_get (context, state, "font", &bold_font, NULL); - pango_font_description_set_weight (bold_font, PANGO_WEIGHT_MEDIUM); - pango_layout_set_font_description (layout, bold_font); - week_start = gcal_date_time_get_start_of_week (self->active_date); week_end = g_date_time_add_days (week_start, 6); current_cell = g_date_time_get_day_of_week (self->active_date) - 1; current_cell = (7 + current_cell - self->first_weekday) % 7; - today_column = get_today_column (self); cell_width = (alloc.width - start_x) / 7.0; /* Drag and Drop highlight */ if (self->dnd_cell != -1) { - gtk_drag_highlight (widget); - gtk_render_background (context, - cr, - start_x + self->dnd_cell * cell_width, - start_y, - cell_width, - alloc.height - start_y + 6); + gtk_style_context_save (context); + gtk_style_context_add_class (context, "dnd"); - gtk_drag_unhighlight (widget); + gtk_snapshot_render_background (snapshot, + context, + start_x + self->dnd_cell * cell_width, + start_y, + cell_width, + alloc.height - start_y); + + gtk_style_context_restore (context); } /* Draw the selection background */ @@ -1277,216 +1226,45 @@ gcal_week_header_draw (GtkWidget *widget, selection_width = (end - start + 1) * cell_width; selection_x = ltr ? (start * cell_width) : (alloc.width - (start * cell_width + selection_width)); - gtk_render_background (context, - cr, - ALIGNED (start_x + selection_x) + 0.33, - start_y - 6, - ALIGNED (selection_width + 1), - alloc.height - start_y + 6); + gtk_snapshot_render_background (snapshot, + context, + ALIGNED (start_x + selection_x) + 0.33, + start_y - 6, + ALIGNED (selection_width + 1), + alloc.height - start_y + 6); - gtk_render_frame (context, - cr, - ALIGNED (start_x + selection_x) + 0.33, - start_y - 6, - ALIGNED (selection_width + 1), - alloc.height - start_y + 6); + gtk_snapshot_render_frame (snapshot, + context, + ALIGNED (start_x + selection_x) + 0.33, + start_y - 6, + ALIGNED (selection_width + 1), + alloc.height - start_y + 6); gtk_style_context_restore (context); } - pango_layout_get_pixel_size (layout, NULL, &day_abv_font_height); + x = ALIGNED (ltr ? start_x : alloc.width - start_x); + y = start_y; + width = gtk_widget_get_width (widget) - start_x; + height = gtk_widget_get_height (widget) - start_y; - for (i = 0; i < 7; i++) - { - g_autoptr (GDateTime) day = NULL; - WeatherInfoDay *wdinfo; /* unowned */ - gchar *weekday_date, *weekday_abv, *weekday; - gdouble x; - gint day_num_font_height, day_num_font_baseline; - gint font_width; - gint n_day; + gtk_style_context_save (context); + gtk_style_context_add_class (context, "lines"); + gtk_style_context_get_color (context, &color); + gtk_style_context_restore (context); - day = g_date_time_add_days (week_start, i); - n_day = g_date_time_get_day_of_month (day); + gtk_snapshot_save (snapshot); + gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (x, y)); + gcal_week_view_common_snapshot_hour_lines (widget, snapshot, GTK_ORIENTATION_HORIZONTAL, &color, width, height); + gtk_snapshot_restore (snapshot); - if (n_day > g_date_get_days_in_month (g_date_time_get_month (week_start), g_date_time_get_year (week_start))) - n_day = n_day - g_date_get_days_in_month (g_date_time_get_month (week_start), g_date_time_get_year (week_start)); - - /* Draws the date of days in the week */ - weekday_date = g_strdup_printf ("%d", n_day); - - gtk_style_context_save (context); - gtk_style_context_add_class (context, "week-dates"); - gtk_style_context_get (context, state, "font", &bold_font, NULL); - - if (i == today_column) - gtk_style_context_add_class (context, "today"); - - pango_layout_set_font_description (layout, bold_font); - pango_layout_set_text (layout, weekday_date, -1); - - pango_layout_get_pixel_size (layout, &font_width, &day_num_font_height); - day_num_font_baseline = pango_layout_get_baseline (layout) / PANGO_SCALE; - - if (ltr) - x = padding.left + cell_width * i + COLUMN_PADDING + start_x; - else - x = alloc.width - (cell_width * i + font_width + COLUMN_PADDING + start_x); - - gtk_render_layout (context, - cr, - x, - day_abv_font_height + padding.bottom + start_y, - layout); - - gtk_style_context_restore (context); - - /* Draws the days name */ - weekday = g_date_time_format (day, "%a"); - weekday_abv = g_utf8_strup (weekday, -1); - g_free (weekday); - - gtk_style_context_save (context); - gtk_style_context_add_class (context, "week-names"); - gtk_style_context_get (context, state, "font", &bold_font, NULL); - - if (i == today_column) - gtk_style_context_add_class (context, "today"); - - pango_layout_set_font_description (layout, bold_font); - pango_layout_set_text (layout, weekday_abv, -1); - - pango_layout_get_pixel_size (layout, &font_width, NULL); - - if (ltr) - x = padding.left + cell_width * i + COLUMN_PADDING + start_x; - else - x = alloc.width - (cell_width * i + font_width + COLUMN_PADDING + start_x); - - gtk_render_layout (context, - cr, - x, - start_y, - layout); - - gtk_style_context_restore (context); - - /* Draws weather icon if given */ - wdinfo = &self->weather_infos[ltr? i : 6 - i]; - if (wdinfo->winfo != NULL) - { - const gchar *weather_icon_name = gcal_weather_info_get_icon_name (wdinfo->winfo); - const gchar *weather_temp = gcal_weather_info_get_temperature (wdinfo->winfo); - - /* Imagine a box around weather indicators with length MAX(imgW,tempW) - * We compute its width and position in this section. - * Its height is derived from day name and numbers. The icon sticks on - * the very top and the temperature on its base. - */ - gint icon_size = day_num_font_height - 3; /* available space - space between temperature and icon */ - gint temp_width = 0; - gint temp_height = 0; - gint temp_baseline = 0; - - gdouble wibox_width; - gdouble wibox_x; - - if (weather_icon_name != NULL && wdinfo->icon_buf == NULL) - { - GtkIconTheme *theme; /* unowned */ - g_autoptr (GError) err = NULL; - gint icon_flags; - - theme = gtk_icon_theme_get_default (); - /* TODO: catch icon theme changes */ - - icon_flags = ltr? GTK_ICON_LOOKUP_FORCE_SIZE | GTK_ICON_LOOKUP_DIR_LTR - : GTK_ICON_LOOKUP_FORCE_SIZE | GTK_ICON_LOOKUP_DIR_RTL; - wdinfo->icon_buf = gtk_icon_theme_load_icon (theme, weather_icon_name, icon_size, icon_flags, &err); - if (err != NULL) - { - g_assert (wdinfo->icon_buf == NULL); - g_warning ("Could not load icon %s: %s", weather_icon_name, err->message); - } - } - - if (G_LIKELY (weather_temp != NULL)) - { - gtk_style_context_save (context); - gtk_style_context_add_class (context, "week-temperature"); - gtk_style_context_get (context, state, "font", &bold_font, NULL); - - pango_layout_set_font_description (layout, bold_font); - pango_layout_set_text (layout, weather_temp, -1); - - pango_layout_get_pixel_size (layout, &temp_width, &temp_height); - temp_baseline = pango_layout_get_baseline (layout) / PANGO_SCALE; - } - - wibox_width = MAX(icon_size, temp_width); - wibox_x = ltr ? alloc.width - (cell_width * (6 - i) + wibox_width + COLUMN_PADDING) - : padding.left + cell_width * i + COLUMN_PADDING + start_x; - - /* Actually draw weather indicator: */ - if (G_LIKELY (weather_temp != NULL)) - { - gtk_render_layout (context, - cr, - wibox_x + (wibox_width - temp_width) / 2, - day_abv_font_height + padding.bottom + start_y + day_num_font_baseline - temp_baseline, - layout); - gtk_style_context_restore (context); - } - - if (G_LIKELY (wdinfo->icon_buf != NULL)) - { - gdk_cairo_set_source_pixbuf (cr, - wdinfo->icon_buf, - wibox_x + (wibox_width - icon_size) / 2, - start_y); - cairo_paint (cr); - } - } - - - /* Draws the lines after each day of the week */ - gtk_style_context_save (context); - gtk_style_context_add_class (context, "lines"); - - gtk_style_context_get_color (context, state, &color); - gdk_cairo_set_source_rgba (cr, &color); - - cairo_set_line_width (cr, 0.25); - - cairo_move_to (cr, - ALIGNED (ltr ? (cell_width * i + start_x) : (alloc.width - (cell_width * i + start_x))), - day_abv_font_height + padding.bottom + start_y); - - cairo_rel_line_to (cr, - 0.0, - gtk_widget_get_allocated_height (widget) - day_abv_font_height - start_y + padding.bottom); - cairo_stroke (cr); - - gtk_style_context_restore (context); - - g_free (weekday_date); - g_free (weekday_abv); - } - - cairo_restore (cr); - - pango_font_description_free (bold_font); - g_object_unref (layout); - - GTK_WIDGET_CLASS (gcal_week_header_parent_class)->draw (widget, cr); + GTK_WIDGET_CLASS (gcal_week_header_parent_class)->snapshot (widget, snapshot); g_clear_pointer (&week_start, g_date_time_unref); g_clear_pointer (&week_end, g_date_time_unref); - - return FALSE; } - +#if 0 // TODO: DND static gint get_dnd_cell (GtkWidget *widget, gint x, @@ -1633,6 +1411,28 @@ gcal_week_header_drag_leave (GtkWidget *widget, gtk_widget_queue_draw (widget); } +#endif + +/* + * GObject overrides + */ + +static void +gcal_week_header_finalize (GObject *object) +{ + GcalWeekHeader *self = GCAL_WEEK_HEADER (object); + gint i; + + gcal_clear_date_time (&self->active_date); + + for (i = 0; i < 7; i++) + g_list_free (self->events[i]); + + for (i = 0; i < G_N_ELEMENTS (self->weather_infos); i++) + wid_clear (&self->weather_infos[i]); + + G_OBJECT_CLASS (gcal_week_header_parent_class)->finalize (object); +} static void gcal_week_header_class_init (GcalWeekHeaderClass *kclass) @@ -1642,11 +1442,7 @@ gcal_week_header_class_init (GcalWeekHeaderClass *kclass) object_class->finalize = gcal_week_header_finalize; - widget_class->draw = gcal_week_header_draw; - widget_class->size_allocate = gcal_week_header_size_allocate; - widget_class->drag_motion = gcal_week_header_drag_motion; - widget_class->drag_leave = gcal_week_header_drag_leave; - widget_class->drag_drop = gcal_week_header_drag_drop; + widget_class->snapshot = gcal_week_header_snapshot; signals[EVENT_ACTIVATED] = g_signal_new ("event-activated", GCAL_TYPE_WEEK_HEADER, @@ -1660,13 +1456,14 @@ gcal_week_header_class_init (GcalWeekHeaderClass *kclass) gtk_widget_class_bind_template_child (widget_class, GcalWeekHeader, expand_button); gtk_widget_class_bind_template_child (widget_class, GcalWeekHeader, expand_button_box); - gtk_widget_class_bind_template_child (widget_class, GcalWeekHeader, expand_button_image); gtk_widget_class_bind_template_child (widget_class, GcalWeekHeader, grid); gtk_widget_class_bind_template_child (widget_class, GcalWeekHeader, header_labels_box); gtk_widget_class_bind_template_child (widget_class, GcalWeekHeader, month_label); + gtk_widget_class_bind_template_child (widget_class, GcalWeekHeader, motion_controller); gtk_widget_class_bind_template_child (widget_class, GcalWeekHeader, scrolledwindow); gtk_widget_class_bind_template_child (widget_class, GcalWeekHeader, sizegroup); gtk_widget_class_bind_template_child (widget_class, GcalWeekHeader, week_label); + gtk_widget_class_bind_template_child (widget_class, GcalWeekHeader, weekdays_box); gtk_widget_class_bind_template_child (widget_class, GcalWeekHeader, year_label); gtk_widget_class_bind_template_callback (widget_class, on_button_pressed); @@ -1680,6 +1477,8 @@ gcal_week_header_class_init (GcalWeekHeaderClass *kclass) static void gcal_week_header_init (GcalWeekHeader *self) { + gint i; + self->expanded = FALSE; self->selection_start = -1; self->selection_end = -1; @@ -1688,17 +1487,42 @@ gcal_week_header_init (GcalWeekHeader *self) gtk_widget_init_template (GTK_WIDGET (self)); - /* This is to avoid stray lines when adding and removing events */ -G_GNUC_BEGIN_IGNORE_DEPRECATIONS - gtk_container_set_reallocate_redraws (GTK_CONTAINER (self), TRUE); -G_GNUC_END_IGNORE_DEPRECATIONS + for (i = 0; i < 7; i++) + { + WeekdayHeader *header = &self->weekday_header[i]; + GtkWidget *box; + box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_widget_set_margin_start (box, 6); + + header->weekday_name_label = gtk_label_new (""); + gtk_widget_set_hexpand (header->weekday_name_label, TRUE); + gtk_widget_add_css_class (header->weekday_name_label, "heading"); + gtk_widget_add_css_class (header->weekday_name_label, "dim-label"); + gtk_label_set_xalign (GTK_LABEL (header->weekday_name_label), 0.0); + gtk_box_append (GTK_BOX (box), header->weekday_name_label); + + header->day_number_label = gtk_label_new (""); + gtk_widget_set_hexpand (header->day_number_label, TRUE); + gtk_widget_add_css_class (header->day_number_label, "title-2"); + gtk_widget_add_css_class (header->day_number_label, "dim-label"); + gtk_label_set_xalign (GTK_LABEL (header->day_number_label), 0.0); + gtk_box_append (GTK_BOX (box), header->day_number_label); + + gtk_box_append (self->weekdays_box, box); + + /* Add 7 empty widget to the grid to ensure proper spacing */ + gtk_grid_attach (self->grid, gtk_box_new (GTK_ORIENTATION_VERTICAL, 0), i, 0, 1, 1); + } + +#if 0 // TODO: DND /* Setup the week header as a drag n' drop destination */ gtk_drag_dest_set (GTK_WIDGET (self), 0, NULL, 0, GDK_ACTION_MOVE); +#endif } void @@ -1811,7 +1635,7 @@ gcal_week_header_remove_event (GcalWeekHeader *self, const gchar *uuid) { g_autoptr (GcalEvent) removed_event = NULL; - GList *children, *l; + GtkWidget *child; gint weekday; g_return_if_fail (GCAL_IS_WEEK_HEADER (self)); @@ -1821,26 +1645,30 @@ gcal_week_header_remove_event (GcalWeekHeader *self, if (!removed_event) return; - children = gtk_container_get_children (GTK_CONTAINER (self->grid)); - - for (l = children; l != NULL; l = l->next) + child = gtk_widget_get_first_child (GTK_WIDGET (self->grid)); + while (child) { - GcalEventWidget *child_widget; - GcalEvent *event; + GtkWidget *next = gtk_widget_get_next_sibling (child); - if (!GCAL_IS_EVENT_WIDGET (l->data)) - continue; + if (GCAL_IS_EVENT_WIDGET (child)) + { + GcalEventWidget *child_widget; + GcalEvent *event; - child_widget = GCAL_EVENT_WIDGET (l->data); - event = gcal_event_widget_get_event (child_widget); + child_widget = GCAL_EVENT_WIDGET (child); + event = gcal_event_widget_get_event (child_widget); - if (g_strcmp0 (uuid, gcal_event_get_uid (event)) == 0) - destroy_event_widget (self, l->data); + if (g_strcmp0 (uuid, gcal_event_get_uid (event)) == 0) + destroy_event_widget (self, child); + } + + child = next; } /* Remove from the weekday's GList */ for (weekday = 0; weekday < 7; weekday++) { + GList *l; gint event_position; l = self->events[weekday]; @@ -1855,7 +1683,6 @@ gcal_week_header_remove_event (GcalWeekHeader *self, /* Move remaining events up */ move_events_at_column (self, UP, weekday, event_position); - } /* Check if we eventually can merge events */ @@ -1863,8 +1690,6 @@ gcal_week_header_remove_event (GcalWeekHeader *self, /* And also update the overflow labels */ update_overflow (self); - - g_clear_pointer (&children, g_list_free); } GList* @@ -1872,16 +1697,13 @@ gcal_week_header_get_children_by_uuid (GcalWeekHeader *self, GcalRecurrenceModType mod, const gchar *uuid) { - GList *children, *result; + g_autolist (GcalEventWidget) result = NULL; GCAL_ENTRY; - children = gtk_container_get_children (GTK_CONTAINER (self->grid)); - result = filter_event_list_by_uid_and_modtype (children, mod, uuid); + result = filter_children_by_uid_and_modtype (GTK_WIDGET (self->grid), mod, uuid); - g_list_free (children); - - GCAL_RETURN (result); + GCAL_RETURN (g_steal_pointer (&result)); } GtkSizeGroup* diff --git a/src/gui/views/gcal-week-header.h b/src/gui/views/gcal-week-header.h index 5a37940f..5d45b976 100644 --- a/src/gui/views/gcal-week-header.h +++ b/src/gui/views/gcal-week-header.h @@ -30,7 +30,7 @@ G_BEGIN_DECLS #define GCAL_TYPE_WEEK_HEADER (gcal_week_header_get_type()) -G_DECLARE_FINAL_TYPE (GcalWeekHeader, gcal_week_header, GCAL, WEEK_HEADER, GtkGrid) +G_DECLARE_FINAL_TYPE (GcalWeekHeader, gcal_week_header, GCAL, WEEK_HEADER, GtkBox) void gcal_week_header_set_context (GcalWeekHeader *self, GcalContext *context); diff --git a/src/gui/views/gcal-week-header.ui b/src/gui/views/gcal-week-header.ui index fbf001f2..262b9619 100644 --- a/src/gui/views/gcal-week-header.ui +++ b/src/gui/views/gcal-week-header.ui @@ -1,19 +1,17 @@ -