Skip to content

Commit

Permalink
Add support for construct properties
Browse files Browse the repository at this point in the history
  • Loading branch information
antoyo committed Aug 15, 2017
1 parent 06ec550 commit 39ea6da
Show file tree
Hide file tree
Showing 7 changed files with 257 additions and 28 deletions.
58 changes: 43 additions & 15 deletions Cargo.toml
Expand Up @@ -9,19 +9,32 @@ repository = "https://github.com/antoyo/relm"
version = "0.9.6"

[badges]
travis-ci = { repository = "antoyo/relm", branch = "master" }
appveyor = { repository = "antoyo/relm", branch = "master" }

[badges.appveyor]
branch = "master"
repository = "antoyo/relm"

[badges.travis-ci]
branch = "master"
repository = "antoyo/relm"

[dependencies]
futures = { git = "https://github.com/alexcrichton/futures-rs", branch = "spawn-trait" }
futures-glib = { git = "https://github.com/antoyo/futures-glib-rs" }
glib = "^0.1.2"
glib-itc = "^0.1.1"
glib-sys = "^0.3.4"
gobject-sys = "^0.3.3"
gtk = "^0.1.1"
gtk-sys = "^0.3.3"
libc = "^0.2.22"
log = "^0.3.7"

[dependencies.futures]
branch = "spawn-trait"
git = "https://github.com/alexcrichton/futures-rs"

[dependencies.futures-glib]
git = "https://github.com/antoyo/futures-glib-rs"

[dependencies.relm-core]
path = "relm-core"
version = "^0.1.1"
Expand All @@ -39,6 +52,10 @@ tokio-service = "^0.1.0"
twist = "^0.5.0"
url = "^1.4.0"

[dev-dependencies.relm-attributes]
path = "relm-attributes"
version = "^0.9.0"

[dev-dependencies.relm-derive]
path = "relm-derive"
version = "^0.9.0"
Expand All @@ -47,20 +64,31 @@ version = "^0.9.0"
path = "relm-test"
version = "^0.1.0"

[dev-dependencies.relm-attributes]
path = "relm-attributes"
version = "^0.9.0"

[features]
nightly = []
use_impl_trait = []

[package.metadata.release]
pre-release-replacements = [
{file="README.adoc", search="relm = \"[a-z0-9^\\.-]+\"", replace="relm = \"{{version}}\""},
{file="src/lib.rs", search="relm = \"[a-z0-9^\\.-]+\"", replace="relm = \"{{version}}\""},
{file="examples/buttons-derive/Cargo.toml", search="relm = \"[a-z0-9^\\.-]+\"", replace="relm = \"{{version}}\""},
]
[metadata]

[metadata.release]

[[metadata.release.pre-release-replacements]]
file = "README.adoc"
replace = "relm = \"{{version}}\""
search = "relm = \"[a-z0-9^\\.-]+\""

[[metadata.release.pre-release-replacements]]
file = "src/lib.rs"
replace = "relm = \"{{version}}\""
search = "relm = \"[a-z0-9^\\.-]+\""

[[metadata.release.pre-release-replacements]]
file = "examples/buttons-derive/Cargo.toml"
replace = "relm = \"{{version}}\""
search = "relm = \"[a-z0-9^\\.-]+\""

[replace]
"futures:0.1.13" = { git = "https://github.com/alexcrichton/futures-rs", branch = "spawn-trait" }

[replace."futures:0.1.13"]
branch = "spawn-trait"
git = "https://github.com/alexcrichton/futures-rs"
100 changes: 100 additions & 0 deletions examples/buttons-construct-prop-attribute.rs
@@ -0,0 +1,100 @@
/*
* Copyright (c) 2017 Boucher, Antoni <bouanto@zoho.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#![feature(proc_macro)]

extern crate gtk;
#[macro_use]
extern crate relm;
extern crate relm_attributes;
#[macro_use]
extern crate relm_derive;

use gtk::{
ButtonExt,
Inhibit,
OrientableExt,
WidgetExt,
};
use gtk::Orientation::Vertical;
use relm::Widget;
use relm_attributes::widget;

use self::Msg::*;

// Define the structure of the model.
pub struct Model {
counter: i32,
}

// The messages that can be sent to the update function.
#[derive(Msg)]
pub enum Msg {
Decrement,
Increment,
Quit,
}

#[widget]
impl Widget for Win {
// The initial model.
fn model() -> Model {
Model {
counter: 0,
}
}

// Update the model according to the message received.
fn update(&mut self, event: Msg) {
match event {
Decrement => self.model.counter -= 1,
Increment => self.model.counter += 1,
Quit => gtk::main_quit(),
}
}

view! {
gtk::Window {
gtk::Box {
// Set the orientation property of the Box.
orientation: Vertical,
// Create a Button inside the Box.
gtk::Button({ label: "+" }) {
// Send the message Increment when the button is clicked.
clicked => Increment,
},
gtk::Label {
// Bind the text property of the label to the counter attribute of the model.
text: &self.model.counter.to_string(),
},
gtk::Button {
clicked => Decrement,
label: "-",
},
},
delete_event(_, _) => (Quit, Inhibit(false)),
}
}
}

fn main() {
Win::run(()).unwrap();
}
46 changes: 39 additions & 7 deletions relm-gen-widget/src/gen.rs
Expand Up @@ -307,7 +307,7 @@ impl<'a> Generator<'a> {
self.relm_widgets.insert(widget_name.clone(), struct_name.clone());
}

let construct_widget = gen_construct_widget(widget);
let construct_widget = gen_construct_widget(widget, gtk_widget);
self.collect_events(widget, gtk_widget);

let children: Vec<_> = widget.children.iter()
Expand Down Expand Up @@ -369,23 +369,55 @@ impl<'a> Generator<'a> {
}
}

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

let params = &widget.init_parameters;
let properties_count = gtk_widget.construct_properties.len() as u32;
let mut parameters = vec![];
for (key, value) in gtk_widget.construct_properties.iter() {
let key = key.to_string();
parameters.push(quote! {
::relm::GParameter {
name: ::relm::ToGlibPtr::to_glib_full(#key),
value: ::std::ptr::read(::relm::ToGlibPtr::to_glib_none(&::gtk::ToValue::to_value(&#value)).0),
}
});
}
// TODO: use this new code when g_object_new_with_properties() is released.
/*let mut names = vec![];
let mut values = vec![];
for (key, value) in gtk_widget.construct_properties.iter() {
let key = key.to_string();
names.push(quote! {
#key
});
values.push(quote! {
&#value
});
}*/

if widget.init_parameters.is_empty() {
quote! {
unsafe {
use gtk::StaticType;
use relm::{Downcast, FromGlibPtrNone, ToGlib};
::gtk::Widget::from_glib_none(::relm::g_object_new(#struct_name::static_type().to_glib(),
#(#params,)* ::std::ptr::null() as *const _) as *mut _)
.downcast_unchecked()
use relm::{Downcast, FromGlibPtrNone};
let mut parameters = [#(#parameters),*];
::gtk::Widget::from_glib_none(::relm::g_object_newv(
::relm::ToGlib::to_glib(&#struct_name::static_type()),
#properties_count, parameters.as_mut_ptr()) as *mut _)
.downcast_unchecked()
// TODO: use this new code when g_object_new_with_properties() is released.
/*let names: &[&str] = &[#(#names),*];
let values: &[&::gtk::ToValue] = &[#(#values),*];
::gtk::Widget::from_glib_none(::relm::g_object_new_with_properties(#struct_name::static_type().to_glib(),
#properties_count, ::relm::ToGlibPtr::to_glib_full(&names),
::relm::ToGlibPtr::to_glib_full(&values) as *mut _) as *mut _)
.downcast_unchecked()*/
}
}
}
else {
let params = &widget.init_parameters;
quote! {
#struct_name::new(#(#params),*)
}
Expand Down
2 changes: 2 additions & 0 deletions relm-gen-widget/src/lib.rs
Expand Up @@ -25,6 +25,8 @@
* TODO: think about conditions and loops (widget-list).
*/

#![recursion_limit="128"]

#[macro_use]
extern crate lazy_static;
#[macro_use]
Expand Down
62 changes: 60 additions & 2 deletions relm-gen-widget/src/parser.rs
Expand Up @@ -22,6 +22,7 @@
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::mem;
use std::sync::Mutex;

use quote::{Tokens, ToTokens};
Expand Down Expand Up @@ -150,6 +151,7 @@ pub enum EitherWidget {
#[derive(Debug)]
pub struct GtkWidget {
pub child_events: HashMap<(String, String), Event>,
pub construct_properties: HashMap<syn::Ident, Tokens>,
pub events: HashMap<String, Event>,
pub relm_name: Option<Ty>,
pub save: bool,
Expand All @@ -159,6 +161,7 @@ impl GtkWidget {
fn new() -> Self {
GtkWidget {
child_events: HashMap::new(),
construct_properties: HashMap::new(),
events: HashMap::new(),
relm_name: None,
save: false,
Expand Down Expand Up @@ -228,8 +231,12 @@ fn parse_widget(tokens: &[TokenTree], save: bool) -> (Widget, &[TokenTree]) {
let mut child_properties = HashMap::new();
gtk_widget.save = save;
if let TokenTree::Delimited(Delimited { delim: Paren, ref tts }) = tokens[0] {
let parameters = parse_comma_list(tts);
init_parameters = parameters;
if let TokenTree::Delimited(Delimited { delim: Brace, ref tts }) = tts[0] {
gtk_widget.construct_properties = parse_hash(tts);
}
else {
init_parameters = parse_comma_list(tts);
}
tokens = &tokens[1..];
}
if let TokenTree::Delimited(Delimited { delim: Brace, ref tts }) = tokens[0] {
Expand Down Expand Up @@ -357,6 +364,56 @@ fn parse_comma_ident_list(tokens: &[TokenTree]) -> Vec<syn::Ident> {
params
}

enum HashState {
InName,
AfterName,
InValue,
}

use self::HashState::*;

fn parse_hash(tokens: &[TokenTree]) -> HashMap<syn::Ident, Tokens> {
let mut params = HashMap::new();
let mut current_param = Tokens::new();
let mut state = InName;
let mut name = syn::Ident::new("");
for token in tokens {
match state {
InName => {
// FIXME: support ident with dash (-).
if let Token(Ident(ref ident)) = *token {
name = ident.clone();
state = AfterName;
}
else {
panic!("Expected ident, but found `{:?}` in view! macro", token);
}
},
AfterName => {
if *token == Token(Colon) {
state = InValue;
}
else {
panic!("Expected colon, but found `{:?}` in view! macro", token);
}
},
InValue => {
if *token == Token(Comma) {
let ident = mem::replace(&mut name, syn::Ident::new(""));
params.insert(ident, current_param);
current_param = Tokens::new();
}
else {
token.to_tokens(&mut current_param);
}
},
}
}
// FIXME: could be an empty hash.
params.insert(name, current_param);
params
}

fn parse_comma_list(tokens: &[TokenTree]) -> Vec<Tokens> {
let mut params = vec![];
let mut current_param = Tokens::new();
Expand All @@ -369,6 +426,7 @@ fn parse_comma_list(tokens: &[TokenTree]) -> Vec<Tokens> {
token.to_tokens(&mut current_param);
}
}
// FIXME: could be an empty list.
params.push(current_param);
params
}
Expand Down
3 changes: 1 addition & 2 deletions src/container.rs
Expand Up @@ -19,9 +19,8 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

use glib::Cast;
use gtk;
use gtk::{ContainerExt, IsA, Object, WidgetExt};
use gtk::{Cast, ContainerExt, IsA, Object, WidgetExt};

use component::Component;
use super::{DisplayVariant, Relm, create_widget, init_component};
Expand Down

0 comments on commit 39ea6da

Please sign in to comment.