2011-12-22 16:19:25 +01:00
|
|
|
/*
|
|
|
|
* \brief User state implementation
|
|
|
|
* \author Norman Feske
|
|
|
|
* \date 2006-08-27
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2017-02-20 13:23:52 +01:00
|
|
|
* Copyright (C) 2006-2017 Genode Labs GmbH
|
2011-12-22 16:19:25 +01:00
|
|
|
*
|
|
|
|
* This file is part of the Genode OS framework, which is distributed
|
2017-02-20 13:23:52 +01:00
|
|
|
* under the terms of the GNU Affero General Public License version 3.
|
2011-12-22 16:19:25 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <input/event.h>
|
|
|
|
#include <input/keycodes.h>
|
2013-09-06 17:34:16 +02:00
|
|
|
|
2011-12-22 16:19:25 +01:00
|
|
|
#include "user_state.h"
|
|
|
|
|
|
|
|
using namespace Input;
|
|
|
|
|
|
|
|
|
|
|
|
/***************
|
|
|
|
** Utilities **
|
|
|
|
***************/
|
|
|
|
|
2013-09-06 17:34:16 +02:00
|
|
|
static inline bool _mouse_button(Keycode keycode) {
|
2011-12-22 16:19:25 +01:00
|
|
|
return keycode >= BTN_LEFT && keycode <= BTN_MIDDLE; }
|
|
|
|
|
|
|
|
|
2017-11-16 17:07:52 +01:00
|
|
|
/**
|
|
|
|
* Determine number of events that can be merged into one
|
|
|
|
*
|
|
|
|
* \param ev pointer to first event array element to check
|
|
|
|
* \param max size of the event array
|
|
|
|
* \return number of events subjected to merge
|
|
|
|
*/
|
|
|
|
static unsigned num_consecutive_events(Input::Event const *ev, unsigned max)
|
|
|
|
{
|
|
|
|
if (max < 1) return 0;
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2018-04-20 14:27:45 +02:00
|
|
|
bool const first_is_absolute_motion = ev->absolute_motion();
|
|
|
|
bool const first_is_relative_motion = ev->relative_motion();
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2017-11-16 17:07:52 +01:00
|
|
|
unsigned cnt = 1;
|
|
|
|
for (ev++ ; cnt < max; cnt++, ev++) {
|
2018-04-20 14:27:45 +02:00
|
|
|
if (first_is_absolute_motion && ev->absolute_motion()) continue;
|
|
|
|
if (first_is_relative_motion && ev->relative_motion()) continue;
|
|
|
|
break;
|
|
|
|
};
|
2017-11-16 17:07:52 +01:00
|
|
|
return cnt;
|
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2017-11-16 17:07:52 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Merge consecutive motion events
|
|
|
|
*
|
|
|
|
* \param ev event array to merge
|
|
|
|
* \param n number of events to merge
|
|
|
|
* \return merged motion event
|
|
|
|
*/
|
|
|
|
static Input::Event merge_motion_events(Input::Event const *ev, unsigned n)
|
2014-06-23 13:15:53 +02:00
|
|
|
{
|
2018-04-20 14:27:45 +02:00
|
|
|
if (n == 0) return Event();
|
|
|
|
|
|
|
|
if (ev->relative_motion()) {
|
|
|
|
int rx = 0, ry = 0;
|
|
|
|
for (unsigned i = 0; i < n; i++, ev++)
|
|
|
|
ev->handle_relative_motion([&] (int x, int y) { rx += x; ry += y; });
|
|
|
|
|
|
|
|
if (rx || ry)
|
|
|
|
return Relative_motion{rx, ry};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ev->absolute_motion()) {
|
|
|
|
int ax = 0, ay = 0;
|
|
|
|
for (unsigned i = 0; i < n; i++, ev++)
|
|
|
|
ev->handle_absolute_motion([&] (int x, int y) { ax = x; ay = y; });
|
|
|
|
|
|
|
|
return Absolute_motion{ax, ay};
|
|
|
|
}
|
|
|
|
|
|
|
|
return Event();
|
2014-06-04 15:58:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-11-16 17:07:52 +01:00
|
|
|
/**************************
|
|
|
|
** User state interface **
|
|
|
|
**************************/
|
|
|
|
|
|
|
|
void User_state::_handle_input_event(Input::Event ev)
|
2011-12-22 16:19:25 +01:00
|
|
|
{
|
2018-04-20 14:27:45 +02:00
|
|
|
/* transparently convert relative into absolute motion event */
|
|
|
|
ev.handle_relative_motion([&] (int x, int y) {
|
2013-09-06 17:34:16 +02:00
|
|
|
|
2018-04-20 14:27:45 +02:00
|
|
|
int const ox = _pointer_pos.x(),
|
|
|
|
oy = _pointer_pos.y();
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2018-04-20 14:27:45 +02:00
|
|
|
int const ax = max(0, min((int)_view_stack.size().w() - 1, ox + x)),
|
|
|
|
ay = max(0, min((int)_view_stack.size().h() - 1, oy + y));
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2018-04-20 14:27:45 +02:00
|
|
|
ev = Absolute_motion{ax, ay};
|
|
|
|
});
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2018-04-20 14:27:45 +02:00
|
|
|
/* respond to motion events by updating the pointer position */
|
|
|
|
ev.handle_absolute_motion([&] (int x, int y) {
|
|
|
|
_pointer_pos = Point(x, y); });
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2017-11-16 17:07:52 +01:00
|
|
|
bool const drag = _key_cnt > 0;
|
|
|
|
|
2013-09-07 02:02:26 +02:00
|
|
|
/* count keys */
|
2018-04-20 14:27:45 +02:00
|
|
|
if (ev.press()) _key_cnt++;
|
|
|
|
if (ev.release() && drag) _key_cnt--;
|
2013-09-07 02:02:26 +02:00
|
|
|
|
2017-10-27 12:14:48 +02:00
|
|
|
/* track key states */
|
2018-04-20 14:27:45 +02:00
|
|
|
ev.handle_press([&] (Keycode key, Codepoint) {
|
|
|
|
if (_key_array.pressed(key))
|
|
|
|
Genode::warning("suspicious double press of ", Input::key_name(key));
|
|
|
|
_key_array.pressed(key, true);
|
|
|
|
});
|
|
|
|
|
|
|
|
ev.handle_release([&] (Keycode key) {
|
|
|
|
if (!_key_array.pressed(key))
|
|
|
|
Genode::warning("suspicious double release of ", Input::key_name(key));
|
|
|
|
_key_array.pressed(key, false);
|
|
|
|
});
|
2017-10-27 12:14:48 +02:00
|
|
|
|
2017-11-16 17:07:52 +01:00
|
|
|
View_component const * const pointed_view = _view_stack.find_view(_pointer_pos);
|
|
|
|
|
|
|
|
View_owner * const hovered = pointed_view ? &pointed_view->owner() : 0;
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
/*
|
2018-04-27 14:10:45 +02:00
|
|
|
* Deliver a leave event if pointed-to session changed, notify newly
|
|
|
|
* hovered session about the current pointer position.
|
2011-12-22 16:19:25 +01:00
|
|
|
*/
|
2018-04-27 14:10:45 +02:00
|
|
|
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;
|
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2013-09-06 17:34:16 +02:00
|
|
|
/*
|
|
|
|
* Handle start of a key sequence
|
|
|
|
*/
|
2018-04-20 14:27:45 +02:00
|
|
|
ev.handle_press([&] (Keycode keycode, Codepoint) {
|
|
|
|
|
|
|
|
if (_key_cnt != 1)
|
|
|
|
return;
|
2013-09-07 02:02:26 +02:00
|
|
|
|
2017-11-16 17:07:52 +01:00
|
|
|
View_owner *global_receiver = nullptr;
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2014-06-04 15:58:49 +02:00
|
|
|
/* update focused session */
|
2015-12-08 12:44:34 +01:00
|
|
|
if (_mouse_button(keycode)
|
2017-11-16 17:07:52 +01:00
|
|
|
&& _hovered
|
|
|
|
&& (_hovered != _focused)
|
|
|
|
&& (_hovered->has_focusable_domain()
|
|
|
|
|| _hovered->has_same_domain(_focused))) {
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
/*
|
2014-06-04 15:58:49 +02:00
|
|
|
* Notify both the old focused session and the new one.
|
2011-12-22 16:19:25 +01:00
|
|
|
*/
|
2018-04-20 14:27:45 +02:00
|
|
|
if (_focused)
|
|
|
|
_focused->submit_input_event(Focus_leave());
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2017-11-16 17:07:52 +01:00
|
|
|
if (_hovered) {
|
2018-04-20 14:27:45 +02:00
|
|
|
_hovered->submit_input_event(Absolute_motion{_pointer_pos.x(),
|
|
|
|
_pointer_pos.y()});
|
|
|
|
_hovered->submit_input_event(Focus_enter());
|
2013-01-12 23:34:49 +01:00
|
|
|
}
|
|
|
|
|
2017-11-19 20:11:50 +01:00
|
|
|
if (_hovered->has_transient_focusable_domain()) {
|
2018-04-05 19:50:56 +02:00
|
|
|
global_receiver = &_hovered->forwarded_focus();
|
2017-11-19 20:11:50 +01:00
|
|
|
} else {
|
2017-11-19 22:24:58 +01:00
|
|
|
/*
|
|
|
|
* Distinguish the use of the builtin focus switching and the
|
|
|
|
* alternative use of an external focus policy. In the latter
|
|
|
|
* case, focusable domains are handled like transiently
|
|
|
|
* focusable domains. The permanent focus change is triggered
|
|
|
|
* by an external focus-policy component by posting and updated
|
|
|
|
* focus ROM, which is then propagated into the user state via
|
|
|
|
* the 'User_state::focus' and 'User_state::reset_focus'
|
|
|
|
* methods.
|
|
|
|
*/
|
|
|
|
if (_focus_via_click)
|
2018-04-05 19:50:56 +02:00
|
|
|
_focus_view_owner_via_click(_hovered->forwarded_focus());
|
2017-11-19 22:24:58 +01:00
|
|
|
else
|
2018-04-05 19:50:56 +02:00
|
|
|
global_receiver = &_hovered->forwarded_focus();
|
2017-11-19 22:24:58 +01:00
|
|
|
|
2017-11-19 20:11:50 +01:00
|
|
|
_last_clicked = _hovered;
|
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
}
|
|
|
|
|
2013-09-06 17:34:16 +02:00
|
|
|
/*
|
|
|
|
* If there exists a global rule for the pressed key, set the
|
|
|
|
* corresponding session as receiver of the input stream until the key
|
|
|
|
* count reaches zero. Otherwise, the input stream is directed to the
|
2014-06-04 15:58:49 +02:00
|
|
|
* pointed-at session.
|
2013-09-06 17:34:16 +02:00
|
|
|
*
|
|
|
|
* If we deliver a global key sequence, we temporarily change the focus
|
|
|
|
* to the global receiver. To reflect that change, we need to update
|
|
|
|
* the whole screen.
|
|
|
|
*/
|
2015-09-30 11:30:02 +02:00
|
|
|
if (!global_receiver)
|
|
|
|
global_receiver = _global_keys.global_receiver(keycode);
|
|
|
|
|
2013-09-06 17:34:16 +02:00
|
|
|
if (global_receiver) {
|
2017-11-16 17:07:52 +01:00
|
|
|
_global_key_sequence = true;
|
|
|
|
_input_receiver = global_receiver;
|
2013-09-06 17:34:16 +02:00
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2013-09-06 17:34:16 +02:00
|
|
|
/*
|
|
|
|
* No global rule matched, so the input stream gets directed to the
|
2014-06-04 15:58:49 +02:00
|
|
|
* focused session or refers to a built-in operation.
|
2013-09-06 17:34:16 +02:00
|
|
|
*/
|
2017-11-16 17:07:52 +01:00
|
|
|
if (!global_receiver)
|
|
|
|
_input_receiver = _focused;
|
2018-04-20 14:27:45 +02:00
|
|
|
});
|
2011-12-22 16:19:25 +01:00
|
|
|
|
|
|
|
/*
|
2015-09-30 11:30:02 +02:00
|
|
|
* Deliver event to session
|
2011-12-22 16:19:25 +01:00
|
|
|
*/
|
2018-04-20 14:27:45 +02:00
|
|
|
if (ev.absolute_motion() || ev.wheel() || ev.touch() || ev.touch_release()) {
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2017-11-16 17:07:52 +01:00
|
|
|
if (_key_cnt == 0) {
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2017-11-16 17:07:52 +01:00
|
|
|
if (_hovered) {
|
2015-09-30 11:30:02 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Unless the domain of the pointed session is configured to
|
|
|
|
* always receive hover events, we deliver motion events only
|
|
|
|
* to the focused domain.
|
|
|
|
*/
|
2017-11-16 17:07:52 +01:00
|
|
|
if (_hovered->hover_always()
|
|
|
|
|| _hovered->has_same_domain(_focused))
|
|
|
|
_hovered->submit_input_event(ev);
|
2015-09-30 11:30:02 +02:00
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2013-09-06 17:34:16 +02:00
|
|
|
} else if (_input_receiver)
|
2013-09-07 02:02:26 +02:00
|
|
|
_input_receiver->submit_input_event(ev);
|
2011-12-22 16:19:25 +01:00
|
|
|
}
|
|
|
|
|
2014-06-17 10:25:58 +02:00
|
|
|
/*
|
2015-09-30 11:30:02 +02:00
|
|
|
* Deliver press/release event to focused session or the receiver of global
|
|
|
|
* key.
|
2014-06-17 10:25:58 +02:00
|
|
|
*/
|
2018-04-20 14:27:45 +02:00
|
|
|
ev.handle_press([&] (Keycode key, Codepoint) {
|
|
|
|
|
|
|
|
if (!_input_receiver)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!_mouse_button(key)
|
2017-11-16 17:07:52 +01:00
|
|
|
|| (_hovered
|
|
|
|
&& (_hovered->has_focusable_domain()
|
|
|
|
|| _hovered->has_same_domain(_focused))))
|
2013-09-07 02:02:26 +02:00
|
|
|
_input_receiver->submit_input_event(ev);
|
2015-12-08 12:44:34 +01:00
|
|
|
else
|
|
|
|
_input_receiver = nullptr;
|
2018-04-20 14:27:45 +02:00
|
|
|
});
|
2015-12-08 12:44:34 +01:00
|
|
|
|
2018-04-20 14:27:45 +02:00
|
|
|
if (ev.release() && _input_receiver)
|
2017-02-13 16:20:28 +01:00
|
|
|
_input_receiver->submit_input_event(ev);
|
|
|
|
|
2013-09-06 17:34:16 +02:00
|
|
|
/*
|
|
|
|
* Detect end of global key sequence
|
|
|
|
*/
|
2018-04-20 14:27:45 +02:00
|
|
|
if (ev.release() && (_key_cnt == 0) && _global_key_sequence) {
|
|
|
|
_input_receiver = _focused;
|
2017-11-16 17:07:52 +01:00
|
|
|
_global_key_sequence = false;
|
|
|
|
}
|
|
|
|
}
|
2014-02-14 14:31:03 +01:00
|
|
|
|
|
|
|
|
2017-11-16 17:07:52 +01:00
|
|
|
User_state::Handle_input_result
|
|
|
|
User_state::handle_input_events(Input::Event const * const ev_buf,
|
|
|
|
unsigned const num_ev)
|
|
|
|
{
|
|
|
|
Point const old_pointer_pos = _pointer_pos;
|
|
|
|
View_owner * const old_hovered = _hovered;
|
|
|
|
View_owner const * const old_focused = _focused;
|
|
|
|
View_owner const * const old_input_receiver = _input_receiver;
|
2017-11-19 20:11:50 +01:00
|
|
|
View_owner const * const old_last_clicked = _last_clicked;
|
2014-02-14 14:31:03 +01:00
|
|
|
|
2017-11-20 20:23:41 +01:00
|
|
|
bool button_activity = false;
|
2017-11-16 17:07:52 +01:00
|
|
|
|
|
|
|
if (num_ev > 0) {
|
|
|
|
/*
|
|
|
|
* Take events from input event buffer, merge consecutive motion
|
|
|
|
* events, and pass result to the user state.
|
|
|
|
*/
|
|
|
|
for (unsigned src_ev_cnt = 0; src_ev_cnt < num_ev; src_ev_cnt++) {
|
|
|
|
|
|
|
|
Input::Event const *e = &ev_buf[src_ev_cnt];
|
|
|
|
Input::Event curr = *e;
|
|
|
|
|
2018-04-20 14:27:45 +02:00
|
|
|
if (e->absolute_motion() || e->relative_motion()) {
|
|
|
|
unsigned const n = num_consecutive_events(e, num_ev - src_ev_cnt);
|
2017-11-16 17:07:52 +01:00
|
|
|
curr = merge_motion_events(e, n);
|
|
|
|
|
|
|
|
/* skip merged events */
|
|
|
|
src_ev_cnt += n - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we detect a pressed key sometime during the event processing,
|
|
|
|
* we regard the user as active. This check captures the presence
|
|
|
|
* of press-release combinations within one batch of input events.
|
|
|
|
*/
|
2017-11-20 20:23:41 +01:00
|
|
|
button_activity |= _key_pressed();
|
2017-11-16 17:07:52 +01:00
|
|
|
|
|
|
|
/* pass event to user state */
|
|
|
|
_handle_input_event(curr);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Besides handling input events, 'user_state.handle_event()' also
|
|
|
|
* updates the pointed session, which might have changed by other
|
|
|
|
* means, for example view movement.
|
|
|
|
*/
|
2018-04-20 14:27:45 +02:00
|
|
|
_handle_input_event(Event());
|
2013-09-06 17:34:16 +02:00
|
|
|
}
|
2017-11-16 17:07:52 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If at least one key is kept pressed, we regard the user as active.
|
|
|
|
*/
|
2017-11-20 20:23:41 +01:00
|
|
|
button_activity |= _key_pressed();
|
2017-11-16 17:07:52 +01:00
|
|
|
|
|
|
|
bool key_state_affected = false;
|
|
|
|
for (unsigned i = 0; i < num_ev; i++)
|
2018-04-20 14:27:45 +02:00
|
|
|
key_state_affected |= (ev_buf[i].press() || ev_buf[i].release());
|
2017-11-16 17:07:52 +01:00
|
|
|
|
|
|
|
_apply_pending_focus_change();
|
|
|
|
|
|
|
|
return {
|
2017-11-20 20:23:41 +01:00
|
|
|
.hover_changed = _hovered != old_hovered,
|
|
|
|
.focus_changed = (_focused != old_focused) ||
|
|
|
|
(_input_receiver != old_input_receiver),
|
|
|
|
.key_state_affected = key_state_affected,
|
|
|
|
.button_activity = button_activity,
|
|
|
|
.motion_activity = (_pointer_pos != old_pointer_pos),
|
|
|
|
.key_pressed = _key_pressed(),
|
|
|
|
.last_clicked_changed = (_last_clicked != old_last_clicked)
|
2017-11-16 17:07:52 +01:00
|
|
|
};
|
2011-12-22 16:19:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-11-16 17:07:52 +01:00
|
|
|
void User_state::report_keystate(Xml_generator &xml) const
|
2017-10-27 12:14:48 +02:00
|
|
|
{
|
2017-11-16 17:07:52 +01:00
|
|
|
xml.attribute("count", _key_cnt);
|
2017-10-27 12:14:48 +02:00
|
|
|
_key_array.report_state(xml);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-11-16 17:07:52 +01:00
|
|
|
void User_state::report_pointer_position(Xml_generator &xml) const
|
|
|
|
{
|
|
|
|
xml.attribute("xpos", _pointer_pos.x());
|
|
|
|
xml.attribute("ypos", _pointer_pos.y());
|
|
|
|
}
|
2011-12-22 16:19:25 +01:00
|
|
|
|
2017-11-16 17:07:52 +01:00
|
|
|
|
2017-11-20 20:23:41 +01:00
|
|
|
void User_state::report_hovered_view_owner(Xml_generator &xml, bool active) const
|
2011-12-22 16:19:25 +01:00
|
|
|
{
|
2017-11-16 17:07:52 +01:00
|
|
|
if (_hovered)
|
|
|
|
_hovered->report(xml);
|
2017-11-20 20:23:41 +01:00
|
|
|
|
|
|
|
if (active) xml.attribute("active", "yes");
|
2017-11-16 17:07:52 +01:00
|
|
|
}
|
2013-09-07 02:02:26 +02:00
|
|
|
|
2017-11-16 17:07:52 +01:00
|
|
|
|
|
|
|
void User_state::report_focused_view_owner(Xml_generator &xml, bool active) const
|
|
|
|
{
|
|
|
|
if (_focused) {
|
|
|
|
_focused->report(xml);
|
|
|
|
|
|
|
|
if (active) xml.attribute("active", "yes");
|
2014-06-04 15:58:49 +02:00
|
|
|
}
|
2017-11-16 17:07:52 +01:00
|
|
|
}
|
2014-06-06 17:26:53 +02:00
|
|
|
|
2017-11-16 17:07:52 +01:00
|
|
|
|
2017-11-19 20:11:50 +01:00
|
|
|
void User_state::report_last_clicked_view_owner(Xml_generator &xml) const
|
|
|
|
{
|
|
|
|
if (_last_clicked)
|
|
|
|
_last_clicked->report(xml);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-09-10 23:05:45 +02:00
|
|
|
User_state::Handle_forget_result User_state::forget(View_owner const &owner)
|
2017-11-16 17:07:52 +01:00
|
|
|
{
|
|
|
|
_focus.forget(owner);
|
|
|
|
|
2018-01-12 22:11:48 +01:00
|
|
|
bool const need_to_update_all_views = (&owner == _focused);
|
2018-09-10 23:05:45 +02:00
|
|
|
bool const hover_changed = &owner == _hovered;
|
|
|
|
bool const focus_changed = &owner == _focused;
|
2018-01-12 22:11:48 +01:00
|
|
|
|
2017-11-16 17:07:52 +01:00
|
|
|
if (&owner == _focused) _focused = nullptr;
|
|
|
|
if (&owner == _next_focused) _next_focused = nullptr;
|
2017-11-19 20:11:50 +01:00
|
|
|
if (&owner == _last_clicked) _last_clicked = nullptr;
|
2017-11-16 17:07:52 +01:00
|
|
|
|
|
|
|
if (_hovered == &owner) {
|
|
|
|
View_component * const pointed_view = _view_stack.find_view(_pointer_pos);
|
|
|
|
_hovered = pointed_view ? &pointed_view->owner() : nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_input_receiver == &owner)
|
2014-06-06 17:26:53 +02:00
|
|
|
_input_receiver = nullptr;
|
2018-01-12 22:11:48 +01:00
|
|
|
|
|
|
|
if (need_to_update_all_views)
|
|
|
|
_view_stack.update_all_views();
|
2018-09-10 23:05:45 +02:00
|
|
|
|
|
|
|
return {
|
|
|
|
.hover_changed = hover_changed,
|
|
|
|
.focus_changed = focus_changed,
|
|
|
|
};
|
2011-12-22 16:19:25 +01:00
|
|
|
}
|
|
|
|
|
2014-06-04 15:58:49 +02:00
|
|
|
|
2017-11-16 17:07:52 +01:00
|
|
|
void User_state::_focus_view_owner_via_click(View_owner &owner)
|
2014-06-04 15:58:49 +02:00
|
|
|
{
|
2017-11-16 17:07:52 +01:00
|
|
|
_next_focused = &owner;
|
|
|
|
_focused = &owner;
|
|
|
|
|
|
|
|
_focus.assign(owner);
|
2014-06-04 15:58:49 +02:00
|
|
|
|
|
|
|
if (!_global_key_sequence)
|
2017-11-16 17:07:52 +01:00
|
|
|
_input_receiver = &owner;
|
2014-06-04 15:58:49 +02:00
|
|
|
}
|