267 lines
6.9 KiB
C++
267 lines
6.9 KiB
C++
/*
|
|
* \brief Floating window layouter
|
|
* \author Norman Feske
|
|
* \date 2013-02-14
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2013-2015 Genode Labs GmbH
|
|
*
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
* under the terms of the GNU General Public License version 2.
|
|
*/
|
|
|
|
#ifndef _USER_STATE_H_
|
|
#define _USER_STATE_H_
|
|
|
|
/* local includes */
|
|
#include "operations.h"
|
|
|
|
namespace Floating_window_layouter { class User_state; }
|
|
|
|
|
|
class Floating_window_layouter::User_state
|
|
{
|
|
public:
|
|
|
|
struct Hover_state
|
|
{
|
|
Window_id window_id;
|
|
Window::Element element { Window::Element::UNDEFINED };
|
|
|
|
Hover_state(Window_id id, Window::Element element)
|
|
:
|
|
window_id(id), element(element)
|
|
{ }
|
|
};
|
|
|
|
private:
|
|
|
|
Window_id _hovered_window_id;
|
|
Window_id _focused_window_id;
|
|
Window_id _dragged_window_id;
|
|
|
|
unsigned _key_cnt = 0;
|
|
|
|
Window::Element _hovered_element = Window::Element::UNDEFINED;
|
|
Window::Element _dragged_element = Window::Element::UNDEFINED;
|
|
|
|
/*
|
|
* True while drag operation in progress
|
|
*/
|
|
bool _drag_state = false;
|
|
|
|
/*
|
|
* False if the hover state (hovered window and element) was not known
|
|
* at the initial click of a drag operation. In this case, the drag
|
|
* operation is initiated as soon as the hover state becomes known.
|
|
*/
|
|
bool _drag_init_done = false;
|
|
|
|
/*
|
|
* Pointer position at the beginning of a drag operation
|
|
*/
|
|
Point _pointer_clicked;
|
|
|
|
/*
|
|
* Current pointer position
|
|
*/
|
|
Point _pointer_curr;
|
|
|
|
Operations &_operations;
|
|
|
|
inline void _handle_event(Input::Event const &);
|
|
|
|
void _initiate_drag(Window_id hovered_window_id,
|
|
Window::Element hovered_element)
|
|
{
|
|
/*
|
|
* This function must never be called without the hover state to be
|
|
* defined. This assertion checks this precondition.
|
|
*/
|
|
if (!hovered_window_id.valid()) {
|
|
struct Drag_with_undefined_hover_state { };
|
|
throw Drag_with_undefined_hover_state();
|
|
}
|
|
|
|
_drag_init_done = true;
|
|
_dragged_window_id = hovered_window_id;
|
|
_dragged_element = hovered_element;
|
|
|
|
/*
|
|
* Toggle maximized (fullscreen) state
|
|
*/
|
|
if (_hovered_element == Window::Element::MAXIMIZER) {
|
|
|
|
_dragged_window_id = _hovered_window_id;
|
|
_focused_window_id = _hovered_window_id;
|
|
|
|
_operations.toggle_fullscreen(_hovered_window_id);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Bring hovered window to front when clicked
|
|
*/
|
|
if (_focused_window_id != _hovered_window_id) {
|
|
|
|
_focused_window_id = _hovered_window_id;
|
|
|
|
_operations.to_front(_hovered_window_id);
|
|
_operations.focus(_hovered_window_id);
|
|
}
|
|
|
|
_operations.drag(_dragged_window_id, _dragged_element,
|
|
_pointer_clicked, _pointer_curr);
|
|
}
|
|
|
|
public:
|
|
|
|
User_state(Operations &operations) : _operations(operations) { }
|
|
|
|
void handle_input(Input::Event const events[], unsigned num_events)
|
|
{
|
|
Point const pointer_last = _pointer_curr;
|
|
|
|
for (size_t i = 0; i < num_events; i++)
|
|
_handle_event(events[i]);
|
|
|
|
/*
|
|
* Issue drag operation when in dragged state
|
|
*/
|
|
if (_drag_state && _drag_init_done && (_pointer_curr != pointer_last))
|
|
_operations.drag(_dragged_window_id, _dragged_element,
|
|
_pointer_clicked, _pointer_curr);
|
|
}
|
|
|
|
void hover(Window_id window_id, Window::Element element)
|
|
{
|
|
Window_id const last_hovered_window_id = _hovered_window_id;
|
|
|
|
_hovered_window_id = window_id;
|
|
_hovered_element = element;
|
|
|
|
/*
|
|
* Check if we have just received an update while already being in
|
|
* dragged state.
|
|
*
|
|
* This can happen when the user selects a new nitpicker domain by
|
|
* clicking on a window decoration. Prior the click, the new
|
|
* session is not aware of the current mouse position. So the hover
|
|
* model is not up to date. As soon as nitpicker assigns the focus
|
|
* to the new session and delivers the corresponding press event,
|
|
* we enter the drag state (in the 'handle_input' function. But we
|
|
* don't know which window is dragged until the decorator updates
|
|
* the hover model. Now, when the model is updated and we are still
|
|
* in dragged state, we can finally initiate the window-drag
|
|
* operation for the now-known window.
|
|
*/
|
|
if (_drag_state && !_drag_init_done && _hovered_window_id.valid())
|
|
_initiate_drag(_hovered_window_id, _hovered_element);
|
|
|
|
/*
|
|
* Let focus follows the pointer
|
|
*
|
|
* XXX obtain policy from config
|
|
*/
|
|
if (!_drag_state && _hovered_window_id.valid()
|
|
&& _hovered_window_id != last_hovered_window_id) {
|
|
|
|
_focused_window_id = _hovered_window_id;
|
|
_operations.focus(_focused_window_id);
|
|
}
|
|
}
|
|
|
|
void reset_hover()
|
|
{
|
|
/* ignore hover resets when in drag state */
|
|
if (_drag_state)
|
|
return;
|
|
|
|
_hovered_element = Window::Element::UNDEFINED;
|
|
_hovered_window_id = Window_id();
|
|
}
|
|
|
|
Window_id focused_window_id() const { return _focused_window_id; }
|
|
|
|
void focused_window_id(Window_id id) { _focused_window_id = id; }
|
|
|
|
Hover_state hover_state() const { return { _hovered_window_id, _hovered_element }; }
|
|
};
|
|
|
|
|
|
void Floating_window_layouter::User_state::_handle_event(Input::Event const &e)
|
|
{
|
|
if (e.type() == Input::Event::MOTION
|
|
|| e.type() == Input::Event::FOCUS) {
|
|
|
|
_pointer_curr = Point(e.ax(), e.ay());
|
|
|
|
if (_drag_state && _drag_init_done)
|
|
_operations.drag(_dragged_window_id, _dragged_element,
|
|
_pointer_clicked, _pointer_curr);
|
|
}
|
|
|
|
/* track number of pressed buttons/keys */
|
|
if (e.type() == Input::Event::PRESS) _key_cnt++;
|
|
if (e.type() == Input::Event::RELEASE) _key_cnt--;
|
|
|
|
if (e.type() == Input::Event::PRESS
|
|
&& e.keycode() == Input::BTN_LEFT
|
|
&& _key_cnt == 1) {
|
|
|
|
/*
|
|
* Initiate drag operation if possible
|
|
*/
|
|
_drag_state = true;
|
|
_pointer_clicked = _pointer_curr;
|
|
|
|
if (_hovered_window_id.valid()) {
|
|
|
|
/*
|
|
* Initiate drag operation
|
|
*
|
|
* If the hovered window is known at the time of the press event,
|
|
* we can initiate the drag operation immediately. Otherwise,
|
|
* the initiation is deferred to the next update of the hover
|
|
* model.
|
|
*/
|
|
|
|
_initiate_drag(_hovered_window_id, _hovered_element);
|
|
|
|
} else {
|
|
|
|
/*
|
|
* If the hovering state is undefined at the time of the click,
|
|
* we defer the drag handling until the next update of the hover
|
|
* state. This intermediate state is captured by '_drag_init_done'.
|
|
*/
|
|
_drag_init_done = false;
|
|
_dragged_window_id = Window_id();
|
|
_dragged_element = Window::Element(Window::Element::UNDEFINED);
|
|
}
|
|
}
|
|
|
|
/* detect end of drag operation */
|
|
if (e.type() == Input::Event::RELEASE
|
|
&& _key_cnt == 0
|
|
&& _dragged_window_id.valid()) {
|
|
|
|
_drag_state = false;
|
|
|
|
/*
|
|
* Issue resize to 0x0 when releasing the the window closer
|
|
*/
|
|
if (_dragged_element == Window::Element::CLOSER) {
|
|
|
|
if (_dragged_element == _hovered_element)
|
|
_operations.close(_dragged_window_id);
|
|
}
|
|
|
|
_operations.finalize_drag(_dragged_window_id, _dragged_element,
|
|
_pointer_clicked, _pointer_curr);
|
|
}
|
|
}
|
|
|
|
#endif /* _USER_STATE_H_ */
|