[california/wip/730601-week-drag] Day pane and all-day cell selection in place
- From: Jim Nelson <jnelson src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [california/wip/730601-week-drag] Day pane and all-day cell selection in place
- Date: Wed, 28 May 2014 23:28:06 +0000 (UTC)
commit 67be0d48148faff6ecee2946ec796622981c6c6f
Author: Jim Nelson <jim yorba org>
Date: Wed May 28 16:27:45 2014 -0700
Day pane and all-day cell selection in place
src/toolkit/toolkit-button-connector.vala | 20 ++++--
src/toolkit/toolkit-motion-connector.vala | 10 +++
src/toolkit/toolkit-motion-event.vala | 14 ++--
src/view/month/month-grid.vala | 11 +---
src/view/week/week-controller.vala | 7 ++
src/view/week/week-day-pane.vala | 15 ++++-
src/view/week/week-grid.vala | 103 +++++++++++++++++++++++-----
7 files changed, 135 insertions(+), 45 deletions(-)
---
diff --git a/src/toolkit/toolkit-button-connector.vala b/src/toolkit/toolkit-button-connector.vala
index eac3765..e97e183 100644
--- a/src/toolkit/toolkit-button-connector.vala
+++ b/src/toolkit/toolkit-button-connector.vala
@@ -84,7 +84,7 @@ public class ButtonConnector : EventConnector {
* Signal subscribers should cancel the Cancellable to prevent propagation of the event.
* This will prevent the various "clicked" signals from firing.
*/
- public signal void pressed(ButtonEvent details);
+ public signal void pressed(Gtk.Widget widget, Button button, Gdk.Point point, Gdk.EventType event_type);
/**
* The "raw" "button-released" signal received by { link ButtonConnector}.
@@ -92,7 +92,7 @@ public class ButtonConnector : EventConnector {
* Signal subscribers should cancel the Cancellable to prevent propagation of the event.
* This will prevent the various "clicked" signals from firing.
*/
- public signal void released(ButtonEvent details);
+ public signal void released(Gtk.Widget widget, Button button, Gdk.Point point, Gdk.EventType event_type);
/**
* Fired when a button is pressed and released once.
@@ -138,8 +138,9 @@ public class ButtonConnector : EventConnector {
*
* @return { link EVENT_STOP} or { link EVENT_PROPAGATE}.
*/
- protected virtual bool notify_pressed(ButtonEvent details) {
- pressed(details);
+ protected virtual bool notify_pressed(Gtk.Widget widget, Button button, Gdk.Point point,
+ Gdk.EventType event_type) {
+ pressed(widget, button, point, event_type);
return stop_propagation();
}
@@ -150,8 +151,9 @@ public class ButtonConnector : EventConnector {
*
* @return { link EVENT_STOP} or { link EVENT_PROPAGATE}.
*/
- protected virtual bool notify_released(ButtonEvent details) {
- released(details);
+ protected virtual bool notify_released(Gtk.Widget widget, Button button, Gdk.Point point,
+ Gdk.EventType event_type) {
+ released(widget, button, point, event_type);
return stop_propagation();
}
@@ -244,7 +246,8 @@ public class ButtonConnector : EventConnector {
case Gdk.EventType.2BUTTON_PRESS:
case Gdk.EventType.3BUTTON_PRESS:
// notify of raw event
- if (notify_pressed(new ButtonEvent(widget, event)) == EVENT_STOP) {
+ Gdk.Point point = Gdk.Point() { x = (int) event.x, y = (int) event.y };
+ if (notify_pressed(widget, button, point, event.type) == EVENT_STOP) {
// drop any lingering state
if (button_states != null)
button_states.unset(widget);
@@ -269,7 +272,8 @@ public class ButtonConnector : EventConnector {
case Gdk.EventType.BUTTON_RELEASE:
// notify of raw event
- if (notify_released(new ButtonEvent(widget, event)) == EVENT_STOP) {
+ Gdk.Point point = Gdk.Point() { x = (int) event.x, y = (int) event.y };
+ if (notify_released(widget, button, point, event.type) == EVENT_STOP) {
// release lingering state
if (button_states != null)
button_states.unset(widget);
diff --git a/src/toolkit/toolkit-motion-connector.vala b/src/toolkit/toolkit-motion-connector.vala
index 515dfcb..dafda24 100644
--- a/src/toolkit/toolkit-motion-connector.vala
+++ b/src/toolkit/toolkit-motion-connector.vala
@@ -41,6 +41,16 @@ public class MotionConnector : EventConnector {
}
/**
+ * Create a { link MotionConnector} that only reports motion when a button is depressed.
+ *
+ * This generates fewer events and should be used if the subscribers signal handlers are only
+ * interested in motion while a button is depressed.
+ */
+ public MotionConnector.button_only() {
+ base (Gdk.EventMask.BUTTON_MOTION_MASK | Gdk.EventMask.ENTER_NOTIFY_MASK |
Gdk.EventMask.LEAVE_NOTIFY_MASK);
+ }
+
+ /**
* Subclasses may override this call to update state before or after the signal fires.
*/
protected void notify_entered(MotionEvent event) {
diff --git a/src/toolkit/toolkit-motion-event.vala b/src/toolkit/toolkit-motion-event.vala
index 0142fbd..697b64e 100644
--- a/src/toolkit/toolkit-motion-event.vala
+++ b/src/toolkit/toolkit-motion-event.vala
@@ -19,8 +19,8 @@ public class MotionEvent : BaseObject {
/**
* The pointer location at the time of the event.
*/
- private Gdk.Point _location = Gdk.Point();
- public Gdk.Point location { get { return _location; } }
+ private Gdk.Point _point = Gdk.Point();
+ public Gdk.Point point { get { return _point; } }
/**
* The state of the modifier keys at the time of the event.
@@ -29,15 +29,15 @@ public class MotionEvent : BaseObject {
internal MotionEvent(Gtk.Widget widget, Gdk.EventMotion event) {
this.widget = widget;
- _location.x = (int) event.x;
- _location.y = (int) event.y;
+ _point.x = (int) event.x;
+ _point.y = (int) event.y;
modifiers = event.state;
}
internal MotionEvent.for_crossing(Gtk.Widget widget, Gdk.EventCrossing event) {
this.widget = widget;
- _location.x = (int) event.x;
- _location.y = (int) event.y;
+ _point.x = (int) event.x;
+ _point.y = (int) event.y;
modifiers = event.state;
}
@@ -61,7 +61,7 @@ public class MotionEvent : BaseObject {
}
public override string to_string() {
- return "MotionEvent %d,%d".printf(location.x, location.y);
+ return "MotionEvent %d,%d".printf(point.x, point.y);
}
}
diff --git a/src/view/month/month-grid.vala b/src/view/month/month-grid.vala
index 80ec831..497e4c9 100644
--- a/src/view/month/month-grid.vala
+++ b/src/view/month/month-grid.vala
@@ -380,14 +380,11 @@ private class Grid : Gtk.Grid {
owner.request_display_event(event, release_cell, release_point);
stop_propagation = true;
}
- } else if (press_cell.date != null && release_cell.date != null) {
+ } else {
// create multi-day event
owner.request_create_all_day_event(new Calendar.DateSpan(press_cell.date, release_cell.date),
release_cell, release_point);
stop_propagation = true;
- } else {
- // make sure to clear selections if no action is taken
- unselect_all();
}
return stop_propagation;
@@ -441,15 +438,11 @@ private class Grid : Gtk.Grid {
if (hover_cell == null || hover_cell == press_cell)
return false;
- // both must have dates as well
- if (press_cell.date == null || hover_cell.date == null)
- return false;
-
// mark two cells and all in-between as selected, being sure to mark any previous selected
// as unselected
Calendar.DateSpan span = new Calendar.DateSpan(press_cell.date, hover_cell.date);
foreach_cell((cell) => {
- cell.selected = (cell.date != null) ? cell.date in span : false;
+ cell.selected = cell.date in span;
return true;
});
diff --git a/src/view/week/week-controller.vala b/src/view/week/week-controller.vala
index 4a4789c..fbba969 100644
--- a/src/view/week/week-controller.vala
+++ b/src/view/week/week-controller.vala
@@ -108,6 +108,13 @@ public class Controller : BaseObject, View.Controllable {
* @inheritDoc
*/
public void unselect_all() {
+ Grid? current = get_current_grid();
+ if (current != null)
+ current.unselect_all();
+ }
+
+ private Grid? get_current_grid() {
+ return stack.get_visible_child() as Grid;
}
private Gtk.Widget model_presentation(Calendar.Week week, out string? id) {
diff --git a/src/view/week/week-day-pane.vala b/src/view/week/week-day-pane.vala
index 82be367..1171fe8 100644
--- a/src/view/week/week-day-pane.vala
+++ b/src/view/week/week-day-pane.vala
@@ -146,11 +146,22 @@ internal class DayPane : Pane, Common.InstanceContainer {
}
public void update_selection(Calendar.WallTime wall_time) {
+ // down down to the nearest 15-minute mark
+ Calendar.WallTime rounded_time = wall_time.round_down(15, Calendar.TimeUnit.MINUTE);
+
+ // assign start first, end second (ordering doesn't matter, possible to select upwards)
if (selection_start == null) {
- selection_start = wall_time;
+ selection_start = rounded_time;
selection_end = null;
} else {
- selection_end = wall_time;
+ selection_end = rounded_time;
+ }
+
+ // if same, treat as unselected
+ if (selection_start != null && selection_end != null && selection_start.equal_to(selection_end)) {
+ clear_selection();
+
+ return;
}
queue_draw();
diff --git a/src/view/week/week-grid.vala b/src/view/week/week-grid.vala
index 45359de..a6aaa14 100644
--- a/src/view/week/week-grid.vala
+++ b/src/view/week/week-grid.vala
@@ -44,6 +44,8 @@ internal class Grid : Gtk.Box {
private Gee.HashMap<Calendar.Date, AllDayCell> date_to_all_day = new Gee.HashMap<Calendar.Date,
AllDayCell>();
private Toolkit.ButtonConnector instance_container_button_connector = new Toolkit.ButtonConnector();
+ private Toolkit.ButtonConnector all_day_button_connector = new Toolkit.ButtonConnector();
+ private Toolkit.ButtonConnector day_pane_button_connector = new Toolkit.ButtonConnector();
private Toolkit.MotionConnector day_pane_motion_connector = new Toolkit.MotionConnector();
private Toolkit.MotionConnector all_day_cell_motion_connector = new Toolkit.MotionConnector();
private Gtk.ScrolledWindow scrolled_panes;
@@ -106,6 +108,7 @@ internal class Grid : Gtk.Box {
// label and the day panes
AllDayCell all_day_cell = new AllDayCell(this, date);
instance_container_button_connector.connect_to(all_day_cell);
+ all_day_button_connector.connect_to(all_day_cell);
all_day_cell_motion_connector.connect_to(all_day_cell);
top_grid.attach(all_day_cell, col, 1, 1, 1);
@@ -115,6 +118,7 @@ internal class Grid : Gtk.Box {
DayPane pane = new DayPane(this, date);
pane.expand = true;
instance_container_button_connector.connect_to(pane);
+ day_pane_button_connector.connect_to(pane);
day_pane_motion_connector.connect_to(pane);
pane_grid.attach(pane, col, 1, 1, 1);
@@ -137,15 +141,18 @@ internal class Grid : Gtk.Box {
scrolled_panes.get_vscrollbar().realize.connect(on_realloc_right_spacer);
scrolled_panes.get_vscrollbar().size_allocate.connect(on_realloc_right_spacer);
- // connect panes' button event signal handlers
- instance_container_button_connector.released.connect(on_instance_container_button_released);
+ // connect instance connectors button event signal handlers for click/double-clicked
instance_container_button_connector.clicked.connect(on_instance_container_clicked);
instance_container_button_connector.double_clicked.connect(on_instance_container_double_clicked);
- // connect to motion event handlers
+ // connect to individual motion event handlers for different types of instance containers
all_day_cell_motion_connector.button_motion.connect(on_all_day_cell_button_motion);
day_pane_motion_connector.motion.connect(on_day_pane_motion);
+ // connect to individual button released handlers for different types of instance containers
+ all_day_button_connector.released.connect(on_all_day_cell_button_released);
+ day_pane_button_connector.released.connect(on_day_pane_button_released);
+
// set up calendar subscriptions for the week
subscriptions = new Backing.CalendarSubscriptionManager(
new Calendar.ExactTimeSpan.from_span(week, Calendar.Timezone.local));
@@ -176,6 +183,14 @@ internal class Grid : Gtk.Box {
scrolled_panes.vadjustment.changed.connect(on_vadjustment_changed);
}
+ public void unselect_all() {
+ foreach (AllDayCell day_cell in date_to_all_day.values)
+ day_cell.selected = false;
+
+ foreach (DayPane day_pane in date_to_panes.values)
+ day_pane.clear_selection();
+ }
+
private void on_vadjustment_changed(Gtk.Adjustment vadj) {
// wait for vadjustment to look like something reasonable; also, only do this once
if (vadj.upper <= 1.0 || vadj_init)
@@ -348,36 +363,86 @@ internal class Grid : Gtk.Box {
details.press_point);
}
- private void on_instance_container_button_released(Toolkit.ButtonEvent details) {
- DayPane? day_pane = details.widget as DayPane;
- if (day_pane == null)
+ private void on_day_pane_motion(Toolkit.MotionEvent details) {
+ DayPane day_pane = (DayPane) details.widget;
+
+ // only update selection as long as
+ if (details.is_button_pressed(Toolkit.Button.PRIMARY))
+ day_pane.update_selection(day_pane.get_wall_time(details.point.y));
+ else
+ day_pane.clear_selection();
+ }
+
+ private void on_day_pane_button_released(Gtk.Widget widget, Toolkit.Button button, Gdk.Point point,
+ Gdk.EventType event_type) {
+ if (button != Toolkit.Button.PRIMARY)
return;
+ DayPane day_pane = (DayPane) widget;
+
Calendar.ExactTimeSpan? selection_span = day_pane.get_selection_span();
- if (selection_span == null)
- return;
+ if (selection_span != null)
+ owner.request_create_timed_event(selection_span, widget, point);
+ }
+
+ private AllDayCell? get_cell_at(AllDayCell widget, Gdk.Point widget_location) {
+ // convert widget's coordinates into grid coordinates
+ int grid_x, grid_y;
+ if (!widget.translate_coordinates(this, widget_location.x, widget_location.y,
+ out grid_x, out grid_y)) {
+ return null;
+ }
- // clear selection on button release, always
- day_pane.clear_selection();
+ // convert those coordinates into the day cell now being hovered over
+ // TODO: Obviously a better hit-test could be done here
+ foreach (AllDayCell day_cell in date_to_all_day.values) {
+ int cell_x, cell_y;
+ if (!translate_coordinates(day_cell, grid_x, grid_y, out cell_x, out cell_y))
+ continue;
+
+ if (day_cell.is_hit(cell_x, cell_y))
+ return day_cell;
+ }
- owner.request_create_timed_event(selection_span, details.widget, details.release_point);
+ return null;
}
private void on_all_day_cell_button_motion(Toolkit.MotionEvent details) {
if (!details.is_button_pressed(Toolkit.Button.PRIMARY))
return;
- debug("cell");
+ // widget is always the cell where the drag began, not ends
+ AllDayCell start_cell = (AllDayCell) details.widget;
+
+ // get the widget now being hovered over
+ AllDayCell? hit_cell = get_cell_at(start_cell, details.point);
+ if (hit_cell == null)
+ return;
+
+ // select everything from the start cell to the hit cell
+ Calendar.DateSpan span = new Calendar.DateSpan(start_cell.date, hit_cell.date);
+ foreach (AllDayCell day_cell in date_to_all_day.values)
+ day_cell.selected = day_cell.date in span;
}
- private void on_day_pane_motion(Toolkit.MotionEvent details) {
- DayPane day_pane = (DayPane) details.widget;
+ private void on_all_day_cell_button_released(Gtk.Widget widget, Toolkit.Button button, Gdk.Point point,
+ Gdk.EventType event_type) {
+ if (button != Toolkit.Button.PRIMARY)
+ return;
- // only update selection as long as
- if (details.is_button_pressed(Toolkit.Button.PRIMARY))
- day_pane.update_selection(day_pane.get_wall_time(details.location.y));
- else
- day_pane.clear_selection();
+ AllDayCell start_cell = (AllDayCell) widget;
+
+ // only convert drag-and-release to new event if start is selected (this prevents single-clicks
+ // from being turned into new events)
+ if (!start_cell.selected)
+ return;
+
+ // get widget button was released over
+ AllDayCell? release_cell = get_cell_at(start_cell, point);
+ if (release_cell != null) {
+ owner.request_create_all_day_event(new Calendar.DateSpan(start_cell.date, release_cell.date),
+ widget, point);
+ }
}
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]