summary history branches tags files
commit:83045c6c3df8a0a1390a592159327ab99307a75e
author:Trevor Bentley
committer:Trevor Bentley
date:Tue Jul 25 23:49:21 2017 +0200
parents:9663065d88385fce9e12c63f655df087993c5c75
Support tap gestures
diff --git a/CHANGELOG.md b/CHANGELOG.md
line changes: +4/-0
index db623ee..6fc52c1
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+0.5.0
+-----
+ * Support tap gestures
+
 0.4.0
 -----
  * Fix crash when setting main touchbar icon

diff --git a/examples/example.rs b/examples/example.rs
line changes: +5/-0
index e19455d..b60d76c
--- a/examples/example.rs
+++ b/examples/example.rs
@@ -76,6 +76,11 @@ fn populate(bar_rc: Rc<RefCell<Touchbar>>, count: u32) {
     tb.update_label(&label1_id, "This is a label\nWith two rows");
     tb.update_label_width(&label1_id, 100);
 
+    // Support double-clicking the label with one finger
+    tb.add_item_tap_gesture(&label1_id, 2, 1, Box::new(move |item| {
+        info!("Label double-clicked!");
+    }));
+
     // Add a swipe gesture to the label that changes the text color to
     // increasingly green as you swipe right, or increasingly red as you swipe
     // left, and resets to white when released.

diff --git a/src/interface.rs b/src/interface.rs
line changes: +21/-0
index 4771356..f60eb50
--- a/src/interface.rs
+++ b/src/interface.rs
@@ -430,6 +430,27 @@ pub trait TTouchbar {
     ///
     fn refresh_scrubber(&mut self, scrub_id: &ItemId) {}
 
+    /// Register a tap gesture handler with a Touch Bar item
+    ///
+    /// Registers a callback to be called when the given item is tapped with a
+    /// one or more fingers.  This works  even for items that are normally
+    /// non-interactive (like labels).
+    ///
+    /// Supports specifying number of taps (i.e. double- or triple-click), and
+    /// number of fingers independently.  For instance, set `taps` to 2 and
+    /// `fingers` to 3 to require double-clicking with 3 fingers at the same
+    /// time.
+    ///
+    /// # Arguments
+    ///
+    /// * `item_id` - Item to add the gesture detection to
+    /// * `taps` - Number of discrete taps to trigger callback
+    /// * `fingers` - Number of simultaneous fingers needed
+    /// * `cb` - Callback to call when a tap is detected
+    ///
+    fn add_item_tap_gesture(&mut self, item_id: &ItemId, taps: u32,
+                            fingers: u32, cb: ButtonCb) {}
+
     /// Register a swipe gesture handler with a Touch Bar item
     ///
     /// Registers a callback to be called when the given item is swiped with a

diff --git a/src/touchbar.rs b/src/touchbar.rs
line changes: +58/-0
index 366b637..de41d95
--- a/src/touchbar.rs
+++ b/src/touchbar.rs
@@ -306,6 +306,7 @@ struct InternalItem {
     button_cb: Option<ButtonCb>,
     slider_cb: Option<SliderCb>,
     swipe_cb: Option<SwipeCb>,
+    tap_cb: Option<ButtonCb>,
     child_bar: Option<ItemId>,
 }
 
@@ -335,6 +336,7 @@ impl InternalItem {
             self.scrubber = None;
             self.button_cb = None;
             self.swipe_cb = None;
+            self.tap_cb = None;
             self.slider_cb = None;
         }
     }
@@ -465,6 +467,14 @@ impl RustTouchbarDelegateWrapper {
             None => None,
         }
     }
+    fn find_tap_cb(&self, item: u64) -> Option<&ButtonCb> {
+        match self.item_map.values().into_iter().filter(|x| {
+            x.control.is_some() && x.control.unwrap() as u64 == item
+        }).next() {
+            Some(item) => item.tap_cb.as_ref(),
+            None => None,
+        }
+    }
     fn find_slider_cb(&self, sldr: u64) -> Option<&SliderCb> {
         match self.item_map.values().into_iter().filter(|x| {
             x._type == ItemType::Slider && x.view as u64 == sldr
@@ -617,6 +627,7 @@ impl TTouchbar for Touchbar {
                 button_cb: None,
                 slider_cb: None,
                 swipe_cb: None,
+                tap_cb: None,
                 child_bar: Some(bar as ItemId),
             };
             self.item_map.insert(item as u64, internal);
@@ -685,6 +696,7 @@ impl TTouchbar for Touchbar {
                 button_cb: None,
                 slider_cb: None,
                 swipe_cb: None,
+                tap_cb: None,
                 child_bar: None
             };
             self.item_map.insert(item as u64, internal);
@@ -752,6 +764,7 @@ impl TTouchbar for Touchbar {
                 button_cb: None,
                 slider_cb: None,
                 swipe_cb: None,
+                tap_cb: None,
                 child_bar: None,
             };
             self.item_map.insert(item as u64, internal);
@@ -793,12 +806,38 @@ impl TTouchbar for Touchbar {
                                                  action: sel!(swipeGesture:)];
             msg_send![gesture, setAllowedTouchTypes: 1]; // NSTouchTypeMaskDirect
             msg_send![view, addGestureRecognizer: gesture];
+            msg_send![gesture, release];
             let mut internal_item = self.item_map.remove(item_id).unwrap();
             internal_item.swipe_cb = Some(cb);
             self.item_map.insert(*item_id, internal_item);
         }
     }
 
+    fn add_item_tap_gesture(&mut self, item_id: &ItemId, taps: u32,
+                            fingers: u32, cb: ButtonCb) {
+        unsafe {
+            let item = *item_id as *mut Object;
+            let view: *mut Object = msg_send![item, view];
+            if view == nil {
+                return;
+            }
+            msg_send![view, setAllowedTouchTypes: 1]; // NSTouchTypeMaskDirect
+            let cls = Class::get("NSClickGestureRecognizer").unwrap();
+            let gesture: *mut Object = msg_send![cls, alloc];
+            let gesture: *mut Object = msg_send![gesture,
+                                                 initWithTarget: self.objc.clone()
+                                                 action: sel!(tapGesture:)];
+            msg_send![gesture, setAllowedTouchTypes: 1]; // NSTouchTypeMaskDirect
+            msg_send![gesture, setNumberOfTouchesRequired: fingers];
+            msg_send![gesture, setNumberOfClicksRequired: taps];
+            msg_send![view, addGestureRecognizer: gesture];
+            msg_send![gesture, release];
+            let mut internal_item = self.item_map.remove(item_id).unwrap();
+            internal_item.tap_cb = Some(cb);
+            self.item_map.insert(*item_id, internal_item);
+        }
+    }
+
     fn create_spacer(&mut self, space: SpacerType) -> ItemId {
         unsafe {
             let s = match space {
@@ -826,6 +865,7 @@ impl TTouchbar for Touchbar {
                 button_cb: None,
                 slider_cb: None,
                 swipe_cb: None,
+                tap_cb: None,
                 child_bar: None,
             };
             self.item_map.insert(s as u64, internal);
@@ -872,6 +912,7 @@ impl TTouchbar for Touchbar {
                 button_cb: Some(cb),
                 slider_cb: None,
                 swipe_cb: None,
+                tap_cb: None,
                 child_bar: None,
             };
             self.item_map.insert(item as u64, internal);
@@ -935,6 +976,7 @@ impl TTouchbar for Touchbar {
                 button_cb: None,
                 slider_cb: Some(cb),
                 swipe_cb: None,
+                tap_cb: None,
                 child_bar: None,
             };
             self.item_map.insert(item as u64, internal);
@@ -1092,6 +1134,19 @@ impl INSObject for ObjcAppDelegate {
                     }
                 }
             }
+            extern fn objc_tap_gesture(this: &mut Object, _cmd: Sel, sender: u64) {
+                unsafe {
+                    let ptr: u64 = *this.get_ivar("_rust_wrapper");
+                    let wrapper = &mut *(ptr as *mut RustTouchbarDelegateWrapper);
+                    let gesture = sender as *mut Object;
+                    let view: *mut Object = msg_send![gesture, view];
+                    if let Some(ref cb) = wrapper.find_tap_cb(view as u64) {
+                        // Sender is the view.  Find the owning touchbar item:
+                        let item = wrapper.find_view_from_control(&(view as u64)).unwrap();
+                        cb(&(item as u64));
+                    }
+                }
+            }
             extern fn objc_swipe_gesture(this: &mut Object, _cmd: Sel, sender: u64) {
                 unsafe {
                     let ptr: u64 = *this.get_ivar("_rust_wrapper");
@@ -1219,6 +1274,9 @@ impl INSObject for ObjcAppDelegate {
                 let f: extern fn(&mut Object, Sel, u64) = objc_button;
                 decl.add_method(sel!(button:), f);
 
+                let f: extern fn(&mut Object, Sel, u64) = objc_tap_gesture;
+                decl.add_method(sel!(tapGesture:), f);
+
                 let f: extern fn(&mut Object, Sel, u64) = objc_swipe_gesture;
                 decl.add_method(sel!(swipeGesture:), f);