#![cfg_attr(feature = "use_impl_trait", feature(conservative_impl_trait))]
#![warn(missing_docs, trivial_casts, trivial_numeric_casts, unused_extern_crates, unused_import_braces, unused_qualifications, unused_results)]
extern crate futures;
extern crate glib;
extern crate glib_itc;
extern crate gobject_sys;
extern crate gtk;
extern crate gtk_sys;
#[macro_use]
extern crate log;
extern crate relm_core;
mod component;
mod container;
pub mod gtk_ext;
mod macros;
mod stream;
mod widget;
use std::sync::{Arc, Mutex};
use std::time::SystemTime;
use futures::{Future, Stream};
use glib::Continue;
#[doc(hidden)]
pub use glib::object::Downcast;
#[doc(hidden)]
pub use glib::translate::{FromGlibPtrNone, ToGlib};
use glib_itc::{Receiver, channel};
#[doc(hidden)]
pub use gobject_sys::g_object_new;
use relm_core::Core;
#[doc(hidden)]
pub use relm_core::{EventStream, Handle, Remote};
use component::Comp;
pub use container::{Container, ContainerWidget, RelmContainer};
pub use component::Component;
use stream::ToStream;
pub use widget::Widget;
#[macro_export]
macro_rules! impl_widget {
($($tt:tt)*) => {
()
};
}
macro_rules! relm_connect {
($_self:expr, $to_stream:expr, $success_callback:expr, $failure_callback:expr) => {{
let event_stream = $_self.stream.clone();
let fail_event_stream = $_self.stream.clone();
let stream = $to_stream.to_stream();
stream.map_err(move |error| {
fail_event_stream.emit($failure_callback(error));
()
})
.for_each(move |result| {
event_stream.emit($success_callback(result));
Ok::<(), STREAM::Error>(())
}
.map_err(|_| ()))
}};
}
macro_rules! relm_connect_ignore {
($_self:expr, $to_stream:expr, $success_callback:expr) => {{
let event_stream = $_self.stream.clone();
let stream = $to_stream.to_stream();
stream.map_err(|_| ())
.for_each(move |result| {
event_stream.emit($success_callback(result));
Ok::<(), STREAM::Error>(())
}
.map_err(|_| ()))
}};
}
#[macro_export]
macro_rules! relm_widget {
($($tts:tt)*) => {
mod __relm_gen_private {
use super::*;
#[derive(Widget)]
struct __RelmPrivateWidget {
widget: impl_widget! {
$($tts)*
}
}
}
use_impl_self_type!($($tts)*);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! use_impl_self_type {
(impl Widget for $self_type:ident { $($tts:tt)* }) => {
pub use __relm_gen_private::$self_type;
};
}
pub struct Relm<MSG: Clone + DisplayVariant> {
handle: Handle,
stream: EventStream<MSG>,
}
impl<MSG: Clone + DisplayVariant + Send + 'static> Relm<MSG> {
#[cfg(feature = "use_impl_trait")]
pub fn connect<CALLBACK, FAILCALLBACK, STREAM, TOSTREAM>(&self, to_stream: TOSTREAM, success_callback: CALLBACK, failure_callback: FAILCALLBACK) -> impl Future<Item=(), Error=()>
where CALLBACK: Fn(STREAM::Item) -> MSG + 'static,
FAILCALLBACK: Fn(STREAM::Error) -> MSG + 'static,
STREAM: Stream + 'static,
TOSTREAM: ToStream<STREAM, Item=STREAM::Item, Error=STREAM::Error> + 'static,
{
relm_connect!(self, to_stream, success_callback, failure_callback)
}
#[cfg(not(feature = "use_impl_trait"))]
pub fn connect<CALLBACK, FAILCALLBACK, STREAM, TOSTREAM>(&self, to_stream: TOSTREAM, success_callback: CALLBACK, failure_callback: FAILCALLBACK) -> Box<Future<Item=(), Error=()>>
where CALLBACK: Fn(STREAM::Item) -> MSG + 'static,
FAILCALLBACK: Fn(STREAM::Error) -> MSG + 'static,
STREAM: Stream + 'static,
TOSTREAM: ToStream<STREAM, Item=STREAM::Item, Error=STREAM::Error> + 'static,
{
let future = relm_connect!(self, to_stream, success_callback, failure_callback);
Box::new(future)
}
#[cfg(feature = "use_impl_trait")]
pub fn connect_ignore_err<CALLBACK, STREAM, TOSTREAM>(&self, to_stream: TOSTREAM, success_callback: CALLBACK) -> impl Future<Item=(), Error=()>
where CALLBACK: Fn(STREAM::Item) -> MSG + 'static,
STREAM: Stream + 'static,
TOSTREAM: ToStream<STREAM, Item=STREAM::Item, Error=STREAM::Error> + 'static,
{
relm_connect_ignore!(self, to_stream, success_callback)
}
#[cfg(not(feature = "use_impl_trait"))]
pub fn connect_ignore_err<CALLBACK, STREAM, TOSTREAM>(&self, to_stream: TOSTREAM, success_callback: CALLBACK) -> Box<Future<Item=(), Error=()>>
where CALLBACK: Fn(STREAM::Item) -> MSG + 'static,
STREAM: Stream + 'static,
TOSTREAM: ToStream<STREAM, Item=STREAM::Item, Error=STREAM::Error> + 'static,
{
Box::new(relm_connect_ignore!(self, to_stream, success_callback))
}
pub fn connect_exec<CALLBACK, FAILCALLBACK, STREAM, TOSTREAM>(&self, to_stream: TOSTREAM, callback: CALLBACK, failure_callback: FAILCALLBACK)
where CALLBACK: Fn(STREAM::Item) -> MSG + 'static,
FAILCALLBACK: Fn(STREAM::Error) -> MSG + 'static,
STREAM: Stream + 'static,
TOSTREAM: ToStream<STREAM, Item=STREAM::Item, Error=STREAM::Error> + 'static,
{
self.exec(self.connect(to_stream, callback, failure_callback));
}
pub fn connect_exec_ignore_err<CALLBACK, STREAM, TOSTREAM>(&self, to_stream: TOSTREAM, callback: CALLBACK)
where CALLBACK: Fn(STREAM::Item) -> MSG + 'static,
STREAM: Stream + 'static,
TOSTREAM: ToStream<STREAM, Item=STREAM::Item, Error=STREAM::Error> + 'static,
{
self.exec(self.connect_ignore_err(to_stream, callback));
}
pub fn exec<FUTURE: Future<Item=(), Error=()> + 'static>(&self, future: FUTURE) {
self.handle.spawn(future);
}
pub fn handle(&self) -> &Handle {
&self.handle
}
pub fn stream(&self) -> &EventStream<MSG> {
&self.stream
}
}
#[derive(Clone)]
pub struct RemoteRelm<WIDGET: Widget> {
model: Arc<Mutex<WIDGET::Model>>,
remote: Remote,
stream: EventStream<WIDGET::Msg>,
}
impl<WIDGET: Widget> RemoteRelm<WIDGET> {
pub fn model(&self) -> &Arc<Mutex<WIDGET::Model>> {
&self.model
}
pub fn stream(&self) -> &EventStream<WIDGET::Msg> {
&self.stream
}
}
fn create_widget_test<WIDGET>(remote: &Remote, model_param: WIDGET::ModelParam) -> Component<WIDGET>
where WIDGET: Widget + Clone + 'static,
WIDGET::Model: Clone + Send,
WIDGET::Msg: Clone + DisplayVariant + Send + 'static,
{
let component = create_widget(remote, model_param);
init_component::<WIDGET>(&component, remote);
Component::new(component)
}
pub fn create_component<CHILDWIDGET, WIDGET>(relm: &RemoteRelm<WIDGET>,
model_param: CHILDWIDGET::ModelParam) -> Component<CHILDWIDGET>
where CHILDWIDGET: Widget + 'static,
CHILDWIDGET::Model: Clone + Send,
CHILDWIDGET::Msg: Clone + DisplayVariant + Send + 'static,
WIDGET: Widget,
{
let component = create_widget::<CHILDWIDGET>(&relm.remote, model_param);
init_component::<CHILDWIDGET>(&component, &relm.remote);
Component::new(component)
}
fn create_widget<WIDGET>(remote: &Remote, model_param: WIDGET::ModelParam) -> Comp<WIDGET>
where WIDGET: Widget + 'static,
WIDGET::Msg: Clone + DisplayVariant + 'static,
{
let (sender, mut receiver) = channel();
let stream = EventStream::new(Arc::new(Mutex::new(sender)));
let (widget, model) = {
let model = Arc::new(Mutex::new(WIDGET::model(model_param)));
let relm = RemoteRelm {
model: model,
remote: remote.clone(),
stream: stream.clone(),
};
let view = {
let model_guard = relm.model.lock().unwrap();
WIDGET::view(&relm, &*model_guard)
};
(view, relm.model)
};
widget.init_view();
{
let mut widget = widget.clone();
let stream = stream.clone();
let model = model.clone();
receiver.connect_recv(move || {
if let Some(event) = stream.pop_ui_events() {
let mut model = model.lock().unwrap();
update_widget(&mut widget, event, &mut *model);
}
Continue(true)
});
}
Comp {
model: model,
_receiver: Arc::new(receiver),
stream: stream,
widget: widget,
}
}
fn init_component<WIDGET>(component: &Comp<WIDGET>, remote: &Remote)
where WIDGET: Widget + 'static,
WIDGET::Model: Send,
WIDGET::Msg: Clone + DisplayVariant + Send + 'static,
{
let stream = component.stream.clone();
let model = component.model.clone();
remote.spawn(move |handle| {
let relm = Relm {
handle: handle.clone(),
stream: stream.clone(),
};
WIDGET::subscriptions(&relm);
let event_future = stream.for_each(move |event| {
let mut model = model.lock().unwrap();
WIDGET::update_command(&relm, event, &mut *model);
Ok(())
});
handle.spawn(event_future);
Ok(())
});
}
fn init_gtk() {
let mut argc = 0;
unsafe {
gtk_sys::gtk_init(&mut argc, std::ptr::null_mut());
gtk::set_initialized();
}
}
pub fn init_test<WIDGET>(model_param: WIDGET::ModelParam) -> Result<Component<WIDGET>, ()>
where WIDGET: Widget + Clone + 'static,
WIDGET::Model: Clone + Send,
WIDGET::Msg: Clone + DisplayVariant + Send + 'static
{
init_gtk();
let remote = Core::run();
let component = create_widget_test::<WIDGET>(&remote, model_param);
Ok(component)
}
fn init<WIDGET>(model_param: WIDGET::ModelParam) -> Result<Component<WIDGET>, ()>
where WIDGET: Widget + 'static,
WIDGET::Model: Clone + Send,
WIDGET::Msg: Clone + DisplayVariant + Send + 'static
{
gtk::init()?;
let remote = Core::run();
let component = create_widget::<WIDGET>(&remote, model_param);
init_component::<WIDGET>(&component, &remote);
Ok(Component::new(component))
}
pub fn run<WIDGET>(model_param: WIDGET::ModelParam) -> Result<(), ()>
where WIDGET: Widget + 'static,
WIDGET::Model: Clone + Send,
WIDGET::ModelParam: Default,
WIDGET::Msg: Send,
{
let _component = init::<WIDGET>(model_param)?;
gtk::main();
Ok(())
}
fn update_widget<WIDGET>(widget: &mut WIDGET, event: WIDGET::Msg, model: &mut WIDGET::Model)
where WIDGET: Widget,
{
if cfg!(debug_assertions) {
let time = SystemTime::now();
let debug = event.display_variant();
let debug =
if debug.len() > 100 {
format!("{}…", &debug[..100])
}
else {
debug.to_string()
};
widget.update(event, model);
if let Ok(duration) = time.elapsed() {
let ms = duration.subsec_nanos() as u64 / 1_000_000 + duration.as_secs() * 1000;
if ms >= 200 {
warn!("The update function was slow to execute for message {}: {}ms", debug, ms);
}
}
}
else {
widget.update(event, model)
}
}
pub trait DisplayVariant {
fn display_variant(&self) -> &'static str;
}
impl DisplayVariant for () {
fn display_variant(&self) -> &'static str {
""
}
}