summary history branches tags files
commit:29ed314877e3b0f04f0aab136c01ad9bac4e6cb5
author:Trevor Bentley
committer:Trevor Bentley
date:Sun Jul 9 22:10:17 2017 +0200
parents:1fd25ec65ce6647ffdd0a9e17b6844c53a2a82af
touchbar scrubber temp progress
diff --git a/src/lib.rs b/src/lib.rs
line changes: +2/-0
index 09079d5..4750aaa
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -78,6 +78,7 @@ pub type MenuItem = *mut Object;
 pub trait TStatusBar {
     type S: TStatusBar;
     fn new(tx: Sender<String>) -> Self::S;
+    fn touchbar(&mut self);
     fn can_redraw(&mut self) -> bool;
     fn clear_items(&mut self);
     fn add_separator(&mut self);
@@ -98,6 +99,7 @@ pub struct DummyStatusBar {}
 impl TStatusBar for DummyStatusBar {
     type S = DummyStatusBar;
     fn new(_: Sender<String>) -> Self::S { DummyStatusBar {} }
+    fn touchbar(&mut self) {}
     fn can_redraw(&mut self) -> bool { true }
     fn clear_items(&mut self) {}
     fn add_separator(&mut self) {}

diff --git a/src/main.rs b/src/main.rs
line changes: +15/-0
index 5a9fe79..8f5b456
--- a/src/main.rs
+++ b/src/main.rs
@@ -37,6 +37,7 @@ enum CallbackAction {
     SkipPrev,
     Volume,
     Preset,
+    Debug,
 }
 
 #[derive(Serialize, Deserialize, Debug)]
@@ -215,6 +216,17 @@ fn fill_menu<T: TStatusBar>(app: &mut ConnectrApp, spotify: &mut connectr::Spoti
         }
     }
     status.add_separator();
+    let cb: NSCallback = Box::new(move |sender, tx| {
+        let cmd = MenuCallbackCommand {
+            action: CallbackAction::Debug,
+            sender: sender,
+            data: String::new(),
+        };
+        let _ = tx.send(serde_json::to_string(&cmd).unwrap());
+    });
+    status.add_item("Fuck With Touchbar", cb, false);
+    
+    status.add_separator();
     status.add_quit("Exit");
 }
 
@@ -303,6 +315,9 @@ fn handle_callback<T: TStatusBar>(app: &mut ConnectrApp, spotify: &mut connectr:
             }
             status.sel_item(cmd.sender);
         }
+        CallbackAction::Debug => {
+            status.touchbar();
+        }  
     }
 }
 

diff --git a/src/osx/mod.rs b/src/osx/mod.rs
line changes: +112/-1
index 1d544e1..8a11d83
--- a/src/osx/mod.rs
+++ b/src/osx/mod.rs
@@ -65,6 +65,53 @@ pub struct OSXStatusBar {
     run_count: Cell<u64>,
     run_mode: *mut objc::runtime::Object,
     run_date: *mut objc::runtime::Object,
+
+    label: Cell<u64>,
+    devices: Vec<String>,
+}
+
+const ITEMS: &'static [&'static str] = &["a rather longer first one", "one","two","much longer than two","three", "seventeen", "A speaker with a very long name is not an impossible thing."];
+
+fn scrub_count(context: *const u32, item: touchbar::ItemId) -> u32 {
+    info!("MOD GOT SCRUBBER COUNT REQUEST");
+    let context = context as *const OSXStatusBar;
+    unsafe {
+        (*context).devices.len() as u32
+    } 
+}
+fn scrub_text(context: *const u32, item: touchbar::ItemId, idx: u32) -> String {
+    let context = context as *const OSXStatusBar;
+    unsafe {
+        info!("MOD GOT SCRUBBER TEXT REQUEST {}", idx);
+        //"froop".to_string()
+        (*context).devices[idx as usize].to_string()
+    }
+}
+fn scrub_width(context: *const u32, item: touchbar::ItemId, idx: u32) -> u32 {
+    info!("scrub_width {} {:?}", idx, context);
+    // 10px per character + some padding seems to work nicely for the default
+    // font.  no idea what it's like on other machines.  does the touchbar
+    // font change? ¯\_(ツ)_/¯
+    unsafe {
+        let context = context as *const OSXStatusBar;
+        info!("context: {:?}", context);
+        let context: &OSXStatusBar = &*context;
+        info!("get len");
+        let devices: &Vec<String> = &context.devices;
+        info!("got devices");
+        info!("devices ptr: {:?}", devices as *const Vec<String>);
+        info!("devices len: {}", devices.len());
+        info!("{:?}", devices);
+        let ref s = (*context).devices[idx as usize];
+        info!("get string {}", s);
+        let len = s.len() as u32;
+        info!("got len {}", len);
+        let width = len * 8 + 20;
+        info!("Width for {}: {}", len, width);
+        width
+    }
+}
+fn scrub_touch(context: *const u32, item: touchbar::ItemId, idx: u32) {
 }
 
 impl TStatusBar for OSXStatusBar {
@@ -85,6 +132,8 @@ impl TStatusBar for OSXStatusBar {
                 run_count: Cell::new(0),
                 run_mode: NSString::alloc(nil).init_str("kCFRunLoopDefaultMode"),
                 run_date: msg_send![date_cls, distantPast],
+                label: Cell::new(0),
+                devices: Vec::new(),
             };
             // Don't become foreground app on launch
             bar.app.setActivationPolicy_(NSApplicationActivationPolicyAccessory);
@@ -135,11 +184,73 @@ impl TStatusBar for OSXStatusBar {
                     cb(sender, &s.tx);
                 }
             ));
+
+            let barid = bar.touchbar.create_bar();
+            let text = NSString::alloc(nil).init_str("hi1");
+            let b1id = bar.touchbar.create_button(nil, text, Box::new(move |_| {}));
+            let text = NSString::alloc(nil).init_str("hi2");
+            let b2id = bar.touchbar.create_button(nil, text, Box::new(move |_| {}));
+
+            let popid = bar.touchbar.create_bar();
+            let p1id = bar.touchbar.create_popover_item(popid);
+            let text = NSString::alloc(nil).init_str("hi3");
+            let b3id = bar.touchbar.create_button(nil, text, Box::new(move |_| {}));
+            bar.touchbar.add_items_to_bar(popid, vec![b3id]);
+
+            let l1id = bar.touchbar.create_label();
+            bar.label.set(l1id);
+
+            for item in ITEMS {
+                bar.devices.push(item.to_string());
+            }
+            info!("devices: {:?}", bar.devices);
+            info!("devices: {:?}", (&bar.devices) as *const Vec<String>);
+            let bar_ptr = &bar as *const OSXStatusBar as *const u32;
+            info!("bar ptr: {:?}", bar_ptr);
+            let s1id = bar.touchbar.create_text_scrubber(
+                bar_ptr,
+                scrub_count,
+                scrub_text,
+                scrub_width,
+                scrub_touch);
+            bar.touchbar.select_scrubber_item(s1id, 1);
+
+            //bar.touchbar.add_items_to_bar(barid, vec![b1id, b2id, p1id]);
+            bar.touchbar.add_items_to_bar(barid, vec![b1id, b2id, p1id, l1id, s1id]);
+            bar.touchbar.set_bar_as_root(barid);
+
             let _: () = msg_send![app, finishLaunching];
-            bar.touchbar.enable()
+            bar.touchbar.enable();
         }
         bar
     }
+    fn touchbar(&mut self) {
+        info!("Touchbar fucker!");
+        info!("devices: {:?}", (&self.devices) as *const Vec<String>);
+        let bar_ptr = self as *const OSXStatusBar as *const u32;
+        info!("bar ptr: {:?}", bar_ptr);
+
+        unsafe {
+            let l1id = self.label.get();
+            self.touchbar.update_label(l1id);
+
+            //let barid = self.touchbar.create_bar();
+            //let text = NSString::alloc(nil).init_str("hi1");
+            //let b1id = self.touchbar.create_button(nil, text, Box::new(move |_| {}));
+            //let text = NSString::alloc(nil).init_str("hi2");
+            //let b2id = self.touchbar.create_button(nil, text, Box::new(move |_| {}));
+            //
+            //let popid = self.touchbar.create_bar();
+            //let p1id = self.touchbar.create_popover_item(popid);
+            //let text = NSString::alloc(nil).init_str("hi3");
+            //let b3id = self.touchbar.create_button(nil, text, Box::new(move |_| {}));
+            //self.touchbar.add_items_to_bar(popid, vec![b3id]);
+            //
+            ////bar.touchbar.add_items_to_bar(barid, vec![b1id, b2id, p1id]);
+            //self.touchbar.add_items_to_bar(barid, vec![b1id]);
+            //self.touchbar.set_bar_as_root(barid);
+        }
+    }
     fn can_redraw(&mut self) -> bool {
         true
     }

diff --git a/src/osx/touchbar.rs b/src/osx/touchbar.rs
line changes: +484/-31
index 56b20ff..b642500
--- a/src/osx/touchbar.rs
+++ b/src/osx/touchbar.rs
@@ -7,28 +7,56 @@ extern crate objc_foundation;
 extern crate objc_id;
 extern crate cocoa;
 
-pub use ::NSCallback;
-
+use std::cell::Cell;
 use std::sync::{Once, ONCE_INIT};
+use std::collections::BTreeMap;
 
 use objc::Message;
 use objc::declare::ClassDecl;
 use objc::runtime::{Class, Object, Sel};
 use self::objc_foundation::{INSObject, NSObject, INSArray, NSArray, INSString};
-use self::cocoa::base::{nil, YES};
+use self::cocoa::base::{nil, YES, NO};
 use self::cocoa::foundation::NSString;
+use self::cocoa::foundation::{NSRect, NSPoint, NSSize};
 use self::cocoa::appkit::NSApp;
 use self::objc_id::Id;
 use self::objc_id::Shared;
 
+const IDENT_PREFIX: &'static str = "com.trevorbentley.";
+
 #[link(name = "DFRFoundation", kind = "framework")]
 extern {
     pub fn DFRSystemModalShowsCloseBoxWhenFrontMost(x: i8);
     pub fn DFRElementSetControlStripPresenceForIdentifier(n: *mut Object, x: i8);
 }
 
+extern crate libc;
+use std::ffi::CStr;
+fn print_nsstring(str: *mut Object) {
+    unsafe {
+        let cstr: *const libc::c_char = msg_send![str, UTF8String];
+        let rstr = CStr::from_ptr(cstr).to_string_lossy().into_owned();
+        info!("{}", rstr);
+    }
+}
+
+struct Scrubber {
+    count_cb: ScrubberCountFn,
+    text_cb: ScrubberTextFn,
+    width_cb: ScrubberWidthFn,
+    touch_cb: ScrubberTouchFn,
+    context: *const u32,
+    _item: ItemId,
+    _scrubber: ItemId,
+    _ident: Ident,
+}
+
 pub struct RustTouchbarDelegateWrapper {
     objc: Id<ObjcAppDelegate, Shared>,
+    next_item_id: Cell<u64>,
+    bar_obj_map: BTreeMap<ItemId, Ident>,
+    control_obj_map: BTreeMap<ItemId, ItemId>,
+    scrubber_obj_map: BTreeMap<ItemId, Scrubber>,
 }
 
 pub type Touchbar = Box<RustTouchbarDelegateWrapper>;
@@ -37,6 +65,38 @@ pub trait TouchbarTrait {
     fn alloc() -> Touchbar;
     fn set_icon(&self, icon: *mut Object);
     fn enable(&self);
+    fn create_bar(&mut self) -> BarId;
+    fn create_popover_item(&mut self, bar_id: BarId) -> BarId;
+    fn add_items_to_bar(&mut self, bar_id: BarId, items: Vec<ItemId>);
+    fn set_bar_as_root(&mut self, bar_id: BarId);
+    fn create_label(&mut self) -> ItemId;
+    fn update_label(&mut self, label_id: ItemId);
+    fn create_text_scrubber(&mut self,
+                            context: *const u32,
+                            count_fn: ScrubberCountFn,
+                            text_fn: ScrubberTextFn,
+                            width_fn: ScrubberWidthFn,
+                            touch_fn: ScrubberTouchFn) -> ItemId;
+    fn select_scrubber_item(&mut self, scrub_id: ItemId, index: u32);
+    fn create_button(&mut self, image: *mut Object, text: *mut Object, cb: ButtonCb) -> ItemId;
+}
+
+pub type ScrubberCountFn = fn(*const u32, ItemId) -> u32;
+pub type ScrubberTextFn = fn(*const u32, ItemId, u32) -> String;
+pub type ScrubberWidthFn = fn(*const u32, ItemId, u32) -> u32;
+pub type ScrubberTouchFn = fn(*const u32, ItemId, u32);
+
+impl RustTouchbarDelegateWrapper {
+    fn generate_ident(&mut self) -> u64 {
+        unsafe {
+            // Create string identifier
+            let next_item_id = self.next_item_id.get();
+            self.next_item_id.set(next_item_id + 1);
+            let ident = format!("{}{}", IDENT_PREFIX, next_item_id);
+            let objc_ident = NSString::alloc(nil).init_str(&ident);
+            objc_ident as u64
+        }
+    }
 }
 
 impl TouchbarTrait for Touchbar {
@@ -44,6 +104,10 @@ impl TouchbarTrait for Touchbar {
         let objc = ObjcAppDelegate::new().share();
         let rust = Box::new(RustTouchbarDelegateWrapper {
             objc: objc.clone(),
+            next_item_id: Cell::new(0),
+            bar_obj_map: BTreeMap::<ItemId, Ident>::new(),
+            control_obj_map: BTreeMap::<ItemId, ItemId>::new(),
+            scrubber_obj_map: BTreeMap::<ItemId, Scrubber>::new(),
         });
         unsafe {
             let ptr: u64 = &*rust as *const RustTouchbarDelegateWrapper as u64;
@@ -57,17 +121,219 @@ impl TouchbarTrait for Touchbar {
     fn enable(&self) {
         unsafe {
             let app = NSApp();
-            let _: () = msg_send![app, setDelegate: objc];
-            let _: () = msg_send![self.objc, applicationDidFinishLaunching: 0];
+            let _: () = msg_send![app, setDelegate: self.objc.clone()];
+            //let _: () = msg_send![self.objc, applicationDidFinishLaunching: 0];
         }
     }
 
 //    pub fn add_button() {}
 //    pub fn add_quit_button() {}
 //    pub fn add_label() {}
-//    pub fn add_slider() {}
+    //    pub fn add_slider() {}
+    fn create_bar(&mut self) -> BarId {
+        unsafe {
+            let ident = self.generate_ident();
+            // Create touchbar
+            let cls = Class::get("NSTouchBar").unwrap();
+            let bar: *mut Object = msg_send![cls, alloc];
+            let bar: *mut objc::runtime::Object = msg_send![bar, init];
+            let _ : () = msg_send![bar, retain];
+            let _ : () = msg_send![bar, setDelegate: self.objc.clone()];
+            // Save tuple
+            self.bar_obj_map.insert(bar as u64, ident as u64);
+            info!("bar: {}", bar as u64);
+            bar as u64
+        }
+    }
+    fn create_popover_item(&mut self, bar_id: BarId) -> ItemId {
+        unsafe {
+            let bar = bar_id as *mut Object;
+            let bar_ident = *self.bar_obj_map.get(&bar_id).unwrap() as *mut Object; 
+            let ident = self.generate_ident();
+            // Save tuple
+            let cls = Class::get("NSPopoverTouchBarItem").unwrap();
+            let item: *mut Object = msg_send![cls, alloc];
+            let item: *mut Object = msg_send![item, initWithIdentifier: ident];
+            let cls = Class::get("NSButton").unwrap();
+            let text = NSString::alloc(nil).init_str("pop");
+            let btn: *mut Object = msg_send![cls,
+                                             buttonWithTitle:text
+                                             target:self.objc.clone()
+                                             action:sel!(popbar:)];
+            let _:() = msg_send![item, setShowsCloseButton: YES];
+            let gesture: *mut Object = msg_send![item, makeStandardActivatePopoverGestureRecognizer];
+            let _:() = msg_send![btn, addGestureRecognizer: gesture];
+            let _:() = msg_send![item, setCollapsedRepresentation: btn];
+            
+            //let idents = NSArray::from_vec(vec![b1_ident]);
+            //let _ : () = msg_send![bar, setDefaultItemIdentifiers: idents];
+            let _:() = msg_send![item, setPopoverTouchBar: bar];
+            let _:() = msg_send![item, setPressAndHoldTouchBar: bar];
+
+            self.bar_obj_map.insert(item as u64, ident as u64);
+            self.control_obj_map.insert(btn as u64, item as u64);
+            item as u64
+        }
+    }    
+    fn add_items_to_bar(&mut self, bar_id: BarId, items: Vec<ItemId>) {
+        unsafe {
+            let cls = Class::get("NSMutableArray").unwrap();
+            let idents: *mut Object = msg_send![cls, arrayWithCapacity: items.len()];
+            for item in items {
+                let ident = *self.bar_obj_map.get(&item).unwrap() as *mut Object;
+                let _ : () = msg_send![idents, addObject: ident];
+            }
+            let bar = bar_id as *mut Object;
+            let _ : () = msg_send![bar, setDefaultItemIdentifiers: idents];
+        }
+    }
+    fn set_bar_as_root(&mut self, bar_id: BarId) {
+        unsafe {
+            let old_bar: *mut Object = msg_send![self.objc, groupTouchBar];
+            if old_bar != nil {
+                // TODO: store in temp place until it's not visible,
+                // delete and replace when it is closed.  otherwise
+                // it forces the bar to close on each update.
+                let visible: bool = msg_send![old_bar, isVisible];
+                info!("DELETING OLD BAR: {}", visible);
+                let cls = Class::get("NSTouchBar").unwrap();
+                msg_send![cls, dismissSystemModalFunctionBar: old_bar];
+            }
+            let _ : () = msg_send![self.objc, setGroupTouchBar: bar_id];
+            let ident: *mut Object = *self.bar_obj_map.get(&bar_id).unwrap() as *mut Object;
+            let _ : () = msg_send![self.objc, setGroupIdent: ident];
+            let _: () = msg_send![self.objc, applicationDidFinishLaunching: 0];
+        }
+    }
+    fn create_label(&mut self) -> ItemId {
+        unsafe {
+            let frame = NSRect::new(NSPoint::new(0., 0.), NSSize::new(300., 44.));
+            let cls = Class::get("NSTextField").unwrap();
+            let label: *mut Object = msg_send![cls, alloc];
+            let label: *mut Object = msg_send![label, initWithFrame: frame];
+            let _:() = msg_send![label, setEditable: NO];
+            let text = NSString::alloc(nil).init_str("froop doop poop\nsecond level");
+            let _:() = msg_send![label, setStringValue: text];
+
+            let ident = self.generate_ident();
+            let cls = Class::get("NSCustomTouchBarItem").unwrap();
+            let item: *mut Object = msg_send![cls, alloc];
+            let item: *mut Object = msg_send![item, initWithIdentifier: ident];
+            msg_send![item, setView: label];
+
+            self.bar_obj_map.insert(item as u64, ident as u64);
+            self.control_obj_map.insert(label as u64, item as u64);
+            item as u64
+        } 
+    }
+    fn update_label(&mut self, label_id: ItemId) {
+        unsafe {
+            let item: *mut Object = label_id as *mut Object;
+            let label: *mut Object = msg_send![item, view];
+            let text = NSString::alloc(nil).init_str("updated\nthis shit");
+            let _:() = msg_send![label, setStringValue: text];
+        }
+    }
+    fn create_text_scrubber(&mut self,
+                            context: *const u32,
+                            count_fn: ScrubberCountFn,
+                            text_fn: ScrubberTextFn,
+                            width_fn: ScrubberWidthFn,
+                            touch_fn: ScrubberTouchFn) -> ItemId {
+        unsafe {
+            let ident = self.generate_ident();
+            let cls = Class::get("NSCustomTouchBarItem").unwrap();
+            let item: *mut Object = msg_send![cls, alloc];
+            let item: *mut Object = msg_send![item, initWithIdentifier: ident];
+            
+            // note: frame is ignored, but must be provided.
+            let frame = NSRect::new(NSPoint::new(0., 0.), NSSize::new(0., 30.));
+            let cls = Class::get("NSScrubber").unwrap();
+            let scrubber: *mut Object = msg_send![cls, alloc];
+            let scrubber: *mut Object = msg_send![scrubber, initWithFrame: frame];
+
+            let cls = Class::get("NSScrubberSelectionStyle").unwrap();
+            let style: *mut Object = msg_send![cls, outlineOverlayStyle];
+            
+            let cls = Class::get("NSScrubberTextItemView").unwrap();
+            let _:() = msg_send![scrubber, registerClass: cls forItemIdentifier: ident];
+            let _:() = msg_send![scrubber, setDelegate: self.objc.clone()];
+            let _:() = msg_send![scrubber, setDataSource: self.objc.clone()];
+            let _:() = msg_send![scrubber, setSelectionOverlayStyle: style];
+            let _:() = msg_send![scrubber, setMode: 1]; // NSScrubberModeFree
+            //(*scrubber).set_ivar("selectedIndex", 3);
+            //let _:() = msg_send![scrubber, ];
+            let _:() = msg_send![item, setView: scrubber];
+
+            self.bar_obj_map.insert(item as u64, ident as u64);
+            let scrub_struct = Scrubber {
+                text_cb: text_fn,
+                count_cb: count_fn,
+                width_cb: width_fn,
+                touch_cb: touch_fn,
+                context: context,
+                _ident: ident as u64,
+                _item: item as u64,
+                _scrubber: scrubber as u64,
+            };
+            self.scrubber_obj_map.insert(scrubber as u64, scrub_struct);
+            item as u64
+        }
+    } 
+    fn select_scrubber_item(&mut self, scrub_id: ItemId, index: u32) {
+        unsafe {
+            let item = scrub_id as *mut Object;
+            let scrubber: *mut Object = msg_send![item, view];
+            let _:() = msg_send![scrubber, setSelectedIndex: index];
+        }
+    }
+    fn create_button(&mut self, image: *mut Object, text: *mut Object, cb: ButtonCb) -> ItemId {
+        unsafe {
+            let ident = self.generate_ident();
+            let cls = Class::get("NSButton").unwrap();
+            let btn: *mut Object;
+            // Match on (image, text) as booleans.   false == null.
+            match ((image as u64) != 0, (text as u64) != 0) {
+                (false,true) => {
+                    btn = msg_send![cls,
+                                    buttonWithTitle: text
+                                    target:self.objc.clone()
+                                    action:sel!(button:)];
+                }
+                (true,false) => {
+                    btn = msg_send![cls,
+                                    buttonWithImage: image
+                                    target:self.objc.clone()
+                                    action:sel!(button:)];
+                }
+                (true,true) => {
+                    btn = msg_send![cls,
+                                    buttonWithTitle: text
+                                    image:image
+                                    target:self.objc.clone()
+                                    action:sel!(button:)];
+                }
+                _ => { return 0 }
+            }
+
+            let cls = Class::get("NSCustomTouchBarItem").unwrap();
+            let item: *mut Object = msg_send![cls, alloc];
+            let item: *mut Object = msg_send![item, initWithIdentifier: ident];
+            msg_send![item, setView: btn];
+
+            self.bar_obj_map.insert(item as u64, ident as u64);
+            self.control_obj_map.insert(btn as u64, item as u64);
+            item as u64
+        }
+    }
 }
 
+pub type BarId = u64;
+pub type ItemId = u64;
+pub type Ident = u64;
+//pub type ButtonCb = Box<Fn(u64, &Sender<String>)>;
+pub type ButtonCb = Box<Fn(u64)>;
+
 pub enum ObjcAppDelegate {}
 impl ObjcAppDelegate {}
 
@@ -84,6 +350,8 @@ impl INSObject for ObjcAppDelegate {
             decl.add_ivar::<u64>("_groupbar");
             decl.add_ivar::<u64>("_groupId");
             decl.add_ivar::<u64>("_icon");
+            decl.add_ivar::<u64>("_popbar");
+            decl.add_ivar::<u64>("_popover");
 
             extern fn objc_set_rust_wrapper(this: &mut Object, _cmd: Sel, ptr: u64) {
                 unsafe {this.set_ivar("_rust_wrapper", ptr);}
@@ -94,9 +362,109 @@ impl INSObject for ObjcAppDelegate {
             extern fn objc_set_group_touch_bar(this: &mut Object, _cmd: Sel, bar: u64) {
                 unsafe {this.set_ivar("_groupbar", bar);}
             }
+            extern fn objc_set_group_ident(this: &mut Object, _cmd: Sel, bar: u64) {
+                unsafe {this.set_ivar("_groupId", bar);}
+            }
             extern fn objc_set_icon(this: &mut Object, _cmd: Sel, icon: u64) {
                 unsafe {this.set_ivar("_icon", icon);}
             }
+            extern fn objc_number_of_items_for_scrubber(this: &mut Object, _cmd: Sel,
+                                                        scrub: u64) -> u32 {
+                info!("scrubber item count");
+                unsafe {
+                    let ptr: u64 = *this.get_ivar("_rust_wrapper");
+                    let wrapper = &mut *(ptr as *mut RustTouchbarDelegateWrapper);
+                    let scrubber = scrub as *mut Object;
+                    let scrub_struct = wrapper.scrubber_obj_map.get(&scrub).unwrap();
+                    let item = scrub_struct._item;
+                    (scrub_struct.count_cb)(scrub_struct.context, item)
+                }
+            }
+            extern fn objc_scrubber_view_for_item_at_index(this: &mut Object, _cmd: Sel,
+                                                           scrub: u64, idx: u32) -> u64 {
+                info!("scrubber item view");
+                unsafe {
+                    let ptr: u64 = *this.get_ivar("_rust_wrapper");
+                    let wrapper = &mut *(ptr as *mut RustTouchbarDelegateWrapper);
+                    let scrubber = scrub as *mut Object;
+                    let scrub_struct = wrapper.scrubber_obj_map.get(&scrub).unwrap();
+                    let item = scrub_struct._item;
+                    let ident = scrub_struct._ident as *mut Object;
+                    let view: *mut Object = msg_send![scrubber,
+                                                      makeItemWithIdentifier:ident owner:nil];
+                    let text = (scrub_struct.text_cb)(scrub_struct.context, item, idx);
+                    let text_field: *mut Object = msg_send![view, textField];
+                    let objc_text: *mut Object = NSString::alloc(nil).init_str(&text);
+                    let _:() = msg_send![text_field, setStringValue: objc_text];
+                    view as u64
+                }
+            }
+            extern fn objc_scrubber_layout_size_for_item_at_index(this: &mut Object, _cmd: Sel,
+                                                                  scrub: u64,
+                                                                  _layout: u64, idx: u32) -> NSSize {
+                info!("scrubber item size");
+                unsafe {
+                    let ptr: u64 = *this.get_ivar("_rust_wrapper");
+                    let wrapper = &mut *(ptr as *mut RustTouchbarDelegateWrapper);
+                    let scrubber = scrub as *mut Object;
+                    let scrub_struct = wrapper.scrubber_obj_map.get(&scrub).unwrap();
+                    let item = scrub_struct._item;
+                    info!("scrubber item size call CB");
+                    let width = (scrub_struct.width_cb)(scrub_struct.context, item, idx);
+                    NSSize::new(width as f64, 30.)
+                }
+            }
+            extern fn objc_scrubber_did_select_item_at_index(this: &mut Object, _cmd: Sel,
+                                                             _scrub: u64, _idx: u32) {
+                info!("scrubber selected");
+            }
+            extern fn objc_popbar(this: &mut Object, _cmd: Sel, sender: u64) {
+                info!("Popbar push: {}", sender as u64);
+                unsafe {
+                    let ptr: u64 = *this.get_ivar("_rust_wrapper");
+                    let wrapper = &mut *(ptr as *mut RustTouchbarDelegateWrapper);
+                    
+                    info!("got wrapper");
+                    let btn = sender as *mut Object;
+                    info!("got button");
+                    let item = *wrapper.control_obj_map.get(&sender).unwrap() as *mut Object;
+                    info!("got item");
+                    let bar: *mut Object = msg_send![item, popoverTouchBar];
+                    info!("got bar");
+                    let ident = *wrapper.bar_obj_map.get(&(bar as u64)).unwrap() as *mut Object;
+                    info!("got ident");
+
+                    let cls = Class::get("NSTouchBar").unwrap();
+                    msg_send![cls,
+                              presentSystemModalFunctionBar: bar
+                              systemTrayItemIdentifier: ident];
+                    let app = NSApp();
+                    let _:() = msg_send![app, setTouchBar: nil];
+                }
+
+                //unsafe {
+                //    // Present the request popover.  This must be done instead of
+                //    // using the popover's built-in showPopover because that pops
+                //    // _under_ a system function bar.
+                //    let ptr: u64 = *this.get_ivar("_popbar");
+                //    let bar = ptr as *mut Object;
+                //    let ptr: u64 = *this.get_ivar("_popover");
+                //    let item = ptr as *mut Object;
+                //    msg_send![item, showPopover: bar];
+                //    let pop_ident = objc_foundation::NSString::from_str("com.trevorbentley.pop");
+                //    let cls = Class::get("NSTouchBar").unwrap();
+                //    msg_send![cls,
+                //              presentSystemModalFunctionBar: bar
+                //              systemTrayItemIdentifier: pop_ident];
+                //
+                //    // Alternative: popup, and close the main bar
+                //    //let bar: u64 = *this.get_ivar("_groupbar");
+                //    //let cls = Class::get("NSTouchBar").unwrap();
+                //    //msg_send![cls, minimizeSystemModalFunctionBar: bar];
+                //    //msg_send![item, showPopover: bar];
+                //
+                //}
+            }
             extern fn objc_button(_this: &mut Object, _cmd: Sel, _sender: u64) {
                 let sender = _sender as *mut Object;
                 info!("Button push: {}", sender as u64);
@@ -107,28 +475,54 @@ impl INSObject for ObjcAppDelegate {
                 //}
             }
             extern fn objc_present(this: &mut Object, _cmd: Sel, _sender: u64) {
+                info!("present");
                 unsafe {
-                    let ident: u64 = *this.get_ivar("_groupId");
-                    let bar: u64 = *this.get_ivar("_groupbar");
+                    let ident_int: u64 = *this.get_ivar("_groupId");
+                    let bar_int: u64 = *this.get_ivar("_groupbar");
+                    let ident = ident_int as *mut Object;
+                    print_nsstring(ident);
+                    let bar = bar_int as *mut Object;
                     let cls = Class::get("NSTouchBar").unwrap();
+                    info!("present msg_send");
+                    let len: u64 = msg_send![ident, length];
+                    info!("set _groupId len: {}", len);
+                    info!("bar: {}", bar_int);
                     msg_send![cls,
                               presentSystemModalFunctionBar: bar
                               systemTrayItemIdentifier: ident];
+                    info!("present sent");
                 }
             }
-            extern fn objc_touch_bar_make_item_for_identifier(_this: &mut Object, _cmd: Sel, _bar: u64, _id: u64) -> u64 {
+            extern fn objc_touch_bar_make_item_for_identifier(this: &mut Object, _cmd: Sel,
+                                                              _bar: u64, id_ptr: u64) -> u64 {
+                info!("MAKE");
                 unsafe {
-                    let id = _id as *mut Object;
+                    // Find the touchbar item matching this identifier in the
+                    // Objective-C object map of the Rust wrapper class, and
+                    // return it if found.
+                    let id = id_ptr as *mut Object;
+                    print_nsstring(id);
+                    let ptr: u64 = *this.get_ivar("_rust_wrapper");
+                    let wrapper = &mut *(ptr as *mut RustTouchbarDelegateWrapper);
+                    for (obj_ref, ident_ref) in &wrapper.bar_obj_map {
+                        let ident = *ident_ref as *mut Object;
+                        let obj = *obj_ref as *mut Object;
+                        if msg_send![id, isEqualToString: ident] {
+                            return obj as u64;
+                        }
+                    }
+
                     let b1_ident = objc_foundation::NSString::from_str("com.trevorbentley.b1");
                     let b2_ident = objc_foundation::NSString::from_str("com.trevorbentley.b2");
                     let b3_ident = objc_foundation::NSString::from_str("com.trevorbentley.b3");
                     let slide_ident = objc_foundation::NSString::from_str("com.trevorbentley.slide");
+                    let pop_ident = objc_foundation::NSString::from_str("com.trevorbentley.pop");
                     if msg_send![b1_ident, isEqualToString: id] {
                         let cls = Class::get("NSButton").unwrap();
-                        let icon_ptr: u64 = *_this.get_ivar("_icon");
+                        let icon_ptr: u64 = *this.get_ivar("_icon");
                         let btn: *mut Object = msg_send![cls,
                                                          buttonWithImage:icon_ptr
-                                                         target:_this
+                                                         target:this
                                                          action:sel!(button:)];
                         let cls = Class::get("NSCustomTouchBarItem").unwrap();
                         let item: *mut Object = msg_send![cls, alloc];
@@ -138,10 +532,10 @@ impl INSObject for ObjcAppDelegate {
                     }
                     else if msg_send![b2_ident, isEqualToString: id] {
                         let cls = Class::get("NSButton").unwrap();
-                        let icon_ptr: u64 = *_this.get_ivar("_icon");
+                        let icon_ptr: u64 = *this.get_ivar("_icon");
                         let btn: *mut Object = msg_send![cls,
                                                          buttonWithImage:icon_ptr
-                                                         target:_this
+                                                         target:this
                                                          action:sel!(button:)];
                         let cls = Class::get("NSCustomTouchBarItem").unwrap();
                         let item: *mut Object = msg_send![cls, alloc];
@@ -170,10 +564,45 @@ impl INSObject for ObjcAppDelegate {
                         msg_send![slider, setMinValue: 0f32];
                         msg_send![slider, setMaxValue: 100.0];
                         msg_send![slider, setContinuous: YES];
-                        msg_send![item, setTarget: _this];
+                        msg_send![item, setTarget: this];
                         msg_send![item, setAction: sel!(button:)];
                         return item as u64;
                     }
+                    else if msg_send![pop_ident, isEqualToString: id] {
+                        let cls = Class::get("NSPopoverTouchBarItem").unwrap();
+                        let item: *mut Object = msg_send![cls, alloc];
+                        let item: *mut Object = msg_send![item, initWithIdentifier: pop_ident];
+                        let cls = Class::get("NSButton").unwrap();
+                        let icon_ptr: u64 = *this.get_ivar("_icon");
+                        let this_copy = this as *mut Object as u64;
+                        let btn: *mut Object = msg_send![cls,
+                                                         buttonWithImage:icon_ptr
+                                                         // TODO: instead of showPopover, use a
+                                                         // handler that minimizes the group bar
+                                                         // before doing the pop.
+                                                         //
+                                                         // Alternative: present on top of current
+                                                         // one.
+                                                         target:this_copy
+                                                         action:sel!(popbar:)];
+                        let _:() = msg_send![item, setShowsCloseButton: YES];
+                        let gesture: *mut Object = msg_send![item, makeStandardActivatePopoverGestureRecognizer];
+                        let _:() = msg_send![btn, addGestureRecognizer: gesture];
+                        let _:() = msg_send![item, setCollapsedRepresentation: btn];
+                        let cls = Class::get("NSTouchBar").unwrap();
+                        let bar: *mut Object = msg_send![cls, alloc];
+                        let bar: *mut objc::runtime::Object = msg_send![bar, init];
+                        let this_copy = this as *mut Object as u64;
+                        let _ : () = msg_send![bar, setDelegate: this_copy as *mut Object];
+                        let idents = NSArray::from_vec(vec![b1_ident]);
+                        let _ : () = msg_send![bar, setDefaultItemIdentifiers: idents];
+                        let _:() = msg_send![item, setPopoverTouchBar: bar];
+                        let _:() = msg_send![item, setPressAndHoldTouchBar: bar];
+                        this.set_ivar("_popbar", bar as u64);
+                        this.set_ivar("_popover", item as u64);
+                        
+                        return item as u64;
+                    }
                 }
                 0
             }
@@ -181,24 +610,32 @@ impl INSObject for ObjcAppDelegate {
                 unsafe {
                     DFRSystemModalShowsCloseBoxWhenFrontMost(YES);
 
-                    // Initialize touchbar singleton with button layout
-                    // TODO: break out into function.  this needs to be runtime reconfigured
-                    let b1_ident = objc_foundation::NSString::from_str("com.trevorbentley.b1");
-                    let b2_ident = objc_foundation::NSString::from_str("com.trevorbentley.b2");
-                    let b3_ident = objc_foundation::NSString::from_str("com.trevorbentley.b3");
-                    let slide_ident = objc_foundation::NSString::from_str("com.trevorbentley.slide");
-                    let cls = Class::get("NSTouchBar").unwrap();
-                    let bar: *mut Object = msg_send![cls, alloc];
-                    let bar: *mut objc::runtime::Object = msg_send![bar, init];
-                    let idents = NSArray::from_vec(vec![b1_ident, b2_ident, b3_ident, slide_ident]);
-                    let _ : () = msg_send![bar, setDefaultItemIdentifiers: idents];
-                    let this_copy = this as *mut Object as u64;
-                    let _ : () = msg_send![bar, setDelegate: this_copy as *mut Object];
-                    let _ : () = msg_send![this, setGroupTouchBar: bar];
+//                    // Initialize touchbar singleton with button layout
+//                    // TODO: break out into function.  this needs to be runtime reconfigured
+//                    let b1_ident = objc_foundation::NSString::from_str("com.trevorbentley.b1");
+//                    let b2_ident = objc_foundation::NSString::from_str("com.trevorbentley.b2");
+//                    let b3_ident = objc_foundation::NSString::from_str("com.trevorbentley.b3");
+//                    let slide_ident = objc_foundation::NSString::from_str("com.trevorbentley.slide");
+//                    let pop_ident = objc_foundation::NSString::from_str("com.trevorbentley.pop");
+//                    let cls = Class::get("NSTouchBar").unwrap();
+//                    let bar: *mut Object = msg_send![cls, alloc];
+//                    let bar: *mut objc::runtime::Object = msg_send![bar, init];
+//                    let idents = NSArray::from_vec(vec![b1_ident, b2_ident, b3_ident,
+//                                                        slide_ident, pop_ident]);
+//                    let _ : () = msg_send![bar, setDefaultItemIdentifiers: idents];
+//                    let this_copy = this as *mut Object as u64;
+//                    let _ : () = msg_send![bar, setDelegate: this_copy as *mut Object];
+//                    let _ : () = msg_send![this, setGroupTouchBar: bar];
 
                     // Add icon to touchbar's Control Strip.
-                    let ident = NSString::alloc(nil).init_str("com.trevorbentley.group");
-                    this.set_ivar("_groupId", ident as u64);
+                    //let ident = NSString::alloc(nil).init_str("com.trevorbentley.group");
+                    //info!("set _groupId");
+                    //this.set_ivar("_groupId", ident as u64);
+                    //let len: u64 = msg_send![ident, length];
+                    //info!("set _groupId len: {}", len);
+
+                    let ident_int: u64 = *this.get_ivar("_groupId");
+                    let ident = ident_int as *mut Object;
                     let cls = Class::get("NSCustomTouchBarItem").unwrap();
                     let item: *mut Object = msg_send![cls, alloc];
                     msg_send![item, initWithIdentifier:ident];
@@ -230,17 +667,33 @@ 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_popbar;
+                decl.add_method(sel!(popbar:), f);
+
                 let f: extern fn(&mut Object, Sel) -> u64 = objc_group_touch_bar;
                 decl.add_method(sel!(groupTouchBar), f);
 
                 let f: extern fn(&mut Object, Sel, u64) = objc_set_group_touch_bar;
                 decl.add_method(sel!(setGroupTouchBar:), f);
 
+                let f: extern fn(&mut Object, Sel, u64) = objc_set_group_ident;
+                decl.add_method(sel!(setGroupIdent:), f);
+
                 let f: extern fn(&mut Object, Sel, u64) = objc_set_icon;
                 decl.add_method(sel!(setIcon:), f);
 
                 let f: extern fn(&mut Object, Sel, u64) = objc_set_rust_wrapper;
                 decl.add_method(sel!(setRustWrapper:), f);
+
+                // Scrubber delegates
+                let f: extern fn(&mut Object, Sel, u64) -> u32 = objc_number_of_items_for_scrubber;
+                decl.add_method(sel!(numberOfItemsForScrubber:), f);
+                let f: extern fn(&mut Object, Sel, u64, u32) -> u64 = objc_scrubber_view_for_item_at_index;
+                decl.add_method(sel!(scrubber:viewForItemAtIndex:), f);
+                let f: extern fn(&mut Object, Sel, u64, u64, u32) -> NSSize = objc_scrubber_layout_size_for_item_at_index;
+                decl.add_method(sel!(scrubber:layout:sizeForItemAtIndex:), f);
+                let f: extern fn(&mut Object, Sel, u64, u32) = objc_scrubber_did_select_item_at_index;
+                decl.add_method(sel!(scrubber:didSelectItemAtIndex:), f);
             }
 
             decl.register();