menu_view: support multiple floats within a frame

This patch refines the hover handling such that a float widget never
responds to hovering unless a child is hovered. This way, it becomes
possible to stack multiple float widgets within one frame and still
reach all child widgets.

Issue #3629
This commit is contained in:
Norman Feske 2020-01-27 14:29:17 +01:00 committed by Christian Helmuth
parent 49ae4a834f
commit 5aae0f2379
2 changed files with 65 additions and 17 deletions

View File

@ -82,6 +82,32 @@ struct Menu_view::Float_widget : Widget
child.size(child.geometry().area());
});
}
/*
* A float widget cannot be hovered on its own. It only responds to
* hovering if its child is hovered. This way, multiple floats can
* be stacked in one frame without interfering with each other.
*/
Hovered hovered(Point at) const override
{
Hovered const result = Widget::hovered(at);
/* respond positively whenever one of our children is hovered */
if (result.unique_id != _unique_id)
return result;
return { .unique_id = { }, .detail = { } };
}
void gen_hover_model(Xml_generator &xml, Point at) const override
{
/* omit ourself from hover model unless one of our children is hovered */
if (!_inner_geometry().contains(at))
return;
Widget::gen_hover_model(xml, at);
}
};
#endif /* _FLOAT_WIDGET_H_ */

View File

@ -254,6 +254,24 @@ class Menu_view::Widget : List_model<Widget>::Element
_geometry = Rect(position, _geometry.area());
}
static Point _at_child(Point at, Widget const &w)
{
return at - w.geometry().p1();
}
static bool _child_hovered(Point at, Widget const &w)
{
return w.hovered(_at_child(at, w)).unique_id.valid();
}
bool _any_child_hovered(Point at) const
{
bool result = false;
_children.for_each([&] (Widget const &w) {
result = result | _child_hovered(at, w); });
return result;
}
/**
* Return unique ID of inner-most hovered widget
*
@ -265,33 +283,37 @@ class Menu_view::Widget : List_model<Widget>::Element
return { };
Hovered result { .unique_id = _unique_id, .detail = { } };
if (!_any_child_hovered(at))
return result;
_children.for_each([&] (Widget const &w) {
Hovered const hovered = w.hovered(at - w.geometry().p1());
Hovered const hovered = w.hovered(_at_child(at, w));
if (hovered.unique_id.valid())
result = hovered;
});
result = hovered; });
return result;
}
virtual void gen_hover_model(Xml_generator &xml, Point at) const
{
if (!_inner_geometry().contains(at))
return;
xml.node(_type_name.string(), [&]() {
_gen_common_hover_attr(xml);
_children.for_each([&] (Widget const &w) {
if (_child_hovered(at, w))
w.gen_hover_model(xml, _at_child(at, w)); });
});
}
void print(Output &out) const
{
Genode::print(out, _name);
}
virtual void gen_hover_model(Xml_generator &xml, Point at) const
{
if (_inner_geometry().contains(at)) {
xml.node(_type_name.string(), [&]() {
_gen_common_hover_attr(xml);
_children.for_each([&] (Widget const &w) {
w.gen_hover_model(xml, at - w.geometry().p1()); });
});
}
}
};
#endif /* _WIDGET_H_ */