Skip to content

Commit

Permalink
Close #5 - allow #[widget(recursive)] on builder fields
Browse files Browse the repository at this point in the history
  • Loading branch information
idanarye committed Apr 2, 2021
1 parent 86fa5ce commit 605cff3
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- `woab::NamespacedSignalRouter` for routing signals from the same builder to
different actors.
- `BuilderConnector::with_object`.
- `#[widget(nested)]` for nesting widget structs.

### Changed
- [**BREAKING**] Updated Actix to 0.11 and Tokio to 1.14. Consequences:
Expand Down
26 changes: 26 additions & 0 deletions macros/src/widgets_from_builder_derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,19 @@ pub fn impl_widgets_from_builder_derive(ast: &syn::DeriveInput) -> Result<proc_m
.iter()
.map(|field| {
/* Handle renaming */
let mut nested = false;
let mut name = None;
iter_attrs_parameters(&field.attrs, "widget", |attr_name, value| {
match path_to_single_string(&attr_name)?.as_str() {
"nested" => {
if nested {
return Err(Error::new_spanned(value, "attribute `nested` can only be specified once"));
}
if value.is_some() {
return Err(Error::new_spanned(value, "attribute `nested` cannot have a value"));
}
nested = true;
}
"name" => {
let value = value.ok_or_else(|| Error::new_spanned(attr_name, "attribute `name` must have a value"))?;
if name.is_some() {
Expand All @@ -38,11 +48,27 @@ pub fn impl_widgets_from_builder_derive(ast: &syn::DeriveInput) -> Result<proc_m
}
Ok(())
})?;
if nested && name.is_some() {
return Err(Error::new_spanned(field, "`nested` and `name` are mutually exclusive"));
}

let field_ident = field
.ident
.as_ref()
.ok_or_else(|| Error::new(field.span(), "Nameless field"))?;

if nested {
// NOTE: Not using `?` because it `into`es the error and the type checker does not like that.
return Ok(quote! {
#field_ident: {
match std::convert::TryInto::try_into(builder) {
Ok(ok) => ok,
Err(err) => return Err(err),
}
},
})
}

let field_type = &field.ty;
let ident_as_str = match name {
Some(syn::Expr::Lit(syn::ExprLit {
Expand Down
59 changes: 59 additions & 0 deletions tests/four_texts.glade
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.24"/>
<object class="GtkApplicationWindow" id="win_test">
<property name="can-focus">False</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkEntry" id="text1">
<property name="visible">True</property>
<property name="can-focus">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="text2">
<property name="visible">True</property>
<property name="can-focus">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="text3">
<property name="visible">True</property>
<property name="can-focus">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="text4">
<property name="visible">True</property>
<property name="can-focus">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
</object>
</child>
</object>
</interface>
65 changes: 65 additions & 0 deletions tests/recusive_widgets_from_builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use gtk::prelude::*;

#[macro_use]
mod util;

#[derive(woab::WidgetsFromBuilder)]
pub struct FlatWidgets {
text1: gtk::Entry,
text2: gtk::Entry,
text3: gtk::Entry,
text4: gtk::Entry,
}

#[derive(woab::WidgetsFromBuilder)]
pub struct GroupedWidgets {
#[widget(nested)]
group_a: GroupA,
#[widget(nested)]
group_b: GroupB,
}

#[derive(woab::WidgetsFromBuilder)]
pub struct GroupA {
text1: gtk::Entry,
text2: gtk::Entry,
}

#[derive(woab::WidgetsFromBuilder)]
pub struct GroupB {
text3: gtk::Entry,
text4: gtk::Entry,
}

#[test]
fn test_recusive_widgets_from_builder() -> anyhow::Result<()> {
let factory = woab::BuilderFactory::from(include_str!("four_texts.glade").to_owned());
gtk::init()?;
woab::run_actix_inside_gtk_event_loop()?;
let bld = factory.instantiate();

let flat_widgets: FlatWidgets = bld.widgets()?;
let grouped_widgets: GroupedWidgets = bld.widgets()?;

assert!(grouped_widgets.group_a.text1.get_text().is_empty());
assert!(grouped_widgets.group_a.text2.get_text().is_empty());
assert!(grouped_widgets.group_b.text3.get_text().is_empty());
assert!(grouped_widgets.group_b.text4.get_text().is_empty());

flat_widgets.text1.set_text("Text 1");
flat_widgets.text2.set_text("Text 2");
flat_widgets.text3.set_text("Text 3");
flat_widgets.text4.set_text("Text 4");

wait_for!(flat_widgets.text1.get_text() == "Text 1")?;
wait_for!(flat_widgets.text2.get_text() == "Text 2")?;
wait_for!(flat_widgets.text3.get_text() == "Text 3")?;
wait_for!(flat_widgets.text4.get_text() == "Text 4")?;

assert!(grouped_widgets.group_a.text1.get_text() == "Text 1");
assert!(grouped_widgets.group_a.text2.get_text() == "Text 2");
assert!(grouped_widgets.group_b.text3.get_text() == "Text 3");
assert!(grouped_widgets.group_b.text4.get_text() == "Text 4");

Ok(())
}

0 comments on commit 605cff3

Please sign in to comment.