[california] Zoom to increase font size: Bug #730345



commit 0da98111f022d6fa1e4152ea1879387e291165a9
Author: Jim Nelson <jim yorba org>
Date:   Wed Jun 4 17:02:58 2014 -0700

    Zoom to increase font size: Bug #730345
    
    To make zoom available in the UI, the header bar close button has
    been replaced with a drop-down gear menu which offers the font size
    controls as well as Quit.

 data/org.yorba.california.gschema.xml    |   17 ++++
 po/POTFILES.in                           |    2 +
 src/Makefile.am                          |    1 +
 src/application/california-resource.vala |    6 +-
 src/application/california-settings.vala |   39 +++++++++
 src/california-resources.xml             |    3 +
 src/host/host-main-window.vala           |   61 +++++++++++---
 src/rc/window-menu.interface             |   26 ++++++
 src/view/common/common-events-cell.vala  |   37 +++++----
 src/view/month/month-cell.vala           |    8 +-
 src/view/month/month-controller.vala     |   10 ++-
 src/view/view-palette.vala               |  128 ++++++++++++++++++++++--------
 src/view/view.vala                       |    4 -
 src/view/week/week-all-day-cell.vala     |   10 +-
 src/view/week/week-controller.vala       |    9 ++-
 src/view/week/week-day-pane.vala         |   12 ++--
 src/view/week/week-grid.vala             |    8 +-
 src/view/week/week-hour-runner.vala      |    4 +-
 src/view/week/week-pane.vala             |   24 ++++--
 19 files changed, 313 insertions(+), 96 deletions(-)
---
diff --git a/data/org.yorba.california.gschema.xml b/data/org.yorba.california.gschema.xml
index 88cedb0..8ac56fc 100644
--- a/data/org.yorba.california.gschema.xml
+++ b/data/org.yorba.california.gschema.xml
@@ -8,6 +8,23 @@
             Legal values include "week" and "month".
         </description>
     </key>
+    
+    <key name="small-font-pts" type="i">
+        <default>8</default>
+        <summary>Size (in points) of small font</summary>
+        <description>
+            The small font is generally used where lots of detail is being displayed.
+        </description>
+    </key>
+    
+    <key name="normal-font-pts" type="i">
+        <default>11</default>
+        <summary>Size (in points) of normal font</summary>
+        <description>
+            The normal font is used where less details is being displayed, such as the day of the
+            month.
+        </description>
+    </key>
 </schema>
 
 </schemalist>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index aaac90f..b7c7762 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -27,3 +27,5 @@ src/view/week/week-controller.vala
 [type: gettext/glade]src/rc/quick-create-event.ui
 [type: gettext/glade]src/rc/show-event.ui
 [type: gettext/glade]src/rc/webcal-subscribe.ui
+[type: gettext/glade]src/rc/window-menu.interface
+
diff --git a/src/Makefile.am b/src/Makefile.am
index 5cdbef3..7eef19d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -175,6 +175,7 @@ california_RC = \
        rc/quick-create-event.ui \
        rc/show-event.ui \
        rc/webcal-subscribe.ui \
+       rc/window-menu.interface \
        $(NULL)
 
 california_OPTIONAL_VALAFLAGS =
diff --git a/src/application/california-resource.vala b/src/application/california-resource.vala
index 129acf2..fa73b1e 100644
--- a/src/application/california-resource.vala
+++ b/src/application/california-resource.vala
@@ -32,8 +32,10 @@ public T load<T>(string resource, string object_name, string path = DEFAULT_PATH
     if (object == null)
         error("Unable to load object \"%s\" from %s: not found", object_name, fullpath);
     
-    if (!object.get_type().is_a(typeof(T)))
-        error("Unable to load object \"%s\" from %s: not of type", object_name, fullpath);
+    if (!object.get_type().is_a(typeof(T))) {
+        error("Unable to load object \"%s\" from %s: not of type %s (is %s)", object_name, fullpath,
+            typeof(T).name(), object.get_type().name());
+    }
     
     return object;
 }
diff --git a/src/application/california-settings.vala b/src/application/california-settings.vala
index 226c03e..7c1fc57 100644
--- a/src/application/california-settings.vala
+++ b/src/application/california-settings.vala
@@ -12,6 +12,8 @@ namespace California {
 
 public class Settings : BaseObject {
     public const string PROP_CALENDAR_VIEW = "calendar-view";
+    public const string PROP_SMALL_FONT_PTS = "small-font-pts";
+    public const string PROP_NORMAL_FONT_PTS = "normal-font-pts";
     
     // GSettings schema identifier.
     private const string SCHEMA_ID = "org.yorba.california";
@@ -19,11 +21,48 @@ public class Settings : BaseObject {
     // schema key ids may be the same as property names, but want to keep them different in case
     // one or the other changes
     private const string KEY_CALENDAR_VIEW = "calendar-view";
+    private const string KEY_SMALL_FONT_PTS = "small-font-pts";
+    private const string KEY_NORMAL_FONT_PTS = "normal-font-pts";
     
     public static Settings instance { get; private set; }
     
+    /**
+     * Which view ("month", "week") is currently displayed.
+     *
+     * The string is determined by the various views' { link View.Controllable.id}.
+     */
     public string calendar_view { get; set; }
     
+    /**
+     * The size of the small font used throughout the application (in points).
+     */
+    public int small_font_pts {
+        get {
+            return settings.get_int(KEY_SMALL_FONT_PTS).clamp(View.Palette.MIN_SMALL_FONT_PTS,
+                View.Palette.MAX_SMALL_FONT_PTS);
+        }
+        
+        set {
+            settings.set_int(KEY_SMALL_FONT_PTS, value.clamp(View.Palette.MIN_SMALL_FONT_PTS,
+                View.Palette.MAX_SMALL_FONT_PTS));
+        }
+    }
+    
+    /**
+     * The size of the "normal" font used throughout the application (in points).
+     */
+    public int normal_font_pts {
+        get {
+            return settings.get_int(KEY_NORMAL_FONT_PTS).clamp(View.Palette.MIN_NORMAL_FONT_PTS,
+                View.Palette.MAX_NORMAL_FONT_PTS);
+        }
+        
+        set {
+            settings.set_int(KEY_NORMAL_FONT_PTS, value.clamp(View.Palette.MIN_NORMAL_FONT_PTS,
+                View.Palette.MAX_NORMAL_FONT_PTS));
+        }
+    }
+    
     private GLib.Settings settings;
     
     private Settings() {
diff --git a/src/california-resources.xml b/src/california-resources.xml
index 9bae540..e52d60d 100644
--- a/src/california-resources.xml
+++ b/src/california-resources.xml
@@ -39,5 +39,8 @@
     <gresource prefix="/org/yorba/california">
         <file compressed="true">rc/webcal-subscribe.ui</file>
     </gresource>
+    <gresource prefix="/org/yorba/california">
+        <file compressed="true">rc/window-menu.interface</file>
+    </gresource>
 </gresources>
 
diff --git a/src/host/host-main-window.vala b/src/host/host-main-window.vala
index 00aee5e..16b7509 100644
--- a/src/host/host-main-window.vala
+++ b/src/host/host-main-window.vala
@@ -31,21 +31,34 @@ public class MainWindow : Gtk.ApplicationWindow {
     private const string ACTION_WEEK = "win.view-week";
     private const string ACCEL_WEEK = "<Ctrl>W";
     
+    private const string ACTION_INCREASE_FONT = "win.increase-font";
+    private const string ACCEL_INCREASE_FONT = "KP_Add";
+    
+    private const string ACTION_DECREASE_FONT = "win.decrease-font";
+    private const string ACCEL_DECREASE_FONT = "KP_Subtract";
+    
+    private const string ACTION_RESET_FONT = "win.reset-font";
+    private const string ACCEL_RESET_FONT = "KP_Multiply";
+    
     private static const ActionEntry[] action_entries = {
         { "quick-create-event", on_quick_create_event },
         { "jump-to-today", on_jump_to_today },
         { "next", on_next },
         { "previous", on_previous },
         { "view-month", on_view_month },
-        { "view-week", on_view_week }
+        { "view-week", on_view_week },
+        { "increase-font", on_increase_font },
+        { "decrease-font", on_decrease_font },
+        { "reset-font", on_reset_font }
     };
     
     // Set as a property so it can be bound to the current View.Controllable
     public Calendar.FirstOfWeek first_of_week { get; set; }
     
     private Gtk.Button quick_add_button;
-    private View.Controllable month_view = new View.Month.Controller();
-    private View.Controllable week_view = new View.Week.Controller();
+    private View.Palette palette;
+    private View.Controllable month_view;
+    private View.Controllable week_view;
     private View.Controllable? current_controller = null;
     private Gee.HashSet<Binding> current_bindings = new Gee.HashSet<Binding>();
     private Gtk.Stack view_stack = new Gtk.Stack();
@@ -71,6 +84,9 @@ public class MainWindow : Gtk.ApplicationWindow {
         Application.instance.add_accelerator(rtl ? ACCEL_NEXT : ACCEL_PREVIOUS, ACTION_PREVIOUS, null);
         Application.instance.add_accelerator(ACCEL_MONTH, ACTION_MONTH, null);
         Application.instance.add_accelerator(ACCEL_WEEK, ACTION_WEEK, null);
+        Application.instance.add_accelerator(ACCEL_INCREASE_FONT, ACTION_INCREASE_FONT, null);
+        Application.instance.add_accelerator(ACCEL_DECREASE_FONT, ACTION_DECREASE_FONT, null);
+        Application.instance.add_accelerator(ACCEL_RESET_FONT, ACTION_RESET_FONT, null);
         
         // view stack settings
         view_stack.homogeneous = true;
@@ -80,6 +96,13 @@ public class MainWindow : Gtk.ApplicationWindow {
         // subscribe before adding so first add to initialize UI
         view_stack.notify["visible-child"].connect(on_view_changed);
         
+        // create a View.Palette for all the hosted views ...
+        palette = new View.Palette(this);
+        
+        // ... then create the hosted views
+        month_view = new View.Month.Controller(palette);
+        week_view = new View.Week.Controller(palette);
+        
         // add views to view stack, first added is first shown
         add_controller(month_view);
         add_controller(week_view);
@@ -89,7 +112,6 @@ public class MainWindow : Gtk.ApplicationWindow {
 #if !ENABLE_UNITY
         // Unity doesn't support GtkHeaderBar-as-title-bar very well yet; when set, the main
         // window can't be resized no matter what additional GtkWindow properties are set
-        headerbar.show_close_button = true;
         set_titlebar(headerbar);
 #endif
         
@@ -140,7 +162,11 @@ public class MainWindow : Gtk.ApplicationWindow {
         calendars.valign = Gtk.Align.CENTER;
         calendars.tooltip_text = _("Calendars (Ctrl+L)");
         calendars.set_action_name(Application.ACTION_CALENDAR_MANAGER);
-
+        
+        Gtk.MenuButton window_menu = new Gtk.MenuButton();
+        window_menu.menu_model = Resource.load<MenuModel>("window-menu.interface", "window-menu");
+        window_menu.add(new Gtk.Image.from_icon_name("emblem-system-symbolic", Gtk.IconSize.SMALL_TOOLBAR));
+        
         // Vertically center all buttons and put them in a SizeGroup to handle situations where
         // the text button is smaller than the icons buttons due to language (i.e. Hebrew)
         // see https://bugzilla.gnome.org/show_bug.cgi?id=729771
@@ -150,10 +176,12 @@ public class MainWindow : Gtk.ApplicationWindow {
         size.add_widget(next);
         size.add_widget(quick_add_button);
         size.add_widget(calendars);
+        size.add_widget(window_menu);
         
         // pack right-side of window
         headerbar.pack_end(quick_add_button);
         headerbar.pack_end(calendars);
+        headerbar.pack_end(window_menu);
         
         Gtk.Box layout = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
         // if on Unity, since headerbar is not the titlebar, need to pack it like any other widget
@@ -185,14 +213,6 @@ public class MainWindow : Gtk.ApplicationWindow {
         return true;
     }
     
-    public override void map() {
-        // give View.Palette a chance to gather display metrics for the various Views (week, months,
-        // etc.)
-        View.Palette.instance.main_window_mapped(this);
-        
-        base.map();
-    }
-    
     private void add_controller(View.Controllable controller) {
         view_stack_ids.add(controller.id);
         view_stack.add_titled(controller.get_container(), controller.id, controller.title);
@@ -283,6 +303,21 @@ public class MainWindow : Gtk.ApplicationWindow {
         view_stack.set_visible_child(week_view.get_container());
     }
     
+    private void on_increase_font() {
+        Settings.instance.small_font_pts++;
+        Settings.instance.normal_font_pts++;
+    }
+    
+    private void on_decrease_font() {
+        Settings.instance.small_font_pts--;
+        Settings.instance.normal_font_pts--;
+    }
+    
+    private void on_reset_font() {
+        Settings.instance.small_font_pts = View.Palette.DEFAULT_SMALL_FONT_PTS;
+        Settings.instance.normal_font_pts = View.Palette.DEFAULT_NORMAL_FONT_PTS;
+    }
+    
     private void on_request_create_timed_event(Calendar.ExactTimeSpan initial, Gtk.Widget relative_to,
         Gdk.Point? for_location) {
         Component.Event event = new Component.Event.blank();
diff --git a/src/rc/window-menu.interface b/src/rc/window-menu.interface
new file mode 100644
index 0000000..a31fdee
--- /dev/null
+++ b/src/rc/window-menu.interface
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+    <menu id="window-menu">
+        <section>
+            <item>
+                <attribute name="label" translatable="yes">_Increase font size</attribute>
+                <attribute name="action">win.increase-font</attribute>
+            </item>
+            <item>
+                <attribute name="label" translatable="yes">_Decrease font size</attribute>
+                <attribute name="action">win.decrease-font</attribute>
+            </item>
+            <item>
+                <attribute name="label" translatable="yes">_Reset font size</attribute>
+                <attribute name="action">win.reset-font</attribute>
+            </item>
+        </section>
+        <section>
+            <item>
+                <attribute name="label" translatable="yes">_Quit</attribute>
+                <attribute name="action">app.quit</attribute>
+            </item>
+        </section>
+    </menu>
+</interface>
+
diff --git a/src/view/common/common-events-cell.vala b/src/view/common/common-events-cell.vala
index d4e02f6..b25fe81 100644
--- a/src/view/common/common-events-cell.vala
+++ b/src/view/common/common-events-cell.vala
@@ -92,17 +92,24 @@ internal abstract class EventsCell : Gtk.EventBox, InstanceContainer {
      */
     public Calendar.Span contained_span { get { return date; } }
     
+    /**
+     * { link View.Palette} used by cell.
+     *
+     * The palette should be associated with the toplevel Gtk.Window this cell is contained within.
+     */
+    public View.Palette palette { get; private set; }
+    
     private Gee.TreeSet<Component.Event> sorted_events = new 
Gee.TreeSet<Component.Event>(all_day_comparator);
     private Gee.HashMap<int, Component.Event> line_to_event = new Gee.HashMap<int, Component.Event>();
-    
     private Gtk.DrawingArea canvas = new Gtk.DrawingArea();
     
-    public EventsCell(Calendar.Date date, Calendar.DateSpan neighbors) {
+    public EventsCell(View.Palette palette, Calendar.Date date, Calendar.DateSpan neighbors) {
         assert(date in neighbors);
         
+        this.palette = palette;
         this.date = date;
         this.neighbors = neighbors;
-        top_line_rgba = Palette.instance.day_in_range;
+        top_line_rgba = palette.day_in_range;
         
         // see query_tooltip() for implementation
         has_tooltip = true;
@@ -113,7 +120,7 @@ internal abstract class EventsCell : Gtk.EventBox, InstanceContainer {
         
         notify[PROP_TOP_LINE_TEXT].connect(queue_draw);
         
-        Palette.instance.palette_changed.connect(queue_draw);
+        palette.palette_changed.connect(queue_draw);
         Calendar.System.instance.is_24hr_changed.connect(on_24hr_changed);
         Calendar.System.instance.today_changed.connect(on_today_changed);
         
@@ -121,7 +128,7 @@ internal abstract class EventsCell : Gtk.EventBox, InstanceContainer {
     }
     
     ~EventsCell() {
-        Palette.instance.palette_changed.disconnect(queue_draw);
+        palette.palette_changed.disconnect(queue_draw);
         Calendar.System.instance.is_24hr_changed.disconnect(on_24hr_changed);
         Calendar.System.instance.today_changed.disconnect(on_today_changed);
     }
@@ -416,10 +423,10 @@ internal abstract class EventsCell : Gtk.EventBox, InstanceContainer {
     private bool on_draw(Cairo.Context ctx) {
         // shade background of cell for selection or if today
         if (selected) {
-            Gdk.cairo_set_source_rgba(ctx, Palette.instance.selection);
+            Gdk.cairo_set_source_rgba(ctx, palette.selection);
             ctx.paint();
         } else if (date.equal_to(Calendar.System.today)) {
-            Gdk.cairo_set_source_rgba(ctx, Palette.instance.current_day);
+            Gdk.cairo_set_source_rgba(ctx, palette.current_day);
             ctx.paint();
         }
         
@@ -498,10 +505,10 @@ internal abstract class EventsCell : Gtk.EventBox, InstanceContainer {
             
             // starting y of top line
             if (top_line_text != null)
-                y += Palette.instance.normal_font_height_px + Palette.LINE_PADDING_PX;
+                y += palette.normal_font_height_px + Palette.LINE_PADDING_PX;
             
             // add additional lines
-            y += line_number * (Palette.instance.small_font_height_px + Palette.LINE_PADDING_PX);
+            y += line_number * (palette.small_font_height_px + Palette.LINE_PADDING_PX);
         }
         
         return y;
@@ -516,7 +523,7 @@ internal abstract class EventsCell : Gtk.EventBox, InstanceContainer {
         int left = 0;
         int right = get_allocated_width();
         int top = get_line_top_y(line_number);
-        int bottom = top + Palette.instance.small_font_height_px;
+        int bottom = top + palette.small_font_height_px;
         
         // use event color for text unless reversed, where it becomes the background color
         Gdk.cairo_set_source_rgba(ctx, rgba);
@@ -534,7 +541,7 @@ internal abstract class EventsCell : Gtk.EventBox, InstanceContainer {
                 
                 case CapEffect.POINTED:
                     ctx.move_to(right - POINTED_CAP_WIDTH_PX, top);
-                    ctx.line_to(right - 1, top + (Palette.instance.small_font_height_px / 2));
+                    ctx.line_to(right - 1, top + (palette.small_font_height_px / 2));
                     ctx.line_to(right - POINTED_CAP_WIDTH_PX, bottom);
                 break;
                 
@@ -556,7 +563,7 @@ internal abstract class EventsCell : Gtk.EventBox, InstanceContainer {
                 
                 case CapEffect.POINTED:
                     ctx.line_to(left + POINTED_CAP_WIDTH_PX, bottom);
-                    ctx.line_to(left + 1, top + (Palette.instance.small_font_height_px / 2));
+                    ctx.line_to(left + 1, top + (palette.small_font_height_px / 2));
                     ctx.line_to(left + POINTED_CAP_WIDTH_PX, top);
                 break;
                 
@@ -585,8 +592,8 @@ internal abstract class EventsCell : Gtk.EventBox, InstanceContainer {
         Pango.Layout layout = create_pango_layout(text);
         // Use normal font for very top line, small font for all others (see get_line_top_y())
         layout.set_font_description((line_number < 0)
-            ? Palette.instance.normal_font
-            : Palette.instance.small_font);
+            ? palette.normal_font
+            : palette.small_font);
         layout.set_ellipsize(Pango.EllipsizeMode.END);
         layout.set_width((right - left - left_text_margin - right_text_margin) * Pango.SCALE);
         
@@ -604,7 +611,7 @@ internal abstract class EventsCell : Gtk.EventBox, InstanceContainer {
     public Component.Event? get_event_at(Gdk.Point point) {
         for (int line_number = 0; line_number < line_to_event.size; line_number++) {
             int y = get_line_top_y(line_number);
-            if (point.y >= y && point.y < (y + Palette.instance.small_font_height_px))
+            if (point.y >= y && point.y < (y + palette.small_font_height_px))
                 return line_to_event.get(line_number);
         }
         
diff --git a/src/view/month/month-cell.vala b/src/view/month/month-cell.vala
index 95fc710..1c788aa 100644
--- a/src/view/month/month-cell.vala
+++ b/src/view/month/month-cell.vala
@@ -18,7 +18,7 @@ internal class Cell : Common.EventsCell {
     public int col { get; private set; }
     
     public Cell(Grid owner, Calendar.Date date, int row, int col) {
-        base (date, date.week_of(owner.first_of_week).to_date_span());
+        base (owner.owner.palette, date, date.week_of(owner.first_of_week).to_date_span());
         
         this.owner = owner;
         this.row = row;
@@ -48,7 +48,7 @@ internal class Cell : Common.EventsCell {
         int height = get_allocated_height();
         
         // draw border lines (creates grid effect)
-        Palette.prepare_hairline(ctx, Palette.instance.border);
+        Palette.prepare_hairline(ctx, palette.border);
         
         // only draw top line if on the top row
         if (row == 0) {
@@ -74,8 +74,8 @@ internal class Cell : Common.EventsCell {
     private void update_top_line() {
         top_line_text = date.day_of_month.informal_number;
         top_line_rgba = (date in owner.month_of_year)
-            ? Palette.instance.day_in_range
-            : Palette.instance.day_outside_range;
+            ? palette.day_in_range
+            : palette.day_outside_range;
     }
 }
 
diff --git a/src/view/month/month-controller.vala b/src/view/month/month-controller.vala
index 022173b..f1b4b54 100644
--- a/src/view/month/month-controller.vala
+++ b/src/view/month/month-controller.vala
@@ -15,6 +15,7 @@ namespace California.View.Month {
 
 public class Controller : BaseObject, View.Controllable {
     public const string PROP_MONTH_OF_YEAR = "month-of-year";
+    public const string PROP_PALETTE = "palette";
     
     public const string VIEW_ID = "month";
     
@@ -69,12 +70,19 @@ public class Controller : BaseObject, View.Controllable {
      */
     public Calendar.Date default_date { get; protected set; }
     
+    /**
+     * { link View.Palette} for the entire view.
+     */
+    public View.Palette palette { get; private set; }
+    
     private MasterGrid master_grid;
     private Gtk.Stack stack = new Gtk.Stack();
     private Toolkit.StackModel<Calendar.MonthOfYear> stack_model;
     private Calendar.MonthSpan cache_span;
     
-    public Controller() {
+    public Controller(View.Palette palette) {
+        this.palette = palette;
+        
         master_grid = new MasterGrid(this);
         master_grid.column_homogeneous = true;
         master_grid.column_spacing = 0;
diff --git a/src/view/view-palette.vala b/src/view/view-palette.vala
index 68a8238..6932b14 100644
--- a/src/view/view-palette.vala
+++ b/src/view/view-palette.vala
@@ -7,7 +7,8 @@
 namespace California.View {
 
 /**
- * A singleton holding colors and theme information for drawing the various views.
+ * A "bag" of properties and constants holding colors and theme information for drawing the various
+ * views to a particular Gtk.Window.
  *
  * TODO: Currently colors are hard-coded.  In the future we'll probably need to get these from the
  * system or the theme.
@@ -34,10 +35,39 @@ public class Palette : BaseObject {
      */
     public const double DASHES[] = { 1.0, 3.0 };
     
-    private const int NORMAL_FONT_SIZE_PT = 11;
-    private const int SMALL_FONT_SIZE_PT = 8;
+    /**
+     * Minimum size (in points) of the { link small_font}.
+     */
+    public const int MIN_SMALL_FONT_PTS = 7;
     
-    public static Palette instance { get; private set; }
+    /**
+     * Maximum size (in points) of the { link small_font}.
+     */
+    public const int MAX_SMALL_FONT_PTS = 14;
+    
+    /**
+     * Default size (in points) of the { link small_font}.
+     *
+     * This is also set in the GSettings schema file.
+     */
+    public const int DEFAULT_SMALL_FONT_PTS = 8;
+    
+    /**
+     * Minimum size (in points) of the { link normal_font}.
+     */
+    public const int MIN_NORMAL_FONT_PTS = 9;
+    
+    /**
+     * Maximum size (in points) of the { link normal_font}.
+     */
+    public const int MAX_NORMAL_FONT_PTS = 16;
+    
+    /**
+     * Default size (in points) of the { link normal_font}.
+     *
+     * This is also set in the GSettings schema file.
+     */
+    public const int DEFAULT_NORMAL_FONT_PTS = 11;
     
     /**
      * Border color (when separating days, for example).
@@ -117,7 +147,11 @@ public class Palette : BaseObject {
      */
     public signal void palette_changed();
     
-    private Palette() {
+    private unowned Gtk.Window window;
+    
+    public Palette(Gtk.Window window) {
+        this.window = window;
+        
         border = { red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0 };
         day_in_range = { red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0 };
         day_outside_range = { red: 0.6, green: 0.6, blue: 0.6, alpha: 1.0 };
@@ -125,47 +159,77 @@ public class Palette : BaseObject {
         current_time = { red: 1.0, green: 0.0, blue: 0.0, alpha: 0.90 };
         selection = { red: 0.0, green: 0.50, blue: 0.50, alpha: 0.10 };
         
-        normal_font = new Pango.FontDescription();
-        normal_font.set_size(NORMAL_FONT_SIZE_PT * Pango.SCALE);
+        Settings.instance.notify[Settings.PROP_NORMAL_FONT_PTS].connect(on_normal_font_changed);
+        Settings.instance.notify[Settings.PROP_SMALL_FONT_PTS].connect(on_small_font_changed);
         
-        small_font = new Pango.FontDescription();
-        small_font.set_size(SMALL_FONT_SIZE_PT * Pango.SCALE);
+        window.map.connect(on_window_mapped);
     }
     
-    internal static void init() {
-        instance = new Palette();
+    ~Palette() {
+        Settings.instance.notify[Settings.PROP_NORMAL_FONT_PTS].disconnect(on_normal_font_changed);
+        Settings.instance.notify[Settings.PROP_SMALL_FONT_PTS].disconnect(on_small_font_changed);
+        
+        window.map.disconnect(on_window_mapped);
     }
     
-    internal static void terminate() {
-        instance = null;
+    // Font extents can only be determined when the window is mapped
+    private void on_window_mapped() {
+        on_normal_font_changed();
+        on_small_font_changed();
+        palette_changed();
     }
     
-    /**
-     * Called by { link Host.MainWindow} when it's mapped to the screen.
-     *
-     * This allows for { link Palette} to retrieve display metrics and other information.
-     */
-    public void main_window_mapped(Gtk.Window window) {
-        bool updated = false;
+    private void on_normal_font_changed() {
+        Pango.FontDescription? new_normal_font;
+        int new_normal_height_px;
+        if (!on_font_changed(Settings.instance.normal_font_pts, normal_font, normal_font_height_px,
+            out new_normal_font, out new_normal_height_px)) {
+            // nothing changed
+            return;
+        }
         
-        int height = get_height_extent(window, normal_font);
-        if (height != normal_font_height_px) {
-            normal_font_height_px = height;
-            updated = true;
+        normal_font = new_normal_font;
+        normal_font_height_px = new_normal_height_px;
+        
+        palette_changed();
+    }
+    
+    private void on_small_font_changed() {
+        Pango.FontDescription? new_small_font;
+        int new_small_height_px;
+        if (!on_font_changed(Settings.instance.small_font_pts, small_font, small_font_height_px,
+            out new_small_font, out new_small_height_px)) {
+            // nothing changed
+            return;
         }
         
-        height = get_height_extent(window, small_font);
-        if (height != small_font_height_px) {
-            small_font_height_px = height;
-            updated = true;
+        small_font = new_small_font;
+        small_font_height_px = new_small_height_px;
+        
+        palette_changed();
+    }
+    
+    private bool on_font_changed(int new_pts, Pango.FontDescription? current_font,
+        int current_height_px, out Pango.FontDescription? new_font, out int new_height_px) {
+        Pango.FontDescription font = new Pango.FontDescription();
+        font.set_size(new_pts * Pango.SCALE);
+        
+        // if nothing changed, do nothing
+        if (current_font != null && current_font.get_size() == font.get_size()) {
+            new_font = current_font;
+            new_height_px = current_height_px;
+            
+            return false;
         }
         
-        if (updated)
-            palette_changed();
+        new_font = font;
+        new_height_px = get_height_extent(font);
+        
+        return true;
     }
     
-    private static int get_height_extent(Gtk.Widget widget, Pango.FontDescription font) {
-        Pango.Layout layout = widget.create_pango_layout("Gg");
+    private int get_height_extent(Pango.FontDescription font) {
+        Pango.Layout layout = window.create_pango_layout("Gg");
         layout.set_font_description(font);
         
         int width, height;
diff --git a/src/view/view.vala b/src/view/view.vala
index ef61572..57e3e19 100644
--- a/src/view/view.vala
+++ b/src/view/view.vala
@@ -18,8 +18,6 @@ public void init() throws Error {
     if (!Unit.do_init(ref init_count))
         return;
     
-    Palette.init();
-    
     // subunit initialization
     View.Common.init();
     View.Month.init();
@@ -33,8 +31,6 @@ public void terminate() {
     View.Week.terminate();
     View.Month.terminate();
     View.Common.terminate();
-    
-    Palette.terminate();
 }
 
 }
diff --git a/src/view/week/week-all-day-cell.vala b/src/view/week/week-all-day-cell.vala
index 8886056..66f242a 100644
--- a/src/view/week/week-all-day-cell.vala
+++ b/src/view/week/week-all-day-cell.vala
@@ -20,18 +20,18 @@ internal class AllDayCell : Common.EventsCell {
     public Grid owner { get; private set; }
     
     public AllDayCell(Grid owner, Calendar.Date date) {
-        base (date, date.week_of(owner.owner.first_of_week).to_date_span());
+        base (owner.owner.palette, date, date.week_of(owner.owner.first_of_week).to_date_span());
         
         this.owner = owner;
         
-        Palette.instance.palette_changed.connect(on_palette_changed);
+        palette.palette_changed.connect(on_palette_changed);
         
         // use for initialization
         on_palette_changed();
     }
     
     ~AllDayCell() {
-        Palette.instance.palette_changed.disconnect(on_palette_changed);
+        palette.palette_changed.disconnect(on_palette_changed);
     }
     
     protected override Common.EventsCell? get_cell_for_date(Calendar.Date cell_date) {
@@ -40,7 +40,7 @@ internal class AllDayCell : Common.EventsCell {
     
     private void on_palette_changed() {
         // set fixed size for cell, as it won't grow with the toplevel window
-        set_size_request(-1, (Palette.instance.small_font_height_px + Palette.LINE_PADDING_PX) * 
LINES_SHOWN);
+        set_size_request(-1, (palette.small_font_height_px + Palette.LINE_PADDING_PX) * LINES_SHOWN);
     }
     
     protected override void draw_borders(Cairo.Context ctx) {
@@ -48,7 +48,7 @@ internal class AllDayCell : Common.EventsCell {
         int height = get_allocated_height();
         
         // draw border lines (creates grid effect)
-        Palette.prepare_hairline(ctx, Palette.instance.border);
+        Palette.prepare_hairline(ctx, palette.border);
         
         // draw right border, unless last one in row, in which case spacer deals with that
         if (date.equal_to(neighbors.end_date)) {
diff --git a/src/view/week/week-controller.vala b/src/view/week/week-controller.vala
index ab602f2..7802306 100644
--- a/src/view/week/week-controller.vala
+++ b/src/view/week/week-controller.vala
@@ -56,11 +56,18 @@ public class Controller : BaseObject, View.Controllable {
      */
     public Calendar.FirstOfWeek first_of_week { get; set; }
     
+    /**
+     * { link View.Palette} for the entire hosted view.
+     */
+    public View.Palette palette { get; private set; }
+    
     private ViewContainer stack;
     private Toolkit.StackModel<Calendar.Week> stack_model;
     private Calendar.WeekSpan cache_span;
     
-    public Controller() {
+    public Controller(View.Palette palette) {
+        this.palette = palette;
+        
         stack = new ViewContainer(this);
         stack.homogeneous = true;
         stack.transition_duration = Toolkit.SLOW_STACK_TRANSITION_DURATION_MSEC;
diff --git a/src/view/week/week-day-pane.vala b/src/view/week/week-day-pane.vala
index a0d7954..4dbb13c 100644
--- a/src/view/week/week-day-pane.vala
+++ b/src/view/week/week-day-pane.vala
@@ -192,14 +192,14 @@ internal class DayPane : Pane, Common.InstanceContainer {
     protected override bool on_draw(Cairo.Context ctx) {
         // shade background color if this is current day
         if (date.equal_to(Calendar.System.today)) {
-            Gdk.cairo_set_source_rgba(ctx, Palette.instance.current_day);
+            Gdk.cairo_set_source_rgba(ctx, palette.current_day);
             ctx.paint();
         }
         
         base.on_draw(ctx);
         
         // each event is drawn with a slightly-transparent rectangle with a solid hairline bounding
-        Palette.prepare_hairline(ctx, Palette.instance.border);
+        Palette.prepare_hairline(ctx, palette.border);
         
         foreach (Component.Event event in days_events) {
             // All-day events are handled in separate container ...
@@ -253,7 +253,7 @@ internal class DayPane : Pane, Common.InstanceContainer {
         if (date.equal_to(Calendar.System.today)) {
             int time_of_day_y = get_line_y(Calendar.System.now.to_wall_time());
             
-            Palette.prepare_hairline(ctx, Palette.instance.current_time);
+            Palette.prepare_hairline(ctx, palette.current_time);
             ctx.move_to(0, time_of_day_y);
             ctx.line_to(get_allocated_width(), time_of_day_y);
             ctx.stroke();
@@ -268,7 +268,7 @@ internal class DayPane : Pane, Common.InstanceContainer {
             int height = int.max(start_y, end_y) - y;
             
             ctx.rectangle(0, y, get_allocated_width(), height);
-            Gdk.cairo_set_source_rgba(ctx, Palette.instance.selection);
+            Gdk.cairo_set_source_rgba(ctx, palette.selection);
             ctx.fill();
         }
         
@@ -282,12 +282,12 @@ internal class DayPane : Pane, Common.InstanceContainer {
             layout.set_markup(text, -1);
         else
             layout.set_text(text, -1);
-        layout.set_font_description(Palette.instance.small_font);
+        layout.set_font_description(palette.small_font);
         layout.set_width((total_width - (Palette.TEXT_MARGIN_PX * 2)) * Pango.SCALE);
         layout.set_ellipsize(Pango.EllipsizeMode.END);
         
         int y = get_line_y(start_time) + Palette.LINE_PADDING_PX
-            + (Palette.instance.small_font_height_px * lineno);
+            + (palette.small_font_height_px * lineno);
         
         ctx.move_to(Palette.TEXT_MARGIN_PX, y);
         Gdk.cairo_set_source_rgba(ctx, rgba);
diff --git a/src/view/week/week-grid.vala b/src/view/week/week-grid.vala
index b570327..f38ea4b 100644
--- a/src/view/week/week-grid.vala
+++ b/src/view/week/week-grid.vala
@@ -210,7 +210,7 @@ internal class Grid : Gtk.Box {
     }
     
     private bool on_draw_top_line(Gtk.Widget widget, Cairo.Context ctx) {
-        Palette.prepare_hairline(ctx, Palette.instance.border);
+        Palette.prepare_hairline(ctx, owner.palette.border);
         
         ctx.move_to(0, 0);
         ctx.line_to(widget.get_allocated_width(), 0);
@@ -223,7 +223,7 @@ internal class Grid : Gtk.Box {
         int width = widget.get_allocated_width();
         int height = widget.get_allocated_height();
         
-        Palette.prepare_hairline(ctx, Palette.instance.border);
+        Palette.prepare_hairline(ctx, owner.palette.border);
         
         ctx.move_to(0, height);
         ctx.line_to(width, height);
@@ -239,7 +239,7 @@ internal class Grid : Gtk.Box {
         int height = widget.get_allocated_height();
         Gtk.Widget adjacent = date_to_all_day.get(week.start_date);
         
-        Palette.prepare_hairline(ctx, Palette.instance.border);
+        Palette.prepare_hairline(ctx, owner.palette.border);
         
         ctx.move_to(width, height - adjacent.get_allocated_height());
         ctx.line_to(width, height);
@@ -253,7 +253,7 @@ internal class Grid : Gtk.Box {
         int height = widget.get_allocated_height();
         Gtk.Widget adjacent = date_to_all_day.get(week.end_date);
         
-        Palette.prepare_hairline(ctx, Palette.instance.border);
+        Palette.prepare_hairline(ctx, owner.palette.border);
         
         ctx.move_to(0, height - adjacent.get_allocated_height());
         ctx.line_to(0, height);
diff --git a/src/view/week/week-hour-runner.vala b/src/view/week/week-hour-runner.vala
index 9a843c3..2876456 100644
--- a/src/view/week/week-hour-runner.vala
+++ b/src/view/week/week-hour-runner.vala
@@ -31,14 +31,14 @@ internal class HourRunner : Pane {
         int right_justify_px = get_allocated_width() - Palette.TEXT_MARGIN_PX;
         
         // draw hours in the border color
-        Gdk.cairo_set_source_rgba(ctx, Palette.instance.border);
+        Gdk.cairo_set_source_rgba(ctx, palette.border);
         
         // draw time-of-day down right-hand side of HourRunner pane, which acts as tick marks for
         // the rest of the week view
         Calendar.WallTime wall_time = Calendar.WallTime.earliest;
         for (;;) {
             Pango.Layout layout = create_pango_layout(wall_time.to_pretty_string(TIME_FLAGS));
-            layout.set_font_description(Palette.instance.small_font);
+            layout.set_font_description(palette.small_font);
             layout.set_width(right_justify_px);
             layout.set_alignment(Pango.Alignment.RIGHT);
             
diff --git a/src/view/week/week-pane.vala b/src/view/week/week-pane.vala
index b9603ec..c75fe1a 100644
--- a/src/view/week/week-pane.vala
+++ b/src/view/week/week-pane.vala
@@ -9,7 +9,16 @@ namespace California.View.Week {
 internal abstract class Pane : Gtk.EventBox {
     public weak Grid owner { get; private set; }
     
-    // The height of each "line" of text, including top and bottom padding
+    /**
+     * { link View.Palette} for the { link Pane}.
+     *
+     * The palette should be associated with the Gtk.Window hosting the Pane.
+     */
+    public View.Palette palette { get; private set; }
+    
+    /**
+     * The height of each "line" of text, including top and bottom padding
+     */
     protected int line_height_px { get; private set; default = 0; }
     
     private int requested_width;
@@ -17,6 +26,7 @@ internal abstract class Pane : Gtk.EventBox {
     
     public Pane(Grid owner, int requested_width) {
         this.owner = owner;
+        palette = owner.owner.palette;
         this.requested_width = requested_width;
         
         margin = 0;
@@ -24,19 +34,19 @@ internal abstract class Pane : Gtk.EventBox {
         add(canvas);
         
         update_palette_metrics();
-        Palette.instance.palette_changed.connect(on_palette_changed);
+        palette.palette_changed.connect(on_palette_changed);
         
         canvas.draw.connect(on_draw);
     }
     
     ~Pane() {
-        Palette.instance.palette_changed.disconnect(on_palette_changed);
+        palette.palette_changed.disconnect(on_palette_changed);
     }
     
     private void update_palette_metrics() {
         // calculate the amount of space each "line" gets when drawing (normal font height plus
         // padding on top and bottom)
-        line_height_px = Palette.instance.normal_font_height_px + (Palette.LINE_PADDING_PX * 2);
+        line_height_px = palette.normal_font_height_px + (Palette.LINE_PADDING_PX * 2);
         
         // update the height request based on the number of lines needed to show the entire day
         canvas.set_size_request(requested_width, get_line_y(Calendar.WallTime.latest));
@@ -55,7 +65,7 @@ internal abstract class Pane : Gtk.EventBox {
         ctx.save();
         
         // draw right-side border line
-        Palette.prepare_hairline(ctx, Palette.instance.border);
+        Palette.prepare_hairline(ctx, palette.border);
         ctx.move_to(width, 0);
         ctx.line_to(width, height);
         ctx.line_to(0, height);
@@ -73,9 +83,9 @@ internal abstract class Pane : Gtk.EventBox {
             
             // solid line on the hour, dashed on the half-hour
             if (wall_time.minute == 0)
-                Palette.prepare_hairline(ctx, Palette.instance.border);
+                Palette.prepare_hairline(ctx, palette.border);
             else
-                Palette.prepare_hairline_dashed(ctx, Palette.instance.border);
+                Palette.prepare_hairline_dashed(ctx, palette.border);
             
             ctx.move_to(0, line_y);
             ctx.line_to(width, line_y);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]