relm-core 0.16.0

Core streams and event loop primitives for asynchronous GUI in Rust. Foundation for the relm crate.
Documentation
/*
 * Copyright (c) 2017 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

extern crate chrono;
extern crate glib;
extern crate gtk;
extern crate relm_core;

use std::time::Duration;

use chrono::Local;
use gtk::{
    Button,
    ButtonExt,
    ContainerExt,
    Inhibit,
    Label,
    LabelExt,
    WidgetExt,
    Window,
    WindowType,
};
use gtk::Orientation::Vertical;
use relm_core::EventStream;

use Msg::*;


// There will be several widgets involved in this example, but this struct
// will act as a container just for the widgets that we will be updating.
struct Widgets {
    clock_label: Label,
    counter_label: Label,
}

// This enum holds the various messages that will be passed between our
// widgets. Note that we aren't deriving `Msg` because this example uses
// the `relm-core` crate, which is the basic event-handling library that
// `relm` depends on.
#[derive(Clone, Debug)]
enum Msg {
    Clock,
    Decrement,
    Increment,
    Quit,
}

// This struct represents the model, and it maintains the state needed to
// populate the widget. The model is updated in the `update` method.
struct Model {
    counter: i32,
}

fn main() {
    gtk::init().expect("gtk::init failed");

    // This is a layout container that will stack widgets vertically.
    let vbox = gtk::Box::new(Vertical, 0);

    // Add some widgets to the layout.
    let clock_label = Label::new(None);
    vbox.add(&clock_label);
    let plus_button = Button::new_with_label("+");
    vbox.add(&plus_button);
    let counter_label = Label::new("0");
    vbox.add(&counter_label);
    let minus_button = Button::new_with_label("-");
    vbox.add(&minus_button);

    // As mentioned above, this struct holds the labels that we're going
    // to be updating.
    let widgets = Widgets {
        clock_label: clock_label,
        counter_label: counter_label,
    };

    // Create a new window and add our layout container to it.
    let window = Window::new(WindowType::Toplevel);
    window.add(&vbox);

    // Now we're going to create two event streams. The first stream will be
    // passed messages directly from the widgets in the application, and will
    // pass the same messages on to the second stream.
    //
    // In this example we're just printing the messages to stdout, but in practice
    // you could take some other kind of action i.e. if stream 1 receives the message `Foo`,
    // send the message `Bar` to stream 2.
    let main_stream = EventStream::new();
    let echo_stream = EventStream::new();

    // Here we add an observer (a closure) to the second event stream. Any message
    // passed to the stream will be passed as an argument to this closure, so adding
    // an observer is how you respond to events generated by your application.
    echo_stream.observe(move |event: &Msg| {
        println!("...Echo: {:?}", event);
    });

    // Here we add an observer to the first stream. First it prints the message, and
    // then it passes a copy of the message to the second stream.
    {
        main_stream.observe(move |event: &Msg| {
            println!("Event: {:?}", event);
            echo_stream.emit(event.clone());
        });
    }

    // Send the `Increment` message when `plus_button` emits the `clicked` signal.
    {
        let stream = main_stream.clone();
        plus_button.connect_clicked(move |_| {
            stream.emit(Increment);
        });
    }

    // Send the `Decrement` message when `minus_button` emits the `clicked` signal.
    {
        let stream = main_stream.clone();
        minus_button.connect_clicked(move |_| {
            stream.emit(Decrement);
        });
    }

    window.show_all();

    // Close the window and quit when the window close button is clicked.
    {
        let stream = main_stream.clone();
        window.connect_delete_event(move |_, _| {
            stream.emit(Quit);
            Inhibit(false)
        });
    }

    // Create the initial state of the model.
    let mut model = Model {
        counter: 0,
    };

    // Here we respond to messages that are generated and update the model.
    fn update(event: Msg, model: &mut Model, widgets: &Widgets) {
        match event {
            Clock => {
                let now = Local::now();
                widgets.clock_label.set_text(&now.format("%H:%M:%S").to_string());
            },
            Decrement => {
                model.counter -= 1;
                widgets.counter_label.set_text(&model.counter.to_string());
            },
            Increment => {
                model.counter += 1;
                widgets.counter_label.set_text(&model.counter.to_string());
            },
            Quit => gtk::main_quit(),
        }
    }

    // Create an interval which will produce a stream of type `()` at regular
    // intervals (1 second in this case). A closure is executed on each value
    // produced by the stream. This specific closure happens to ignore the values
    // passed to it, but you could of course do otherwise. Each time we receive
    // a new value (which again happens every second) we send the `Clock` signal
    // to the main event stream (which causes the clock label to update). Finally,
    // we return `Ok` so that iteration over the stream continues.
    /*let interval_future = {
        let interval = Interval::new(Duration::from_secs(1));
        let stream = main_stream.clone();
        interval.map_err(|_| ()).for_each(move |_| {
            stream.emit(Clock);
            Ok(())
        })
    };*/

    // Now we create another future by applying our `update` function to the main
    // event stream. Again, we pass the future to the executor.
    main_stream.set_callback(move |msg| {
        update(msg, &mut model, &widgets);
    });

    gtk::main();
}