[california] Indicate with cursor when hovering over hot spot: Bug #731635



commit 5d437d691b8524aa8ff7638a89a5a07cab7fa28b
Author: Jim Nelson <jim yorba org>
Date:   Fri Sep 19 16:15:50 2014 -0700

    Indicate with cursor when hovering over hot spot: Bug #731635
    
    This moves Month.Grid entirely onto the Toolkit.EventConnectors
    by using MotionConnector for both hot spot detection as well as
    selection dragging.

 src/toolkit/toolkit.vala       |   37 +++++++++++++++++++++++-----------
 src/view/month/month-grid.vala |   42 ++++++++++++++++++++++++++++++++-------
 src/view/week/week-grid.vala   |   34 ++++++++++++++++++++++++++++---
 3 files changed, 89 insertions(+), 24 deletions(-)
---
diff --git a/src/toolkit/toolkit.vala b/src/toolkit/toolkit.vala
index ca38290..f407356 100644
--- a/src/toolkit/toolkit.vala
+++ b/src/toolkit/toolkit.vala
@@ -72,18 +72,7 @@ public void spin_event_loop() {
  * { link set_window_unbusy}.
  */
 public Gdk.Cursor? set_busy(Gtk.Widget widget) {
-    Gtk.Widget toplevel = widget.get_toplevel();
-    if (!toplevel.is_toplevel()) {
-        debug("Unable to set busy: widget has no toplevel window");
-        
-        return null;
-    }
-    
-    Gdk.Window gdk_window = toplevel.get_window();
-    Gdk.Cursor? unbusy_cursor = gdk_window.get_cursor();
-    gdk_window.set_cursor(new Gdk.Cursor.for_display(toplevel.get_display(), Gdk.CursorType.WATCH));
-    
-    return unbusy_cursor;
+    return set_toplevel_cursor(widget, Gdk.CursorType.WATCH);
 }
 
 /**
@@ -104,6 +93,30 @@ public void set_unbusy(Gtk.Widget widget, Gdk.Cursor? unbusy_cursor) {
 }
 
 /**
+ * Sets the Gtk.Widget's toplevel's cursor.
+ *
+ * @returns The toplevel's current cursor.  This can be saved to restore later or simply dropped.
+ */
+public Gdk.Cursor? set_toplevel_cursor(Gtk.Widget widget, Gdk.CursorType? cursor_type) {
+    Gtk.Widget toplevel = widget.get_toplevel();
+    if (!toplevel.is_toplevel()) {
+        debug("Unable to set cursor: widget has no toplevel window");
+        
+        return null;
+    }
+    
+    Gdk.Window gdk_window = toplevel.get_window();
+    Gdk.Cursor? old_cursor = gdk_window.get_cursor();
+    
+    if (cursor_type != null)
+        gdk_window.set_cursor(new Gdk.Cursor.for_display(toplevel.get_display(), cursor_type));
+    else
+        gdk_window.set_cursor(null);
+    
+    return old_cursor;
+}
+
+/**
  * Destroy a Gtk.Widget when the event loop is idle.
  */
 public void destroy_later(Gtk.Widget widget) {
diff --git a/src/view/month/month-grid.vala b/src/view/month/month-grid.vala
index f124f94..47444d2 100644
--- a/src/view/month/month-grid.vala
+++ b/src/view/month/month-grid.vala
@@ -52,6 +52,7 @@ private class Grid : Gtk.Grid {
     private Gee.HashMap<Calendar.Date, Cell> date_to_cell = new Gee.HashMap<Calendar.Date, Cell>();
     private Backing.CalendarSubscriptionManager? subscriptions = null;
     private Toolkit.ButtonConnector cell_button_connector = new Toolkit.ButtonConnector();
+    private Toolkit.MotionConnector cell_motion_connector = new Toolkit.MotionConnector();
     private Scheduled? scheduled_subscription_update = null;
     
     public Grid(Controller owner, Calendar.MonthOfYear month_of_year) {
@@ -65,6 +66,10 @@ private class Grid : Gtk.Grid {
         
         cell_button_connector.clicked.connect(on_cell_single_click);
         cell_button_connector.double_clicked.connect(on_cell_double_click);
+        cell_motion_connector.entered.connect(on_cell_entered_exited);
+        cell_motion_connector.exited.connect(on_cell_entered_exited);
+        cell_motion_connector.motion.connect(on_cell_motion);
+        cell_motion_connector.button_motion.connect(on_cell_button_motion);
         
         // create a WeekSpan for the first week of the month to the last week of the month
         Calendar.WeekSpan span = new Calendar.WeekSpan.from_span(month_of_year, 
Calendar.System.first_of_week);
@@ -87,7 +92,7 @@ private class Grid : Gtk.Grid {
                 Cell cell = new Cell(this, date, rows, col);
                 cell.expand = true;
                 cell_button_connector.connect_to(cell);
-                cell.motion_notify_event.connect(on_cell_motion_event);
+                cell_motion_connector.connect_to(cell);
                 
                 attach(cell, col, rows, 1, 1);
                 
@@ -300,6 +305,8 @@ private class Grid : Gtk.Grid {
         
         Cell press_cell = (Cell) details.widget;
         
+        Toolkit.set_toplevel_cursor(this, null);
+        
         // if pressed and released on the same cell, display the event at the released location
         if (press_cell == release_cell) {
             Component.Event? event = release_cell.get_event_at(details.release_point);
@@ -343,27 +350,48 @@ private class Grid : Gtk.Grid {
         if (release_cell.get_event_at(release_cell_point) != null)
             return Toolkit.STOP;
         
+        Toolkit.set_toplevel_cursor(this, null);
+        
         owner.request_create_all_day_event(new Calendar.DateSpan(press_cell.date, release_cell.date),
             release_cell, release_cell_point);
         
         return Toolkit.STOP;
     }
     
-    private bool on_cell_motion_event(Gtk.Widget widget, Gdk.EventMotion event) {
+    private void on_cell_entered_exited(Toolkit.MotionEvent details) {
+        // when entering or leaving cell, reset the cursor
+        Toolkit.set_toplevel_cursor(details.widget, null);
+    }
+    
+    private void on_cell_motion(Toolkit.MotionEvent details) {
+        Cell cell = (Cell) details.widget;
+        
+        // if hovering over an event, show the "hyperlink" cursor
+        Gdk.CursorType? cursor_type = null;
+        if (cell.get_event_at(details.point) != null)
+            cursor_type = Gdk.CursorType.HAND1;
+        
+        Toolkit.set_toplevel_cursor(cell, cursor_type);
+    }
+    
+    private void on_cell_button_motion(Toolkit.MotionEvent event) {
+        if (!event.is_button_pressed(Toolkit.Button.PRIMARY))
+            return;
+        
         // Because using button 1 motion mask, widget is always the original cell the button-pressed
         // event originated at
-        Cell press_cell = (Cell) widget;
+        Cell press_cell = (Cell) event.widget;
         
         // turn Cell coordinates into GtkGrid coordinates
         int grid_x, grid_y;
-        if (!press_cell.translate_coordinates(this, (int) event.x, (int) event.y, out grid_x, out grid_y))
-            return false;
+        if (!press_cell.translate_coordinates(this, event.point.x, event.point.y, out grid_x, out grid_y))
+            return;
         
         // get the cell the pointer is currently over ... if not found or the same as the original,
         // do nothing
         Cell? hover_cell = translate_to_cell(grid_x, grid_y);
         if (hover_cell == null || hover_cell == press_cell)
-            return false;
+            return;
         
         // mark two cells and all in-between as selected, being sure to mark any previous selected
         // as unselected
@@ -373,8 +401,6 @@ private class Grid : Gtk.Grid {
             
             return true;
         });
-        
-        return true;
     }
 }
 
diff --git a/src/view/week/week-grid.vala b/src/view/week/week-grid.vala
index e79af4e..9ee6a7a 100644
--- a/src/view/week/week-grid.vala
+++ b/src/view/week/week-grid.vala
@@ -49,8 +49,8 @@ internal class Grid : Gtk.Box {
     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.button_only();
-    private Toolkit.MotionConnector all_day_cell_motion_connector = new 
Toolkit.MotionConnector.button_only();
+    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;
     private Gtk.Widget right_spacer;
     private bool vadj_init = false;
@@ -150,8 +150,15 @@ internal class Grid : Gtk.Box {
         instance_container_button_connector.double_clicked.connect(on_instance_container_double_clicked);
         
         // connect to individual motion event handlers for different types of instance containers
+        all_day_cell_motion_connector.entered.connect(on_instance_container_entered_exited);
+        all_day_cell_motion_connector.exited.connect(on_instance_container_entered_exited);
+        all_day_cell_motion_connector.motion.connect(on_instance_container_motion);
         all_day_cell_motion_connector.button_motion.connect(on_all_day_cell_button_motion);
-        day_pane_motion_connector.button_motion.connect(on_day_pane_motion);
+        
+        day_pane_motion_connector.entered.connect(on_instance_container_entered_exited);
+        day_pane_motion_connector.exited.connect(on_instance_container_entered_exited);
+        day_pane_motion_connector.motion.connect(on_instance_container_motion);
+        day_pane_motion_connector.button_motion.connect(on_day_pane_button_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);
@@ -336,6 +343,8 @@ internal class Grid : Gtk.Box {
         if (details.button != Toolkit.Button.PRIMARY)
             return Toolkit.PROPAGATE;
         
+        Toolkit.set_toplevel_cursor(this, null);
+        
         Common.InstanceContainer instance_container = (Common.InstanceContainer) details.widget;
         
         Component.Event? event = instance_container.get_event_at(details.press_point);
@@ -345,6 +354,21 @@ internal class Grid : Gtk.Box {
         return Toolkit.STOP;
     }
     
+    private void on_instance_container_motion(Toolkit.MotionEvent details) {
+        Common.InstanceContainer instance_container = (Common.InstanceContainer) details.widget;
+        
+        Gdk.CursorType? cursor_type = null;
+        if (instance_container.get_event_at(details.point) != null)
+            cursor_type = Gdk.CursorType.HAND1;
+        
+        Toolkit.set_toplevel_cursor(instance_container, cursor_type);
+    }
+    
+    private void on_instance_container_entered_exited(Toolkit.MotionEvent details) {
+        // when entering or leaving instance container (all day cell or day pane), reset the cursor
+        Toolkit.set_toplevel_cursor(details.widget, null);
+    }
+    
     private bool on_instance_container_double_clicked(Toolkit.ButtonEvent details) {
         if (details.button != Toolkit.Button.PRIMARY)
             return Toolkit.PROPAGATE;
@@ -355,6 +379,8 @@ internal class Grid : Gtk.Box {
         if (instance_container.get_event_at(details.press_point) != null)
             return Toolkit.PROPAGATE;
         
+        Toolkit.set_toplevel_cursor(instance_container, null);
+        
         // if a DayPane, use double-click to determine rounded time of the event's start
         DayPane? day_pane = instance_container as DayPane;
         if (day_pane != null) {
@@ -379,7 +405,7 @@ internal class Grid : Gtk.Box {
         return Toolkit.STOP;
     }
     
-    private void on_day_pane_motion(Toolkit.MotionEvent details) {
+    private void on_day_pane_button_motion(Toolkit.MotionEvent details) {
         DayPane day_pane = (DayPane) details.widget;
         
         // only update selection as long as button is depressed


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