[gnome-contacts/new-design] Add clickable and use it to support clickable rows



commit 0f47cf2d159b76d8e5532de8bd0c4baae5499dbb
Author: Alexander Larsson <alexl redhat com>
Date:   Mon Dec 12 16:31:59 2011 +0100

    Add clickable and use it to support clickable rows

 src/Makefile.am                |    1 +
 src/contacts-clickable.vala    |  256 ++++++++++++++++++++++++++++++++++++++++
 src/contacts-contact-pane.vala |   16 +++
 src/contacts-row.vala          |    2 +-
 4 files changed, 274 insertions(+), 1 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index b38c8c4..c44540b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -30,6 +30,7 @@ vala_sources = \
 	contacts-store.vala \
 	contacts-view.vala \
 	contacts-utils.vala \
+	contacts-clickable.vala \
 	main.vala \
 	$(NULL)
 
diff --git a/src/contacts-clickable.vala b/src/contacts-clickable.vala
new file mode 100644
index 0000000..64ccfe7
--- /dev/null
+++ b/src/contacts-clickable.vala
@@ -0,0 +1,256 @@
+/* -*- Mode: vala; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2011 Alexander Larsson <alexl redhat com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+using Gtk;
+
+public class Contacts.Clickable : Object {
+  Widget widget;
+
+  bool in_button;
+  bool button_down;
+  bool depressed;
+  bool depress_on_activate;
+  bool focus_on_click;
+  uint activate_timeout;
+  uint32  grab_time;
+  Gdk.Device grab_keyboard;
+
+  Gdk.Window? event_window;
+
+  public Clickable (Widget w) {
+    widget = w;
+
+    widget.button_press_event.connect (button_press_event);
+    widget.button_release_event.connect (button_release_event);
+    widget.enter_notify_event.connect (enter_notify_event);
+    widget.leave_notify_event.connect (leave_notify_event);
+    widget.grab_broken_event.connect (grab_broken_event);
+    widget.state_changed.connect (state_changed);
+    widget.grab_notify.connect (grab_notify);
+
+    widget.add_events (Gdk.EventMask.ENTER_NOTIFY_MASK |
+		       Gdk.EventMask.LEAVE_NOTIFY_MASK |
+		       Gdk.EventMask.BUTTON_PRESS_MASK |
+		       Gdk.EventMask.BUTTON_RELEASE_MASK);
+  }
+
+  public void realize_for (Gdk.Window? event_window) {
+    this.event_window = event_window;
+  }
+
+  public void unrealize (Gdk.Window? event_window) {
+    if (activate_timeout != 0)
+      finish_activate (false);
+  }
+
+  private Gdk.Window get_event_window () {
+    return this.event_window ?? widget.get_window ();
+  }
+
+  private void set_depressed (bool depressed) {
+    if (depressed != this.depressed) {
+      this.depressed = depressed;
+      widget.queue_resize ();
+    }
+  }
+
+  private bool button_press_event (Gdk.EventButton event) {
+    if (event.type == Gdk.EventType.BUTTON_PRESS) {
+      if (focus_on_click && !widget.has_focus)
+	widget.grab_focus ();
+
+      if (event.button == 1)
+	pressed ();
+    }
+
+    return true;
+  }
+
+  private bool button_release_event (Gdk.EventButton event) {
+    if (event.button == 1)
+      released ();
+
+    return true;
+  }
+
+  private bool grab_broken_event (Gdk.EventGrabBroken event) {
+
+    /* Simulate a button release without the pointer in the button */
+    if (button_down) {
+      var save_in = in_button;
+      in_button = false;
+      released ();
+      if (save_in != in_button)
+	{
+	  in_button = save_in;
+	  update_state ();
+	}
+    }
+
+    return true;
+  }
+
+  /* TODO: key_release_event */
+
+  private bool enter_notify_event (Gdk.EventCrossing event) {
+    if ((event.window == get_event_window ()) &&
+	(event.detail != Gdk.NotifyType.INFERIOR))
+      in_button = true;
+
+    return false;
+  }
+
+  private bool leave_notify_event (Gdk.EventCrossing event) {
+    if ((event.window == get_event_window ()) &&
+	(event.detail != Gdk.NotifyType.INFERIOR) &&
+	widget.get_sensitive ())
+      in_button = false;
+
+    return false;
+  }
+
+  private void pressed () {
+    if (activate_timeout != 0)
+      return;
+
+    button_down = true;
+    update_state ();
+  }
+
+  private void released () {
+    if (button_down) {
+      button_down = false;
+
+      if (activate_timeout != 0)
+	return;
+
+      if (in_button)
+	clicked ();
+
+      update_state ();
+    }
+  }
+
+  [CCode (action_signal = true)]
+  public signal void clicked ();
+
+  [CCode (action_signal = true)]
+  public virtual signal void activate () {
+    var device = Gtk.get_current_event_device ();
+
+    if (device != null && device.get_source () != Gdk.InputSource.KEYBOARD)
+      device = device.get_associated_device ();
+
+  if (widget.get_realized () && activate_timeout == 0)
+    {
+      var time = Gtk.get_current_event_time ();
+
+      /* bgo#626336 - Only grab if we have a device (from an event), not if we
+       * were activated programmatically when no event is available.
+       */
+      if (device != null && device.get_source () == Gdk.InputSource.KEYBOARD)
+	{
+	  if (device.grab (get_event_window (),
+			   Gdk.GrabOwnership.WINDOW, true,
+			   Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON_RELEASE_MASK,
+			   null, time) == Gdk.GrabStatus.SUCCESS)
+	    {
+	      Gtk.device_grab_add (widget, device, true);
+	      grab_keyboard = device;
+	      grab_time = time;
+	    }
+	}
+
+      activate_timeout = Gdk.threads_add_timeout (250,
+						  activate_timeout_cb);
+      button_down = true;
+      update_state ();
+      widget.queue_draw ();
+    }
+  }
+
+  private void finish_activate (bool do_it) {
+    Source.remove (activate_timeout);
+    activate_timeout = 0;
+
+    if (grab_keyboard != null) {
+      grab_keyboard.ungrab (grab_time);
+      Gtk.device_grab_remove (widget, grab_keyboard);
+      grab_keyboard = null;
+    }
+
+    button_down = false;
+
+    update_state ();
+    widget.queue_draw ();
+
+    if (do_it)
+      clicked ();
+  }
+
+  private  bool activate_timeout_cb () {
+    finish_activate (true);
+
+    return false;
+  }
+
+  private void update_state () {
+    bool depressed;
+
+    if (activate_timeout != 0)
+      depressed = depress_on_activate;
+    else
+      depressed = in_button && button_down;
+
+    StateFlags new_state = widget.get_state_flags () & ~(StateFlags.PRELIGHT | StateFlags.ACTIVE);
+
+    if (in_button)
+      new_state |= StateFlags.PRELIGHT;
+
+    if (button_down || depressed)
+      new_state |= StateFlags.ACTIVE;
+
+    set_depressed (depressed);
+    widget.set_state_flags (new_state, true);
+  }
+
+  private void state_changed (Gtk.StateType previous_state) {
+    if (!widget.is_sensitive ()) {
+      in_button = false;
+      released ();
+    }
+  }
+
+  private void grab_notify (bool was_grabbed) {
+    if (activate_timeout != 0 &&
+	grab_keyboard != null &&
+	widget.device_is_shadowed (grab_keyboard))
+      finish_activate (false);
+
+    if (!was_grabbed) {
+      bool save_in = in_button;
+      in_button = false;
+      released ();
+      if (save_in != in_button)
+	{
+	  in_button = save_in;
+	  update_state ();
+	}
+    }
+  }
+}
diff --git a/src/contacts-contact-pane.vala b/src/contacts-contact-pane.vala
index 8c58942..f7d462a 100644
--- a/src/contacts-contact-pane.vala
+++ b/src/contacts-contact-pane.vala
@@ -414,14 +414,30 @@ public class Contacts.AvatarMenu : Menu {
 }
 
 public class Contacts.FieldRow : Contacts.Row {
+  Clickable clickable;
   int start;
 
   public FieldRow(RowGroup group) {
     base (group);
 
+    clickable = new Clickable (this);
+    clickable.clicked.connect (() => {
+	print ("clicked!\n");
+      });
+
     start = 0;
   }
 
+  public override void realize () {
+    base.realize ();
+    clickable.realize_for (event_window);
+  }
+
+  public override void unrealize () {
+    base.unrealize ();
+    clickable.unrealize (null);
+  }
+
   public void pack (Widget w) {
     this.attach (w, 1, start++);
   }
diff --git a/src/contacts-row.vala b/src/contacts-row.vala
index 2411ab5..ce2a2dc 100644
--- a/src/contacts-row.vala
+++ b/src/contacts-row.vala
@@ -227,7 +227,7 @@ public class Contacts.Row : Container {
     Widget? widget;
   }
 
-  private Gdk.Window event_window;
+  protected Gdk.Window event_window;
   RowGroup group;
   int n_rows;
   Child[,] row_children;



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