nitpicker: fix interplay of hover with dragging

This patch extends the notion of having only one uniquely hovered client
in the presence of held keys.

If motion occurs once a key is pressed (e.g., while dragging), the
receiver of the key sequence observes the motion events. In this case,
we have to submit an artificial leave event to the originally hovered
client so that no more than one client observes itself as being hovered
at the same time. Once the key sequence is finished, the hovering is
updated again, eventually presenting a motion event to the originally
hovered client and a leave event to the receiver of the key sequence.

Issue #4176
This commit is contained in:
Norman Feske 2021-10-12 16:00:48 +02:00
parent 1088035f8e
commit ee463b21ae
2 changed files with 38 additions and 9 deletions

View File

@ -105,11 +105,9 @@ void User_state::_handle_input_event(Input::Event ev)
ev.handle_absolute_motion([&] (int x, int y) {
_pointer_pos = Point(x, y); });
bool const drag = _key_cnt > 0;
/* count keys */
if (ev.press()) _key_cnt++;
if (ev.release() && drag) _key_cnt--;
if (ev.press()) _key_cnt++;
if (ev.release() && (_key_cnt > 0)) _key_cnt--;
/* track key states */
ev.handle_press([&] (Keycode key, Codepoint) {
@ -124,9 +122,25 @@ void User_state::_handle_input_event(Input::Event ev)
_key_array.pressed(key, false);
});
if (ev.absolute_motion() || ev.relative_motion())
if (ev.absolute_motion() || ev.relative_motion()) {
update_hover();
if (_key_cnt > 0) {
_drag = true;
/*
* Submit leave event to the originally hovered client if motion
* occurs while a key is held. Otherwise, both the hovered client
* and the receiver of the key sequence would observe a motion
* event last, each appearing as being hovered at the same time.
*/
if (_hovered && (_input_receiver != _hovered)) {
_hovered->submit_input_event(Hover_leave());
_hovered = nullptr; /* updated when _key_cnt reaches 0 */
}
}
}
/*
* Handle start of a key sequence
*/
@ -252,11 +266,21 @@ void User_state::_handle_input_event(Input::Event ev)
_input_receiver->submit_input_event(ev);
/*
* Detect end of global key sequence
* Detect end of key sequence
*/
if (ev.release() && (_key_cnt == 0) && _global_key_sequence) {
_input_receiver = _focused;
_global_key_sequence = false;
if (ev.release() && (_key_cnt == 0)) {
update_hover();
if (_drag && _input_receiver && (_input_receiver != _hovered))
_input_receiver->submit_input_event(Hover_leave());
_drag = false;
if (_global_key_sequence) {
_input_receiver = _focused;
_global_key_sequence = false;
}
}
}

View File

@ -52,6 +52,11 @@ class Nitpicker::User_state
*/
bool _global_key_sequence = false;
/*
* True if motion events occur while a key is presse
*/
bool _drag = false;
/*
* True if the input focus should change directly whenever the user
* clicks on an unfocused client. This is the traditional behaviour