summary history branches tags files
examples/example.rs
// Rubrail Example
//
// Demonstrates using Rubrail to layout and interact with a persistent Touchbar
// on Macbooks that support it.
//
// Usage notes:
//
// To access the touchbar, Rubrail *must* execute inside an OS X app bundle.
// Since Cargo doesn't do that itself, this example uses the Trampoline feature
// of the fruitbasket crate to relaunch itself in an app bundle.
//
// Simply run:
//
// $ cargo test && cargo run --example example
//
//
extern crate rubrail;
extern crate fruitbasket;

use rubrail::Touchbar;
use rubrail::TTouchbar;
use rubrail::TScrubberData;
use rubrail::ItemId;
use rubrail::SwipeState;

#[macro_use]
extern crate log;

use std::rc::Rc;
use std::cell::RefCell;

struct TouchbarHandler {
    devices: RefCell<Vec<String>>,
}
impl TScrubberData for TouchbarHandler {
    fn count(&self, _item: ItemId) -> u32 {
        self.devices.borrow().len() as u32
    }
    fn text(&self, _item: ItemId, idx: u32) -> String {
        self.devices.borrow()[idx as usize].to_string()
    }
    fn width(&self, _item: ItemId, idx: u32) -> u32 {
        // 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? ¯\_(ツ)_/¯
        let len = self.devices.borrow()[idx as usize].len() as u32;
        let width = len * 8 + 20;
        width
    }
    fn touch(&self, _item: ItemId, idx: u32) {
        info!("scrub touch: {}", idx);
    }
}

fn populate(bar_rc: Rc<RefCell<Touchbar>>, count: u32, stopper: fruitbasket::FruitStopper) {
    // Get touchbar from the refcell.  It's wrapped in a cell so
    // it can be passed around in the button callbacks.
    let mut tb = (bar_rc).borrow_mut();

    // Create the lowest level "root" touchbar
    let mut barid = tb.create_bar();

    // Create a quit button for root bar
    let quit_stopper = stopper.clone();
    let quit_id = tb.create_button(None, Some("Quit"), Box::new(move |_| {quit_stopper.stop();}));

    // Create an action button for the root bar.  When clicked, it will
    // close the bar and re-create itself.
    let bar_copy = bar_rc.clone();
    let text = format!("button{}", count);
    let button1_id = tb.create_button(None, Some(&text), Box::new(move |_| {
        populate(bar_copy.clone(), count+1, stopper.clone())
    }));

    // Create a text label for the root bar
    let label1_id = tb.create_label("This is a label");
    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.
    tb.add_item_swipe_gesture(&label1_id, Box::new(move |item,state,translation| {
        let color: f64 = match translation.abs().trunc() as u32 {
            t if t < 10 => 1.0,
            t if t > 100 => 0.0,
            _ => (45. / translation.abs()),
        };
        let rgba = match state {
            SwipeState::Ended => (1.0, 1.0, 1.0, 1.0),
            _ => {
                match translation.is_sign_positive() {
                    true => (color, 1.0, color, 1.0),
                    false => (1.0, color, color, 1.0),
                }
            }
        };
        unsafe { rubrail::util::set_text_color(item, rgba.0, rgba.1, rgba.2, rgba.3); }
    }));

    // Create a data backend for scrolling text "scrubbers"
    let scrubber = Rc::new(TouchbarHandler {
        devices: RefCell::new(vec![
            "one".to_string(), "two".to_string(),
            "a little bit longer one".to_string(),
            "three".to_string(),
            "this one is really quite a bit longer than the others".to_string()]),
    });

    // Create a scrubber for the root bar
    let scrubber1_id = tb.create_text_scrubber(scrubber.clone());
    tb.select_scrubber_item(&scrubber1_id, 1);

    // Create a 'popbar', a second level deep bar
    let mut popbar1_id = tb.create_bar();
    let popbutton1_id = tb.create_popover_item(None, Some("Popbar1"), &popbar1_id);

    // Create another scrubber with the same data, for the popbar.
    // Note that the data and callbacks are shared, but this is a different
    // instance and can store a different active selection.
    let scrubber2_id = tb.create_text_scrubber(scrubber.clone());
    tb.select_scrubber_item(&scrubber2_id, 3);

    // Create a slider for the popbar.
    let slider1_id = tb.create_slider(0.0, 50.0, Some("Slide"), true,
                                      Box::new(move |_s,v| {info!("Slid to: {}", v);}));
    tb.update_slider(&slider1_id, 15.0);

    // Create a another popbar.  This will make a 2-level deep UI.
    let mut popbar2_id = tb.create_bar();
    let popbutton2_id = tb.create_popover_item(None, Some("Popbar2"), &popbar2_id);

    // Create buttons to display on the popbars
    let popbar_button_id = tb.create_button(None, Some("1 level deep"), Box::new(move |_| {}));
    let deep_button_id = tb.create_button(None, Some("2 levels deep"), Box::new(move |_| {}));

    // Layout the deepest (2-level) popbar
    tb.add_items_to_bar(&mut popbar2_id, vec![deep_button_id]);

    // Layout the middle (1-level) popbar
    tb.add_items_to_bar(&mut popbar1_id, vec![popbar_button_id, popbutton2_id, slider1_id, scrubber2_id]);

    // Layout the root bar
    tb.add_items_to_bar(&mut barid, vec![quit_id, button1_id, popbutton1_id, label1_id, scrubber1_id]);

    // Register the root bar and display it.
    tb.set_bar_as_root(barid);
}

fn main() {
    // Write log to home directory
    fruitbasket::create_logger(".rubrail.log", fruitbasket::LogDir::Home, 5, 2).unwrap();

    // Initialize OS X application.
    let icon = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("examples").join("icon.png");
    let mut nsapp = fruitbasket::Trampoline::new(
        "rubrail_example", "rubrail_example", "com.trevorbentley.rubrail_example")
        .icon("icon.png")
        .version(env!("CARGO_PKG_VERSION"))
        .plist_key("LSBackgroundOnly", "1")
        .resource(icon.to_str().unwrap())
        .build(fruitbasket::InstallDir::Custom("target/".to_string())).unwrap();
    nsapp.set_activation_policy(fruitbasket::ActivationPolicy::Prohibited);

    // Initialize the touchbar
    let bar_rc = Rc::new(RefCell::new(Touchbar::alloc("bar")));

    let stopper = nsapp.stopper();
    // Populate the touchbar with UI elements
    populate(bar_rc.clone(), 1, stopper);

    // Enter OS X application loop.
    nsapp.run(fruitbasket::RunPeriod::Forever).expect("Failed to launch app");
}