From a85337662925fa15b5b4ee4192ff4d27998341df Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Thu, 14 Sep 2017 01:56:45 -0300 Subject: [PATCH] event-widget: turn into a GtkBin subclass The current GcalEventWidget implementation is a direct GtkWidget subclass, which means we have to take care of all the implementation details such as raising GdkWindows and managing the drawing all by ourselves. The event widget, however, can be easily described through a GtkBuilder file. This would simplify the implementation and makes us maintain less code. Fix that by making GcalEventWidget a GtkBin subclass. --- data/calendar.gresource.xml | 1 + data/theme/gtk-styles.css | 18 +- data/ui/event-widget.ui | 66 ++++ src/css-code.h | 7 +- src/gcal-event-widget.c | 762 +++++++++++++----------------------- src/gcal-event-widget.h | 2 +- src/views/gcal-month-view.c | 6 + src/views/gcal-week-grid.c | 10 +- 8 files changed, 372 insertions(+), 500 deletions(-) create mode 100644 data/ui/event-widget.ui diff --git a/data/calendar.gresource.xml b/data/calendar.gresource.xml index 6b5397e4..4921985a 100644 --- a/data/calendar.gresource.xml +++ b/data/calendar.gresource.xml @@ -6,6 +6,7 @@ ui/date-chooser.ui ui/date-selector.ui ui/edit-dialog.ui + ui/event-widget.ui ui/multi-choice.ui ui/online-account-row.ui ui/quick-add-popover.ui diff --git a/data/theme/gtk-styles.css b/data/theme/gtk-styles.css index 6f614c14..abaa206a 100644 --- a/data/theme/gtk-styles.css +++ b/data/theme/gtk-styles.css @@ -212,28 +212,32 @@ popover.events button { box-shadow: none; } -event-widget { + +/* + * Event widget + */ + +event { border-radius: 2px; - margin: 2px 2px 1px; - padding: 3px 4px; + margin: 1px 1px 0 2px; font-size: 9pt; } -event-widget.color-dark { +event.color-dark { color: white; outline-color: rgba(0, 0, 0, 0.3); } -event-widget.color-dark:backdrop { +event.color-dark:backdrop { color: rgba(255, 255, 255, 0.3); } -event-widget.color-light { +event.color-light { color: black; outline-color: rgba(255, 255, 255, 0.5); } -event-widget.color-light:backdrop { +event.color-light:backdrop { color: rgba(0, 0, 0, 0.3); } diff --git a/data/ui/event-widget.ui b/data/ui/event-widget.ui new file mode 100644 index 00000000..68bc3eef --- /dev/null +++ b/data/ui/event-widget.ui @@ -0,0 +1,66 @@ + + + + diff --git a/src/css-code.h b/src/css-code.h index a0786145..e1e9061c 100644 --- a/src/css-code.h +++ b/src/css-code.h @@ -257,7 +257,12 @@ " alpha(%2$s, 0) 10px,"\ " alpha(%2$s, 0) 16px,"\ " %2$s 17px);"\ -"}" +"}"\ +".color-%1$d.slanted box { margin: 0 16px 0 16px; }"\ +".color-%1$d.slanted-start box { margin-left: 16px; }"\ +".color-%1$d.slanted-start:dir(rtl) box { margin-right: 16px; }"\ +".color-%1$d.slanted-end box { margin-right: 16px; }"\ +".color-%1$d.slanted-end:dir(rtl) box { margin-left: 16px; }" #endif /* CSS_CODE_H */ diff --git a/src/gcal-event-widget.c b/src/gcal-event-widget.c index bee13f2b..6025fc17 100644 --- a/src/gcal-event-widget.c +++ b/src/gcal-event-widget.c @@ -31,12 +31,19 @@ struct _GcalEventWidget { - GtkWidget parent; + GtkBin parent; /* properties */ GDateTime *dt_start; GDateTime *dt_end; + /* widgets */ + GtkWidget *alarm_icon; + GtkWidget *event_box; + GtkWidget *hour_label; + GtkWidget *main_grid; + GtkWidget *summary_label; + /* internal data */ gboolean clock_format_24h : 1; gboolean read_only : 1; @@ -46,7 +53,6 @@ struct _GcalEventWidget GtkOrientation orientation; - GdkWindow *event_window; gboolean button_pressed; }; @@ -66,98 +72,59 @@ enum NUM_SIGNALS }; + + +typedef enum +{ + CURSOR_NONE, + CURSOR_GRAB, + CURSOR_GRABBING +} CursorType; + static guint signals[NUM_SIGNALS] = { 0, }; -G_DEFINE_TYPE_WITH_CODE (GcalEventWidget, gcal_event_widget, GTK_TYPE_WIDGET, +G_DEFINE_TYPE_WITH_CODE (GcalEventWidget, gcal_event_widget, GTK_TYPE_BIN, G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL)); /* * Auxiliary methods */ -static gchar* -get_visible_text (GtkWidget *widget, - GtkOrientation orientation) + +static void +set_cursor (GtkWidget *widget, + CursorType type) { - GcalEventWidget *self; - gboolean is_ltr; - gchar *display_text; - gchar *escaped_text; + GdkDisplay *display; + GdkCursor *cursor; - self = GCAL_EVENT_WIDGET (widget); - is_ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL; + if (!gtk_widget_get_realized (widget)) + return; - /* - * Since we set markup labels, we have to escape text before - * using it, or we get empty event widgets. - */ - escaped_text = g_markup_escape_text (gcal_event_get_summary (self->event), -1); + display = gtk_widget_get_display (widget); - /* - * The text displayed in the widget includes the time when the event - * is timed, or not if the event is all day. - */ - if (gcal_event_get_all_day (self->event)) + switch (type) { - display_text = escaped_text; - } - else - { - GDateTime *local_start_time; - gchar *start_time; - local_start_time = g_date_time_to_local (gcal_event_get_date_start (self->event)); + case CURSOR_NONE: + cursor = NULL; + break; - if (self->clock_format_24h) - start_time = g_date_time_format (local_start_time, "%R"); - else - start_time = g_date_time_format (local_start_time, "%I:%M %P"); + case CURSOR_GRAB: + cursor = gdk_cursor_new_from_name (display, "grab"); + break; - /* - * When in horizontal, draw the elements sequentially; otherwise, - * draw the time in the first line, and the title in subsequent - * lines. - */ - if (orientation == GTK_ORIENTATION_HORIZONTAL) - { - if (is_ltr) - display_text = g_strdup_printf ("(%s) %s", start_time, escaped_text); - else - display_text = g_strdup_printf ("%s (%s)", escaped_text, start_time); - } - else - { - display_text = g_strdup_printf ("%s\n%s", start_time, escaped_text); - } + case CURSOR_GRABBING: + cursor = gdk_cursor_new_from_name (display, "grabbing"); + break; - g_clear_pointer (&local_start_time, g_date_time_unref); - g_clear_pointer (&escaped_text, g_free); - g_clear_pointer (&start_time, g_free); + default: + cursor = NULL; } - return display_text; -} + gdk_window_set_cursor (gtk_widget_get_window (widget), cursor); + gdk_display_flush (display); -static gint -get_vertical_text_height (GtkWidget *widget) -{ - GtkBorder border, padding; - GtkStyleContext *context; - PangoLayout *layout; - gint layout_height; - gchar* display_text; - - context = gtk_widget_get_style_context (widget); - display_text = get_visible_text (widget, GTK_ORIENTATION_VERTICAL); - layout = gtk_widget_create_pango_layout (widget, display_text); - pango_layout_get_pixel_size (layout, NULL, &layout_height); - g_object_unref (layout); - - gtk_style_context_get_border (context, gtk_style_context_get_state (context), &border); - gtk_style_context_get_padding (context, gtk_style_context_get_state (context), &padding); - - g_clear_pointer(&display_text, g_free); - - return layout_height + padding.top + padding.bottom + border.top + border.bottom; + g_clear_object (&cursor); } static void @@ -192,6 +159,10 @@ gcal_event_widget_update_style (GcalEventWidget *self) gtk_style_context_add_class (context, "slanted-start"); else if (slanted_end) gtk_style_context_add_class (context, "slanted-end"); + + /* TODO: adjust margins based on the CSS gradients sizes, not hardcoded */ + gtk_widget_set_margin_start (self->main_grid, slanted_start ? 20 : 4); + gtk_widget_set_margin_end (self->main_grid, slanted_end ? 20 : 4); } static void @@ -397,10 +368,25 @@ gcal_event_widget_set_event_tooltip (GcalEventWidget *self, g_string_free (tooltip_mesg, TRUE); } +static gchar* +get_hour_label (GcalEventWidget *self) +{ + g_autoptr (GDateTime) local_start_time; + + local_start_time = g_date_time_to_local (gcal_event_get_date_start (self->event)); + + if (self->clock_format_24h) + return g_date_time_format (local_start_time, "%R"); + else + return g_date_time_format (local_start_time, "%I:%M %P"); +} + static void gcal_event_widget_set_event_internal (GcalEventWidget *self, GcalEvent *event) { + g_autofree gchar *hour_str = NULL; + /* * This function is called only once, since the property is * set as CONSTRUCT_ONLY. Any other attempt to set an event @@ -433,7 +419,199 @@ gcal_event_widget_set_event_internal (GcalEventWidget *self, G_CALLBACK (gtk_widget_queue_draw), self); + /* Tooltip */ gcal_event_widget_set_event_tooltip (self, event); + + /* Alarm icon */ + gtk_widget_set_visible (self->alarm_icon, gcal_event_has_alarms (event)); + + /* Hour label */ + hour_str = get_hour_label (self); + + gtk_widget_set_visible (self->hour_label, !gcal_event_get_all_day (event)); + gtk_label_set_label (GTK_LABEL (self->hour_label), hour_str); + + /* Summary label */ + g_object_bind_property (event, + "summary", + self->summary_label, + "label", + G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); +} + + +/* + * Callbacks + */ + +static gboolean +enter_notify_event_cb (GtkWidget *event_box, + GdkEvent *event, + GtkWidget *widget) +{ + set_cursor (widget, CURSOR_GRAB); + return GDK_EVENT_PROPAGATE; +} + + +static gboolean +leave_notify_event_cb (GtkWidget *event_box, + GdkEvent *event, + GtkWidget *widget) +{ + set_cursor (widget, CURSOR_NONE); + return GDK_EVENT_PROPAGATE; +} + + +/* + * GtkWidget overrides + */ + +static gboolean +gcal_event_widget_draw (GtkWidget *widget, + cairo_t *cr) +{ + GtkStyleContext *context; + guint width, height; + + context = gtk_widget_get_style_context (widget); + + width = gtk_widget_get_allocated_width (widget); + height = gtk_widget_get_allocated_height (widget); + + gtk_render_background (context, cr, 0, 0, width, height); + gtk_render_frame (context, cr, 0, 0, width, height); + + return GTK_WIDGET_CLASS (gcal_event_widget_parent_class)->draw (widget, cr); +} + +static gboolean +gcal_event_widget_button_press_event (GtkWidget *widget, + GdkEventButton *event) +{ + GcalEventWidget *self; + + self = GCAL_EVENT_WIDGET (widget); + self->button_pressed = TRUE; + + return GDK_EVENT_STOP; +} + +static gboolean +gcal_event_widget_button_release_event (GtkWidget *widget, + GdkEventButton *event) +{ + GcalEventWidget *self; + + self = GCAL_EVENT_WIDGET (widget); + + if (self->button_pressed) + { + set_cursor (widget, CURSOR_NONE); + + self->button_pressed = FALSE; + g_signal_emit (widget, signals[ACTIVATE], 0); + + return GDK_EVENT_STOP; + } + + return GDK_EVENT_PROPAGATE; +} + +static void +gcal_event_widget_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GcalEventWidget *self; + + self = GCAL_EVENT_WIDGET (widget); + + /* Try to put the summary label below if the orientation is vertical */ + if (self->orientation == GTK_ORIENTATION_VERTICAL && !gcal_event_get_all_day (self->event)) + { + gint minimum_grid_height; + + gtk_label_set_line_wrap (GTK_LABEL (self->summary_label), TRUE); + /* This is needed to push the alarm icon to the end of the widget */ + gtk_widget_set_hexpand (self->hour_label, TRUE); + + gtk_container_child_set (GTK_CONTAINER (self->main_grid), + self->summary_label, + "left-attach", 0, + "top-attach", 1, + "width", 3, + NULL); + + gtk_widget_get_preferred_height (self->event_box, &minimum_grid_height, NULL); + + /* + * There is no vertical space to put the summary label below the hour label. + * Thus, reset the label's original attributes. + */ + if (allocation->height < minimum_grid_height) + { + gtk_container_child_set (GTK_CONTAINER (self->main_grid), + self->summary_label, + "left-attach", 1, + "top-attach", 0, + "width", 1, + NULL); + + gtk_label_set_line_wrap (GTK_LABEL (self->summary_label), FALSE); + gtk_widget_set_hexpand (self->hour_label, FALSE); + } + } + + GTK_WIDGET_CLASS (gcal_event_widget_parent_class)->size_allocate (widget, allocation); +} + +static cairo_surface_t* +get_dnd_icon (GtkWidget *widget) +{ + cairo_surface_t *surface; + cairo_t *cr; + + /* Make it transparent */ + surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + gtk_widget_get_allocated_width (widget), + gtk_widget_get_allocated_height (widget)); + + cr = cairo_create (surface); + + gtk_widget_draw (widget, cr); + cairo_destroy (cr); + + return surface; +} + +static void +gcal_event_widget_drag_begin (GtkWidget *widget, + GdkDragContext *context) +{ + GcalEventWidget *self = GCAL_EVENT_WIDGET (widget); + cairo_surface_t *surface; + + if (self->read_only) + { + gtk_drag_cancel (context); + return; + } + + /* Setup the drag n' drop icon */ + surface = get_dnd_icon (widget); + + gtk_drag_set_icon_surface (context, surface); + set_cursor (widget, CURSOR_GRABBING); + + g_clear_pointer (&surface, cairo_surface_destroy); +} + +static gboolean +gcal_event_widget_scroll_event (GtkWidget *widget, + GdkEventScroll *scroll_event) +{ + return GDK_EVENT_PROPAGATE; } @@ -524,407 +702,7 @@ gcal_event_widget_finalize (GObject *object) } static void -gcal_event_widget_get_preferred_width (GtkWidget *widget, - gint *minimum, - gint *natural) -{ - GtkBorder border, padding; - GtkStyleContext *context; - PangoLayout *layout; - gint layout_width; - - context = gtk_widget_get_style_context (widget); - layout = gtk_widget_create_pango_layout (widget, "00:00:00 00:00"); - pango_layout_get_pixel_size (layout, &layout_width, NULL); - g_object_unref (layout); - - gtk_style_context_get_border (context, gtk_style_context_get_state (context), &border); - gtk_style_context_get_padding (context, gtk_style_context_get_state (context), &padding); - - if (minimum != NULL) - *minimum = layout_width + padding.left + padding.right + border.left + border.right; - if (natural != NULL) - *natural = layout_width + padding.left + padding.right + border.left + border.right; -} - -static void -gcal_event_widget_get_preferred_height (GtkWidget *widget, - gint *minimum, - gint *natural) -{ - GcalEventWidget *self; - GtkBorder border, padding; - GtkStyleContext *context; - PangoLayout *layout; - gint layout_height; - gchar* display_text; - - self = GCAL_EVENT_WIDGET (widget); - context = gtk_widget_get_style_context (widget); - display_text = get_visible_text (widget, self->orientation); - layout = gtk_widget_create_pango_layout (widget, display_text); - pango_layout_get_pixel_size (layout, NULL, &layout_height); - g_object_unref (layout); - - gtk_style_context_get_border (context, gtk_style_context_get_state (context), &border); - gtk_style_context_get_padding (context, gtk_style_context_get_state (context), &padding); - - if (minimum != NULL) - *minimum = layout_height + padding.top + padding.bottom + border.top + border.bottom; - if (natural != NULL) - *natural = layout_height + padding.top + padding.bottom + border.top + border.bottom; - - g_clear_pointer(&display_text, g_free); -} - -static void -gcal_event_widget_realize (GtkWidget *widget) -{ - GcalEventWidget *self; - GdkWindow *parent_window; - GdkWindowAttr attributes; - gint attributes_mask; - GtkAllocation allocation; - - GdkCursor* pointer_cursor; - - self = GCAL_EVENT_WIDGET (widget); - gtk_widget_set_realized (widget, TRUE); - - parent_window = gtk_widget_get_parent_window (widget); - 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_SMOOTH_SCROLL_MASK | - GDK_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); - gdk_window_show (self->event_window); - - pointer_cursor = gdk_cursor_new_for_display (gdk_display_get_default (), - GDK_HAND1); - gdk_window_set_cursor (self->event_window, pointer_cursor); -} - -static void -gcal_event_widget_unrealize (GtkWidget *widget) -{ - GcalEventWidget *self; - - self = GCAL_EVENT_WIDGET (widget); - - if (self->event_window != NULL) - { - gtk_widget_unregister_window (widget, self->event_window); - gdk_window_destroy (self->event_window); - self->event_window = NULL; - } - - GTK_WIDGET_CLASS (gcal_event_widget_parent_class)->unrealize (widget); -} - -static void -gcal_event_widget_map (GtkWidget *widget) -{ - GcalEventWidget *self; - - self = GCAL_EVENT_WIDGET (widget); - - GTK_WIDGET_CLASS (gcal_event_widget_parent_class)->map (widget); - - if (self->event_window != NULL) - gdk_window_show (self->event_window); -} - -static void -gcal_event_widget_unmap (GtkWidget *widget) -{ - GcalEventWidget *self; - - self = GCAL_EVENT_WIDGET (widget); - - GTK_WIDGET_CLASS (gcal_event_widget_parent_class)->unmap (widget); - - if (self->event_window != NULL) - gdk_window_hide (self->event_window); -} - -static void -gcal_event_widget_size_allocate (GtkWidget *widget, - GtkAllocation *allocation) -{ - GcalEventWidget *self; - - self = GCAL_EVENT_WIDGET (widget); - gtk_widget_set_allocation (widget, allocation); - - if (gtk_widget_get_realized (widget)) - { - gdk_window_move_resize (self->event_window, - allocation->x, - allocation->y, - allocation->width, - allocation->height); - } -} - -static gboolean -gcal_event_widget_draw (GtkWidget *widget, - cairo_t *cr) -{ - GcalEventWidget *self; - - GtkStyleContext *context; - GtkStateFlags state; - GtkBorder padding; - - gint width, height, layout_height, x; - gint end_gap, icon_size = 0; - gboolean is_ltr; - - PangoLayout *layout; - PangoFontDescription *font_desc; - gchar *display_text; - - self = GCAL_EVENT_WIDGET (widget); - context = gtk_widget_get_style_context (widget); - state = gtk_style_context_get_state (context); - is_ltr = gtk_widget_get_direction (widget) != GTK_TEXT_DIR_RTL; - - gtk_style_context_get_padding (context, state, &padding); - - width = gtk_widget_get_allocated_width (widget); - height = gtk_widget_get_allocated_height (widget); - - gtk_render_background (context, cr, 0, 0, width, height); - gtk_render_frame (context, cr, 0, 0, width, height); - - /* Set the text to be displayed */ - if (self->orientation == GTK_ORIENTATION_HORIZONTAL) - display_text = get_visible_text (widget, GTK_ORIENTATION_HORIZONTAL); - else - { - if (get_vertical_text_height (widget) >= height) - display_text = get_visible_text (widget, GTK_ORIENTATION_HORIZONTAL); - else - display_text = get_visible_text (widget, GTK_ORIENTATION_VERTICAL); - } - - /* Retrieve the font description */ - gtk_style_context_get (context, state, "font", &font_desc, NULL); - layout = gtk_widget_create_pango_layout (widget, ""); - pango_layout_set_markup (layout, display_text, -1); - pango_layout_set_font_description (layout, font_desc); - pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END); - - /* Icon size */ - icon_size = ICON_SIZE; - end_gap = 0; - - if (gcal_event_has_alarms (self->event)) - end_gap += icon_size + (is_ltr ? padding.left : padding.right); - - if (self->read_only) - end_gap += icon_size + (is_ltr ? padding.left : padding.right); - - x = is_ltr ? padding.left : padding.left + end_gap; - - pango_layout_set_width (layout, (width - (padding.left + padding.right + end_gap)) * PANGO_SCALE); - pango_layout_get_pixel_size (layout, NULL, &layout_height); - gtk_render_layout (context, cr, x, padding.top, layout); - -#if 0 - /* TODO: find out a good way to handle this */ - /* - * On vertical event widgets, we assume we might have space to draw the - * event's detail (or at least part of it). - */ - if (self->orientation == GTK_ORIENTATION_VERTICAL && - 2 * padding.top + layout_height + padding.bottom < height && - strlen (gcal_event_get_description (self->event)) > 0) - { - g_autofree gchar *description; - - description = g_strdup_printf ("%s", gcal_event_get_description (self->event)); - pango_layout_set_markup (layout, description, -1); - pango_layout_set_width (layout, (width - (padding.left + padding.right) ) * PANGO_SCALE); - pango_layout_set_height (layout, (height - (2 * padding.top + layout_height + padding.bottom) ) * PANGO_SCALE); - pango_layout_set_font_description (layout, font_desc); - pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END); - - gtk_render_layout (context, - cr, - x, - 2 * padding.top + layout_height + padding.bottom, - layout); - } -#endif - - x += is_ltr ? (width - end_gap - padding.left - padding.right) : (- padding.right - padding.left - icon_size); - - /* render reminder icon */ - if (gcal_event_has_alarms (self->event)) - { - GtkIconTheme *icon_theme; - GtkIconInfo *icon_info; - GdkPixbuf *pixbuf; - gboolean was_symbolic; - - icon_theme = gtk_icon_theme_get_default (); - icon_info = gtk_icon_theme_lookup_icon (icon_theme, - "alarm-symbolic", - icon_size, - 0); - pixbuf = gtk_icon_info_load_symbolic_for_context (icon_info, - context, - &was_symbolic, - NULL); - - gtk_render_icon (context, - cr, - pixbuf, - x + padding.left, - padding.top); - - x += is_ltr ? (padding.left + icon_size) : (- padding.right - icon_size); - - g_object_unref (pixbuf); - cairo_paint (cr); - } - - /* render locked icon */ - if (self->read_only) - { - GtkIconTheme *icon_theme; - GtkIconInfo *icon_info; - GdkPixbuf *pixbuf; - gboolean was_symbolic; - - icon_theme = gtk_icon_theme_get_default (); - icon_info = gtk_icon_theme_lookup_icon (icon_theme, - "changes-prevent-symbolic", - icon_size, - 0); - pixbuf = gtk_icon_info_load_symbolic_for_context (icon_info, - context, - &was_symbolic, - NULL); - - gtk_render_icon (context, - cr, - pixbuf, - x + padding.left, - padding.top); - - g_object_unref (pixbuf); - cairo_paint (cr); - } - - pango_font_description_free (font_desc); - g_object_unref (layout); - g_free (display_text); - - return GTK_WIDGET_CLASS (gcal_event_widget_parent_class)->draw (widget, cr); -} - -static gboolean -gcal_event_widget_button_press_event (GtkWidget *widget, - GdkEventButton *event) -{ - GcalEventWidget *self; - - self = GCAL_EVENT_WIDGET (widget); - self->button_pressed = TRUE; - return TRUE; -} - -static gboolean -gcal_event_widget_button_release_event (GtkWidget *widget, - GdkEventButton *event) -{ - GcalEventWidget *self; - - self = GCAL_EVENT_WIDGET (widget); - - if (self->button_pressed) - { - self->button_pressed = FALSE; - g_signal_emit (widget, signals[ACTIVATE], 0); - return TRUE; - } - - return FALSE; -} - -static cairo_surface_t* -get_dnd_icon (GtkWidget *widget) -{ - cairo_surface_t *surface; - cairo_t *cr; - - /* Make it transparent */ - surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, - gtk_widget_get_allocated_width (widget), - gtk_widget_get_allocated_height (widget)); - - cr = cairo_create (surface); - - gtk_widget_draw (widget, cr); - cairo_destroy (cr); - - return surface; -} - -static void -gcal_event_widget_drag_begin (GtkWidget *widget, - GdkDragContext *context) -{ - GcalEventWidget *self = GCAL_EVENT_WIDGET (widget); - cairo_surface_t *surface; - - if (self->read_only) - { - gtk_drag_cancel (context); - return; - } - - /* Setup the drag n' drop icon */ - surface = get_dnd_icon (widget); - - gtk_drag_set_icon_surface (context, surface); - - g_clear_pointer (&surface, cairo_surface_destroy); -} - -static gboolean -gcal_event_widget_scroll_event (GtkWidget *widget, - GdkEventScroll *scroll_event) -{ - return GDK_EVENT_PROPAGATE; -} - -static void -gcal_event_widget_class_init(GcalEventWidgetClass *klass) +gcal_event_widget_class_init (GcalEventWidgetClass *klass) { GObjectClass *object_class; GtkWidgetClass *widget_class; @@ -935,17 +713,11 @@ gcal_event_widget_class_init(GcalEventWidgetClass *klass) object_class->finalize = gcal_event_widget_finalize; widget_class = GTK_WIDGET_CLASS (klass); - widget_class->get_preferred_width = gcal_event_widget_get_preferred_width; - widget_class->get_preferred_height = gcal_event_widget_get_preferred_height; - widget_class->realize = gcal_event_widget_realize; - widget_class->unrealize = gcal_event_widget_unrealize; - widget_class->map = gcal_event_widget_map; - widget_class->unmap = gcal_event_widget_unmap; - widget_class->size_allocate = gcal_event_widget_size_allocate; - widget_class->draw = gcal_event_widget_draw; widget_class->button_press_event = gcal_event_widget_button_press_event; widget_class->button_release_event = gcal_event_widget_button_release_event; widget_class->drag_begin = gcal_event_widget_drag_begin; + widget_class->draw = gcal_event_widget_draw; + widget_class->size_allocate = gcal_event_widget_size_allocate; widget_class->scroll_event = gcal_event_widget_scroll_event; /** @@ -954,7 +726,7 @@ gcal_event_widget_class_init(GcalEventWidgetClass *klass) * The end date this widget represents. Notice that this may * differ from the event's end date. For example, if the event * spans more than one month and we're in Month View, the end - * date marks the last day this event is visible. + * date marks the last day this event widget is visible. */ g_object_class_install_property (object_class, PROP_DATE_END, @@ -970,7 +742,7 @@ gcal_event_widget_class_init(GcalEventWidgetClass *klass) * The start date this widget represents. Notice that this may * differ from the event's start date. For example, if the event * spans more than one month and we're in Month View, the start - * date marks the first day this event is visible. + * date marks the first day this event widget is visible. */ g_object_class_install_property (object_class, PROP_DATE_START, @@ -1009,7 +781,18 @@ gcal_event_widget_class_init(GcalEventWidgetClass *klass) G_TYPE_NONE, 0); - gtk_widget_class_set_css_name (widget_class, "event-widget"); + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/calendar/event-widget.ui"); + + gtk_widget_class_bind_template_child (widget_class, GcalEventWidget, alarm_icon); + gtk_widget_class_bind_template_child (widget_class, GcalEventWidget, event_box); + gtk_widget_class_bind_template_child (widget_class, GcalEventWidget, hour_label); + gtk_widget_class_bind_template_child (widget_class, GcalEventWidget, main_grid); + gtk_widget_class_bind_template_child (widget_class, GcalEventWidget, summary_label); + + gtk_widget_class_bind_template_callback (widget_class, enter_notify_event_cb); + gtk_widget_class_bind_template_callback (widget_class, leave_notify_event_cb); + + gtk_widget_class_set_css_name (widget_class, "event"); } static void @@ -1021,7 +804,8 @@ gcal_event_widget_init (GcalEventWidget *self) self->clock_format_24h = is_clock_format_24h (); self->orientation = GTK_ORIENTATION_HORIZONTAL; - gtk_widget_set_has_window (widget, FALSE); + gtk_widget_init_template (GTK_WIDGET (self)); + gtk_widget_set_can_focus (widget, TRUE); /* Setup the event widget as a drag source */ @@ -1048,7 +832,7 @@ gcal_event_widget_set_read_only (GcalEventWidget *event, { g_return_if_fail (GCAL_IS_EVENT_WIDGET (event)); - if (read_only) + if (!read_only) { GtkWidget *widget = GTK_WIDGET (event); diff --git a/src/gcal-event-widget.h b/src/gcal-event-widget.h index b4034bda..f54216d5 100644 --- a/src/gcal-event-widget.h +++ b/src/gcal-event-widget.h @@ -31,7 +31,7 @@ G_BEGIN_DECLS #define GCAL_TYPE_EVENT_WIDGET (gcal_event_widget_get_type ()) -G_DECLARE_FINAL_TYPE (GcalEventWidget, gcal_event_widget, GCAL, EVENT_WIDGET, GtkWidget) +G_DECLARE_FINAL_TYPE (GcalEventWidget, gcal_event_widget, GCAL, EVENT_WIDGET, GtkBin) GtkWidget* gcal_event_widget_new (GcalEvent *event); diff --git a/src/views/gcal-month-view.c b/src/views/gcal-month-view.c index 7b80348e..2bc25f66 100644 --- a/src/views/gcal-month-view.c +++ b/src/views/gcal-month-view.c @@ -1791,6 +1791,9 @@ gcal_month_view_size_allocate (GtkWidget *widget, pos_x = cell_width * column + margin.left; pos_y = cell_height * (row + first_row_gap) + start_grid_y + margin.top; + /* TODO: find a better way to take the lines into account */ + pos_y += 2; + child_allocation.x = pos_x; child_allocation.y = pos_y + vertical_cell_space - size_left[cell_idx]; child_allocation.width = cell_width * length - (margin.left + margin.right); @@ -1865,6 +1868,9 @@ gcal_month_view_size_allocate (GtkWidget *widget, pos_x = cell_width * (i % 7) + margin.left; pos_y = cell_height * ((i / 7) + first_row_gap) + start_grid_y + margin.top; + /* TODO: find a better way to take the lines into account */ + pos_y += 2; + child_allocation.x = pos_x; child_allocation.y = pos_y + vertical_cell_space - size_left[i]; child_allocation.width = cell_width - (margin.left + margin.right); diff --git a/src/views/gcal-week-grid.c b/src/views/gcal-week-grid.c index a070a3d5..a8eddea8 100644 --- a/src/views/gcal-week-grid.c +++ b/src/views/gcal-week-grid.c @@ -615,8 +615,8 @@ gcal_week_grid_size_allocate (GtkWidget *widget, gboolean ltr; gdouble minutes_height; gdouble column_width; + guint x, y; guint i; - guint x; /* No need to relayout stuff if nothing changed */ if (allocation->height == gtk_widget_get_allocated_height (widget) && @@ -698,15 +698,21 @@ gcal_week_grid_size_allocate (GtkWidget *widget, width = column_width / events_at_range - margin.left - margin.right; height = (data->end - data->start) * minutes_height - margin.top - margin.bottom; offset = (width + margin.left + margin.right) * widget_index; + y = (data->start % MINUTES_PER_DAY) * minutes_height + margin.top; if (ltr) x = column_width * i + offset + allocation->x + margin.left + 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 += 1; + /* Setup the child position and size */ child_allocation.x = x; - child_allocation.y = (data->start % MINUTES_PER_DAY) * minutes_height + margin.top; + child_allocation.y = y; child_allocation.width = width; child_allocation.height = height;