Permalink
Browse files

Add the ability to use message in relm widgets view

  • Loading branch information...
antoyo committed Jun 11, 2017
1 parent c5461e8 commit 15204e9b41be8db88cc861a236997bbc56ad4b66
@@ -116,9 +116,9 @@ impl Widget for Win {
label: "Reset",
}
Text {
// TODO: set the property by sending a message.
//self.text.stream().emit(SetText(self.model.text.clone()));
//SetText: self.model.text.clone(),
// Send the message SetText(self.model.text.clone()) at initialization and when
// the model attribute is updated.
SetText: self.model.text.clone(),
},
},
delete_event(_, _) => (Quit, Inhibit(false)),
@@ -29,15 +29,15 @@ use syn::fold::{Folder, noop_fold_expr};
use syn::Stmt::Semi;
use syn::Unsafety::Normal;
use super::PropertyModelMap;
use super::{MsgModelMap, PropertyModelMap};
macro_rules! fold_assign {
($_self:expr, $lhs:expr, $new_assign:expr) => {{
let mut statements = vec![];
let new_statements =
if let Field(ref field_expr, ref ident) = $lhs.node {
if is_model_path(field_expr) {
Some(create_stmts(ident, $_self.map))
Some(create_stmts(ident, $_self.property_map, $_self.msg_map))
}
else {
None
@@ -58,13 +58,15 @@ macro_rules! fold_assign {
}
pub struct Adder<'a> {
map: &'a PropertyModelMap,
msg_map: &'a MsgModelMap,
property_map: &'a PropertyModelMap,
}
impl<'a> Adder<'a> {
pub fn new(map: &'a PropertyModelMap) -> Self {
pub fn new(property_map: &'a PropertyModelMap, msg_map: &'a MsgModelMap) -> Self {
Adder {
map: map,
msg_map,
property_map,
}
}
}
@@ -85,6 +87,13 @@ impl<'a> Folder for Adder<'a> {
}
}
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct Message {
pub expr: Expr,
pub name: String,
pub widget_name: Ident,
}
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct Property {
pub expr: Expr,
@@ -93,9 +102,37 @@ pub struct Property {
pub widget_name: Ident,
}
fn create_stmts(ident: &Ident, map: &PropertyModelMap) -> Vec<Stmt> {
fn create_stmts(ident: &Ident, property_map: &PropertyModelMap, msg_map: &MsgModelMap) -> Vec<Stmt> {
let mut stmts = vec![];
stmts.append(&mut create_stmts_for_props(ident, property_map));
stmts.append(&mut create_stmts_for_msgs(ident, msg_map));
stmts
}
fn create_stmts_for_msgs(ident: &Ident, msg_map: &MsgModelMap) -> Vec<Stmt> {
let mut stmts = vec![];
if let Some(messages) = msg_map.get(ident) {
for msg in messages {
let widget_name = &msg.widget_name;
let mut value = Tokens::new();
value.append_all(&[&msg.expr]);
let variant = Ident::new(msg.name.as_str());
let stmt = quote! {
{ self.#widget_name.stream().emit(#variant(#value)); }
};
let expr = parse_expr(&stmt.parse::<String>().expect("parse::<String>() in create_stmts"))
.expect("parse_expr() in create_stmts");
if let ExprKind::Block(_, ref block) = expr.node {
stmts.push(block.stmts[0].clone());
}
}
}
stmts
}
fn create_stmts_for_props(ident: &Ident, property_map: &PropertyModelMap) -> Vec<Stmt> {
let mut stmts = vec![];
if let Some(properties) = map.get(ident) {
if let Some(properties) = property_map.get(ident) {
for property in properties {
let widget_name = &property.widget_name;
let prop_name = Ident::new(format!("set_{}", property.name));
View
@@ -39,29 +39,6 @@ use super::{Driver, MODEL_IDENT};
use self::WidgetType::*;
macro_rules! gen_set_prop_calls {
($widget:expr, $ident:expr, $model_ident:expr) => {{
let ident = $ident;
let mut properties = vec![];
let mut visible_properties = vec![];
for (key, value) in &$widget.properties {
let mut remover = Transformer::new($model_ident);
let new_value = remover.fold_expr(value.clone());
let property_func = Ident::new(format!("set_{}", key));
let property = quote! {
#ident.#property_func(#new_value);
};
if key == "visible" {
visible_properties.push(property);
}
else {
properties.push(property);
}
}
(properties, visible_properties)
}};
}
macro_rules! set_container {
($_self:expr, $widget:expr, $widget_name:expr, $widget_type:expr) => {
if let Some(ref container_type) = $widget.container_type {
@@ -299,6 +276,26 @@ impl<'a> Generator<'a> {
}
}
fn gtk_set_prop_calls(&self, widget: &Widget, ident: Tokens) -> (Vec<Tokens>, Vec<Tokens>) {
let mut properties = vec![];
let mut visible_properties = vec![];
for (key, value) in &widget.properties {
let mut remover = Transformer::new(MODEL_IDENT);
let new_value = remover.fold_expr(value.clone());
let property_func = Ident::new(format!("set_{}", key));
let property = quote! {
#ident.#property_func(#new_value);
};
if key == "visible" {
visible_properties.push(property);
}
else {
properties.push(property);
}
}
(properties, visible_properties)
}
fn gtk_widget(&mut self, widget: &Widget, gtk_widget: &GtkWidget, parent: Option<&Ident>,
parent_widget_type: WidgetType) -> Tokens
{
@@ -320,7 +317,7 @@ impl<'a> Generator<'a> {
let add_child_or_show_all = self.add_child_or_show_all(widget, parent, parent_widget_type);
let ident = quote! { #widget_name };
let (properties, visible_properties) = gen_set_prop_calls!(widget, ident, MODEL_IDENT);
let (properties, visible_properties) = self.gtk_set_prop_calls(widget, ident);
let child_properties = gen_set_child_prop_calls(widget, parent, parent_widget_type, IsGtk);
quote! {
@@ -350,21 +347,38 @@ impl<'a> Generator<'a> {
.map(|child| self.widget(child, Some(widget_name), IsRelm))
.collect();
let ident = quote! { #widget_name.widget() };
let (mut properties, mut visible_properties) = gen_set_prop_calls!(widget, ident, MODEL_IDENT);
let (mut properties, mut visible_properties) = self.gtk_set_prop_calls(widget, ident);
self.properties.append(&mut properties);
self.properties.append(&mut visible_properties);
let add_or_create_widget = self.add_or_create_widget(
parent, parent_widget_type, widget_name, widget_type_ident, &widget.init_parameters, widget.is_container);
let child_properties = gen_set_child_prop_calls(widget, parent, parent_widget_type, IsRelm);
let messages = self.messages(widget, relm_widget);
quote! {
#add_or_create_widget
#messages
#(#children)*
#(#child_properties)*
}
}
fn messages(&self, widget: &Widget, relm_widget: &RelmWidget) -> Tokens {
let mut tokens = quote! {};
let name = &widget.name;
for (variant, value) in &relm_widget.messages {
let mut remover = Transformer::new(MODEL_IDENT);
let value = remover.fold_expr(value.clone());
let variant = Ident::new(variant.as_str());
tokens = quote! {
#tokens
#name.stream().emit(#variant(#value));
};
}
tokens
}
fn widget(&mut self, widget: &Widget, parent: Option<&Ident>, parent_widget_type: WidgetType) -> Tokens {
match widget.widget {
Gtk(ref gtk_widget) => self.gtk_widget(widget, gtk_widget, parent, parent_widget_type),
View
@@ -41,7 +41,7 @@ mod walker;
use std::collections::{HashMap, HashSet};
use adder::{Adder, Property};
use adder::{Adder, Message, Property};
use gen::gen;
pub use gen::gen_where_clause;
use parser::EitherWidget::{Gtk, Relm};
@@ -71,6 +71,7 @@ use walker::ModelVariableVisitor;
const MODEL_IDENT: &str = "__relm_model";
type MsgModelMap = HashMap<Ident, HashSet<Message>>;
type PropertyModelMap = HashMap<Ident, HashSet<Property>>;
#[derive(Debug)]
@@ -79,6 +80,7 @@ pub struct Driver {
generic_types: Option<Generics>,
model_type: Option<ImplItem>,
model_param_type: Option<ImplItem>,
msg_model_map: Option<MsgModelMap>,
msg_type: Option<ImplItem>,
other_methods: Vec<ImplItem>,
properties_model_map: Option<PropertyModelMap>,
@@ -98,6 +100,7 @@ pub struct Driver {
struct View {
container_impl: Tokens,
item: ImplItem,
msg_model_map: MsgModelMap,
properties_model_map: PropertyModelMap,
relm_widgets: HashMap<Ident, Path>,
widget: Widget,
@@ -110,6 +113,7 @@ impl Driver {
generic_types: None,
model_type: None,
model_param_type: None,
msg_model_map: None,
msg_type: None,
other_methods: vec![],
properties_model_map: None,
@@ -129,7 +133,9 @@ impl Driver {
fn add_set_property_to_method(&self, func: &mut ImplItem) {
if let Method(_, ref mut block) = func.node {
let mut adder = Adder::new(self.properties_model_map.as_ref().expect("update method"));
let msg_map = self.msg_model_map.as_ref().expect("update method");
let property_map = self.properties_model_map.as_ref().expect("update method");
let mut adder = Adder::new(property_map, msg_map);
*block = adder.fold_block(block.clone());
}
}
@@ -219,6 +225,7 @@ impl Driver {
if let Some(on_add) = gen_set_child_prop_calls(&view.widget) {
new_items.push(on_add);
}
self.msg_model_map = Some(view.msg_model_map);
self.properties_model_map = Some(view.properties_model_map);
new_items.push(view.item);
self.widgets.insert(self.root_widget.clone().expect("root widget"),
@@ -351,8 +358,10 @@ impl Driver {
widget.relm_name = Some(typ.clone());
}
self.widget_parent_id = widget.parent_id.clone();
let mut msg_model_map = HashMap::new();
let mut properties_model_map = HashMap::new();
get_properties_model_map(&widget, &mut properties_model_map);
get_msg_model_map(&widget, &mut msg_model_map);
self.add_widgets(&widget, &properties_model_map);
let (view, relm_widgets, container_impl) = gen(name, &widget, self);
let model_ident = Ident::new(MODEL_IDENT);
@@ -365,6 +374,7 @@ impl Driver {
View {
container_impl,
item,
msg_model_map,
properties_model_map,
relm_widgets,
widget,
@@ -468,6 +478,34 @@ macro_rules! get_map {
}};
}
fn get_msg_model_map(widget: &Widget, map: &mut MsgModelMap) {
match widget.widget {
Gtk(_) => {
for child in &widget.children {
get_msg_model_map(child, map);
}
},
Relm(ref relm_widget) => {
for (name, expr) in &relm_widget.messages {
let mut visitor = ModelVariableVisitor::new();
visitor.visit_expr(&expr);
let model_variables = visitor.idents;
for var in model_variables {
let set = map.entry(var).or_insert_with(HashSet::new);
set.insert(Message {
expr: expr.clone(),
name: name.clone(),
widget_name: widget.name.clone(),
});
}
}
for child in &widget.children {
get_msg_model_map(child, map);
}
},
}
}
/*
* The map maps model variable name to a vector of tuples (widget name, property name).
*/
@@ -86,8 +86,8 @@ impl Event {
#[derive(Debug)]
pub struct Widget {
pub child_events: ChildEvents,
pub child_properties: HashMap<String, Expr>,
pub child_events: ChildEvents, // TODO: does it make sense for a relm widget?
pub child_properties: HashMap<String, Expr>, // TODO: does it make sense for a relm widget?
pub children: Vec<Widget>,
pub container_type: Option<Option<String>>, // TODO: Why two Options?
pub init_parameters: Vec<Expr>,
@@ -172,13 +172,15 @@ impl GtkWidget {
pub struct RelmWidget {
pub events: HashMap<String, Vec<Event>>,
pub gtk_events: HashMap<String, Event>,
pub messages: HashMap<String, Expr>,
}
impl RelmWidget {
fn new() -> Self {
RelmWidget {
events: HashMap::new(),
gtk_events: HashMap::new(),
messages: HashMap::new(),
}
}
}
@@ -640,7 +642,16 @@ fn parse_relm_widget(tokens: &[TokenTree]) -> (Widget, &[TokenTree]) {
tts = &tts[1..];
match tts[0] {
Token(Colon) => {
tts = parse_value_or_child_properties(tts, ident, &mut child_properties, &mut properties);
let properties_or_signals =
if ident.chars().next().map(|char| char.is_lowercase()) == Some(false) {
// Uppercase is a msg to send.
&mut relm_widget.messages
}
else {
// Lowercase is a gtk property.
&mut properties
};
tts = parse_value_or_child_properties(tts, ident, &mut child_properties, properties_or_signals);
},
Token(Dot) => {
let child_name = ident;

0 comments on commit 15204e9

Please sign in to comment.