views/month: Implement event creation flow
Add a click gesture to detect when we have to start monitoring cursor motion. Also add a motion event controller to react to cursor motion, and enable and disable the this motion controller on button press and release, respectively. Selection is implemented as a pair of GDateTimes stored in the month view struct. Whenever a selection begins, the start date is set; the end date is only set when moving the cursor. It is valid to have a start of the selection, and no end (meaning, a 1 day selection); it is, however, forbidden to have an end but not a start. Future improvements can be added later, such as scrolling when moving the cursor close to the edges of the month view. We're gonna have to live without that for now though. Implement the GcalView.clear_marks vfunc now that we have a selection to operate on.
This commit is contained in:
@@ -775,3 +775,44 @@ gcal_month_view_row_get_children_by_uuid (GcalMonthViewRow *self,
|
||||
|
||||
return filter_children_by_uid_and_modtype (GTK_WIDGET (self), mod, uuid);
|
||||
}
|
||||
|
||||
GtkWidget*
|
||||
gcal_month_view_row_get_cell_at_x (GcalMonthViewRow *self,
|
||||
gdouble x)
|
||||
{
|
||||
gint column;
|
||||
gint width;
|
||||
|
||||
g_assert (GCAL_IS_MONTH_VIEW_ROW (self));
|
||||
|
||||
width = gtk_widget_get_width (GTK_WIDGET (self));
|
||||
if (x < 0.0 || x > width)
|
||||
return NULL;
|
||||
|
||||
column = floor (7.0 * x / (gdouble) width);
|
||||
|
||||
if (gtk_widget_get_direction (GTK_WIDGET (self)) != GTK_TEXT_DIR_RTL)
|
||||
return self->day_cells[column];
|
||||
else
|
||||
return self->day_cells[7 - column - 1];
|
||||
}
|
||||
|
||||
void
|
||||
gcal_month_view_row_update_selection (GcalMonthViewRow *self,
|
||||
GcalRange *selection_range)
|
||||
{
|
||||
g_assert (GCAL_IS_MONTH_VIEW_ROW (self));
|
||||
|
||||
for (guint i = 0; i < 7; i++)
|
||||
{
|
||||
GcalMonthCell *month_cell;
|
||||
GDateTime *cell_date;
|
||||
gboolean selected;
|
||||
|
||||
month_cell = GCAL_MONTH_CELL (self->day_cells[i]);
|
||||
cell_date = gcal_month_cell_get_date (month_cell);
|
||||
selected = selection_range && gcal_range_contains_datetime (selection_range, cell_date);
|
||||
|
||||
gcal_month_cell_set_selected (month_cell, selected);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,4 +57,9 @@ GList* gcal_month_view_row_get_children_by_uuid (GcalMonthViewR
|
||||
GcalRecurrenceModType mod,
|
||||
const gchar *uuid);
|
||||
|
||||
GtkWidget* gcal_month_view_row_get_cell_at_x (GcalMonthViewRow *self,
|
||||
gdouble x);
|
||||
|
||||
void gcal_month_view_row_update_selection (GcalMonthViewRow *self,
|
||||
GcalRange *selection_range);
|
||||
G_END_DECLS
|
||||
|
||||
@@ -52,6 +52,8 @@ struct _GcalMonthView
|
||||
GtkWidget *year_label;
|
||||
GtkWidget *weekday_label[7];
|
||||
|
||||
GtkEventController *motion_controller;
|
||||
|
||||
GPtrArray *week_rows;
|
||||
GHashTable *events;
|
||||
|
||||
@@ -61,6 +63,11 @@ struct _GcalMonthView
|
||||
GtkWidget *relative_to;
|
||||
} overflow;
|
||||
|
||||
struct {
|
||||
GDateTime *start;
|
||||
GDateTime *end;
|
||||
} selection;
|
||||
|
||||
/* Ranges from [0.0, 1.0] */
|
||||
gdouble row_offset;
|
||||
AdwAnimation *row_offset_animation;
|
||||
@@ -676,11 +683,161 @@ animate_row_scroll (GcalMonthView *self,
|
||||
GCAL_EXIT;
|
||||
}
|
||||
|
||||
static void
|
||||
update_selection_range (GcalMonthView *self)
|
||||
{
|
||||
g_autoptr (GcalRange) selection_range = NULL;
|
||||
GDateTime *selection_start;
|
||||
GDateTime *selection_end;
|
||||
|
||||
selection_start = self->selection.start;
|
||||
selection_end = self->selection.end;
|
||||
|
||||
if (selection_start)
|
||||
{
|
||||
if (!selection_end)
|
||||
selection_end = selection_start;
|
||||
|
||||
/* Swap dates if end is before start */
|
||||
if (gcal_date_time_compare_date (selection_start, selection_end) > 0)
|
||||
{
|
||||
GDateTime *aux = selection_end;
|
||||
selection_end = selection_start;
|
||||
selection_start = aux;
|
||||
}
|
||||
|
||||
selection_range = gcal_range_new (selection_start, selection_end, GCAL_RANGE_DATE_ONLY);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert (selection_end == NULL);
|
||||
selection_range = NULL;
|
||||
}
|
||||
|
||||
for (gint i = 0; i < self->week_rows->len; i++)
|
||||
{
|
||||
GcalMonthViewRow *row = g_ptr_array_index (self->week_rows, i);
|
||||
gcal_month_view_row_update_selection (row, selection_range);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Callbacks
|
||||
*/
|
||||
|
||||
static void
|
||||
on_click_gesture_pressed_cb (GtkGestureClick *click_gesture,
|
||||
gint n_press,
|
||||
gdouble x,
|
||||
gdouble y,
|
||||
GcalMonthView *self)
|
||||
{
|
||||
GtkWidget *widget_at_position;
|
||||
GtkWidget *day_cell;
|
||||
|
||||
GCAL_ENTRY;
|
||||
|
||||
widget_at_position = gtk_widget_pick (GTK_WIDGET (self), x, y, GTK_PICK_DEFAULT);
|
||||
day_cell = gtk_widget_get_ancestor (widget_at_position, GCAL_TYPE_MONTH_CELL);
|
||||
|
||||
if (!day_cell)
|
||||
GCAL_RETURN ();
|
||||
|
||||
g_assert (GCAL_IS_MONTH_CELL (day_cell));
|
||||
|
||||
gcal_clear_date_time (&self->selection.start);
|
||||
self->selection.start = g_date_time_ref (gcal_month_cell_get_date (GCAL_MONTH_CELL (day_cell)));
|
||||
|
||||
gtk_event_controller_set_propagation_phase (self->motion_controller, GTK_PHASE_BUBBLE);
|
||||
|
||||
update_selection_range (self);
|
||||
|
||||
GCAL_EXIT;
|
||||
}
|
||||
|
||||
static void
|
||||
on_click_gesture_released_cb (GtkGestureClick *click_gesture,
|
||||
gint n_press,
|
||||
gdouble x,
|
||||
gdouble y,
|
||||
GcalMonthView *self)
|
||||
{
|
||||
g_autoptr (GcalRange) selection_range = NULL;
|
||||
g_autoptr (GDateTime) selection_start = NULL;
|
||||
g_autoptr (GDateTime) selection_end = NULL;
|
||||
GtkWidget *widget_at_position;
|
||||
|
||||
GCAL_ENTRY;
|
||||
|
||||
gtk_event_controller_set_propagation_phase (self->motion_controller, GTK_PHASE_NONE);
|
||||
|
||||
widget_at_position = gtk_widget_pick (GTK_WIDGET (self), x, y, GTK_PICK_DEFAULT);
|
||||
|
||||
if (!widget_at_position || !gtk_widget_get_ancestor (widget_at_position, GCAL_TYPE_MONTH_VIEW_ROW))
|
||||
{
|
||||
gcal_view_clear_marks (GCAL_VIEW (self));
|
||||
GCAL_RETURN ();
|
||||
}
|
||||
|
||||
g_assert (self->selection.start != NULL);
|
||||
|
||||
selection_start = self->selection.start;
|
||||
selection_end = self->selection.end ?: selection_start;
|
||||
|
||||
/* Swap dates if end is before start */
|
||||
if (gcal_date_time_compare_date (selection_start, selection_end) > 0)
|
||||
{
|
||||
GDateTime *aux = selection_end;
|
||||
selection_end = selection_start;
|
||||
selection_start = aux;
|
||||
}
|
||||
|
||||
selection_start = g_date_time_ref (selection_start);
|
||||
selection_end = g_date_time_add_days (selection_end, 1);
|
||||
|
||||
selection_range = gcal_range_new (selection_start, selection_end, GCAL_RANGE_DEFAULT);
|
||||
gcal_view_create_event (GCAL_VIEW (self), selection_range, x, y);
|
||||
|
||||
GCAL_EXIT;
|
||||
}
|
||||
|
||||
static void
|
||||
on_motion_controller_motion_cb (GtkEventControllerMotion *motion_controller,
|
||||
gdouble x,
|
||||
gdouble y,
|
||||
GcalMonthView *self)
|
||||
{
|
||||
GtkWidget *widget_at_position;
|
||||
GtkWidget *day_cell;
|
||||
GtkWidget *row;
|
||||
|
||||
GCAL_ENTRY;
|
||||
|
||||
widget_at_position = gtk_widget_pick (GTK_WIDGET (self), x, y, GTK_PICK_DEFAULT);
|
||||
|
||||
if (!widget_at_position)
|
||||
GCAL_RETURN ();
|
||||
|
||||
row = gtk_widget_get_ancestor (widget_at_position, GCAL_TYPE_MONTH_VIEW_ROW);
|
||||
|
||||
if (!row)
|
||||
GCAL_RETURN ();
|
||||
|
||||
day_cell = gcal_month_view_row_get_cell_at_x (GCAL_MONTH_VIEW_ROW (row), x);
|
||||
|
||||
g_assert (day_cell != NULL);
|
||||
g_assert (GCAL_IS_MONTH_CELL (day_cell));
|
||||
|
||||
gcal_clear_date_time (&self->selection.end);
|
||||
self->selection.end = g_date_time_ref (gcal_month_cell_get_date (GCAL_MONTH_CELL (day_cell)));
|
||||
|
||||
update_selection_range (self);
|
||||
|
||||
GCAL_EXIT;
|
||||
}
|
||||
|
||||
static void
|
||||
on_event_widget_activated_cb (GcalMonthViewRow *row,
|
||||
GcalEventWidget *event_widget,
|
||||
@@ -1076,7 +1233,18 @@ gcal_month_view_set_date (GcalView *view,
|
||||
static void
|
||||
gcal_month_view_clear_marks (GcalView *view)
|
||||
{
|
||||
GCAL_TODO ("gcal_month_view_clear_marks");
|
||||
GcalMonthView *self;
|
||||
|
||||
GCAL_ENTRY;
|
||||
|
||||
self = GCAL_MONTH_VIEW (view);
|
||||
|
||||
gcal_clear_date_time (&self->selection.start);
|
||||
gcal_clear_date_time (&self->selection.end);
|
||||
|
||||
update_selection_range (self);
|
||||
|
||||
GCAL_EXIT;
|
||||
}
|
||||
|
||||
static GList*
|
||||
@@ -1347,6 +1515,8 @@ gcal_month_view_finalize (GObject *object)
|
||||
{
|
||||
GcalMonthView *self = (GcalMonthView *)object;
|
||||
|
||||
gcal_clear_date_time (&self->selection.start);
|
||||
gcal_clear_date_time (&self->selection.end);
|
||||
gcal_clear_date_time (&self->date);
|
||||
g_clear_object (&self->context);
|
||||
|
||||
@@ -1426,6 +1596,7 @@ gcal_month_view_class_init (GcalMonthViewClass *klass)
|
||||
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/calendar/ui/views/gcal-month-view.ui");
|
||||
|
||||
gtk_widget_class_bind_template_child (widget_class, GcalMonthView, month_label);
|
||||
gtk_widget_class_bind_template_child (widget_class, GcalMonthView, motion_controller);
|
||||
gtk_widget_class_bind_template_child (widget_class, GcalMonthView, year_label);
|
||||
|
||||
gtk_widget_class_bind_template_child_full (widget_class, "label_0", FALSE, G_STRUCT_OFFSET (GcalMonthView, weekday_label[0]));
|
||||
@@ -1436,7 +1607,10 @@ gcal_month_view_class_init (GcalMonthViewClass *klass)
|
||||
gtk_widget_class_bind_template_child_full (widget_class, "label_5", FALSE, G_STRUCT_OFFSET (GcalMonthView, weekday_label[5]));
|
||||
gtk_widget_class_bind_template_child_full (widget_class, "label_6", FALSE, G_STRUCT_OFFSET (GcalMonthView, weekday_label[6]));
|
||||
|
||||
gtk_widget_class_bind_template_callback (widget_class, on_click_gesture_pressed_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, on_click_gesture_released_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, on_discrete_scroll_controller_scroll_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, on_motion_controller_motion_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, on_scroll_controller_scroll_begin_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, on_scroll_controller_scroll_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, on_scroll_controller_scroll_end_cb);
|
||||
|
||||
@@ -20,6 +20,21 @@
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<object class="GtkGestureClick">
|
||||
<property name="button">1</property>
|
||||
<signal name="pressed" handler="on_click_gesture_pressed_cb" object="GcalMonthView" swapped="no" />
|
||||
<signal name="released" handler="on_click_gesture_released_cb" object="GcalMonthView" swapped="no" />
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<object class="GtkEventControllerMotion" id="motion_controller">
|
||||
<property name="propagation-phase">none</property>
|
||||
<signal name="motion" handler="on_motion_controller_motion_cb" object="GcalMonthView" swapped="no" />
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<!-- Header -->
|
||||
<child type="header">
|
||||
<object class="GtkBox">
|
||||
|
||||
Reference in New Issue
Block a user