pub fn DFRElementSetControlStripPresenceForIdentifier(n: *mut Object, x: i8);
}
-#[cfg(feature = "libc")]
pub mod util {
+ //! Utility functions for working with the Apple/TouchBar environment
+ //!
+ //! Contains convenience functions that help with some common actions in
+ //! Mac environments.
+
extern crate libc;
- use objc::runtime::Object;
+ extern crate cocoa;
+ use super::ItemId;
+ use std::ptr;
use std::ffi::CStr;
+ use objc::runtime::Object;
+ use objc::runtime::Class;
+ use self::cocoa::foundation::NSString;
+ use self::cocoa::base::nil;
#[allow(dead_code)]
+ /// Print an NSString object to the global logger
pub fn print_nsstring(str: *mut Object) {
unsafe {
let cstr: *const libc::c_char = msg_send![str, UTF8String];
info!("{}", rstr);
}
}
+
+ /// Convert an NSString object into a Rust String
pub fn nsstring_decode(str: *mut Object) -> String {
unsafe {
let cstr: *const libc::c_char = msg_send![str, UTF8String];
rstr
}
}
+
+ /// Locate a resource in the executing Mac App bundle
+ ///
+ /// If the program is executing from an App bundle (.app), which it must be
+ /// to use Rubrail correctly, this looks for a resource by name and
+ /// extension in the bundled Resources directory.
+ pub fn bundled_resource_path(name: &str, extension: &str) -> Option<String> {
+ unsafe {
+ let cls = Class::get("NSBundle").unwrap();
+ let bundle: *mut Object = msg_send![cls, mainBundle];
+ let res = NSString::alloc(nil).init_str(name);
+ let ext = NSString::alloc(nil).init_str(extension);
+ let ini: *mut Object = msg_send![bundle, pathForResource:res ofType:ext];
+ let _ = msg_send![res, release];
+ let _ = msg_send![ext, release];
+ let cstr: *const libc::c_char = msg_send![ini, UTF8String];
+ if cstr != ptr::null() {
+ let rstr = CStr::from_ptr(cstr).to_string_lossy().into_owned();
+ return Some(rstr);
+ }
+ None
+ }
+ }
+
+ /// Sets the backgroundColor attribute on an item's view.
+ ///
+ /// This is an **unsafe** helper function to set the backgroundColor
+ /// attribute on the view of an item. It does *not* verify that the item
+ /// actually has a view, nor that the view supports backgroundColor, hence
+ /// **unsafe**.
+ ///
+ /// Known compatible items: labels
+ ///
+ /// # Arguments
+ ///
+ /// * `item` - The `ItemId` to color
+ /// * `r` - Red value (0.0 - 1.0)
+ /// * `g` - Green value (0.0 - 1.0)
+ /// * `b` - Blue value (0.0 - 1.0)
+ /// * `alpha` - Alpha value (0.0 - 1.0)
+ pub unsafe fn set_bg_color(item: &ItemId, r: f64, g: f64, b: f64, alpha: f64) {
+ let item = *item as *mut Object;
+ let view: *mut Object = msg_send![item, view];
+ let cls = Class::get("NSColor").unwrap();
+ let color: *mut Object = msg_send![
+ cls, colorWithRed: r green: g blue: b alpha: alpha];
+ msg_send![view, setBackgroundColor: color];
+ }
+
+ /// Sets the textColor attribute on an item's view.
+ ///
+ /// This is an **unsafe** helper function to set the textColor attribute on
+ /// the view of an item. It does *not* verify that the item actually has a
+ /// view, nor that the view supports textColor, hence **unsafe**.
+ ///
+ /// Known compatible items: labels
+ ///
+ /// # Arguments
+ ///
+ /// * `item` - The `ItemId` to color
+ /// * `r` - Red value (0.0 - 1.0)
+ /// * `g` - Green value (0.0 - 1.0)
+ /// * `b` - Blue value (0.0 - 1.0)
+ /// * `alpha` - Alpha value (0.0 - 1.0)
+ pub unsafe fn set_text_color(item: &ItemId, r: f64, g: f64, b: f64, alpha: f64) {
+ let item = *item as *mut Object;
+ let view: *mut Object = msg_send![item, view];
+ let cls = Class::get("NSColor").unwrap();
+ let color: *mut Object = msg_send![
+ cls, colorWithRed: r green: g blue: b alpha: alpha];
+ msg_send![view, setTextColor: color];
+ }
}
-#[cfg(not(feature = "libc"))]
-pub mod util {
- use objc::runtime::Object;
- #[allow(dead_code)]
- pub fn print_nsstring(_str: *mut Object) {}
- pub fn nsstring_decode(_str: *mut Object) -> String { String::new() }
+
+macro_rules! image_template {
+ ( $var:ident, $($template:ident),* ) => {
+ match $var {
+ $(
+ ImageTemplate::$template => format!("NSTouchBar{}", stringify!($template)),
+ )*
+ }
+ }
+}
+
+impl ImageTemplate {
+ fn objc(template: ImageTemplate) -> *mut Object {
+ unsafe {
+ let s = image_template!(
+ template,
+ AddDetailTemplate,
+ AddTemplate,
+ AlarmTemplate,
+ AudioInputMuteTemplate,
+ AudioInputTemplate,
+ AudioOutputMuteTemplate,
+ AudioOutputVolumeHighTemplate,
+ AudioOutputVolumeLowTemplate,
+ AudioOutputVolumeMediumTemplate,
+ AudioOutputVolumeOffTemplate,
+ BookmarksTemplate,
+ ColorPickerFill,
+ ColorPickerFont,
+ ColorPickerStroke,
+ CommunicationAudioTemplate,
+ CommunicationVideoTemplate,
+ ComposeTemplate,
+ DeleteTemplate,
+ DownloadTemplate,
+ EnterFullScreenTemplate,
+ ExitFullScreenTemplate,
+ FastForwardTemplate,
+ FolderCopyToTemplate,
+ FolderMoveToTemplate,
+ FolderTemplate,
+ GetInfoTemplate,
+ GoBackTemplate,
+ GoDownTemplate,
+ GoForwardTemplate,
+ GoUpTemplate,
+ HistoryTemplate,
+ IconViewTemplate,
+ ListViewTemplate,
+ MailTemplate,
+ NewFolderTemplate,
+ NewMessageTemplate,
+ OpenInBrowserTemplate,
+ PauseTemplate,
+ PlayheadTemplate,
+ PlayPauseTemplate,
+ PlayTemplate,
+ QuickLookTemplate,
+ RecordStartTemplate,
+ RecordStopTemplate,
+ RefreshTemplate,
+ RewindTemplate,
+ RotateLeftTemplate,
+ RotateRightTemplate,
+ SearchTemplate,
+ ShareTemplate,
+ SidebarTemplate,
+ SkipAhead15SecondsTemplate,
+ SkipAhead30SecondsTemplate,
+ SkipAheadTemplate,
+ SkipBack15SecondsTemplate,
+ SkipBack30SecondsTemplate,
+ SkipBackTemplate,
+ SkipToEndTemplate,
+ SkipToStartTemplate,
+ SlideshowTemplate,
+ TagIconTemplate,
+ TextBoldTemplate,
+ TextBoxTemplate,
+ TextCenterAlignTemplate,
+ TextItalicTemplate,
+ TextJustifiedAlignTemplate,
+ TextLeftAlignTemplate,
+ TextListTemplate,
+ TextRightAlignTemplate,
+ TextStrikethroughTemplate,
+ TextUnderlineTemplate,
+ UserAddTemplate,
+ UserGroupTemplate,
+ UserTemplate
+ );
+ let name = NSString::alloc(nil).init_str(&s);
+ let _ = msg_send![name, autorelease];
+ name
+ }
+ }
}
#[derive(PartialEq, Debug)]
Slider,
Scrubber,
Popover,
+ Spacer,
}
struct InternalBar {
scrubber: Option<Rc<TScrubberData>>,
button_cb: Option<ButtonCb>,
slider_cb: Option<SliderCb>,
+ swipe_cb: Option<SwipeCb>,
child_bar: Option<ItemId>,
}
self.control = None;
self.scrubber = None;
self.button_cb = None;
+ self.swipe_cb = None;
self.slider_cb = None;
}
}
objc_ident as u64
}
}
- fn alloc_button(&mut self, image: Option<&str>, text: Option<&str>,
+ fn alloc_button(&mut self, image: Option<&TouchbarImage>, text: Option<&str>,
target: *mut Object, sel: SEL) -> *mut Object {
unsafe {
let text = match text {
None => nil,
};
let image = match image {
- Some(i) => {
- let filename = NSString::alloc(nil).init_str(i);
- let objc_image = NSImage::alloc(nil).initWithContentsOfFile_(filename);
- let _ = msg_send![filename, release];
- objc_image
- },
+ Some(i) => *i as *mut Object,
None => nil,
};
let cls = Class::get("NSButton").unwrap();
None => None,
}
}
+ fn find_view_from_control(&self, item: &ItemId) -> Option<*mut Object> {
+ match self.item_map.values().into_iter().filter(|x| {
+ x.control.is_some() && x.control.unwrap() as ItemId == *item
+ }).next() {
+ Some(item) => Some(item.view),
+ None => None,
+ }
+ }
fn find_bar_ident(&self, bar: &ItemId) -> Option<Ident> {
match self.bar_map.values().into_iter().filter(|x| {
x.view as ItemId == *bar
None => None,
}
}
+ fn find_swipe_cb(&self, item: u64) -> Option<&SwipeCb> {
+ match self.item_map.values().into_iter().filter(|x| {
+ x.control.is_some() && x.control.unwrap() as u64 == item
+ }).next() {
+ Some(item) => item.swipe_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
self.free_bar_allocations(subbar);
}
}
+ unsafe fn set_label_font_for_text(label: *mut Object, text: &str) {
+ //let constraints: *mut Object = msg_send![label, constraints];
+ //let height_constraint: *mut Object = msg_send![constraints, firstObject];
+ //if height_constraint != nil {
+ // // TODO: if re-enabled, must use NSLayoutConstraint identifier to
+ // // make sure the correct constraint is removed
+ // msg_send![label, removeConstraint: height_constraint];
+ //}
+ if text.contains("\n") {
+ // Shrink font for multi-line labels. This makes for quite small text.
+ let cls = Class::get("NSFont").unwrap();
+ let default_size:f64 = msg_send![cls, systemFontSize];
+ let custom_font: *mut Object = msg_send![cls, systemFontOfSize: default_size - 3.0];
+ msg_send![label, setFont: custom_font];
+
+ //let anchor: *mut Object = msg_send![label, heightAnchor];
+ //let constraint: *mut Object = msg_send![anchor, constraintEqualToConstant: 30. as f64];
+ //let _ = msg_send![constraint, setActive: YES];
+ }
+ else {
+ // Enlarge the font for single lines. For some reason it defaults
+ // to smaller than other elements.
+ let cls = Class::get("NSFont").unwrap();
+ let default_size:f64 = msg_send![cls, systemFontSize];
+ let custom_font: *mut Object = msg_send![cls, systemFontOfSize: default_size + 3.0];
+ msg_send![label, setFont: custom_font];
+ }
+ }
}
impl TTouchbar for Touchbar {
let objc_image = NSImage::alloc(nil).initWithContentsOfFile_(filename);
let _:() = msg_send![self.objc, setIcon: objc_image];
let _ = msg_send![filename, release];
- let _ = msg_send![objc_image, release];
}
}
bar as u64
}
}
- fn create_popover_item(&mut self, image: Option<&str>,
+ fn create_popover_item(&mut self, image: Option<&TouchbarImage>,
text: Option<&str>, bar_id: &BarId) -> ItemId {
unsafe {
let bar = *bar_id as *mut Object;
scrubber: None,
button_cb: None,
slider_cb: None,
+ swipe_cb: None,
child_bar: Some(bar as ItemId),
};
self.item_map.insert(item as u64, internal);
}
fn create_label(&mut self, text: &str) -> ItemId {
unsafe {
- let frame = NSRect::new(NSPoint::new(0., 0.), NSSize::new(300., 44.));
+ let frame = NSRect::new(NSPoint::new(0., 0.), NSSize::new(0., 40.));
let cls = Class::get("NSTextField").unwrap();
let label: *mut Object = msg_send![cls, alloc];
let label: *mut Object = msg_send![label, initWithFrame: frame];
+ RustTouchbarDelegateWrapper::set_label_font_for_text(label, text);
let _:() = msg_send![label, setEditable: NO];
+ let cell: *mut Object = msg_send![label, cell];
+ let _:() = msg_send![cell, setWraps: NO];
let text = NSString::alloc(nil).init_str(text);
let _:() = msg_send![label, setStringValue: text];
let _ = msg_send![text, release];
scrubber: None,
button_cb: None,
slider_cb: None,
+ swipe_cb: None,
child_bar: None
};
self.item_map.insert(item as u64, internal);
unsafe {
let item: *mut Object = *label_id as *mut Object;
let label: *mut Object = msg_send![item, view];
+ RustTouchbarDelegateWrapper::set_label_font_for_text(label, text);
let text = NSString::alloc(nil).init_str(text);
let _:() = msg_send![label, setStringValue: text];
let _ = msg_send![text, release];
}
}
+ fn update_label_width(&mut self, label_id: &ItemId, width: u32) {
+ unsafe {
+ //let _ = msg_send![label, setAutoresizingMask: 0];
+ //let _ = msg_send![label, setFrameSize: NSSize::new(600., 10.)];
+ //let _ = msg_send![label, setBoundsSize: NSSize::new(600., 10.)];
+ //let _ = msg_send![item, setFrameSize: NSSize::new(600., 30.)];
+ //let _ = msg_send![label, setPreferredMaxLayoutWidth: 500.];
+ //let constraints: *mut Object = msg_send![label, constraints];
+ //let count: u32 = msg_send![constraints, count];
+ //info!("CONSTRAINTS: {}", count);
+ let item: *mut Object = *label_id as *mut Object;
+ let label: *mut Object = msg_send![item, view];
+ let anchor: *mut Object = msg_send![label, widthAnchor];
+ let constraint: *mut Object = msg_send![anchor, constraintEqualToConstant: width as f64];
+ let _ = msg_send![constraint, setActive: YES];
+ }
+ }
+
fn create_text_scrubber(&mut self, data: Rc<TScrubberData>) -> ItemId {
unsafe {
let ident = self.generate_ident();
scrubber: Some(data),
button_cb: None,
slider_cb: None,
+ swipe_cb: None,
child_bar: None,
};
self.item_map.insert(item as u64, internal);
unsafe {
let item = *scrub_id as *mut Object;
let scrubber: *mut Object = msg_send![item, view];
- let sel_idx: u32 = msg_send![scrubber, selectedIndex];
+ let sel_idx: i32 = msg_send![scrubber, selectedIndex];
let _:() = msg_send![scrubber, reloadData];
// reload clears the selected item. re-select it.
- let _:() = msg_send![scrubber, setSelectedIndex: sel_idx];
+ if sel_idx >= 0 {
+ let _:() = msg_send![scrubber, setSelectedIndex: sel_idx];
+ }
}
}
- fn create_button(&mut self, image: Option<&str>, text: Option<&str>, cb: ButtonCb) -> ItemId {
+
+ fn add_item_swipe_gesture(&mut self, item_id: &ItemId, cb: SwipeCb) {
+ 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("NSPanGestureRecognizer").unwrap();
+ let gesture: *mut Object = msg_send![cls, alloc];
+ let gesture: *mut Object = msg_send![gesture,
+ initWithTarget: self.objc.clone()
+ action: sel!(swipeGesture:)];
+ msg_send![gesture, setAllowedTouchTypes: 1]; // NSTouchTypeMaskDirect
+ msg_send![view, addGestureRecognizer: gesture];
+ 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 create_spacer(&mut self, space: SpacerType) -> ItemId {
+ unsafe {
+ let s = match space {
+ SpacerType::Small =>
+ NSString::alloc(nil).init_str("NSTouchBarItemIdentifierFixedSpaceSmall"),
+ SpacerType::Large =>
+ NSString::alloc(nil).init_str("NSTouchBarItemIdentifierFixedSpaceLarge"),
+ SpacerType::Flexible =>
+ NSString::alloc(nil).init_str("NSTouchBarItemIdentifierFlexibleSpace"),
+ };
+ // This is really stupid, since it's just a string, but to fit into the
+ // rest of the system we go ahead and allocate a whole internal item for it.
+
+ // And since it doesn't have an ident, just use it as its own ident. Both
+ // the view and ident will be sent a release at shutdown, so retain it an
+ // extra time here to keep the references balanced.
+ let _ = msg_send![s, retain];
+
+ let internal = InternalItem {
+ _type: ItemType::Spacer,
+ view: s,
+ ident: s as u64,
+ control: None,
+ scrubber: None,
+ button_cb: None,
+ slider_cb: None,
+ swipe_cb: None,
+ child_bar: None,
+ };
+ self.item_map.insert(s as u64, internal);
+ s as ItemId
+ }
+ }
+
+ fn create_image_from_path(&mut self, path: &str) -> TouchbarImage {
+ unsafe {
+ let filename = NSString::alloc(nil).init_str(path);
+ let objc_image = NSImage::alloc(nil).initWithContentsOfFile_(filename);
+ let _ = msg_send![filename, release];
+ objc_image as TouchbarImage
+ }
+ }
+
+ fn create_image_from_template(&mut self, template: ImageTemplate) -> TouchbarImage {
+ unsafe {
+ let cls = Class::get("NSImage").unwrap();
+ let image: *mut Object = msg_send![cls, imageNamed: ImageTemplate::objc(template)];
+ let _ = msg_send![image, retain];
+ image as TouchbarImage
+ }
+ }
+
+ fn create_button(&mut self, image: Option<&TouchbarImage>, text: Option<&str>, cb: ButtonCb) -> ItemId {
unsafe {
let ident = self.generate_ident();
let target = (&*self.objc.clone()) as *const ObjcAppDelegate as *mut Object;
scrubber: None,
button_cb: Some(cb),
slider_cb: None,
+ swipe_cb: None,
child_bar: None,
};
self.item_map.insert(item as u64, internal);
item as u64
}
}
- fn create_slider(&mut self, min: f64, max: f64, cb: SliderCb) -> ItemId {
+
+ fn update_button(&mut self, item: &ItemId, image: Option<&TouchbarImage>, text: Option<&str>) {
+ unsafe {
+ let item = *item as *mut Object;
+ let btn: *mut Object = msg_send![item, view];
+ if let Some(image) = image {
+ let image = *image as *mut Object;
+ let _ = msg_send![btn, setImage: image];
+ let _ = msg_send![image, release];
+ }
+ if let Some(text) = text {
+ let objc_text = NSString::alloc(nil).init_str(text);
+ let _ = msg_send![btn, setTitle: objc_text];
+ let _ = msg_send![objc_text, release];
+ }
+ }
+ }
+
+ fn update_button_width(&mut self, button_id: &ItemId, width: u32) {
+ unsafe {
+ let item: *mut Object = *button_id as *mut Object;
+ let control: *mut Object = msg_send![item, view];
+ let anchor: *mut Object = msg_send![control, widthAnchor];
+ let constraint: *mut Object = msg_send![anchor, constraintEqualToConstant: width as f64];
+ let _ = msg_send![constraint, setActive: YES];
+ }
+ }
+
+ fn create_slider(&mut self, min: f64, max: f64,
+ label: Option<&str>,
+ continuous: bool, cb: SliderCb) -> ItemId {
unsafe {
let ident = self.generate_ident();
let cls = RRSliderTouchBarItem::class();
let item: *mut Object = msg_send![cls, alloc];
let item: *mut Object = msg_send![item, initWithIdentifier: ident];
let slider: *mut Object = msg_send![item, slider];
+ if let Some(label) = label {
+ let objc_text: *mut Object = NSString::alloc(nil).init_str(label);
+ msg_send![item, setLabel: objc_text];
+ msg_send![objc_text, release];
+ }
msg_send![slider, setMinValue: min];
msg_send![slider, setMaxValue: max];
- msg_send![slider, setContinuous: YES];
+ msg_send![slider, setContinuous: continuous];
msg_send![item, setTarget: self.objc.clone()];
msg_send![item, setAction: sel!(slider:)];
scrubber: None,
button_cb: None,
slider_cb: Some(cb),
+ swipe_cb: None,
child_bar: None,
};
self.item_map.insert(item as u64, internal);
let ptr: u64 = *this.get_ivar("_rust_wrapper");
let wrapper = &mut *(ptr as *mut RustTouchbarDelegateWrapper);
if let Some(ref cb) = wrapper.find_button_cb(sender) {
- cb(sender);
+ // Sender is the button. Find the owning touchbar item:
+ let item = wrapper.find_view_from_control(&sender).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");
+ let wrapper = &mut *(ptr as *mut RustTouchbarDelegateWrapper);
+ let gesture = sender as *mut Object;
+ let view: *mut Object = msg_send![gesture, view];
+ let translation: NSPoint = msg_send![gesture,
+ translationInView: view];
+ let gesture_state: u32 = msg_send![gesture, state];
+ let state = match gesture_state {
+ // NSGestureRecognizerStateBegan
+ 1 => SwipeState::Began,
+ // NSGestureRecognizerStateChanged
+ 2 => SwipeState::Changed,
+ // NSGestureRecognizerStateEnded
+ 3 => SwipeState::Ended,
+ // NSGestureRecognizerStatePossible,
+ // NSGestureRecognizerStateCancelled,
+ // NSGestureRecognizerStateFailed
+ _ => SwipeState::Unknown,
+ };
+ if state != SwipeState::Unknown {
+ if let Some(ref cb) = wrapper.find_swipe_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), state, translation.x);
+ }
}
}
}
let item = sender as *mut Object;
let slider: *mut Object = msg_send![item, slider];
let value: f64 = msg_send![slider, doubleValue];
- cb(sender, value);
+ cb(&sender, value);
}
}
}
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_swipe_gesture;
+ decl.add_method(sel!(swipeGesture:), f);
+
let f: extern fn(&mut Object, Sel, u64) = objc_slider;
decl.add_method(sel!(slider:), f);