Skip to content

Commit 15204e9

Browse files
committed
Add the ability to use message in relm widgets view
1 parent c5461e8 commit 15204e9

File tree

5 files changed

+140
-40
lines changed

5 files changed

+140
-40
lines changed

examples/text-fields-prop-attribute.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,9 @@ impl Widget for Win {
116116
label: "Reset",
117117
}
118118
Text {
119-
// TODO: set the property by sending a message.
120-
//self.text.stream().emit(SetText(self.model.text.clone()));
121-
//SetText: self.model.text.clone(),
119+
// Send the message SetText(self.model.text.clone()) at initialization and when
120+
// the model attribute is updated.
121+
SetText: self.model.text.clone(),
122122
},
123123
},
124124
delete_event(_, _) => (Quit, Inhibit(false)),

relm-gen-widget/src/adder.rs

+44-7
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,15 @@ use syn::fold::{Folder, noop_fold_expr};
2929
use syn::Stmt::Semi;
3030
use syn::Unsafety::Normal;
3131

32-
use super::PropertyModelMap;
32+
use super::{MsgModelMap, PropertyModelMap};
3333

3434
macro_rules! fold_assign {
3535
($_self:expr, $lhs:expr, $new_assign:expr) => {{
3636
let mut statements = vec![];
3737
let new_statements =
3838
if let Field(ref field_expr, ref ident) = $lhs.node {
3939
if is_model_path(field_expr) {
40-
Some(create_stmts(ident, $_self.map))
40+
Some(create_stmts(ident, $_self.property_map, $_self.msg_map))
4141
}
4242
else {
4343
None
@@ -58,13 +58,15 @@ macro_rules! fold_assign {
5858
}
5959

6060
pub struct Adder<'a> {
61-
map: &'a PropertyModelMap,
61+
msg_map: &'a MsgModelMap,
62+
property_map: &'a PropertyModelMap,
6263
}
6364

6465
impl<'a> Adder<'a> {
65-
pub fn new(map: &'a PropertyModelMap) -> Self {
66+
pub fn new(property_map: &'a PropertyModelMap, msg_map: &'a MsgModelMap) -> Self {
6667
Adder {
67-
map: map,
68+
msg_map,
69+
property_map,
6870
}
6971
}
7072
}
@@ -85,6 +87,13 @@ impl<'a> Folder for Adder<'a> {
8587
}
8688
}
8789

90+
#[derive(Debug, Eq, Hash, PartialEq)]
91+
pub struct Message {
92+
pub expr: Expr,
93+
pub name: String,
94+
pub widget_name: Ident,
95+
}
96+
8897
#[derive(Debug, Eq, Hash, PartialEq)]
8998
pub struct Property {
9099
pub expr: Expr,
@@ -93,9 +102,37 @@ pub struct Property {
93102
pub widget_name: Ident,
94103
}
95104

96-
fn create_stmts(ident: &Ident, map: &PropertyModelMap) -> Vec<Stmt> {
105+
fn create_stmts(ident: &Ident, property_map: &PropertyModelMap, msg_map: &MsgModelMap) -> Vec<Stmt> {
106+
let mut stmts = vec![];
107+
stmts.append(&mut create_stmts_for_props(ident, property_map));
108+
stmts.append(&mut create_stmts_for_msgs(ident, msg_map));
109+
stmts
110+
}
111+
112+
fn create_stmts_for_msgs(ident: &Ident, msg_map: &MsgModelMap) -> Vec<Stmt> {
113+
let mut stmts = vec![];
114+
if let Some(messages) = msg_map.get(ident) {
115+
for msg in messages {
116+
let widget_name = &msg.widget_name;
117+
let mut value = Tokens::new();
118+
value.append_all(&[&msg.expr]);
119+
let variant = Ident::new(msg.name.as_str());
120+
let stmt = quote! {
121+
{ self.#widget_name.stream().emit(#variant(#value)); }
122+
};
123+
let expr = parse_expr(&stmt.parse::<String>().expect("parse::<String>() in create_stmts"))
124+
.expect("parse_expr() in create_stmts");
125+
if let ExprKind::Block(_, ref block) = expr.node {
126+
stmts.push(block.stmts[0].clone());
127+
}
128+
}
129+
}
130+
stmts
131+
}
132+
133+
fn create_stmts_for_props(ident: &Ident, property_map: &PropertyModelMap) -> Vec<Stmt> {
97134
let mut stmts = vec![];
98-
if let Some(properties) = map.get(ident) {
135+
if let Some(properties) = property_map.get(ident) {
99136
for property in properties {
100137
let widget_name = &property.widget_name;
101138
let prop_name = Ident::new(format!("set_{}", property.name));

relm-gen-widget/src/gen.rs

+39-25
Original file line numberDiff line numberDiff line change
@@ -39,29 +39,6 @@ use super::{Driver, MODEL_IDENT};
3939

4040
use self::WidgetType::*;
4141

42-
macro_rules! gen_set_prop_calls {
43-
($widget:expr, $ident:expr, $model_ident:expr) => {{
44-
let ident = $ident;
45-
let mut properties = vec![];
46-
let mut visible_properties = vec![];
47-
for (key, value) in &$widget.properties {
48-
let mut remover = Transformer::new($model_ident);
49-
let new_value = remover.fold_expr(value.clone());
50-
let property_func = Ident::new(format!("set_{}", key));
51-
let property = quote! {
52-
#ident.#property_func(#new_value);
53-
};
54-
if key == "visible" {
55-
visible_properties.push(property);
56-
}
57-
else {
58-
properties.push(property);
59-
}
60-
}
61-
(properties, visible_properties)
62-
}};
63-
}
64-
6542
macro_rules! set_container {
6643
($_self:expr, $widget:expr, $widget_name:expr, $widget_type:expr) => {
6744
if let Some(ref container_type) = $widget.container_type {
@@ -299,6 +276,26 @@ impl<'a> Generator<'a> {
299276
}
300277
}
301278

279+
fn gtk_set_prop_calls(&self, widget: &Widget, ident: Tokens) -> (Vec<Tokens>, Vec<Tokens>) {
280+
let mut properties = vec![];
281+
let mut visible_properties = vec![];
282+
for (key, value) in &widget.properties {
283+
let mut remover = Transformer::new(MODEL_IDENT);
284+
let new_value = remover.fold_expr(value.clone());
285+
let property_func = Ident::new(format!("set_{}", key));
286+
let property = quote! {
287+
#ident.#property_func(#new_value);
288+
};
289+
if key == "visible" {
290+
visible_properties.push(property);
291+
}
292+
else {
293+
properties.push(property);
294+
}
295+
}
296+
(properties, visible_properties)
297+
}
298+
302299
fn gtk_widget(&mut self, widget: &Widget, gtk_widget: &GtkWidget, parent: Option<&Ident>,
303300
parent_widget_type: WidgetType) -> Tokens
304301
{
@@ -320,7 +317,7 @@ impl<'a> Generator<'a> {
320317

321318
let add_child_or_show_all = self.add_child_or_show_all(widget, parent, parent_widget_type);
322319
let ident = quote! { #widget_name };
323-
let (properties, visible_properties) = gen_set_prop_calls!(widget, ident, MODEL_IDENT);
320+
let (properties, visible_properties) = self.gtk_set_prop_calls(widget, ident);
324321
let child_properties = gen_set_child_prop_calls(widget, parent, parent_widget_type, IsGtk);
325322

326323
quote! {
@@ -350,21 +347,38 @@ impl<'a> Generator<'a> {
350347
.map(|child| self.widget(child, Some(widget_name), IsRelm))
351348
.collect();
352349
let ident = quote! { #widget_name.widget() };
353-
let (mut properties, mut visible_properties) = gen_set_prop_calls!(widget, ident, MODEL_IDENT);
350+
let (mut properties, mut visible_properties) = self.gtk_set_prop_calls(widget, ident);
354351
self.properties.append(&mut properties);
355352
self.properties.append(&mut visible_properties);
356353

357354
let add_or_create_widget = self.add_or_create_widget(
358355
parent, parent_widget_type, widget_name, widget_type_ident, &widget.init_parameters, widget.is_container);
359356
let child_properties = gen_set_child_prop_calls(widget, parent, parent_widget_type, IsRelm);
357+
let messages = self.messages(widget, relm_widget);
360358

361359
quote! {
362360
#add_or_create_widget
361+
#messages
363362
#(#children)*
364363
#(#child_properties)*
365364
}
366365
}
367366

367+
fn messages(&self, widget: &Widget, relm_widget: &RelmWidget) -> Tokens {
368+
let mut tokens = quote! {};
369+
let name = &widget.name;
370+
for (variant, value) in &relm_widget.messages {
371+
let mut remover = Transformer::new(MODEL_IDENT);
372+
let value = remover.fold_expr(value.clone());
373+
let variant = Ident::new(variant.as_str());
374+
tokens = quote! {
375+
#tokens
376+
#name.stream().emit(#variant(#value));
377+
};
378+
}
379+
tokens
380+
}
381+
368382
fn widget(&mut self, widget: &Widget, parent: Option<&Ident>, parent_widget_type: WidgetType) -> Tokens {
369383
match widget.widget {
370384
Gtk(ref gtk_widget) => self.gtk_widget(widget, gtk_widget, parent, parent_widget_type),

relm-gen-widget/src/lib.rs

+40-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ mod walker;
4141

4242
use std::collections::{HashMap, HashSet};
4343

44-
use adder::{Adder, Property};
44+
use adder::{Adder, Message, Property};
4545
use gen::gen;
4646
pub use gen::gen_where_clause;
4747
use parser::EitherWidget::{Gtk, Relm};
@@ -71,6 +71,7 @@ use walker::ModelVariableVisitor;
7171

7272
const MODEL_IDENT: &str = "__relm_model";
7373

74+
type MsgModelMap = HashMap<Ident, HashSet<Message>>;
7475
type PropertyModelMap = HashMap<Ident, HashSet<Property>>;
7576

7677
#[derive(Debug)]
@@ -79,6 +80,7 @@ pub struct Driver {
7980
generic_types: Option<Generics>,
8081
model_type: Option<ImplItem>,
8182
model_param_type: Option<ImplItem>,
83+
msg_model_map: Option<MsgModelMap>,
8284
msg_type: Option<ImplItem>,
8385
other_methods: Vec<ImplItem>,
8486
properties_model_map: Option<PropertyModelMap>,
@@ -98,6 +100,7 @@ pub struct Driver {
98100
struct View {
99101
container_impl: Tokens,
100102
item: ImplItem,
103+
msg_model_map: MsgModelMap,
101104
properties_model_map: PropertyModelMap,
102105
relm_widgets: HashMap<Ident, Path>,
103106
widget: Widget,
@@ -110,6 +113,7 @@ impl Driver {
110113
generic_types: None,
111114
model_type: None,
112115
model_param_type: None,
116+
msg_model_map: None,
113117
msg_type: None,
114118
other_methods: vec![],
115119
properties_model_map: None,
@@ -129,7 +133,9 @@ impl Driver {
129133

130134
fn add_set_property_to_method(&self, func: &mut ImplItem) {
131135
if let Method(_, ref mut block) = func.node {
132-
let mut adder = Adder::new(self.properties_model_map.as_ref().expect("update method"));
136+
let msg_map = self.msg_model_map.as_ref().expect("update method");
137+
let property_map = self.properties_model_map.as_ref().expect("update method");
138+
let mut adder = Adder::new(property_map, msg_map);
133139
*block = adder.fold_block(block.clone());
134140
}
135141
}
@@ -219,6 +225,7 @@ impl Driver {
219225
if let Some(on_add) = gen_set_child_prop_calls(&view.widget) {
220226
new_items.push(on_add);
221227
}
228+
self.msg_model_map = Some(view.msg_model_map);
222229
self.properties_model_map = Some(view.properties_model_map);
223230
new_items.push(view.item);
224231
self.widgets.insert(self.root_widget.clone().expect("root widget"),
@@ -351,8 +358,10 @@ impl Driver {
351358
widget.relm_name = Some(typ.clone());
352359
}
353360
self.widget_parent_id = widget.parent_id.clone();
361+
let mut msg_model_map = HashMap::new();
354362
let mut properties_model_map = HashMap::new();
355363
get_properties_model_map(&widget, &mut properties_model_map);
364+
get_msg_model_map(&widget, &mut msg_model_map);
356365
self.add_widgets(&widget, &properties_model_map);
357366
let (view, relm_widgets, container_impl) = gen(name, &widget, self);
358367
let model_ident = Ident::new(MODEL_IDENT);
@@ -365,6 +374,7 @@ impl Driver {
365374
View {
366375
container_impl,
367376
item,
377+
msg_model_map,
368378
properties_model_map,
369379
relm_widgets,
370380
widget,
@@ -468,6 +478,34 @@ macro_rules! get_map {
468478
}};
469479
}
470480

481+
fn get_msg_model_map(widget: &Widget, map: &mut MsgModelMap) {
482+
match widget.widget {
483+
Gtk(_) => {
484+
for child in &widget.children {
485+
get_msg_model_map(child, map);
486+
}
487+
},
488+
Relm(ref relm_widget) => {
489+
for (name, expr) in &relm_widget.messages {
490+
let mut visitor = ModelVariableVisitor::new();
491+
visitor.visit_expr(&expr);
492+
let model_variables = visitor.idents;
493+
for var in model_variables {
494+
let set = map.entry(var).or_insert_with(HashSet::new);
495+
set.insert(Message {
496+
expr: expr.clone(),
497+
name: name.clone(),
498+
widget_name: widget.name.clone(),
499+
});
500+
}
501+
}
502+
for child in &widget.children {
503+
get_msg_model_map(child, map);
504+
}
505+
},
506+
}
507+
}
508+
471509
/*
472510
* The map maps model variable name to a vector of tuples (widget name, property name).
473511
*/

relm-gen-widget/src/parser.rs

+14-3
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ impl Event {
8686

8787
#[derive(Debug)]
8888
pub struct Widget {
89-
pub child_events: ChildEvents,
90-
pub child_properties: HashMap<String, Expr>,
89+
pub child_events: ChildEvents, // TODO: does it make sense for a relm widget?
90+
pub child_properties: HashMap<String, Expr>, // TODO: does it make sense for a relm widget?
9191
pub children: Vec<Widget>,
9292
pub container_type: Option<Option<String>>, // TODO: Why two Options?
9393
pub init_parameters: Vec<Expr>,
@@ -172,13 +172,15 @@ impl GtkWidget {
172172
pub struct RelmWidget {
173173
pub events: HashMap<String, Vec<Event>>,
174174
pub gtk_events: HashMap<String, Event>,
175+
pub messages: HashMap<String, Expr>,
175176
}
176177

177178
impl RelmWidget {
178179
fn new() -> Self {
179180
RelmWidget {
180181
events: HashMap::new(),
181182
gtk_events: HashMap::new(),
183+
messages: HashMap::new(),
182184
}
183185
}
184186
}
@@ -640,7 +642,16 @@ fn parse_relm_widget(tokens: &[TokenTree]) -> (Widget, &[TokenTree]) {
640642
tts = &tts[1..];
641643
match tts[0] {
642644
Token(Colon) => {
643-
tts = parse_value_or_child_properties(tts, ident, &mut child_properties, &mut properties);
645+
let properties_or_signals =
646+
if ident.chars().next().map(|char| char.is_lowercase()) == Some(false) {
647+
// Uppercase is a msg to send.
648+
&mut relm_widget.messages
649+
}
650+
else {
651+
// Lowercase is a gtk property.
652+
&mut properties
653+
};
654+
tts = parse_value_or_child_properties(tts, ident, &mut child_properties, properties_or_signals);
644655
},
645656
Token(Dot) => {
646657
let child_name = ident;

0 commit comments

Comments
 (0)