Consider the cascade macro
I think you'll find GTK widget construction to be significantly easier to maintain with the cascade macro. I've been using it in all of our GTK projects to simplify construction and keep related calls closely knit together. It's been rather invaluable to keeping the code clean.
Here's a few snippets from one of the projects I maintain:
let po_search_box = cascade! {
gtk::Box::new(gtk::Orientation::Vertical, 12);
..set_margin_start(48);
..set_margin_end(48);
..set_margin_bottom(24);
..add(&cascade! {
gtk::Label::new("<b>Purchase Order Search</b>");
..set_use_markup(true);
..set_halign(gtk::Align::Center);
});
..add(&cascade! {
gtk::Box::new(gtk::Orientation::Horizontal, 70);
..set_homogeneous(true);
..add(&insert_entry(&vendors_combo, "Vendor"));
..add(&insert_entry(&vendor_quote_entry, "Vendor Quote Number"));
..add(&insert_entry(&vendor_invoice_entry, "Vendor Invoice Number"));
..add(&insert_entry(&vendor_so_entry, "Vendor SO"));
});
..add(&cascade! {
gtk::Label::new("Enter SKU numbers to narrow the PO list (space-delimited)");
..set_use_markup(true);
..set_halign(gtk::Align::Center);
});
..add(&sku_search_entry);
};
let serial = cascade! {
s: gtk::Entry::new();
..set_width_chars(20);
| if serial_required {
s.set_placeholder_text("Required");
};
};
let search_grid = {
let po_container = po_list.container.clone();
let search_grid = cascade! {
gtk::Box::new(gtk::Orientation::Vertical, 12);
..add(quick_search.as_ref());
..add(>k::Separator::new(gtk::Orientation::Horizontal));
..add(&po_search_box);
..add(&po_list.container);
// Resize the PO list to be 50% of the width of this view.
..connect_size_allocate(move |_, allocation| {
po_container.set_size_request(allocation.width / 2, -1);
});
};
search_grid
};
let view_stack = cascade! {
stack: gtk::Stack::new();
..add((*inventory).as_ref());
..add(storing.as_ref());
..add(picking.as_ref());
..set_child_title((*inventory).as_ref(), "Receiving");
..set_child_title(storing.as_ref(), "Storing");
..set_child_title(picking.as_ref(), "Picking");
| header.switcher.set_stack(&stack);
};
let main_stack = cascade! {
gtk::Stack::new();
..set_border_width(12);
..set_margin_start(12);
..set_margin_end(12);
..add(login.as_ref());
..add(&view_stack);
..add(error.as_ref());
..set_visible_child(login.as_ref());
};
let window = cascade! {
gtk::ApplicationWindow::new(application);
..set_icon_name("package-x-generic");
..set_property_window_position(gtk::WindowPosition::Center);
..set_titlebar(header.as_ref());
..set_hide_titlebar_when_maximized(false);
..add(&main_stack);
..get_style_context().map(|ctx| ctx.add_class("rounded"));
..show_all();
..connect_delete_event(move |window, _| {
window.destroy();
Inhibit(false)
});
..set_keep_above(true);
..maximize();
// Disable the close button?
//..set_deletable(false);
};
Essentially, it works the same way as cascades in Google Dart. It would be nice if Rust supported cascades as well, but this macro can emulate it. Essentially, the first statement declares the value that each following statement will apply to, so that you need not repeat its name. It's the next best thing to having a builder API pattern.