nitpicker: update hovering on view-stack changes

With the change of nitpicker to the event session interface, the
formerly periodic hovering updates moved to the - now sporadic - input
processing. This has the unfortunate side effect that hovering changes
caused by non-user-input, in particular view-stack changes issued by the
GUI clients that change the view under the current pointer position,
would no longer be reported immediately but only after receiving the
next incoming input event.

This patch reworks the hover handling such that potential hovering
changes due to view-stack operations are evaluated immediately by
those operations, covering the update of the hover report and the
generation of artificial enter/motion events.

Issue #3812
This commit is contained in:
Norman Feske 2020-09-04 13:46:52 +02:00 committed by Christian Helmuth
parent ff82dc1ad5
commit 328a4fa644
6 changed files with 73 additions and 35 deletions

View File

@ -20,6 +20,7 @@
namespace Nitpicker {
struct Focus;
struct Focus_updater : Interface { virtual void update_focus() = 0; };
struct Hover_updater : Interface { virtual void update_hover() = 0; };
}

View File

@ -337,6 +337,8 @@ void Gui_session::destroy_view(View_handle handle)
break;
}
}
_hover_updater.update_hover();
}
@ -386,6 +388,7 @@ void Gui_session::execute()
catch (View_handle_registry::Lookup_failed) {
warning("view lookup failed during command execution"); }
}
_hover_updater.update_hover();
}
@ -446,6 +449,8 @@ void Gui_session::session_control(Label suffix, Session_control control)
_view_stack.to_front(Label(label(), suffix).string());
break;
}
_hover_updater.update_hover();
}

View File

@ -104,6 +104,7 @@ class Nitpicker::Gui_session : public Session_object<Gui::Session>,
View_stack &_view_stack;
Focus_updater &_focus_updater;
Hover_updater &_hover_updater;
Signal_context_capability _mode_sigh { };
@ -174,6 +175,7 @@ class Nitpicker::Gui_session : public Session_object<Gui::Session>,
Diag const &diag,
View_stack &view_stack,
Focus_updater &focus_updater,
Hover_updater &hover_updater,
View &pointer_origin,
View &builtin_background,
bool provides_default_bg,
@ -185,7 +187,8 @@ class Nitpicker::Gui_session : public Session_object<Gui::Session>,
_ram(env.ram(), _ram_quota_guard(), _cap_quota_guard()),
_session_alloc(_ram, env.rm()),
_framebuffer_session_component(view_stack, *this, *this),
_view_stack(view_stack), _focus_updater(focus_updater),
_view_stack(view_stack),
_focus_updater(focus_updater), _hover_updater(hover_updater),
_pointer_origin(pointer_origin),
_builtin_background(builtin_background),
_framebuffer_session_cap(_env.ep().manage(_framebuffer_session_component)),

View File

@ -83,6 +83,7 @@ class Nitpicker::Gui_root : public Root_component<Gui_session>,
Reporter &_focus_reporter;
Reporter &_hover_reporter;
Focus_updater &_focus_updater;
Hover_updater &_hover_updater;
protected:
@ -95,8 +96,8 @@ class Nitpicker::Gui_root : public Root_component<Gui_session>,
Gui_session *session = new (md_alloc())
Gui_session(_env,
session_resources_from_args(args), label,
session_diag_from_args(args),
_view_stack, _focus_updater, _pointer_origin,
session_diag_from_args(args), _view_stack,
_focus_updater, _hover_updater, _pointer_origin,
_builtin_background, provides_default_bg,
_focus_reporter, *this);
@ -104,6 +105,7 @@ class Nitpicker::Gui_root : public Root_component<Gui_session>,
_session_list.insert(session);
_global_keys.apply_config(_config.xml(), _session_list);
_focus_updater.update_focus();
_hover_updater.update_hover();
return session;
}
@ -128,11 +130,8 @@ class Nitpicker::Gui_root : public Root_component<Gui_session>,
Genode::destroy(md_alloc(), session);
/* report hover changes */
if (_hover_reporter.enabled() && result.hover_changed) {
Reporter::Xml_generator xml(_hover_reporter, [&] () {
_user_state.report_hovered_view_owner(xml, false); });
}
if (result.hover_changed)
_hover_updater.update_hover();
/* report focus changes */
if (_focus_reporter.enabled() && result.focus_changed) {
@ -158,7 +157,8 @@ class Nitpicker::Gui_root : public Root_component<Gui_session>,
Allocator &md_alloc,
Reporter &focus_reporter,
Reporter &hover_reporter,
Focus_updater &focus_updater)
Focus_updater &focus_updater,
Hover_updater &hover_updater)
:
Root_component<Gui_session>(&env.ep().rpc_ep(), &md_alloc),
_env(env), _config(config), _session_list(session_list),
@ -167,7 +167,7 @@ class Nitpicker::Gui_root : public Root_component<Gui_session>,
_pointer_origin(pointer_origin),
_builtin_background(builtin_background),
_focus_reporter(focus_reporter), _hover_reporter(hover_reporter),
_focus_updater(focus_updater)
_focus_updater(focus_updater), _hover_updater(hover_updater)
{ }
@ -343,7 +343,7 @@ class Nitpicker::Event_root : public Root_component<Event_session>
};
struct Nitpicker::Main : Focus_updater,
struct Nitpicker::Main : Focus_updater, Hover_updater,
View_stack::Damage,
Capture_session::Handler,
Event_session::Handler
@ -467,12 +467,20 @@ struct Nitpicker::Main : Focus_updater,
Gui_root _gui_root { _env, _config_rom, _session_list, *_domain_registry,
_global_keys, _view_stack, _user_state, _pointer_origin,
_builtin_background, _sliced_heap,
_focus_reporter, _hover_reporter, *this };
_focus_reporter, _hover_reporter, *this, *this };
Capture_root _capture_root { _env, _sliced_heap, _view_stack, *this };
Event_root _event_root { _env, _sliced_heap, *this };
void _generate_hover_report()
{
if (_hover_reporter.enabled()) {
Reporter::Xml_generator xml(_hover_reporter, [&] () {
_user_state.report_hovered_view_owner(xml, false); });
}
}
/**
* View_stack::Damage interface
*/
@ -536,6 +544,17 @@ struct Nitpicker::Main : Focus_updater,
*/
void update_focus() override { _handle_focus(); }
/**
* Hover_updater interface
*
* Called whenever the view composition changes.
*/
void update_hover() override
{
if (_user_state.update_hover().hover_changed)
_generate_hover_report();
}
/*
* Configuration-update handler, executed in the context of the RPC
* entrypoint.

View File

@ -124,23 +124,8 @@ void User_state::_handle_input_event(Input::Event ev)
_key_array.pressed(key, false);
});
View const * const pointed_view = _view_stack.find_view(_pointer_pos);
View_owner * const hovered = pointed_view ? &pointed_view->owner() : 0;
/*
* Deliver a leave event if pointed-to session changed, notify newly
* hovered session about the current pointer position.
*/
if (hovered != _hovered) {
if (_hovered)
_hovered->submit_input_event(Hover_leave());
if (hovered && _key_cnt == 0)
hovered->submit_input_event(Absolute_motion{_pointer_pos.x(),
_pointer_pos.y()});
_hovered = hovered;
}
if (ev.absolute_motion() || ev.relative_motion())
update_hover();
/*
* Handle start of a key sequence
@ -411,17 +396,13 @@ User_state::Handle_forget_result User_state::forget(View_owner const &owner)
_focus.forget(owner);
bool const need_to_update_all_views = (&owner == _focused);
bool const hover_changed = &owner == _hovered;
bool const focus_changed = &owner == _focused;
if (&owner == _focused) _focused = nullptr;
if (&owner == _next_focused) _next_focused = nullptr;
if (&owner == _last_clicked) _last_clicked = nullptr;
if (_hovered == &owner) {
View * const pointed_view = _view_stack.find_view(_pointer_pos);
_hovered = pointed_view ? &pointed_view->owner() : nullptr;
}
Update_hover_result const update_hover_result = update_hover();
if (_input_receiver == &owner)
_input_receiver = nullptr;
@ -430,12 +411,36 @@ User_state::Handle_forget_result User_state::forget(View_owner const &owner)
_view_stack.update_all_views();
return {
.hover_changed = hover_changed,
.hover_changed = update_hover_result.hover_changed,
.focus_changed = focus_changed,
};
}
User_state::Update_hover_result User_state::update_hover()
{
View_owner * const old_hovered = _hovered;
View const * const pointed_view = _view_stack.find_view(_pointer_pos);
_hovered = pointed_view ? &pointed_view->owner() : nullptr;
/*
* Deliver a leave event if pointed-to session changed, notify newly
* hovered session about the current pointer position.
*/
if (old_hovered != _hovered) {
if (old_hovered)
old_hovered->submit_input_event(Hover_leave());
if (_hovered && _key_cnt == 0)
_hovered->submit_input_event(Absolute_motion{_pointer_pos.x(),
_pointer_pos.y()});
}
return { .hover_changed = (_hovered != old_hovered) };
}
void User_state::_focus_view_owner_via_click(View_owner &owner)
{
_next_focused = &owner;

View File

@ -252,8 +252,13 @@ class Nitpicker::User_state
bool const hover_changed;
bool const focus_changed;
};
Handle_forget_result forget(View_owner const &);
struct Update_hover_result { bool const hover_changed; };
Update_hover_result update_hover();
void report_keystate(Xml_generator &) const;
void report_pointer_position(Xml_generator &) const;
void report_hovered_view_owner(Xml_generator &, bool motion_active) const;