From bf52f73d03c8b96c815847f5a5bfcf8215f36b28 Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Wed, 9 May 2018 18:03:56 +0200 Subject: [PATCH] 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. --- repos/gems/run/menu_view.run | 55 +++++++++---- .../src/app/menu_view/box_layout_widget.h | 80 ++++++++++++++----- repos/gems/src/app/menu_view/float_widget.h | 2 +- 3 files changed, 101 insertions(+), 36 deletions(-) diff --git a/repos/gems/run/menu_view.run b/repos/gems/run/menu_view.run index 2eedcc16d4..ddb504e037 100644 --- a/repos/gems/run/menu_view.run +++ b/repos/gems/run/menu_view.run @@ -158,23 +158,44 @@ install_config { - - - - - - + + + + + + + + + + + + + + + + + + + + diff --git a/repos/gems/src/app/menu_view/box_layout_widget.h b/repos/gems/src/app/menu_view/box_layout_widget.h index dd04d37ab1..4df7f346c1 100644 --- a/repos/gems/src/app/menu_view/box_layout_widget.h +++ b/repos/gems/src/app/menu_view/box_layout_widget.h @@ -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(); } }; diff --git a/repos/gems/src/app/menu_view/float_widget.h b/repos/gems/src/app/menu_view/float_widget.h index 80cf1bec52..793ebc65cd 100644 --- a/repos/gems/src/app/menu_view/float_widget.h +++ b/repos/gems/src/app/menu_view/float_widget.h @@ -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)));