[california/wip/728838-transitions: 3/3] Cache improvements, final touches
- From: Jim Nelson <jnelson src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [california/wip/728838-transitions: 3/3] Cache improvements, final touches
- Date: Sat, 26 Apr 2014 01:34:54 +0000 (UTC)
commit b1c3a828c3a581947ef4a7f541dbfafb6ac672fc
Author: Jim Nelson <jim yorba org>
Date: Fri Apr 25 18:34:24 2014 -0700
Cache improvements, final touches
src/Makefile.am | 1 +
.../backing-calendar-subscription-manager.vala | 34 +++++---
src/calendar/calendar-month-of-year.vala | 22 +++++
src/tests/tests-calendar-month-of-year.vala | 47 ++++++++++
src/tests/tests.vala | 1 +
src/view/month/month-cell.vala | 2 +-
src/view/month/month-controller.vala | 90 +++++++++++++-------
src/view/month/month-grid.vala | 54 ++++++++++--
8 files changed, 198 insertions(+), 53 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 549805d..8ca1e2a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -101,6 +101,7 @@ california_VALASOURCES = \
\
tests/tests.vala \
tests/tests-calendar-date.vala \
+ tests/tests-calendar-month-of-year.vala \
tests/tests-calendar-month-span.vala \
tests/tests-quick-add.vala \
\
diff --git a/src/backing/backing-calendar-subscription-manager.vala
b/src/backing/backing-calendar-subscription-manager.vala
index c4851ef..d686310 100644
--- a/src/backing/backing-calendar-subscription-manager.vala
+++ b/src/backing/backing-calendar-subscription-manager.vala
@@ -26,6 +26,11 @@ public class CalendarSubscriptionManager : BaseObject {
public Calendar.ExactTimeSpan window { get; private set; }
/**
+ * Set to true when { link start_async} begins.
+ */
+ public bool is_started { get; private set; default = false; }
+
+ /**
* Indicates a { link CalendarSource} was added to the manager, either listed when first
* created or detected at runtime afterwards.
*/
@@ -62,7 +67,7 @@ public class CalendarSubscriptionManager : BaseObject {
*
* The { link window} cannot be modified once created.
*
- * Events will not be signalled until { link start} is called.
+ * Events will not be signalled until { link start_async} is called.
*/
public CalendarSubscriptionManager(Calendar.ExactTimeSpan window) {
this.window = window;
@@ -85,38 +90,39 @@ public class CalendarSubscriptionManager : BaseObject {
* There is no "stop" method. Destroying the object will cancel all subscriptions, although
* signals will not be fired at that time.
*/
- public void start() {
+ public async void start_async() {
+ // to prevent reentrancy
+ if (is_started)
+ return;
+
+ is_started = true;
+
foreach (Backing.Store store in Backing.Manager.instance.get_stores()) {
// watch each store for future added sources
store.source_added.connect(on_source_added);
store.source_removed.connect(on_source_removed);
foreach (Backing.Source source in store.get_sources_of_type<Backing.CalendarSource>())
- add_calendar((Backing.CalendarSource) source);
+ yield add_calendar_async((Backing.CalendarSource) source, cancellable);
}
}
private void on_source_added(Backing.Source source) {
Backing.CalendarSource? calendar = source as Backing.CalendarSource;
if (calendar != null)
- add_calendar(calendar);
+ add_calendar_async.begin(calendar, cancellable);
}
- private void add_calendar(Backing.CalendarSource calendar) {
+ private async void add_calendar_async(Backing.CalendarSource calendar, Cancellable? cancellable) {
// report calendar as added to subscription
calendar_added(calendar);
// start generating instances on this calendar
- calendar.subscribe_async.begin(window, cancellable, on_subscribed);
- }
-
- // Since this might be called after the dtor has finished (cancelling the operation), don't
- // touch the "this" ref unless the Error is shown not to be a cancellation
- private void on_subscribed(Object? source, AsyncResult result) {
- Backing.CalendarSource calendar = (Backing.CalendarSource) source;
-
try {
- Backing.CalendarSourceSubscription subscription = calendar.subscribe_async.end(result);
+ // Since this might be called after the dtor has finished (cancelling the operation), don't
+ // touch the "this" ref unless the Error is shown not to be a cancellation
+ Backing.CalendarSourceSubscription subscription = yield calendar.subscribe_async(window,
+ cancellable);
// okay to use "this" ref
subscriptions.add(subscription);
diff --git a/src/calendar/calendar-month-of-year.vala b/src/calendar/calendar-month-of-year.vala
index 65d297f..7425e3b 100644
--- a/src/calendar/calendar-month-of-year.vala
+++ b/src/calendar/calendar-month-of-year.vala
@@ -85,6 +85,28 @@ public class MonthOfYear : DateSpan {
}
/**
+ * Returns the number of months between the two { link MonthOfYear}s.
+ *
+ * If the supplied MonthOfYear is earlier than this one, a negative value is returned.
+ */
+ public int difference(MonthOfYear other) {
+ int compare = compare_to(other);
+ if (compare == 0)
+ return 0;
+
+ // TODO: Iterating sucks, but it will have to suffice for now.
+ int count = 0;
+ MonthOfYear current = this;
+ for (;;) {
+ current = (compare > 0) ? current.previous() : current.next();
+ count += (compare > 0) ? -1 : 1;
+
+ if (current.equal_to(other))
+ return count;
+ }
+ }
+
+ /**
* Returns the chronological next { link MonthOfYear}.
*/
public MonthOfYear next() {
diff --git a/src/tests/tests-calendar-month-of-year.vala b/src/tests/tests-calendar-month-of-year.vala
new file mode 100644
index 0000000..48d036c
--- /dev/null
+++ b/src/tests/tests-calendar-month-of-year.vala
@@ -0,0 +1,47 @@
+/* Copyright 2014 Yorba Foundation
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+namespace California.Tests {
+
+private class CalendarMonthOfYear : UnitTest.Harness {
+ public CalendarMonthOfYear() {
+ add_case("difference-same", difference_same);
+ add_case("difference-negative", difference_negative);
+ add_case("difference-positive", difference_positive);
+ }
+
+ protected override void setup() throws Error {
+ Calendar.init();
+ }
+
+ protected override void teardown() {
+ Calendar.terminate();
+ }
+
+ private bool difference_same() throws Error {
+ Calendar.MonthOfYear jan = new Calendar.MonthOfYear(Calendar.Month.JAN, new Calendar.Year(2014));
+ Calendar.MonthOfYear jan2 = new Calendar.MonthOfYear(Calendar.Month.JAN, new Calendar.Year(2014));
+
+ return jan.difference(jan2) == 0;
+ }
+
+ private bool difference_negative() throws Error {
+ Calendar.MonthOfYear jan = new Calendar.MonthOfYear(Calendar.Month.JAN, new Calendar.Year(2014));
+ Calendar.MonthOfYear dec = new Calendar.MonthOfYear(Calendar.Month.DEC, new Calendar.Year(2013));
+
+ return jan.difference(dec) == -1;
+ }
+
+ private bool difference_positive() throws Error {
+ Calendar.MonthOfYear jan = new Calendar.MonthOfYear(Calendar.Month.JAN, new Calendar.Year(2014));
+ Calendar.MonthOfYear feb = new Calendar.MonthOfYear(Calendar.Month.FEB, new Calendar.Year(2014));
+
+ return jan.difference(feb) == 1;
+ }
+}
+
+}
+
diff --git a/src/tests/tests.vala b/src/tests/tests.vala
index c242023..20b6638 100644
--- a/src/tests/tests.vala
+++ b/src/tests/tests.vala
@@ -10,6 +10,7 @@ public int run(string[] args) {
UnitTest.Harness.register(new QuickAdd());
UnitTest.Harness.register(new CalendarDate());
UnitTest.Harness.register(new CalendarMonthSpan());
+ UnitTest.Harness.register(new CalendarMonthOfYear());
return UnitTest.Harness.exec_all();
}
diff --git a/src/view/month/month-cell.vala b/src/view/month/month-cell.vala
index 637ef47..1381fa6 100644
--- a/src/view/month/month-cell.vala
+++ b/src/view/month/month-cell.vala
@@ -10,7 +10,7 @@ namespace California.View.Month {
* A single cell within a { link MonthGrid}.
*/
-public class Cell : Gtk.EventBox {
+private class Cell : Gtk.EventBox {
private const int TOP_LINE_FONT_SIZE_PT = 11;
private const int LINE_FONT_SIZE_PT = 8;
diff --git a/src/view/month/month-controller.vala b/src/view/month/month-controller.vala
index daa4668..fb3dec3 100644
--- a/src/view/month/month-controller.vala
+++ b/src/view/month/month-controller.vala
@@ -20,6 +20,10 @@ public class Controller : BaseObject, View.Controllable {
// Slower than default to make more apparent to user what's occurring
private const int TRANSITION_DURATION_MSEC = 500;
+ // number of Grids to keep in GtkStack and cache (in terms of months) ... this should be an
+ // even number, as it is halved to determine neighboring months depths
+ private const int CACHE_NEIGHBORS_COUNT = 4;
+
/**
* The month and year being displayed.
*
@@ -88,9 +92,10 @@ public class Controller : BaseObject, View.Controllable {
notify[PROP_MONTH_OF_YEAR].connect(on_month_of_year_changed);
Calendar.System.instance.today_changed.connect(on_today_changed);
- // update now that signal handlers are in place
- month_of_year = Calendar.System.today.month_of_year();
+ // update now that signal handlers are in place ... do first_of_week first since more heavy
+ // processing is done when month_of_year changes
first_of_week = Calendar.FirstOfWeek.SUNDAY;
+ month_of_year = Calendar.System.today.month_of_year();
}
~Controller() {
@@ -99,9 +104,9 @@ public class Controller : BaseObject, View.Controllable {
// Creates a new Grid for the MonthOfYear, storing locally and adding to the GtkStack. Will
// reuse existing Grids whenever possible.
- private Grid create_month_grid(Calendar.MonthOfYear month_of_year) {
+ private void ensure_month_grid_exists(Calendar.MonthOfYear month_of_year) {
if (month_grids.has_key(month_of_year))
- return month_grids.get(month_of_year);
+ return;
Grid month_grid = new Grid(this, month_of_year);
month_grid.show_all();
@@ -109,41 +114,41 @@ public class Controller : BaseObject, View.Controllable {
// add to local store and to the GtkStack itself
month_grids.set(month_of_year, month_grid);
stack.add_named(month_grid, month_grid.id);
-
- return month_grid;
}
// Performs Grid caching by ensuring that Grids are available for the current, next, and
// previous month and that Grids outside that range are dropped. The current chronological
// month is never discarded.
private void update_month_grid_cache() {
- Calendar.MonthOfYear next_month = month_of_year.next();
- Calendar.MonthOfYear prev_month = month_of_year.previous();
- Calendar.MonthSpan cache_span = new Calendar.MonthSpan.from_months(prev_month, next_month);
+ Calendar.MonthSpan cache_span = new Calendar.MonthSpan.from_months(
+ month_of_year.adjust(0 - (CACHE_NEIGHBORS_COUNT / 2)),
+ month_of_year.adjust(CACHE_NEIGHBORS_COUNT / 2));
- // drop anything outside three-month range, other than current chronological month
+ // trim cache
Gee.MapIterator<Calendar.MonthOfYear, Grid> iter = month_grids.map_iterator();
while (iter.next()) {
Calendar.MonthOfYear grid_moy = iter.get_key();
+
+ // always keep current month
if (grid_moy.equal_to(Calendar.System.today.month_of_year()))
continue;
+ // keep if grid is in cache span
if (cache_span.has(grid_moy))
continue;
- // remove from GtkStack and local storage
+ // drop, remove from GtkStack and local storage
stack.remove(iter.get_value());
iter.unset();
}
- // ensure three-months worth of grids are available
- create_month_grid(month_of_year);
- create_month_grid(next_month);
- create_month_grid(prev_month);
+ // ensure all-months in span are available
+ foreach (Calendar.MonthOfYear moy in cache_span)
+ ensure_month_grid_exists(moy);
}
- private unowned Grid get_current_month_grid() {
- return (Grid) stack.get_visible_child();
+ private unowned Grid? get_current_month_grid() {
+ return (Grid?) stack.get_visible_child();
}
/**
@@ -170,7 +175,12 @@ public class Controller : BaseObject, View.Controllable {
if (!now.equal_to(month_of_year))
month_of_year = now;
- Cell? cell = get_current_month_grid().get_cell_for_date(Calendar.System.today);
+ // current should be set by the month_of_year being set
+ Grid? current_grid = get_current_month_grid();
+ assert(current_grid != null);
+
+ // this grid better have a cell with this date in it
+ Cell? cell = current_grid.get_cell_for_date(Calendar.System.today);
assert(cell != null);
return cell;
@@ -180,7 +190,9 @@ public class Controller : BaseObject, View.Controllable {
* @inheritDoc
*/
public void unselect_all() {
- get_current_month_grid().unselect_all();
+ Grid? current_grid = get_current_month_grid();
+ if (current_grid != null)
+ current_grid.unselect_all();
}
/**
@@ -213,21 +225,39 @@ public class Controller : BaseObject, View.Controllable {
error("Unable to set default date for %s: %s", month_of_year.to_string(), calerr.message);
}
- // update the cache to store current month and neighbors
- update_month_grid_cache();
-
// set up transition to give appearance of moving chronologically through the pages of
// a calendar
- Calendar.MonthOfYear current_moy = get_current_month_grid().month_of_year;
- int compare = month_of_year.compare_to(current_moy);
- if (compare < 0)
- stack.transition_type = Gtk.StackTransitionType.SLIDE_RIGHT;
- else if (compare > 0)
- stack.transition_type = Gtk.StackTransitionType.SLIDE_LEFT;
- else
- return;
+ Grid? current_grid = get_current_month_grid();
+ if (current_grid != null) {
+ Calendar.MonthOfYear current_moy = current_grid.month_of_year;
+ int compare = month_of_year.compare_to(current_moy);
+ if (compare < 0)
+ stack.transition_type = Gtk.StackTransitionType.SLIDE_RIGHT;
+ else if (compare > 0)
+ stack.transition_type = Gtk.StackTransitionType.SLIDE_LEFT;
+ else
+ return;
+ }
+ // because grid cache is populated/trimmed after sliding month into view, ensure the
+ // desired month already exists
+ ensure_month_grid_exists(month_of_year);
+
+ // make visible using proper transition type
stack.set_visible_child(month_grids.get(month_of_year));
+
+ // now update the cache to store current month and neighbors ... do this after doing above
+ // comparison because this update affects the GtkStack, which may revert to another page
+ // when the cache is trimmed, making the notion of "current" indeterminate; the most
+ // visible symptom of this is navigating far from today's month then clicking the Today
+ // button and no transition occurs because, when the cache is trimmed, today's month is
+ // the current child ... to avoid dropping the Widget before the transition completes,
+ // wait before doing this; 3.12's "transition-running" property would be useful here
+ Idle.add(() => {
+ update_month_grid_cache();
+
+ return false;
+ }, Priority.LOW);
}
public override string to_string() {
diff --git a/src/view/month/month-grid.vala b/src/view/month/month-grid.vala
index c76803b..bb21d85 100644
--- a/src/view/month/month-grid.vala
+++ b/src/view/month/month-grid.vala
@@ -10,7 +10,7 @@ namespace California.View.Month {
* A Gtk.Grid of { link Cell}s, each representing a particular { link Calendar.Date}.
*/
-public class Grid : Gtk.Grid {
+private class Grid : Gtk.Grid {
public const string PROP_MONTH_OF_YEAR = "month-of-year";
public const string PROP_WINDOW = "window";
public const string PROP_FIRST_OF_WEEK = "first-of-week";
@@ -53,7 +53,7 @@ public class Grid : Gtk.Grid {
public string id { get { return month_of_year.full_name; } }
private Gee.HashMap<Calendar.Date, Cell> date_to_cell = new Gee.HashMap<Calendar.Date, Cell>();
- private Backing.CalendarSubscriptionManager subscriptions;
+ private Backing.CalendarSubscriptionManager? subscriptions = null;
private Gdk.EventType button_press_type = Gdk.EventType.NOTHING;
private Gdk.Point button_press_point = Gdk.Point();
@@ -93,11 +93,13 @@ public class Grid : Gtk.Grid {
update_cells();
update_subscriptions();
+ owner.notify[Controller.PROP_MONTH_OF_YEAR].connect(on_controller_month_of_year_changed);
owner.notify[View.Controllable.PROP_FIRST_OF_WEEK].connect(update_first_of_week);
owner.notify[Controller.PROP_SHOW_OUTSIDE_MONTH].connect(update_cells);
}
~Grid() {
+ owner.notify[Controller.PROP_MONTH_OF_YEAR].disconnect(on_controller_month_of_year_changed);
owner.notify[View.Controllable.PROP_FIRST_OF_WEEK].disconnect(update_first_of_week);
owner.notify[Controller.PROP_SHOW_OUTSIDE_MONTH].disconnect(update_cells);
}
@@ -109,7 +111,11 @@ public class Grid : Gtk.Grid {
return (Cell) get_child_at(col, row);
}
- internal Cell? get_cell_for_date(Calendar.Date date) {
+ /**
+ * Returns the { link Cell} for the specified { link Calendar.Date}, if it is contained by this
+ * { link Grid}.
+ */
+ public Cell? get_cell_for_date(Calendar.Date date) {
return date_to_cell.get(date);
}
@@ -179,6 +185,9 @@ public class Grid : Gtk.Grid {
Calendar.ExactTimeSpan time_window = new Calendar.ExactTimeSpan.from_date_span(window,
Calendar.Timezone.local);
+ if (subscriptions != null && subscriptions.window.equal_to(time_window))
+ return;
+
// create new subscription manager, subscribe to its signals, and let them drive
subscriptions = new Backing.CalendarSubscriptionManager(time_window);
subscriptions.calendar_added.connect(on_calendar_added);
@@ -186,10 +195,43 @@ public class Grid : Gtk.Grid {
subscriptions.instance_added.connect(on_instance_added);
subscriptions.instance_removed.connect(on_instance_removed);
- subscriptions.start();
+ // only start if this month is being displayed, otherwise will be started when owner's
+ // month of year changes to this one or a timeout (to prevent only subscribing
+ // when scrolled into view)
+ if (owner.month_of_year.equal_to(month_of_year)) {
+ subscriptions.start_async.begin();
+ } else {
+ // use distance from currently displayed month as a way to space out subscription
+ // starts, which are a little taxing ... assume future months are more likely to be
+ // moved to than past months, hence earlier months get the +1 dinged against them
+ int diff = owner.month_of_year.difference(month_of_year);
+ if (diff < 0)
+ diff = diff.abs() + 1;
+
+ Timeout.add(300 + (diff * 100), () => {
+ subscriptions.start_async.begin();
+
+ return false;
+ });
+ }
+ }
+
+ private void on_controller_month_of_year_changed() {
+ // if this Grid is being displayed, immediately activate subscriptions
+ if (!owner.month_of_year.equal_to(month_of_year))
+ return;
+
+ if (subscriptions == null)
+ update_subscriptions();
+ else if (!subscriptions.is_started)
+ subscriptions.start_async.begin();
}
private void update_first_of_week() {
+ // avoid some extra work
+ if (first_of_week == owner.first_of_week)
+ return;
+
first_of_week = owner.first_of_week;
// requires updating all the cells as well, since all dates have to be shifted
@@ -405,10 +447,6 @@ public class Grid : Gtk.Grid {
return true;
}
-
- public string to_string() {
- return "Month.Grid for %s".printf(month_of_year.to_string());
- }
}
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]