[california/wip/725767-week] Fleshing out Week.Pane, moved drawing metrics to Palette object



commit 9345a5a439a45a0fe0483ea670981723c1e6ff9e
Author: Jim Nelson <jim yorba org>
Date:   Tue May 6 17:45:07 2014 -0700

    Fleshing out Week.Pane, moved drawing metrics to Palette object
    
    Having some trouble getting the Panes inside a ScrolledWindow to
    work correctly, will work on that next.

 src/Makefile.am                    |    2 +
 src/host/host-main-window.vala     |    8 ++
 src/view/month/month-cell.vala     |   85 +++++--------------
 src/view/month/month.vala          |    5 -
 src/view/view-palette.vala         |  167 ++++++++++++++++++++++++++++++++++++
 src/view/view.vala                 |    8 ++-
 src/view/week/week-controller.vala |    8 +-
 src/view/week/week-grid.vala       |   21 ++++-
 src/view/week/week-pane.vala       |   97 +++++++++++++++++++++
 9 files changed, 323 insertions(+), 78 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 9a3aaa8..a5b5de4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -127,6 +127,7 @@ california_VALASOURCES = \
        view/view.vala \
        view/view-container.vala \
        view/view-controllable.vala \
+       view/view-palette.vala \
        \
        view/month/month.vala \
        view/month/month-cell.vala \
@@ -136,6 +137,7 @@ california_VALASOURCES = \
        view/week/week.vala \
        view/week/week-controller.vala \
        view/week/week-grid.vala \
+       view/week/week-pane.vala \
        \
        $(NULL)
 
diff --git a/src/host/host-main-window.vala b/src/host/host-main-window.vala
index dce6443..64c61b6 100644
--- a/src/host/host-main-window.vala
+++ b/src/host/host-main-window.vala
@@ -134,6 +134,14 @@ public class MainWindow : Gtk.ApplicationWindow {
         add(layout);
     }
     
+    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.add_titled(controller.get_container(), controller.title, controller.title);
         controller.get_container().show_all();
diff --git a/src/view/month/month-cell.vala b/src/view/month/month-cell.vala
index 0933d02..ae04260 100644
--- a/src/view/month/month-cell.vala
+++ b/src/view/month/month-cell.vala
@@ -11,12 +11,6 @@ namespace California.View.Month {
  */
 
 private class Cell : Gtk.EventBox {
-    private const int TOP_LINE_FONT_SIZE_PT = 11;
-    private const int LINE_FONT_SIZE_PT = 8;
-    
-    private const int TEXT_MARGIN_PX = 2;
-    private const int LINE_SPACING_PX = 4;
-    
     private const double ROUNDED_CAP_RADIUS = 5.0;
     private const int POINTED_CAP_WIDTH_PX = 6;
     
@@ -74,18 +68,6 @@ private class Cell : Gtk.EventBox {
     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>();
     
-    // TODO: We may need to get these colors from the theme
-    private static Gdk.RGBA RGBA_BORDER = { red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0 };
-    private static Gdk.RGBA RGBA_DAY_OF_MONTH = { red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0 };
-    private static Gdk.RGBA RGBA_DAY_OUTSIDE_MONTH = { red: 0.6, green: 0.6, blue: 0.6, alpha: 1.0 };
-    private static Gdk.RGBA RGBA_CURRENT_DAY = { red: 0.0, green: 0.25, blue: 0.50, alpha: 0.10 };
-    private static Gdk.RGBA RGBA_SELECTED = { red: 0.0, green: 0.50, blue: 0.50, alpha: 0.10 };
-    
-    private static Pango.FontDescription top_line_font;
-    private static Pango.FontDescription line_font;
-    private static int top_line_height_px = -1;
-    private static int line_height_px = -1;
-    
     private Gtk.DrawingArea canvas = new Gtk.DrawingArea();
     
     public Cell(Grid owner, int row, int col) {
@@ -102,6 +84,7 @@ private class Cell : Gtk.EventBox {
         
         notify["date"].connect(queue_draw);
         notify["selected"].connect(queue_draw);
+        Palette.instance.palette_changed.connect(queue_draw);
         Calendar.System.instance.is_24hr_changed.connect(on_24hr_changed);
         Calendar.System.instance.today_changed.connect(on_today_changed);
         
@@ -109,26 +92,11 @@ private class Cell : Gtk.EventBox {
     }
     
     ~Cell() {
+        Palette.instance.palette_changed.disconnect(queue_draw);
         Calendar.System.instance.is_24hr_changed.disconnect(on_24hr_changed);
         Calendar.System.instance.today_changed.disconnect(on_today_changed);
     }
     
-    internal static void init() {
-        top_line_font = new Pango.FontDescription();
-        top_line_font.set_size(TOP_LINE_FONT_SIZE_PT * Pango.SCALE);
-        
-        line_font = new Pango.FontDescription();
-        line_font.set_size(LINE_FONT_SIZE_PT * Pango.SCALE);
-        
-        // top_line_height_px and line_height_px can't be calculated until one of the Cells is
-        // rendered
-    }
-    
-    internal static void terminate() {
-        top_line_font = null;
-        line_font = null;
-    }
-    
     // this comparator uses the standard Event comparator with one exception: if both Events require
     // solid span lines, it sorts the one(s) with the furthest out end dates to the top, to ensure
     // they are at the top of the drawn lines and prevent gaps and skips in the connected bars
@@ -394,16 +362,12 @@ private class Cell : Gtk.EventBox {
     }
     
     private bool on_draw(Cairo.Context ctx) {
-        // calculate extents if not already calculated;
-        if (line_height_px < 0 || top_line_height_px < 0)
-            calculate_extents(out top_line_height_px, out line_height_px);
-        
         // shade background of cell for selection or if today
         if (selected) {
-            Gdk.cairo_set_source_rgba(ctx, RGBA_SELECTED);
+            Gdk.cairo_set_source_rgba(ctx, Palette.instance.selection);
             ctx.paint();
         } else if (date != null && date.equal_to(Calendar.System.today)) {
-            Gdk.cairo_set_source_rgba(ctx, RGBA_CURRENT_DAY);
+            Gdk.cairo_set_source_rgba(ctx, Palette.instance.current_day);
             ctx.paint();
         }
         
@@ -411,7 +375,7 @@ private class Cell : Gtk.EventBox {
         int height = get_allocated_height();
         
         // draw border lines (creates grid effect)
-        Gdk.cairo_set_source_rgba(ctx, RGBA_BORDER);
+        Gdk.cairo_set_source_rgba(ctx, Palette.instance.border);
         ctx.set_line_width(0.5);
         
         // only draw top line if on the top row
@@ -436,7 +400,9 @@ private class Cell : Gtk.EventBox {
         
         // draw day of month as the top line
         if (date != null) {
-            Gdk.RGBA color = (date in owner.month_of_year) ? RGBA_DAY_OF_MONTH : RGBA_DAY_OUTSIDE_MONTH;
+            unowned Gdk.RGBA color = (date in owner.month_of_year)
+                ? Palette.instance.day_in_range
+                : Palette.instance.day_outside_range;
             draw_line_of_text(ctx, -1, color, date.day_of_month.informal_number, CapEffect.NONE,
                 CapEffect.NONE);
         }
@@ -497,31 +463,18 @@ private class Cell : Gtk.EventBox {
         return true;
     }
     
-    private void calculate_extents(out int top_line_height_px, out int line_height_px) {
-        Pango.Layout layout = create_pango_layout("Gg");
-        layout.set_font_description(top_line_font);
-        
-        int width;
-        layout.get_pixel_size(out width, out top_line_height_px);
-        
-        layout = create_pango_layout("Gg");
-        layout.set_font_description(line_font);
-        
-        layout.get_pixel_size(out width, out line_height_px);
-    }
-    
     // Returns top y position of line; negative line numbers are treated as top line
     // The number is currently not clamped to the height of the widget.
     private int get_line_top_y(int line_number) {
         int y;
         if (line_number < 0) {
-            y = TEXT_MARGIN_PX;
+            y = Palette.TEXT_MARGIN_PX;
         } else {
             // starting y of "regular" lines
-            y = TEXT_MARGIN_PX + top_line_height_px + LINE_SPACING_PX;
+            y = Palette.TEXT_MARGIN_PX + Palette.instance.normal_font_height_px + Palette.LINE_SPACING_PX;
             
             // add additional lines
-            y += line_number * (line_height_px + LINE_SPACING_PX);
+            y += line_number * (Palette.instance.small_font_height_px + Palette.LINE_SPACING_PX);
         }
         
         return y;
@@ -536,7 +489,7 @@ private class Cell : Gtk.EventBox {
         int left = 0;
         int right = get_allocated_width();
         int top = get_line_top_y(line_number);
-        int bottom = top + line_height_px;
+        int bottom = top + Palette.instance.small_font_height_px;
         
         // use event color for text unless reversed, where it becomes the background color
         Gdk.cairo_set_source_rgba(ctx, rgba);
@@ -554,7 +507,7 @@ private class Cell : Gtk.EventBox {
                 
                 case CapEffect.POINTED:
                     ctx.move_to(right - POINTED_CAP_WIDTH_PX, top);
-                    ctx.line_to(right, top + (line_height_px / 2));
+                    ctx.line_to(right, top + (Palette.instance.small_font_height_px / 2));
                     ctx.line_to(right - POINTED_CAP_WIDTH_PX, bottom);
                 break;
                 
@@ -576,7 +529,7 @@ private class Cell : Gtk.EventBox {
                 
                 case CapEffect.POINTED:
                     ctx.line_to(left + POINTED_CAP_WIDTH_PX, bottom);
-                    ctx.line_to(left, top + (line_height_px / 2));
+                    ctx.line_to(left, top + (Palette.instance.small_font_height_px / 2));
                     ctx.line_to(left + POINTED_CAP_WIDTH_PX, top);
                 break;
                 
@@ -599,11 +552,13 @@ private class Cell : Gtk.EventBox {
         }
         
         // add a couple of pixels to the text margins if capped
-        int left_text_margin = TEXT_MARGIN_PX + (left_effect != CapEffect.NONE ? 3 : 0);
-        int right_text_margin = TEXT_MARGIN_PX + (right_effect != CapEffect.NONE ? 3 : 0);
+        int left_text_margin = Palette.TEXT_MARGIN_PX + (left_effect != CapEffect.NONE ? 3 : 0);
+        int right_text_margin = Palette.TEXT_MARGIN_PX + (right_effect != CapEffect.NONE ? 3 : 0);
         
         Pango.Layout layout = create_pango_layout(text);
-        layout.set_font_description((line_number < 0) ? top_line_font : line_font);
+        layout.set_font_description((line_number < 0)
+            ? Palette.instance.normal_font
+            : Palette.instance.small_font);
         layout.set_ellipsize(Pango.EllipsizeMode.END);
         layout.set_width((right - left - left_text_margin - right_text_margin) * Pango.SCALE);
         
@@ -621,7 +576,7 @@ private class Cell : Gtk.EventBox {
     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 + line_height_px))
+            if (point.y >= y && point.y < (y + Palette.instance.small_font_height_px))
                 return line_to_event.get(line_number);
         }
         
diff --git a/src/view/month/month.vala b/src/view/month/month.vala
index 8f07f50..6c4f016 100644
--- a/src/view/month/month.vala
+++ b/src/view/month/month.vala
@@ -20,17 +20,12 @@ public void init() throws Error {
     Calendar.init();
     Component.init();
     Backing.init();
-    
-    // internal initialization
-    Cell.init();
 }
 
 public void terminate() {
     if (!Unit.do_terminate(ref init_count))
         return;
     
-    Cell.terminate();
-    
     Backing.terminate();
     Component.terminate();
     Calendar.terminate();
diff --git a/src/view/view-palette.vala b/src/view/view-palette.vala
new file mode 100644
index 0000000..ae9be91
--- /dev/null
+++ b/src/view/view-palette.vala
@@ -0,0 +1,167 @@
+/* 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.View {
+
+/**
+ * A singleton holding colors and theme information for drawing the various views.
+ *
+ * TODO: Currently colors are hard-coded.  In the future we'll probably need to get these from the
+ * system or the theme.
+ */
+
+public class Palette : BaseObject {
+    /**
+     * Margins around text (in pixels).
+     */
+    public const int TEXT_MARGIN_PX = 2;
+    
+    /**
+     * Line spacing when painting text (in pixels).
+     */
+    public const int LINE_SPACING_PX = 4;
+    
+    private const int NORMAL_FONT_SIZE_PT = 11;
+    private const int SMALL_FONT_SIZE_PT = 8;
+    
+    public static Palette instance { get; private set; }
+    
+    /**
+     * Border color (when separating days, for example).
+     */
+    public Gdk.RGBA border { get; private set; }
+    
+    /**
+     * Color to use when drawing details of a day inside the current { link View} range.
+     *
+     * @see day_outside_range
+     */
+    public Gdk.RGBA day_in_range { get; private set; }
+    
+    /**
+     * Color to use when drawing details of a day outside the current { link View} range.
+     *
+     * @see day_in_range
+     */
+    public Gdk.RGBA day_outside_range { get; private set; }
+    
+    /**
+     * Background color for day representing current date.
+     */
+    public Gdk.RGBA current_day { get; private set; }
+    
+    /**
+     * Background color to use for selected days/time.
+     */
+    public Gdk.RGBA selection { get; private set; }
+    
+    /**
+     * Normal-sized font.
+     *
+     * In general this should be used sparingly, as most calendar views need to conserve screen
+     * real estate and use { link Host.ShowEvent} to display a greater amount of detail.
+     *
+     * @see small_font
+     */
+    public Pango.FontDescription normal_font;
+    
+    /**
+     * Font height extent for { link normal_font} (in pixels).
+     *
+     * This will be a negative value until the main window is mapped to the screen.
+     *
+     * @see main_window_mapped
+     */
+    public int normal_font_height_px { get; private set; default = -1; }
+    
+    /**
+     * Small font.
+     *
+     * This is more appropriate than { link normal_font} when displaying calendar information,
+     * especially event detail.
+     */
+    public Pango.FontDescription small_font;
+    
+    /**
+     * Font height extent for { link small_font} (in pixels).
+     *
+     * This will be a negative value until the main window is mapped to the screen.
+     *
+     * @see main_window_mapped
+     */
+    public int small_font_height_px { get; private set; default = -1; }
+    
+    /**
+     * Fired when palette has changed.
+     *
+     * It's generally simpler to subscribe to this signal rather than the "notify" for every
+     * property.
+     */
+    public signal void palette_changed();
+    
+    private Palette() {
+        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 };
+        current_day = { red: 0.0, green: 0.25, blue: 0.50, alpha: 0.10 };
+        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);
+        
+        small_font = new Pango.FontDescription();
+        small_font.set_size(SMALL_FONT_SIZE_PT * Pango.SCALE);
+    }
+    
+    internal static void init() {
+        instance = new Palette();
+    }
+    
+    internal static void terminate() {
+        instance = null;
+    }
+    
+    /**
+     * 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;
+        
+        int height = get_height_extent(window, normal_font);
+        if (height != normal_font_height_px) {
+            normal_font_height_px = height;
+            updated = true;
+        }
+        
+        height = get_height_extent(window, small_font);
+        if (height != small_font_height_px) {
+            small_font_height_px = height;
+            updated = true;
+        }
+        
+        if (updated)
+            palette_changed();
+    }
+    
+    private static int get_height_extent(Gtk.Widget widget, Pango.FontDescription font) {
+        Pango.Layout layout = widget.create_pango_layout("Gg");
+        layout.set_font_description(font);
+        
+        int width, height;
+        layout.get_pixel_size(out width, out height);
+        
+        return height;
+    }
+    
+    public override string to_string() {
+        return "View.Palette";
+    }
+}
+
+}
+
diff --git a/src/view/view.vala b/src/view/view.vala
index e60f7ee..1af104e 100644
--- a/src/view/view.vala
+++ b/src/view/view.vala
@@ -7,7 +7,7 @@
 /**
  * User views of the calendar data.
  *
- * The { link MainWindow} hosts all views and offers an interface to switch between them.
+ * The { link Host.MainWindow} hosts all views and offers an interface to switch between them.
  */
 
 namespace California.View {
@@ -18,15 +18,21 @@ public void init() throws Error {
     if (!Unit.do_init(ref init_count))
         return;
     
+    Palette.init();
+    
     // subunit initialization
     View.Month.init();
+    View.Week.init();
 }
 
 public void terminate() {
     if (!Unit.do_terminate(ref init_count))
         return;
     
+    View.Week.terminate();
     View.Month.terminate();
+    
+    Palette.terminate();
 }
 
 }
diff --git a/src/view/week/week-controller.vala b/src/view/week/week-controller.vala
index aed2748..0be5d54 100644
--- a/src/view/week/week-controller.vala
+++ b/src/view/week/week-controller.vala
@@ -15,11 +15,11 @@ public class Controller : BaseObject, View.Controllable {
     
     private const int CACHE_NEIGHBORS_COUNT = 4;
     
-    private class MasterStack : Gtk.Stack, View.Container {
+    private class ViewContainer : Gtk.Stack, View.Container {
         private Controller _owner;
         public unowned View.Controllable owner { get { return _owner; } }
         
-        public MasterStack(Controller owner) {
+        public ViewContainer(Controller owner) {
             _owner = owner;
         }
     }
@@ -49,12 +49,12 @@ public class Controller : BaseObject, View.Controllable {
      */
     public Calendar.FirstOfWeek first_of_week { get; set; }
     
-    private MasterStack stack;
+    private ViewContainer stack;
     private Toolkit.StackModel<Calendar.Week> stack_model;
     private Calendar.WeekSpan cache_span;
     
     public Controller() {
-        stack = new MasterStack(this);
+        stack = new ViewContainer(this);
         stack.homogeneous = true;
         stack.transition_duration = Toolkit.SLOW_STACK_TRANSITION_DURATION_MSEC;
         
diff --git a/src/view/week/week-grid.vala b/src/view/week/week-grid.vala
index dd82b8a..87a9afc 100644
--- a/src/view/week/week-grid.vala
+++ b/src/view/week/week-grid.vala
@@ -25,6 +25,9 @@ internal class Grid : Gtk.Grid {
      */
     public string id { owned get { return week.to_string(); } }
     
+    private Gtk.Grid pane_grid = new Gtk.Grid();
+    private Gtk.ScrolledWindow scrolled_panes = new Gtk.ScrolledWindow(null, null);
+    
     public Grid(Calendar.Week week) {
         this.week = week;
         
@@ -33,7 +36,17 @@ internal class Grid : Gtk.Grid {
         row_homogeneous = false;
         row_spacing = 0;
         
-        // date labels across the top, week panes extending across the bottom
+        pane_grid.column_homogeneous = true;
+        pane_grid.column_spacing = 0;
+        pane_grid.row_homogeneous = true;
+        pane_grid.row_spacing = 0;
+        
+        scrolled_panes.hscrollbar_policy = Gtk.PolicyType.NEVER;
+        scrolled_panes.vscrollbar_policy = Gtk.PolicyType.ALWAYS;
+        scrolled_panes.add_with_viewport(pane_grid);
+        
+        // date labels across the top, week panes extending across the bottom, all stored inside
+        // a scrolled window
         int col = 0;
         foreach (Calendar.Date date in week) {
             Gtk.Label date_label = new Gtk.Label("%s %d/%d".printf(date.day_of_week.abbrev_name,
@@ -42,12 +55,14 @@ internal class Grid : Gtk.Grid {
             date_label.margin_bottom = 2;
             attach(date_label, col, 0, 1, 1);
             
-            Gtk.DrawingArea pane = new Gtk.DrawingArea();
+            Pane pane = new Pane(this, date);
             pane.expand = true;
-            attach(pane, col, 1, 1, 1);
+            pane_grid.attach(pane, col, 0, 1, 1);
             
             col++;
         }
+        
+        attach(scrolled_panes, 0, 1, col, 1);
     }
 }
 
diff --git a/src/view/week/week-pane.vala b/src/view/week/week-pane.vala
new file mode 100644
index 0000000..87232b7
--- /dev/null
+++ b/src/view/week/week-pane.vala
@@ -0,0 +1,97 @@
+/* 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.View.Week {
+
+internal class Pane : Gtk.EventBox {
+    public const string PROP_OWNER = "owner";
+    public const string PROP_DATE = "date";
+    public const string PROP_SELECTED = "selected";
+    
+    public weak Grid owner { get; private set; }
+    
+    public Calendar.Date date { get; set; }
+    
+    public bool selected { get; set; default = false; }
+    
+    private Gtk.DrawingArea canvas = new Gtk.DrawingArea();
+    
+    public Pane(Grid owner, Calendar.Date initial_date) {
+        this.owner = owner;
+        date = initial_date;
+        
+        canvas.set_size_request(-1, get_line_y(Calendar.WallTime.latest) + get_line_height());
+        add(canvas);
+        
+        notify[PROP_DATE].connect(queue_draw);
+        notify[PROP_SELECTED].connect(queue_draw);
+        Palette.instance.palette_changed.connect(queue_draw);
+        Calendar.System.instance.is_24hr_changed.connect(queue_draw);
+        Calendar.System.instance.today_changed.connect(on_today_changed);
+        
+        canvas.draw.connect(on_draw);
+    }
+    
+    ~Pane() {
+        Palette.instance.palette_changed.disconnect(queue_draw);
+        Calendar.System.instance.is_24hr_changed.disconnect(queue_draw);
+        Calendar.System.instance.today_changed.disconnect(on_today_changed);
+    }
+    
+    private void on_today_changed(Calendar.Date old_today, Calendar.Date new_today) {
+        // need to know re: redrawing background color to indicate current day
+        if (date.equal_to(old_today) || date.equal_to(new_today))
+            queue_draw();
+    }
+    
+    private bool on_draw(Cairo.Context ctx) {
+        // shade background color if this is current day or selected
+        if (selected) {
+            Gdk.cairo_set_source_rgba(ctx, Palette.instance.selection);
+            ctx.paint();
+        } else if (date.equal_to(Calendar.System.today)) {
+            Gdk.cairo_set_source_rgba(ctx, Palette.instance.current_day);
+            ctx.paint();
+        }
+        
+        int width = get_allocated_width();
+        int height = get_allocated_height();
+        
+        // draw border lines (one across top, one on right side)
+        Gdk.cairo_set_source_rgba(ctx, Palette.instance.border);
+        ctx.set_line_width(0.5);
+        ctx.move_to(0, 0);
+        ctx.line_to(width, 0);
+        ctx.line_to(width, height);
+        ctx.stroke();
+        
+        // draw hour lines
+        Calendar.WallTime wall_time = Calendar.WallTime.earliest;
+        bool rollover = false;
+        do {
+            wall_time = wall_time.adjust(1, Calendar.TimeUnit.HOUR, out rollover);
+            
+            int line_y = get_line_y(wall_time);
+            
+            ctx.move_to(0, line_y);
+            ctx.line_to(width, line_y);
+            ctx.stroke();
+        } while (!rollover);
+        
+        return true;
+    }
+    
+    private int get_line_height() {
+        return (Palette.instance.normal_font_height_px + Palette.LINE_SPACING_PX) * 2;
+    }
+    
+    private int get_line_y(Calendar.WallTime wall_time) {
+        return get_line_height() * wall_time.hour;
+    }
+}
+
+}
+


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