[california/wip/726809-rename: 1/3] Works, need to refactor into separate Toolkit component



commit 8a0fb86af9ad6aa726cedfd0bd2fbfb961e6b69c
Author: Jim Nelson <jim yorba org>
Date:   Wed Apr 30 21:01:05 2014 -0700

    Works, need to refactor into separate Toolkit component

 src/Makefile.am                             |    1 +
 src/manager/manager-calendar-list-item.vala |   62 ++++++++++++++++++++++++++-
 src/manager/manager-calendar-list.vala      |   21 ++++++++-
 src/rc/calendar-manager-list-item.ui        |   47 ++++++++++++---------
 src/rc/calendar-manager-list.ui             |    1 +
 src/toolkit/toolkit-calendar-popup.vala     |    2 +-
 src/toolkit/toolkit-editable-label.vala     |   60 ++++++++++++++++++++++++++
 src/toolkit/toolkit-popup.vala              |   34 ++++++++++++--
 8 files changed, 198 insertions(+), 30 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 99df41d..30fa900 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -111,6 +111,7 @@ california_VALASOURCES = \
        toolkit/toolkit-combo-box-text-model.vala \
        toolkit/toolkit-deck.vala \
        toolkit/toolkit-deck-window.vala \
+       toolkit/toolkit-editable-label.vala \
        toolkit/toolkit-listbox-model.vala \
        toolkit/toolkit-mutable-widget.vala \
        toolkit/toolkit-popup.vala \
diff --git a/src/manager/manager-calendar-list-item.vala b/src/manager/manager-calendar-list-item.vala
index 977453d..08af2b7 100644
--- a/src/manager/manager-calendar-list-item.vala
+++ b/src/manager/manager-calendar-list-item.vala
@@ -11,11 +11,16 @@ namespace California.Manager {
  */
 
 [GtkTemplate (ui = "/org/yorba/california/rc/calendar-manager-list-item.ui")]
-public class CalendarListItem : Gtk.Grid {
+internal class CalendarListItem : Gtk.Grid {
     private const int COLOR_DIM = 16;
     
     public Backing.CalendarSource source { get; private set; }
     
+    /**
+     * Set by { link CalendarList}.
+     */
+    public bool is_selected { get; set; default = false; }
+    
     [GtkChild]
     private Gtk.Image readonly_icon;
     
@@ -23,11 +28,17 @@ public class CalendarListItem : Gtk.Grid {
     private Gtk.CheckButton visible_check_button;
     
     [GtkChild]
+    private Gtk.EventBox title_eventbox;
+    
+    [GtkChild]
     private Gtk.Label title_label;
     
     [GtkChild]
     private Gtk.ColorButton color_button;
     
+    private Toolkit.Popup? title_entry_popup = null;
+    private Gtk.Entry? title_entry = null;
+    
     public CalendarListItem(Backing.CalendarSource source) {
         this.source = source;
         
@@ -43,6 +54,8 @@ public class CalendarListItem : Gtk.Grid {
             () => source.read_only ? "changes-prevent-symbolic" : "");
         Properties.xform_to_string(source, Backing.Source.PROP_READONLY, readonly_icon, "tooltip-text",
             () => source.read_only ? _("Calendar is read-only") : null);
+        
+        title_eventbox.button_release_event.connect(on_title_button_release);
     }
     
     public override bool query_tooltip(int x, int y, bool keyboard_mode, Gtk.Tooltip tooltip) {
@@ -67,6 +80,53 @@ public class CalendarListItem : Gtk.Grid {
         
         return true;
     }
+    
+    // Replaces title with a Gtk.Entry for the title that allows for the user to rename the calendar
+    // ... if Enter is pressed, the name is accepted, Escape and clicking off of it is considered a
+    // cancel
+    private void activate_title_entry() {
+        assert(title_entry_popup == null);
+        
+        title_entry = new Gtk.Entry();
+        title_entry.text = source.title;
+        title_entry.width_chars = title_label.width_chars;
+        
+        title_entry_popup = new Toolkit.Popup(title_label, Toolkit.Popup.Position.VERTICAL_CENTER);
+        title_entry_popup.margin = 0;
+        title_entry_popup.add(title_entry);
+        title_entry_popup.dismissed.connect(remove_title_entry);
+        
+        // Enter accepts
+        title_entry.activate.connect(on_title_entry_accepted);
+        
+        title_entry_popup.show_all();
+    }
+    
+    private void remove_title_entry() {
+        assert(title_entry_popup != null);
+        
+        title_entry_popup.destroy();
+        title_entry_popup = null;
+        title_entry = null;
+    }
+    
+    private bool on_title_button_release(Gdk.EventButton event) {
+        // if already accepting input or not selected, don't activate text entry for rename (but
+        // allow signal to propagate further)
+        if (title_entry != null || !is_selected)
+            return false;
+        
+        activate_title_entry();
+        
+        return true;
+    }
+    
+    private void on_title_entry_accepted() {
+        if (!String.is_empty(title_entry.text))
+            source.title = title_entry.text;
+        
+        title_entry_popup.dismiss();
+    }
 }
 
 }
diff --git a/src/manager/manager-calendar-list.vala b/src/manager/manager-calendar-list.vala
index 48246b0..8287e7d 100644
--- a/src/manager/manager-calendar-list.vala
+++ b/src/manager/manager-calendar-list.vala
@@ -11,7 +11,9 @@ namespace California.Manager {
  */
 
 [GtkTemplate (ui = "/org/yorba/california/rc/calendar-manager-list.ui")]
-public class CalendarList : Gtk.Grid, Toolkit.Card {
+internal class CalendarList : Gtk.Grid, Toolkit.Card {
+    public const string PROP_SELECTED = "selected";
+    
     public const string ID = "CalendarList";
     
     public string card_id { get { return ID; } }
@@ -22,6 +24,8 @@ public class CalendarList : Gtk.Grid, Toolkit.Card {
     
     public Gtk.Widget? initial_focus { get { return calendar_list_box; } }
     
+    public CalendarListItem? selected { get; private set; default = null; }
+    
     [GtkChild]
     private Gtk.ListBox calendar_list_box;
     
@@ -84,8 +88,19 @@ public class CalendarList : Gtk.Grid, Toolkit.Card {
     
     [GtkCallback]
     private void on_calendar_list_box_row_activated(Gtk.ListBoxRow row) {
-        CalendarListItem item = (CalendarListItem) row.get_child();
-        debug("activated %s", item.source.to_string());
+    }
+    
+    [GtkCallback]
+    private void on_calendar_list_box_row_selected(Gtk.ListBoxRow? row) {
+        if (selected != null)
+            selected.is_selected = false;
+        
+        if (row != null) {
+            selected = (CalendarListItem) row.get_child();
+            selected.is_selected = true;
+        } else {
+            selected = null;
+        }
     }
     
     [GtkCallback]
diff --git a/src/rc/calendar-manager-list-item.ui b/src/rc/calendar-manager-list-item.ui
index 2b756cf..6b5f2d7 100644
--- a/src/rc/calendar-manager-list-item.ui
+++ b/src/rc/calendar-manager-list-item.ui
@@ -7,26 +7,6 @@
     <property name="can_focus">False</property>
     <property name="column_spacing">4</property>
     <child>
-      <object class="GtkLabel" id="title_label">
-        <property name="visible">True</property>
-        <property name="can_focus">False</property>
-        <property name="valign">center</property>
-        <property name="hexpand">True</property>
-        <property name="vexpand">True</property>
-        <property name="xalign">0</property>
-        <property name="yalign">0</property>
-        <property name="label">calendar name</property>
-        <property name="ellipsize">end</property>
-        <property name="single_line_mode">True</property>
-      </object>
-      <packing>
-        <property name="left_attach">3</property>
-        <property name="top_attach">0</property>
-        <property name="width">1</property>
-        <property name="height">1</property>
-      </packing>
-    </child>
-    <child>
       <object class="GtkColorButton" id="color_button">
         <property name="visible">True</property>
         <property name="can_focus">True</property>
@@ -79,5 +59,32 @@
         <property name="height">1</property>
       </packing>
     </child>
+    <child>
+      <object class="GtkEventBox" id="title_eventbox">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="events">GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+        <child>
+          <object class="GtkLabel" id="title_label">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="valign">center</property>
+            <property name="hexpand">True</property>
+            <property name="vexpand">True</property>
+            <property name="xalign">0</property>
+            <property name="yalign">0</property>
+            <property name="label">calendar name</property>
+            <property name="ellipsize">end</property>
+            <property name="single_line_mode">True</property>
+          </object>
+        </child>
+      </object>
+      <packing>
+        <property name="left_attach">3</property>
+        <property name="top_attach">0</property>
+        <property name="width">1</property>
+        <property name="height">1</property>
+      </packing>
+    </child>
   </template>
 </interface>
diff --git a/src/rc/calendar-manager-list.ui b/src/rc/calendar-manager-list.ui
index c61bc11..65d2a74 100644
--- a/src/rc/calendar-manager-list.ui
+++ b/src/rc/calendar-manager-list.ui
@@ -61,6 +61,7 @@
                 <property name="vexpand">True</property>
                 <property name="activate_on_single_click">False</property>
                 <signal name="row-activated" handler="on_calendar_list_box_row_activated" 
object="CaliforniaManagerCalendarList" swapped="no"/>
+                <signal name="row-selected" handler="on_calendar_list_box_row_selected" 
object="CaliforniaManagerCalendarList" swapped="no"/>
               </object>
             </child>
           </object>
diff --git a/src/toolkit/toolkit-calendar-popup.vala b/src/toolkit/toolkit-calendar-popup.vala
index 391dab0..0c0633c 100644
--- a/src/toolkit/toolkit-calendar-popup.vala
+++ b/src/toolkit/toolkit-calendar-popup.vala
@@ -35,7 +35,7 @@ public class CalendarPopup : Popup {
      * inheritDoc
      */
     public CalendarPopup(Gtk.Widget relative_to, Calendar.Date initial_date) {
-        base (relative_to);
+        base (relative_to, Popup.Position.BELOW);
         
         calendar.day = initial_date.day_of_month.value;
         calendar.month = initial_date.month.value - 1;
diff --git a/src/toolkit/toolkit-editable-label.vala b/src/toolkit/toolkit-editable-label.vala
new file mode 100644
index 0000000..46bd05f
--- /dev/null
+++ b/src/toolkit/toolkit-editable-label.vala
@@ -0,0 +1,60 @@
+/* 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.Toolkit {
+
+/**
+ * Uses a { link Popup} to place a Gtk.Entry over a Gtk.Label, creating the illusion that the user
+ * can "edit" the label.
+ *
+ * If the user presses Enter, { link accepted} will fire.  It is up to the caller to set the
+ * Gtk.Label's text to this field.
+ *
+ * Callers should subscribe to the { link dismissed} label to destroy this widget.
+ *
+ * This currently doesn't deal with font issues (i.e. the editable field will use the system editing
+ * font).
+ */
+
+public class EditableLabel : Popup {
+    /**
+     * The Gtk.Label being "edited".
+     */
+    public Gtk.Label label { get; private set; }
+    
+    private Gtk.Entry entry = new Gtk.Entry();
+    
+    /**
+     * Fired when the user presses Enter indicating the text should be accepted.
+     *
+     * It is up to the caller to set the Gtk.Label's text to this text (if so desired).  The
+     * { link EditableLabel} will be dismissed after this signal completes.
+     */
+    public signal void accepted(string text);
+    
+    public EditableLabel(Gtk.Label label) {
+        base (label, Popup.Position.VERTICAL_CENTER);
+        
+        // set up Gtk.Entry to look and be sized exactly like the Gtk.Label
+        entry.text = label.label;
+        entry.width_chars = label.width_chars;
+        add(entry);
+        
+        // make sure the Popup window is hugging close to label as well
+        margin = 0;
+        
+        // Enter accepts
+        entry.activate.connect(on_entry_accepted);
+    }
+    
+    private void on_entry_accepted() {
+        accepted(entry.text);
+        dismissed();
+    }
+}
+
+}
+
diff --git a/src/toolkit/toolkit-popup.vala b/src/toolkit/toolkit-popup.vala
index beb76b8..fcee4f5 100644
--- a/src/toolkit/toolkit-popup.vala
+++ b/src/toolkit/toolkit-popup.vala
@@ -14,8 +14,16 @@ namespace California.Toolkit {
  */
 
 public class Popup : Gtk.Window {
+    public enum Position {
+        BELOW,
+        VERTICAL_CENTER
+    }
+    
     private Gtk.Widget relative_to;
+    private Position position;
     private Gtk.Widget? prev_focus = null;
+    private int relative_x = 0;
+    private int relative_y = 0;
     
     /**
      * Fired when the { link Popup} is hidden, either due to user interaction (losing focus) or
@@ -29,17 +37,16 @@ public class Popup : Gtk.Window {
      *
      * The GtkWidget must be realized when this is invoked.
      */
-    public Popup(Gtk.Widget relative_to) {
+    public Popup(Gtk.Widget relative_to, Position position) {
         Object(type:Gtk.WindowType.TOPLEVEL);
         
         assert(relative_to.get_realized());
         this.relative_to = relative_to;
+        this.position = position;
         
         set_screen(relative_to.get_screen());
         
-        // move Popup window directly below relative_to widget aligned on the left-hand side
-        // TODO: RTL support
-        // TODO: Better detection to ensure Popup is always fully mapped onto the screen
+        // get coordinates of relative_to widget
         Gtk.Window? relative_to_win = relative_to.get_ancestor(typeof (Gtk.Window)) as Gtk.Window;
         if (relative_to_win != null && relative_to_win.is_toplevel()) {
             int gtk_x, gtk_y;
@@ -50,7 +57,8 @@ public class Popup : Gtk.Window {
             int gdk_x, gdk_y;
             gdk_win.get_position(out gdk_x, out gdk_y);
             
-            move(gtk_x + gdk_x, gtk_y + gdk_y + relative_to.get_allocated_height());
+            relative_x = gtk_x + gdk_x;
+            relative_y = gtk_y + gdk_y;
         }
         
         decorated = false;
@@ -82,6 +90,22 @@ public class Popup : Gtk.Window {
     public override void map() {
         base.map();
         
+        switch (position) {
+            case Position.BELOW:
+                // move Popup window directly below relative_to widget aligned on the left-hand side
+                // TODO: RTL support
+                // TODO: Better detection to ensure Popup is always fully mapped onto the same screen
+                move(relative_x, relative_y + relative_to.get_allocated_height());
+            break;
+            
+            case Position.VERTICAL_CENTER:
+                move(relative_x, relative_y + ((relative_to.get_allocated_height() - get_allocated_height()) 
/ 2));
+            break;
+            
+            default:
+                assert_not_reached();
+        }
+        
         prev_focus = get_focus();
         
         Gtk.grab_add(this);


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