menu_view: improved box and float layouts

This patch enhances the box layout such that child widgets are
equally stretched to the available size whenever the box layout's
size is larger than its min size. Furthermore, it corrects the
mixed-up use of the terms east and west in the float widget.
This commit is contained in:
Norman Feske 2018-05-09 18:03:56 +02:00 committed by Christian Helmuth
parent 2a9b0a163e
commit bf52f73d03
3 changed files with 101 additions and 36 deletions

View File

@ -158,23 +158,44 @@ install_config {
<inline description="example menu">
<dialog>
<frame>
<vbox>
<button name="virtualbox">
<label text="VirtualBox"/>
</button>
<button name="toolchain" hovered="yes">
<label text="Tool chain"
font="monospace/regular"/>
</button>
<button name="log" hovered="yes" selected="yes">
<label text="Log window"
font="title/regular"/>
</button>
<button name="config" selected="yes">
<label text="Configuration"
font="annotation/regular"/>
</button>
</vbox>
<hbox>
<vbox name="x">
<button name="virtualbox">
<label text="VirtualBox"/>
</button>
<button name="toolchain" hovered="yes">
<label text="Tool chain"
font="monospace/regular"/>
</button>
<button name="log" hovered="yes" selected="yes">
<label text="Log window"
font="title/regular"/>
</button>
<button name="config" selected="yes">
<label text="Configuration Configuration Configuration"
font="annotation/regular"/>
</button>
<hbox>
<button name="x1"> <label text="x1"/> </button>
<button name="x2"> <label text="x2"/> </button>
<button name="x3"> <label text="x3"/> </button>
</hbox>
<frame>
<label text="A single line" font="annotation/regular"/>
</frame>
</vbox>
<float north="yes">
<vbox>
<button name="virtualbox">
<label text="VirtualBox"/>
</button>
<button name="toolchain" hovered="yes">
<label text="Tool chain"
font="monospace/regular"/>
</button>
</vbox>
</float>
</hbox>
</frame>
</dialog>
</inline>

View File

@ -28,20 +28,18 @@ struct Menu_view::Box_layout_widget : Widget
Direction const _direction;
Box_layout_widget(Widget_factory &factory, Xml_node node, Unique_id unique_id)
:
Widget(factory, node, unique_id),
_direction(node.has_type("vbox") ? VERTICAL : HORIZONTAL)
{ }
bool _vertical() const { return _direction == VERTICAL; }
void update(Xml_node node) override
unsigned _count = 0;
/**
* Stack and count children, and update min_size for the whole compound
*
* This method performs the part of the layout calculation that can be
* done without knowing the final size of the box layout.
*/
void _stack_and_count_child_widgets()
{
_update_children(node);
/*
* Apply layout to the children
*/
/* determine largest size among our children */
unsigned largest_size = 0;
_children.for_each([&] (Widget const &w) {
@ -51,6 +49,7 @@ struct Menu_view::Box_layout_widget : Widget
/* position children on one row/column */
Point position(0, 0);
_count = 0;
_children.for_each([&] (Widget &w) {
Area const child_min_size = w.min_size();
@ -66,6 +65,7 @@ struct Menu_view::Box_layout_widget : Widget
unsigned const dx = child_min_size.w() - min(w.margin.right, next_left_margin);
position = position + Point(dx, 0);
}
_count++;
});
_min_size = (_direction == VERTICAL)
@ -73,6 +73,54 @@ struct Menu_view::Box_layout_widget : Widget
: Area(position.x(), largest_size);
}
/**
* Adjust layout to actual size of the entire box layout widget
*/
void _stretch_child_widgets_to_available_size()
{
using Genode::max;
unsigned const unused_pixels =
_vertical() ? max(_geometry.h(), _min_size.h()) - _min_size.h()
: max(_geometry.w(), _min_size.w()) - _min_size.w();
/* number of excess pixels at the end of the stack (fixpoint) */
unsigned const step_fp = (_count > 0) ? (unused_pixels << 8) / _count : 0;
unsigned consumed_fp = 0;
_children.for_each([&] (Widget &w) {
unsigned const next_consumed_fp = consumed_fp + step_fp;
unsigned const padding_pixels = (next_consumed_fp >> 8)
- (consumed_fp >> 8);
if (_direction == VERTICAL) {
w.position(w.geometry().p1() + Point(0, consumed_fp >> 8));
w.size(Area(geometry().w(), w.min_size().h() + padding_pixels));
} else {
w.position(w.geometry().p1() + Point(consumed_fp >> 8, 0));
w.size(Area(w.min_size().w() + padding_pixels, geometry().h()));
}
consumed_fp = next_consumed_fp;
});
}
Box_layout_widget(Widget_factory &factory, Xml_node node, Unique_id unique_id)
:
Widget(factory, node, unique_id),
_direction(node.has_type("vbox") ? VERTICAL : HORIZONTAL)
{ }
void update(Xml_node node) override
{
_update_children(node);
/*
* Apply layout to the children
*/
_stack_and_count_child_widgets();
}
Area min_size() const override
{
return _min_size;
@ -87,12 +135,8 @@ struct Menu_view::Box_layout_widget : Widget
void _layout() override
{
_children.for_each([&] (Widget &w) {
if (_direction == VERTICAL)
w.size(Area(geometry().w(), w.min_size().h()));
else
w.size(Area(w.min_size().w(), geometry().h()));
});
_stack_and_count_child_widgets();
_stretch_child_widgets_to_available_size();
}
};

View File

@ -40,7 +40,7 @@ struct Menu_view::Float_widget : Widget
int const h = (_north && _south) ? geometry().h() : child_min.h();
/* align / center child position */
int const x = _east ? 0 : _west ? w_space : w_space / 2;
int const x = _west ? 0 : _east ? w_space : w_space / 2;
int const y = _north ? 0 : _south ? h_space : h_space / 2;
child.geometry(Rect(Point(x, y), Area(w, h)));