Skip to content

Commit 39ea6da

Browse files
committed
Add support for construct properties
1 parent 06ec550 commit 39ea6da

File tree

7 files changed

+257
-28
lines changed

7 files changed

+257
-28
lines changed

Cargo.toml

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,32 @@ repository = "https://github.com/antoyo/relm"
99
version = "0.9.6"
1010

1111
[badges]
12-
travis-ci = { repository = "antoyo/relm", branch = "master" }
13-
appveyor = { repository = "antoyo/relm", branch = "master" }
12+
13+
[badges.appveyor]
14+
branch = "master"
15+
repository = "antoyo/relm"
16+
17+
[badges.travis-ci]
18+
branch = "master"
19+
repository = "antoyo/relm"
1420

1521
[dependencies]
16-
futures = { git = "https://github.com/alexcrichton/futures-rs", branch = "spawn-trait" }
17-
futures-glib = { git = "https://github.com/antoyo/futures-glib-rs" }
1822
glib = "^0.1.2"
1923
glib-itc = "^0.1.1"
24+
glib-sys = "^0.3.4"
2025
gobject-sys = "^0.3.3"
2126
gtk = "^0.1.1"
2227
gtk-sys = "^0.3.3"
28+
libc = "^0.2.22"
2329
log = "^0.3.7"
2430

31+
[dependencies.futures]
32+
branch = "spawn-trait"
33+
git = "https://github.com/alexcrichton/futures-rs"
34+
35+
[dependencies.futures-glib]
36+
git = "https://github.com/antoyo/futures-glib-rs"
37+
2538
[dependencies.relm-core]
2639
path = "relm-core"
2740
version = "^0.1.1"
@@ -39,6 +52,10 @@ tokio-service = "^0.1.0"
3952
twist = "^0.5.0"
4053
url = "^1.4.0"
4154

55+
[dev-dependencies.relm-attributes]
56+
path = "relm-attributes"
57+
version = "^0.9.0"
58+
4259
[dev-dependencies.relm-derive]
4360
path = "relm-derive"
4461
version = "^0.9.0"
@@ -47,20 +64,31 @@ version = "^0.9.0"
4764
path = "relm-test"
4865
version = "^0.1.0"
4966

50-
[dev-dependencies.relm-attributes]
51-
path = "relm-attributes"
52-
version = "^0.9.0"
53-
5467
[features]
5568
nightly = []
5669
use_impl_trait = []
5770

58-
[package.metadata.release]
59-
pre-release-replacements = [
60-
{file="README.adoc", search="relm = \"[a-z0-9^\\.-]+\"", replace="relm = \"{{version}}\""},
61-
{file="src/lib.rs", search="relm = \"[a-z0-9^\\.-]+\"", replace="relm = \"{{version}}\""},
62-
{file="examples/buttons-derive/Cargo.toml", search="relm = \"[a-z0-9^\\.-]+\"", replace="relm = \"{{version}}\""},
63-
]
71+
[metadata]
72+
73+
[metadata.release]
74+
75+
[[metadata.release.pre-release-replacements]]
76+
file = "README.adoc"
77+
replace = "relm = \"{{version}}\""
78+
search = "relm = \"[a-z0-9^\\.-]+\""
79+
80+
[[metadata.release.pre-release-replacements]]
81+
file = "src/lib.rs"
82+
replace = "relm = \"{{version}}\""
83+
search = "relm = \"[a-z0-9^\\.-]+\""
84+
85+
[[metadata.release.pre-release-replacements]]
86+
file = "examples/buttons-derive/Cargo.toml"
87+
replace = "relm = \"{{version}}\""
88+
search = "relm = \"[a-z0-9^\\.-]+\""
6489

6590
[replace]
66-
"futures:0.1.13" = { git = "https://github.com/alexcrichton/futures-rs", branch = "spawn-trait" }
91+
92+
[replace."futures:0.1.13"]
93+
branch = "spawn-trait"
94+
git = "https://github.com/alexcrichton/futures-rs"
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright (c) 2017 Boucher, Antoni <bouanto@zoho.com>
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy of
5+
* this software and associated documentation files (the "Software"), to deal in
6+
* the Software without restriction, including without limitation the rights to
7+
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8+
* the Software, and to permit persons to whom the Software is furnished to do so,
9+
* subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in all
12+
* copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16+
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17+
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18+
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19+
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20+
*/
21+
22+
#![feature(proc_macro)]
23+
24+
extern crate gtk;
25+
#[macro_use]
26+
extern crate relm;
27+
extern crate relm_attributes;
28+
#[macro_use]
29+
extern crate relm_derive;
30+
31+
use gtk::{
32+
ButtonExt,
33+
Inhibit,
34+
OrientableExt,
35+
WidgetExt,
36+
};
37+
use gtk::Orientation::Vertical;
38+
use relm::Widget;
39+
use relm_attributes::widget;
40+
41+
use self::Msg::*;
42+
43+
// Define the structure of the model.
44+
pub struct Model {
45+
counter: i32,
46+
}
47+
48+
// The messages that can be sent to the update function.
49+
#[derive(Msg)]
50+
pub enum Msg {
51+
Decrement,
52+
Increment,
53+
Quit,
54+
}
55+
56+
#[widget]
57+
impl Widget for Win {
58+
// The initial model.
59+
fn model() -> Model {
60+
Model {
61+
counter: 0,
62+
}
63+
}
64+
65+
// Update the model according to the message received.
66+
fn update(&mut self, event: Msg) {
67+
match event {
68+
Decrement => self.model.counter -= 1,
69+
Increment => self.model.counter += 1,
70+
Quit => gtk::main_quit(),
71+
}
72+
}
73+
74+
view! {
75+
gtk::Window {
76+
gtk::Box {
77+
// Set the orientation property of the Box.
78+
orientation: Vertical,
79+
// Create a Button inside the Box.
80+
gtk::Button({ label: "+" }) {
81+
// Send the message Increment when the button is clicked.
82+
clicked => Increment,
83+
},
84+
gtk::Label {
85+
// Bind the text property of the label to the counter attribute of the model.
86+
text: &self.model.counter.to_string(),
87+
},
88+
gtk::Button {
89+
clicked => Decrement,
90+
label: "-",
91+
},
92+
},
93+
delete_event(_, _) => (Quit, Inhibit(false)),
94+
}
95+
}
96+
}
97+
98+
fn main() {
99+
Win::run(()).unwrap();
100+
}

relm-gen-widget/src/gen.rs

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ impl<'a> Generator<'a> {
307307
self.relm_widgets.insert(widget_name.clone(), struct_name.clone());
308308
}
309309

310-
let construct_widget = gen_construct_widget(widget);
310+
let construct_widget = gen_construct_widget(widget, gtk_widget);
311311
self.collect_events(widget, gtk_widget);
312312

313313
let children: Vec<_> = widget.children.iter()
@@ -369,23 +369,55 @@ impl<'a> Generator<'a> {
369369
}
370370
}
371371

372-
fn gen_construct_widget(widget: &Widget) -> Tokens {
372+
fn gen_construct_widget(widget: &Widget, gtk_widget: &GtkWidget) -> Tokens {
373373
let struct_name = &widget.typ;
374374

375-
let params = &widget.init_parameters;
375+
let properties_count = gtk_widget.construct_properties.len() as u32;
376+
let mut parameters = vec![];
377+
for (key, value) in gtk_widget.construct_properties.iter() {
378+
let key = key.to_string();
379+
parameters.push(quote! {
380+
::relm::GParameter {
381+
name: ::relm::ToGlibPtr::to_glib_full(#key),
382+
value: ::std::ptr::read(::relm::ToGlibPtr::to_glib_none(&::gtk::ToValue::to_value(&#value)).0),
383+
}
384+
});
385+
}
386+
// TODO: use this new code when g_object_new_with_properties() is released.
387+
/*let mut names = vec![];
388+
let mut values = vec![];
389+
for (key, value) in gtk_widget.construct_properties.iter() {
390+
let key = key.to_string();
391+
names.push(quote! {
392+
#key
393+
});
394+
values.push(quote! {
395+
&#value
396+
});
397+
}*/
376398

377399
if widget.init_parameters.is_empty() {
378400
quote! {
379401
unsafe {
380402
use gtk::StaticType;
381-
use relm::{Downcast, FromGlibPtrNone, ToGlib};
382-
::gtk::Widget::from_glib_none(::relm::g_object_new(#struct_name::static_type().to_glib(),
383-
#(#params,)* ::std::ptr::null() as *const _) as *mut _)
384-
.downcast_unchecked()
403+
use relm::{Downcast, FromGlibPtrNone};
404+
let mut parameters = [#(#parameters),*];
405+
::gtk::Widget::from_glib_none(::relm::g_object_newv(
406+
::relm::ToGlib::to_glib(&#struct_name::static_type()),
407+
#properties_count, parameters.as_mut_ptr()) as *mut _)
408+
.downcast_unchecked()
409+
// TODO: use this new code when g_object_new_with_properties() is released.
410+
/*let names: &[&str] = &[#(#names),*];
411+
let values: &[&::gtk::ToValue] = &[#(#values),*];
412+
::gtk::Widget::from_glib_none(::relm::g_object_new_with_properties(#struct_name::static_type().to_glib(),
413+
#properties_count, ::relm::ToGlibPtr::to_glib_full(&names),
414+
::relm::ToGlibPtr::to_glib_full(&values) as *mut _) as *mut _)
415+
.downcast_unchecked()*/
385416
}
386417
}
387418
}
388419
else {
420+
let params = &widget.init_parameters;
389421
quote! {
390422
#struct_name::new(#(#params),*)
391423
}

relm-gen-widget/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
* TODO: think about conditions and loops (widget-list).
2626
*/
2727

28+
#![recursion_limit="128"]
29+
2830
#[macro_use]
2931
extern crate lazy_static;
3032
#[macro_use]

relm-gen-widget/src/parser.rs

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use std::collections::HashMap;
2323
use std::fs::File;
2424
use std::io::Read;
25+
use std::mem;
2526
use std::sync::Mutex;
2627

2728
use quote::{Tokens, ToTokens};
@@ -150,6 +151,7 @@ pub enum EitherWidget {
150151
#[derive(Debug)]
151152
pub struct GtkWidget {
152153
pub child_events: HashMap<(String, String), Event>,
154+
pub construct_properties: HashMap<syn::Ident, Tokens>,
153155
pub events: HashMap<String, Event>,
154156
pub relm_name: Option<Ty>,
155157
pub save: bool,
@@ -159,6 +161,7 @@ impl GtkWidget {
159161
fn new() -> Self {
160162
GtkWidget {
161163
child_events: HashMap::new(),
164+
construct_properties: HashMap::new(),
162165
events: HashMap::new(),
163166
relm_name: None,
164167
save: false,
@@ -228,8 +231,12 @@ fn parse_widget(tokens: &[TokenTree], save: bool) -> (Widget, &[TokenTree]) {
228231
let mut child_properties = HashMap::new();
229232
gtk_widget.save = save;
230233
if let TokenTree::Delimited(Delimited { delim: Paren, ref tts }) = tokens[0] {
231-
let parameters = parse_comma_list(tts);
232-
init_parameters = parameters;
234+
if let TokenTree::Delimited(Delimited { delim: Brace, ref tts }) = tts[0] {
235+
gtk_widget.construct_properties = parse_hash(tts);
236+
}
237+
else {
238+
init_parameters = parse_comma_list(tts);
239+
}
233240
tokens = &tokens[1..];
234241
}
235242
if let TokenTree::Delimited(Delimited { delim: Brace, ref tts }) = tokens[0] {
@@ -357,6 +364,56 @@ fn parse_comma_ident_list(tokens: &[TokenTree]) -> Vec<syn::Ident> {
357364
params
358365
}
359366

367+
enum HashState {
368+
InName,
369+
AfterName,
370+
InValue,
371+
}
372+
373+
use self::HashState::*;
374+
375+
fn parse_hash(tokens: &[TokenTree]) -> HashMap<syn::Ident, Tokens> {
376+
let mut params = HashMap::new();
377+
let mut current_param = Tokens::new();
378+
let mut state = InName;
379+
let mut name = syn::Ident::new("");
380+
for token in tokens {
381+
match state {
382+
InName => {
383+
// FIXME: support ident with dash (-).
384+
if let Token(Ident(ref ident)) = *token {
385+
name = ident.clone();
386+
state = AfterName;
387+
}
388+
else {
389+
panic!("Expected ident, but found `{:?}` in view! macro", token);
390+
}
391+
},
392+
AfterName => {
393+
if *token == Token(Colon) {
394+
state = InValue;
395+
}
396+
else {
397+
panic!("Expected colon, but found `{:?}` in view! macro", token);
398+
}
399+
},
400+
InValue => {
401+
if *token == Token(Comma) {
402+
let ident = mem::replace(&mut name, syn::Ident::new(""));
403+
params.insert(ident, current_param);
404+
current_param = Tokens::new();
405+
}
406+
else {
407+
token.to_tokens(&mut current_param);
408+
}
409+
},
410+
}
411+
}
412+
// FIXME: could be an empty hash.
413+
params.insert(name, current_param);
414+
params
415+
}
416+
360417
fn parse_comma_list(tokens: &[TokenTree]) -> Vec<Tokens> {
361418
let mut params = vec![];
362419
let mut current_param = Tokens::new();
@@ -369,6 +426,7 @@ fn parse_comma_list(tokens: &[TokenTree]) -> Vec<Tokens> {
369426
token.to_tokens(&mut current_param);
370427
}
371428
}
429+
// FIXME: could be an empty list.
372430
params.push(current_param);
373431
params
374432
}

src/container.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,8 @@
1919
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2020
*/
2121

22-
use glib::Cast;
2322
use gtk;
24-
use gtk::{ContainerExt, IsA, Object, WidgetExt};
23+
use gtk::{Cast, ContainerExt, IsA, Object, WidgetExt};
2524

2625
use component::Component;
2726
use super::{DisplayVariant, Relm, create_widget, init_component};

0 commit comments

Comments
 (0)