Containers: Add convenience functions taking std::initializer_list or std::vector of children
- Submitted by Daniel Boles
@dboles
- Link to original bug (#780134)**
Background
Often I just want to create a Container
, add a bunch of child Widgets
to it, add it to a Window
or something, and forget about it.
We then get repetitive code like this:
box.add(foo);
box.add(bar);
box.add(bongo);
Alternatives
Way back when, I had a wrapper class around Gtk::Box
, which avoided the need for this by having an add()
method that took an std::initializer_list
:
box.add( {&foo, &bar, &bongo} );
i.e.: it took an std::initializer_list<Gtk::Widget*>
and did the repetition for me - abstracting away that tedious part of the process and making the code look more like a visual representation of the UI layout.
I also had an equivalent constructor overload, which did something like this:
auto custom_box = CustomBox{ Gtk::ORIENTATION_HORIZONTAL, 8, {&foo, &bar, &bongo} );
i.e. which allowed constructing the container then adding to it in one step.
Sometimes such Container
s are even nested, and although I never got around to it, I can imagine being able to support that nicely too. Something like the following:
auto nested_box = Box{
Gtk::ORIENTATION_VERTICAL, 8, // (assumes Box::spacing is kept!)
{ &blah,
&wow,
Gtk::manage( new Box{&some,
&other,
&widgets} )
}
};
I stopped using that class for other reasons, but always missed the more succinct and visual representation of layout that it gave me in the code.
Discussion
I'm aware that .ui
files exist for a similar goal, but call me old-fashioned: I want to create my layouts in code, not in an XML file that takes an additional round trip through Gtk::Builder
to parse and create, requires using C class names, doesn't play nicely with children that have lots of dynamically set properties, etc.
In response to my post on the mailing list, Kjell wrote:
I don't think it has been considered, and certainly not rejected. It's a good idea. A lot of details can be discussed. There could be
Gtk::Container::add(std::initializer_list<Gtk::Widget*> widgets); Gtk::Box::pack_start(std::initializer_list<Gtk::Widget*> widgets, PackOptions options); Gtk::Box::pack_end(std::initializer_list<Gtk::Widget*> widgets, PackOptions options); etc.
It would be even better with a list of references:
std::initializer_list<Gtk::Widget&>
. Is that possible, or would the compiler try to copy the widgets?
That would be preferable to avoid nullability, but C++ does not allow containers of references. However, that ties in well with another idea I had:
struct GridAttachItem {
Gtk::Widget& widget;
int x;
int y;
int w;
int h;
};
void Grid::attach( std::initializer_list`<GridAttachItem>` );
The inability to use references in the list, handily, is subverted if the reference is part of a struct/class - since then there's a way to disambiguate by name, whether you want the element itself, or what it refers to (at least, this is how I understand it).
I can easily imagine similar struct
s for packing in Box
:
struct BoxAddItem {
Gtk::Widget& widget;
Gtk::Align align; // maybe not 100% suitable, but you get the idea
Gtk::AttachOptions attach_options;
};
and etc.