#![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 cairo;
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;
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 gtk::{ContainerExt, IsA, Object, WidgetExt};
use relm_core::Core;
#[doc(hidden)]
pub use relm_core::{EventStream, Handle, Remote};
use self::stream::ToStream;
pub use self::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<$msg_type:ty> for $self_type:ident { $($tts:tt)* }) => {
pub use __relm_gen_private::$self_type;
};
}
#[must_use]
#[derive(Clone)]
pub struct Component<WIDGET: Widget> {
model: Arc<Mutex<WIDGET::Model>>,
_receiver: Arc<Receiver>,
stream: EventStream<WIDGET::Msg>,
widget: WIDGET,
}
impl<WIDGET: Widget> Component<WIDGET> {
pub fn stream(&self) -> &EventStream<WIDGET::Msg> {
&self.stream
}
pub fn widget(&self) -> &WIDGET {
&self.widget
}
}
impl<WIDGET: Widget> Drop for Component<WIDGET> {
fn drop(&mut self) {
let _ = self.stream.close();
}
}
impl<WIDGET: Widget> ContainerExt for Component<WIDGET>
where WIDGET::Container: ContainerExt,
{
fn add<T: IsA<gtk::Widget>>(&self, widget: &T) { self.widget.container().add(widget) }
fn check_resize(&self) { self.widget.container().check_resize() }
fn child_notify<T: IsA<gtk::Widget>>(&self, child: &T, child_property: &str) { self.widget.container().child_notify(child, child_property) }
fn child_type(&self) -> gtk::Type { self.widget.container().child_type() }
fn get_border_width(&self) -> u32 { self.widget.container().get_border_width() }
fn get_children(&self) -> Vec<gtk::Widget> { self.widget.container().get_children() }
fn get_focus_child(&self) -> Option<gtk::Widget> { self.widget.container().get_focus_child() }
fn get_focus_hadjustment(&self) -> Option<gtk::Adjustment> { self.widget.container().get_focus_hadjustment() }
fn get_focus_vadjustment(&self) -> Option<gtk::Adjustment> { self.widget.container().get_focus_vadjustment() }
fn get_resize_mode(&self) -> gtk::ResizeMode { self.widget.container().get_resize_mode() }
fn propagate_draw<T: IsA<gtk::Widget>>(&self, child: &T, cr: &cairo::Context) { self.widget.container().propagate_draw(child, cr) }
fn remove<T: IsA<gtk::Widget>>(&self, widget: &T) { self.widget.container().remove(widget) }
fn resize_children(&self) { self.widget.container().resize_children() }
fn set_border_width(&self, border_width: u32) { self.widget.container().set_border_width(border_width) }
fn set_focus_chain(&self, focusable_widgets: &[gtk::Widget]) { self.widget.container().set_focus_chain(focusable_widgets) }
fn set_focus_child<T: IsA<gtk::Widget>>(&self, child: Option<&T>) { self.widget.container().set_focus_child(child) }
fn set_focus_hadjustment(&self, adjustment: >k::Adjustment) { self.widget.container().set_focus_hadjustment(adjustment) }
fn set_focus_vadjustment(&self, adjustment: >k::Adjustment) { self.widget.container().set_focus_vadjustment(adjustment) }
fn set_reallocate_redraws(&self, needs_redraws: bool) { self.widget.container().set_reallocate_redraws(needs_redraws) }
fn set_resize_mode(&self, resize_mode: gtk::ResizeMode) { self.widget.container().set_resize_mode(resize_mode) }
fn unset_focus_chain(&self) { self.widget.container().unset_focus_chain() }
fn set_property_child(&self, child: Option<>k::Widget>) { self.widget.container().set_property_child(child) }
fn connect_add<F: Fn(&Self, >k::Widget) + 'static>(&self, _f: F) -> u64 { unimplemented!() }
fn connect_check_resize<F: Fn(&Self) + 'static>(&self, _f: F) -> u64 { unimplemented!() }
fn connect_remove<F: Fn(&Self, >k::Widget) + 'static>(&self, _f: F) -> u64 { unimplemented!() }
fn connect_set_focus_child<F: Fn(&Self, >k::Widget) + 'static>(&self, _f: F) -> u64 { unimplemented!() }
}
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<MSG: Clone + DisplayVariant> {
remote: Remote,
stream: EventStream<MSG>,
}
impl<'a, MSG: Clone + DisplayVariant> RemoteRelm<MSG> {
pub fn stream(&self) -> &EventStream<MSG> {
&self.stream
}
}
fn create_widget_test<WIDGET>(remote: &Remote) -> Component<WIDGET>
where WIDGET: Widget + Clone + 'static,
WIDGET::Msg: Clone + DisplayVariant + 'static,
{
let (sender, mut receiver) = channel();
let stream = EventStream::new(Arc::new(sender));
let (widget, model) = {
let relm = RemoteRelm {
remote: remote.clone(),
stream: stream.clone(),
};
let model = WIDGET::model();
(WIDGET::view(relm, &model), model)
};
widget.init_view();
let model = Arc::new(Mutex::new(model));
{
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)
});
}
Component {
model: model,
_receiver: Arc::new(receiver),
stream: stream,
widget: widget,
}
}
fn create_widget<WIDGET>(remote: &Remote) -> Component<WIDGET>
where WIDGET: Widget + 'static,
WIDGET::Msg: Clone + DisplayVariant + 'static,
{
let (sender, mut receiver) = channel();
let stream = EventStream::new(Arc::new(sender));
let (widget, model) = {
let relm = RemoteRelm {
remote: remote.clone(),
stream: stream.clone(),
};
let model = WIDGET::model();
(WIDGET::view(relm, &model), model)
};
widget.init_view();
let model = Arc::new(Mutex::new(model));
{
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)
});
}
Component {
model: model,
_receiver: Arc::new(receiver),
stream: stream,
widget: widget,
}
}
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>() -> Result<Component<WIDGET>, ()>
where WIDGET: Widget + Clone + 'static,
WIDGET::Model: Send,
WIDGET::Msg: Clone + DisplayVariant + Send + 'static
{
init_gtk();
let remote = Core::run();
let component = create_widget_test::<WIDGET>(&remote);
init_component::<WIDGET>(&component, &remote);
Ok(component)
}
fn init<WIDGET>() -> Result<Component<WIDGET>, ()>
where WIDGET: Widget + 'static,
WIDGET::Model: Send,
WIDGET::Msg: Clone + DisplayVariant + Send + 'static
{
gtk::init()?;
let remote = Core::run();
let component = create_widget::<WIDGET>(&remote);
init_component::<WIDGET>(&component, &remote);
Ok(component)
}
fn init_component<WIDGET>(component: &Component<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(())
});
}
pub fn run<WIDGET>() -> Result<(), ()>
where WIDGET: Widget + 'static,
WIDGET::Model: Send,
WIDGET::Msg: Send,
{
let _component = init::<WIDGET>()?;
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 ContainerWidget
where Self: Sized,
{
fn add_widget<WIDGET, MSG>(&self, relm: &RemoteRelm<MSG>) -> Component<WIDGET>
where MSG: Clone + DisplayVariant + Send + 'static,
WIDGET: Widget + 'static,
WIDGET::Container: IsA<Object> + WidgetExt,
WIDGET::Model: Send,
WIDGET::Msg: Clone + DisplayVariant + Send + 'static;
fn remove_widget<WIDGET>(&self, component: Component<WIDGET>)
where WIDGET: Widget,
WIDGET::Container: IsA<gtk::Widget>;
}
impl<W: Clone + ContainerExt + IsA<gtk::Widget> + IsA<Object>> ContainerWidget for W {
fn add_widget<CHILDWIDGET, MSG>(&self, relm: &RemoteRelm<MSG>) -> Component<CHILDWIDGET>
where MSG: Clone + DisplayVariant + Send + 'static,
CHILDWIDGET: Widget + 'static,
CHILDWIDGET::Container: IsA<Object> + WidgetExt,
CHILDWIDGET::Model: Send,
CHILDWIDGET::Msg: Clone + DisplayVariant + Send + 'static,
{
let component = create_widget::<CHILDWIDGET>(&relm.remote);
self.add(component.widget.container());
component.widget.on_add(self.clone());
init_component::<CHILDWIDGET>(&component, &relm.remote);
component
}
fn remove_widget<WIDGET>(&self, component: Component<WIDGET>)
where WIDGET: Widget,
WIDGET::Container: IsA<gtk::Widget>,
{
self.remove(component.widget.container());
}
}
impl<WIDGET: ContainerExt + IsA<gtk::Widget> + IsA<Object> + Widget> ContainerWidget for Component<WIDGET> {
fn add_widget<CHILDWIDGET, MSG>(&self, relm: &RemoteRelm<MSG>) -> Component<CHILDWIDGET>
where MSG: Clone + DisplayVariant + Send + 'static,
CHILDWIDGET: Widget + 'static,
CHILDWIDGET::Container: IsA<Object> + WidgetExt,
CHILDWIDGET::Model: Send,
CHILDWIDGET::Msg: Clone + DisplayVariant + Send + 'static,
{
let component = create_widget::<CHILDWIDGET>(&relm.remote);
self.widget.add(component.widget.container());
component.widget.on_add(self.widget.clone());
init_component::<CHILDWIDGET>(&component, &relm.remote);
component
}
fn remove_widget<CHILDWIDGET>(&self, component: Component<CHILDWIDGET>)
where CHILDWIDGET: Widget,
CHILDWIDGET::Container: IsA<gtk::Widget>,
{
self.widget.remove(component.widget.container());
}
}
pub trait DisplayVariant {
fn display_variant(&self) -> &'static str;
}