menu view: immediate hover update on dialog change

This patch improves the hover handling in situations where the dialog
changes under the pointer. Previously, hover changes were reported
as response to user input only, which failed to cover this case. This
became a problem with Sculpt CE's '+' menu, which changes on the fly
when entering/leaving sub menus.

The patch also cleanly separates the hover handling from the focus
handling. Originally, the hovering was reset when the menu view got
unfocused. In situations like Sculpt's '+' menu where the menu view
receives a transient focus only while clicked and gets unfocused on the
button-release event (aka clack), each clack would invalidate the hover
information until a new input event comes in.

Finally, the patch introduces the clear distinction between situations
where the entire dialog is hovered or not. Previously, this state was
somehow implicitly kept by issuing an invalid hover report whenever a
leave event was observed.

Issue #3209
This commit is contained in:
Norman Feske 2019-03-06 10:20:22 +01:00 committed by Christian Helmuth
parent a61c5b6be3
commit 014e800e8a

View File

@ -43,8 +43,19 @@ struct Menu_view::Main
Nitpicker::Session::View_handle _view_handle = _nitpicker.create_view();
/**
* Dialog position in screen coordinate space
*/
Point _position { };
/**
* Last pointer position at the time of the most recent hovering report,
* in screen coordinate space.
*/
Point _hovered_position { };
bool _dialog_hovered = false;
Area _configured_size { };
Area _visible_size { };
@ -120,7 +131,7 @@ struct Menu_view::Main
Attached_dataspace _input_ds { _env.rm(), _nitpicker.input()->dataspace() };
Widget::Unique_id _hovered { };
Widget::Unique_id _last_reported_hovered { };
void _handle_config();
@ -154,6 +165,8 @@ struct Menu_view::Main
Genode::Reporter _hover_reporter = { _env, "hover" };
void _update_hover_report();
bool _schedule_redraw = false;
/**
@ -190,6 +203,28 @@ struct Menu_view::Main
};
void Menu_view::Main::_update_hover_report()
{
if (!_hover_reporter.enabled())
return;
if (!_dialog_hovered) {
Genode::Reporter::Xml_generator xml(_hover_reporter, [&] () { });
return;
}
Widget::Unique_id const new_hovered = _root_widget.hovered(_hovered_position);
if (_last_reported_hovered != new_hovered) {
Genode::Reporter::Xml_generator xml(_hover_reporter, [&] () {
_root_widget.gen_hover_model(xml, _hovered_position); });
_last_reported_hovered = new_hovered;
}
}
void Menu_view::Main::_handle_dialog_update()
{
try {
@ -204,12 +239,15 @@ void Menu_view::Main::_handle_dialog_update()
_dialog_rom.update();
Xml_node dialog = _dialog_rom.xml();
if (dialog.has_type("empty"))
return;
_root_widget.update(dialog);
_root_widget.size(_root_widget_size());
_update_hover_report();
_schedule_redraw = true;
/*
@ -247,35 +285,29 @@ void Menu_view::Main::_handle_config()
void Menu_view::Main::_handle_input()
{
Point const orig_hovered_position = _hovered_position;
bool const orig_dialog_hovered = _dialog_hovered;
_nitpicker.input()->for_each_event([&] (Input::Event const &ev) {
ev.handle_absolute_motion([&] (int x, int y) {
Point const at = Point(x, y) - _position;
Widget::Unique_id const new_hovered = _root_widget.hovered(at);
if (_hovered != new_hovered) {
if (_hover_reporter.enabled()) {
Genode::Reporter::Xml_generator xml(_hover_reporter, [&] () {
_root_widget.gen_hover_model(xml, at);
});
}
_hovered = new_hovered;
}
_dialog_hovered = true;
_hovered_position = Point(x, y) - _position;
});
/*
* Reset hover model when losing the focus
*/
if (ev.focus_leave() || ev.hover_leave()) {
_hovered = Widget::Unique_id();
if (_hover_reporter.enabled()) {
Genode::Reporter::Xml_generator xml(_hover_reporter, [&] () { });
}
if (ev.hover_leave()) {
_dialog_hovered = false;
_hovered_position = Point();
}
});
bool const hover_changed = orig_dialog_hovered != _dialog_hovered
|| orig_hovered_position != _hovered_position;
if (hover_changed)
_update_hover_report();
}
@ -319,7 +351,6 @@ void Menu_view::Main::_handle_frame_timer()
else
_buffer->reset_surface();
_root_widget.size(size);
_root_widget.position(Point(0, 0));
// XXX restrict redraw to dirty regions