mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-23 15:32:25 +00:00
window layouter: internal restructuring
This patch splits the implementation of the window layouter into several headers to ease the upcoming addition of new functionality.
This commit is contained in:
parent
ff8d790f93
commit
37044eaad8
@ -24,30 +24,22 @@
|
||||
#include <rom_session/connection.h>
|
||||
#include <decorator/xml_utils.h>
|
||||
|
||||
/* local includes */
|
||||
#include "window.h"
|
||||
#include "user_state.h"
|
||||
#include "operations.h"
|
||||
|
||||
namespace Floating_window_layouter {
|
||||
|
||||
using namespace Genode;
|
||||
struct Main;
|
||||
class Window;
|
||||
|
||||
typedef Decorator::Point Point;
|
||||
typedef Decorator::Area Area;
|
||||
typedef Decorator::Rect Rect;
|
||||
|
||||
using Decorator::attribute;
|
||||
using Decorator::string_attribute;
|
||||
using Decorator::area_attribute;
|
||||
using Decorator::point_attribute;
|
||||
|
||||
|
||||
static Xml_node xml_lookup_window_by_id(Xml_node node, unsigned const id)
|
||||
static Xml_node xml_lookup_window_by_id(Xml_node node, Window_id const id)
|
||||
{
|
||||
char const *tag = "window";
|
||||
char const *id_attr = "id";
|
||||
|
||||
for (node = node.sub_node(tag); ; node = node.next(tag))
|
||||
if (attribute(node, id_attr, 0UL) == id)
|
||||
if (attribute(node, id_attr, 0UL) == id.value)
|
||||
return node;
|
||||
|
||||
throw Xml_node::Nonexistent_sub_node();
|
||||
@ -58,323 +50,21 @@ namespace Floating_window_layouter {
|
||||
* Return true if compound XML node contains a sub node with ID
|
||||
*/
|
||||
static bool xml_contains_window_node_with_id(Xml_node node,
|
||||
unsigned const id)
|
||||
Window_id const id)
|
||||
{
|
||||
try { xml_lookup_window_by_id(node, id); return true; }
|
||||
try { xml_lookup_window_by_id(node, id.value); return true; }
|
||||
catch (Xml_node::Nonexistent_sub_node) { return false; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Floating_window_layouter::Window : public List<Window>::Element
|
||||
{
|
||||
public:
|
||||
|
||||
typedef String<256> Title;
|
||||
typedef String<256> Label;
|
||||
|
||||
struct Element
|
||||
{
|
||||
enum Type { UNDEFINED, TITLE, LEFT, RIGHT, TOP, BOTTOM,
|
||||
TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT,
|
||||
CLOSER, MAXIMIZER, MINIMIZER };
|
||||
|
||||
Type type;
|
||||
|
||||
char const *name() const
|
||||
{
|
||||
switch (type) {
|
||||
case UNDEFINED: return "";
|
||||
case TITLE: return "title";
|
||||
case LEFT: return "left";
|
||||
case RIGHT: return "right";
|
||||
case TOP: return "top";
|
||||
case BOTTOM: return "bottom";
|
||||
case TOP_LEFT: return "top_left";
|
||||
case TOP_RIGHT: return "top_right";
|
||||
case BOTTOM_LEFT: return "bottom_left";
|
||||
case BOTTOM_RIGHT: return "bottom_right";
|
||||
case CLOSER: return "closer";
|
||||
case MAXIMIZER: return "maximizer";
|
||||
case MINIMIZER: return "minimizer";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
Element(Type type) : type(type) { }
|
||||
|
||||
bool operator != (Element const &other) const { return other.type != type; }
|
||||
bool operator == (Element const &other) const { return other.type == type; }
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
unsigned const _id = 0;
|
||||
|
||||
Title _title;
|
||||
|
||||
Label _label;
|
||||
|
||||
Rect _geometry;
|
||||
|
||||
/**
|
||||
* Window geometry at the start of the current drag operation
|
||||
*/
|
||||
Rect _orig_geometry;
|
||||
|
||||
/**
|
||||
* Size as desired by the user during resize drag operations
|
||||
*/
|
||||
Area _requested_size;
|
||||
|
||||
/**
|
||||
* Backup of the original geometry while the window is maximized
|
||||
*/
|
||||
Rect _unmaximized_geometry;
|
||||
|
||||
Rect const _maximized_geometry;
|
||||
|
||||
/**
|
||||
* Window may be partially transparent
|
||||
*/
|
||||
bool _has_alpha = false;
|
||||
|
||||
/**
|
||||
* Window is temporarily not visible
|
||||
*/
|
||||
bool _is_hidden = false;
|
||||
|
||||
bool _is_resizeable = false;
|
||||
|
||||
bool _is_maximized = false;
|
||||
|
||||
/*
|
||||
* Number of times the window has been topped. This value is used by
|
||||
* the decorator to detect the need for bringing the window to the
|
||||
* front of nitpicker's global view stack even if the stacking order
|
||||
* stays the same within the decorator instance. This is important in
|
||||
* the presence of more than a single decorator.
|
||||
*/
|
||||
unsigned _topped_cnt = 0;
|
||||
|
||||
bool _drag_left_border = false;
|
||||
bool _drag_right_border = false;
|
||||
bool _drag_top_border = false;
|
||||
bool _drag_bottom_border = false;
|
||||
|
||||
public:
|
||||
|
||||
Window(unsigned id, Rect maximized_geometry)
|
||||
:
|
||||
_id(id), _maximized_geometry(maximized_geometry)
|
||||
{ }
|
||||
|
||||
bool has_id(unsigned id) const { return id == _id; }
|
||||
|
||||
unsigned id() const { return _id; }
|
||||
|
||||
void title(Title const &title) { _title = title; }
|
||||
|
||||
void label(Label const &label) { _label = label; }
|
||||
|
||||
void geometry(Rect geometry) { _geometry = geometry; }
|
||||
|
||||
Point position() const { return _geometry.p1(); }
|
||||
|
||||
void position(Point pos) { _geometry = Rect(pos, _geometry.area()); }
|
||||
|
||||
void has_alpha(bool has_alpha) { _has_alpha = has_alpha; }
|
||||
|
||||
void is_hidden(bool is_hidden) { _is_hidden = is_hidden; }
|
||||
|
||||
void is_resizeable(bool is_resizeable) { _is_resizeable = is_resizeable; }
|
||||
|
||||
bool label_matches(Label const &label) const { return label == _label; }
|
||||
|
||||
/**
|
||||
* Return true if user drags a window border
|
||||
*/
|
||||
bool _drag_border() const
|
||||
{
|
||||
return _drag_left_border || _drag_right_border
|
||||
|| _drag_top_border || _drag_bottom_border;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define window size
|
||||
*
|
||||
* This function is called when the window-list model changes.
|
||||
*/
|
||||
void size(Area size)
|
||||
{
|
||||
if (_is_maximized) {
|
||||
_geometry = Rect(_maximized_geometry.p1(), size);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_drag_border()) {
|
||||
_geometry = Rect(_geometry.p1(), size);
|
||||
return;
|
||||
}
|
||||
|
||||
Point p1 = _geometry.p1(), p2 = _geometry.p2();
|
||||
|
||||
if (_drag_left_border)
|
||||
p1 = Point(p2.x() - size.w() + 1, p1.y());
|
||||
|
||||
if (_drag_right_border)
|
||||
p2 = Point(p1.x() + size.w() - 1, p2.y());
|
||||
|
||||
if (_drag_top_border)
|
||||
p1 = Point(p1.x(), p2.y() - size.h() + 1);
|
||||
|
||||
if (_drag_bottom_border)
|
||||
p2 = Point(p2.x(), p1.y() + size.h() - 1);
|
||||
|
||||
_geometry = Rect(p1, p2);
|
||||
}
|
||||
|
||||
Area size() const { return _geometry.area(); }
|
||||
|
||||
Area requested_size() const { return _requested_size; }
|
||||
|
||||
void serialize(Xml_generator &xml, bool focused, Element highlight)
|
||||
{
|
||||
/* omit window from the layout if hidden */
|
||||
if (_is_hidden)
|
||||
return;
|
||||
|
||||
xml.node("window", [&]() {
|
||||
|
||||
xml.attribute("id", _id);
|
||||
|
||||
/* present concatenation of label and title in the window's title bar */
|
||||
{
|
||||
bool const has_title = Genode::strlen(_title.string()) > 0;
|
||||
|
||||
char buf[Label::capacity()];
|
||||
Genode::snprintf(buf, sizeof(buf), "%s%s%s",
|
||||
_label.string(),
|
||||
has_title ? " " : "",
|
||||
_title.string());
|
||||
|
||||
xml.attribute("title", buf);
|
||||
}
|
||||
|
||||
xml.attribute("xpos", _geometry.x1());
|
||||
xml.attribute("ypos", _geometry.y1());
|
||||
xml.attribute("width", _geometry.w());
|
||||
xml.attribute("height", _geometry.h());
|
||||
xml.attribute("topped", _topped_cnt);
|
||||
|
||||
if (focused)
|
||||
xml.attribute("focused", "yes");
|
||||
|
||||
if (highlight.type != Element::UNDEFINED) {
|
||||
xml.node("highlight", [&] () {
|
||||
xml.node(highlight.name());
|
||||
});
|
||||
}
|
||||
|
||||
if (_has_alpha)
|
||||
xml.attribute("has_alpha", "yes");
|
||||
|
||||
if (_is_resizeable) {
|
||||
xml.attribute("maximizer", "yes");
|
||||
xml.attribute("closer", "yes");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the user starts dragging a window element
|
||||
*/
|
||||
void initiate_drag_operation(Window::Element element)
|
||||
{
|
||||
_drag_left_border = (element.type == Window::Element::LEFT)
|
||||
|| (element.type == Window::Element::TOP_LEFT)
|
||||
|| (element.type == Window::Element::BOTTOM_LEFT);
|
||||
|
||||
_drag_right_border = (element.type == Window::Element::RIGHT)
|
||||
|| (element.type == Window::Element::TOP_RIGHT)
|
||||
|| (element.type == Window::Element::BOTTOM_RIGHT);
|
||||
|
||||
_drag_top_border = (element.type == Window::Element::TOP)
|
||||
|| (element.type == Window::Element::TOP_LEFT)
|
||||
|| (element.type == Window::Element::TOP_RIGHT);
|
||||
|
||||
_drag_bottom_border = (element.type == Window::Element::BOTTOM)
|
||||
|| (element.type == Window::Element::BOTTOM_LEFT)
|
||||
|| (element.type == Window::Element::BOTTOM_RIGHT);
|
||||
|
||||
_orig_geometry = _geometry;
|
||||
|
||||
_requested_size = _geometry.area();
|
||||
}
|
||||
|
||||
void apply_drag_operation(Point offset)
|
||||
{
|
||||
if (!_drag_border())
|
||||
position(_orig_geometry.p1() + offset);
|
||||
|
||||
int requested_w = _orig_geometry.w(),
|
||||
requested_h = _orig_geometry.h();
|
||||
|
||||
if (_drag_left_border) requested_w -= offset.x();
|
||||
if (_drag_right_border) requested_w += offset.x();
|
||||
if (_drag_top_border) requested_h -= offset.y();
|
||||
if (_drag_bottom_border) requested_h += offset.y();
|
||||
|
||||
_requested_size = Area(max(1, requested_w), max(1, requested_h));
|
||||
}
|
||||
|
||||
void finalize_drag_operation()
|
||||
{
|
||||
_requested_size = _geometry.area();
|
||||
}
|
||||
|
||||
void topped() { _topped_cnt++; }
|
||||
|
||||
void close() { _requested_size = Area(0, 0); }
|
||||
|
||||
bool is_maximized() const { return _is_maximized; }
|
||||
|
||||
void is_maximized(bool is_maximized)
|
||||
{
|
||||
/* enter maximized state */
|
||||
if (!_is_maximized && is_maximized) {
|
||||
_unmaximized_geometry = _geometry;
|
||||
_requested_size = _maximized_geometry.area();
|
||||
}
|
||||
|
||||
/* leave maximized state */
|
||||
if (_is_maximized && !is_maximized) {
|
||||
_requested_size = _unmaximized_geometry.area();
|
||||
_geometry = Rect(_unmaximized_geometry.p1(), _geometry.area());
|
||||
}
|
||||
|
||||
_is_maximized = is_maximized;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Floating_window_layouter::Main
|
||||
struct Floating_window_layouter::Main : Operations
|
||||
{
|
||||
Signal_receiver &sig_rec;
|
||||
|
||||
List<Window> windows;
|
||||
|
||||
unsigned hovered_window_id = 0;
|
||||
unsigned focused_window_id = 0;
|
||||
unsigned key_cnt = 0;
|
||||
|
||||
Window::Element hovered_element = Window::Element::UNDEFINED;
|
||||
Window::Element hovered_element_now = Window::Element::UNDEFINED;
|
||||
|
||||
bool drag_state = false;
|
||||
bool drag_init_done = true;
|
||||
|
||||
Window *lookup_window_by_id(unsigned id)
|
||||
Window *lookup_window_by_id(Window_id const id)
|
||||
{
|
||||
for (Window *w = windows.first(); w; w = w->next())
|
||||
if (w->has_id(id))
|
||||
@ -383,6 +73,119 @@ struct Floating_window_layouter::Main
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Window const *lookup_window_by_id(Window_id const id) const
|
||||
{
|
||||
for (Window const *w = windows.first(); w; w = w->next())
|
||||
if (w->has_id(id))
|
||||
return w;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply functor to each window
|
||||
*
|
||||
* The functor is called with 'Window const &' as argument.
|
||||
*/
|
||||
template <typename FUNC>
|
||||
void for_each_window(FUNC const &fn) const
|
||||
{
|
||||
for (Window const *w = windows.first(); w; w = w->next())
|
||||
fn(*w);
|
||||
}
|
||||
|
||||
User_state _user_state { *this };
|
||||
|
||||
|
||||
/**************************
|
||||
** Operations interface **
|
||||
**************************/
|
||||
|
||||
void close(Window_id id) override
|
||||
{
|
||||
Window *window = lookup_window_by_id(id);
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
window->close();
|
||||
generate_resize_request_model();
|
||||
generate_focus_model();
|
||||
}
|
||||
|
||||
void to_front(Window_id id) override
|
||||
{
|
||||
Window *window = lookup_window_by_id(id);
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
if (window != windows.first()) {
|
||||
windows.remove(window);
|
||||
windows.insert(window);
|
||||
window->topped();
|
||||
generate_window_layout_model();
|
||||
}
|
||||
}
|
||||
|
||||
void focus(Window_id id) override
|
||||
{
|
||||
generate_focus_model();
|
||||
}
|
||||
|
||||
void toggle_fullscreen(Window_id id) override
|
||||
{
|
||||
/* make sure that the specified window is the front-most one */
|
||||
to_front(id);
|
||||
|
||||
Window *window = lookup_window_by_id(id);
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
window->is_maximized(!window->is_maximized());
|
||||
|
||||
generate_resize_request_model();
|
||||
}
|
||||
|
||||
void drag(Window_id id, Window::Element element, Point clicked, Point curr) override
|
||||
{
|
||||
to_front(id);
|
||||
|
||||
Window *window = lookup_window_by_id(id);
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
/*
|
||||
* The drag operation may result in a change of the window geometry.
|
||||
* We detect such a change be comparing the original geometry with
|
||||
* the geometry with the drag operation applied.
|
||||
*/
|
||||
Point const last_pos = window->position();
|
||||
Area const last_requested_size = window->requested_size();
|
||||
|
||||
window->drag(element, clicked, curr);
|
||||
|
||||
if (last_pos != window->position())
|
||||
generate_window_layout_model();
|
||||
|
||||
if (last_requested_size != window->requested_size())
|
||||
generate_resize_request_model();
|
||||
}
|
||||
|
||||
void finalize_drag(Window_id id, Window::Element, Point clicked, Point final)
|
||||
{
|
||||
Window *window = lookup_window_by_id(id);
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
window->finalize_drag_operation();
|
||||
|
||||
/*
|
||||
* Update window layout because highlighting may have changed after the
|
||||
* drag operation. E.g., if the window has not kept up with the
|
||||
* dragging of a resize handle, the resize handle is no longer hovered.
|
||||
*/
|
||||
generate_window_layout_model();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Install handler for responding to window-list changes
|
||||
@ -420,10 +223,16 @@ struct Floating_window_layouter::Main
|
||||
|
||||
Attached_rom_dataspace hover { "hover" };
|
||||
|
||||
|
||||
/**
|
||||
* Install handler for responding to user input
|
||||
*/
|
||||
void handle_input(unsigned);
|
||||
void handle_input(unsigned)
|
||||
{
|
||||
while (input.is_pending())
|
||||
_user_state.handle_input(input_ds.local_addr<Input::Event>(),
|
||||
input.flush());
|
||||
}
|
||||
|
||||
Signal_dispatcher<Main> input_dispatcher = {
|
||||
sig_rec, *this, &Main::handle_input };
|
||||
@ -440,17 +249,17 @@ struct Floating_window_layouter::Main
|
||||
Reporter resize_request_reporter = { "resize_request" };
|
||||
Reporter focus_reporter = { "focus" };
|
||||
|
||||
unsigned dragged_window_id = 0;
|
||||
|
||||
Point pointer_clicked;
|
||||
Point pointer_last;
|
||||
Point pointer_curr;
|
||||
bool focused_window_is_maximized() const
|
||||
{
|
||||
Window const *w = lookup_window_by_id(_user_state.focused_window_id());
|
||||
return w && w->is_maximized();
|
||||
}
|
||||
|
||||
void import_window_list(Xml_node);
|
||||
void generate_window_layout_model();
|
||||
void generate_resize_request_model();
|
||||
void generate_focus_model();
|
||||
void initiate_window_drag(Window &window);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -553,11 +362,11 @@ void Floating_window_layouter::Main::generate_window_layout_model()
|
||||
{
|
||||
for (Window *w = windows.first(); w; w = w->next()) {
|
||||
|
||||
bool const is_hovered = w->has_id(hovered_window_id);
|
||||
bool const is_focused = w->has_id(focused_window_id);
|
||||
bool const is_hovered = w->has_id(_user_state.hover_state().window_id);
|
||||
bool const is_focused = w->has_id(_user_state.focused_window_id());
|
||||
|
||||
Window::Element const highlight =
|
||||
is_hovered ? hovered_element : Window::Element::UNDEFINED;
|
||||
is_hovered ? _user_state.hover_state().element : Window::Element::UNDEFINED;
|
||||
|
||||
w->serialize(xml, is_focused, highlight);
|
||||
}
|
||||
@ -569,18 +378,17 @@ void Floating_window_layouter::Main::generate_resize_request_model()
|
||||
{
|
||||
Reporter::Xml_generator xml(resize_request_reporter, [&] ()
|
||||
{
|
||||
Window const *dragged_window = lookup_window_by_id(dragged_window_id);
|
||||
if (dragged_window) {
|
||||
for_each_window([&] (Window const &window) {
|
||||
|
||||
Area const requested_size = dragged_window->requested_size();
|
||||
if (requested_size != dragged_window->size()) {
|
||||
Area const requested_size = window.requested_size();
|
||||
if (requested_size != window.size()) {
|
||||
xml.node("window", [&] () {
|
||||
xml.attribute("id", dragged_window_id);
|
||||
xml.attribute("id", window.id().value);
|
||||
xml.attribute("width", requested_size.w());
|
||||
xml.attribute("height", requested_size.h());
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -590,7 +398,7 @@ void Floating_window_layouter::Main::generate_focus_model()
|
||||
Reporter::Xml_generator xml(focus_reporter, [&] ()
|
||||
{
|
||||
xml.node("window", [&] () {
|
||||
xml.attribute("id", focused_window_id);
|
||||
xml.attribute("id", _user_state.focused_window_id().value);
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -625,27 +433,10 @@ element_from_hover_model(Genode::Xml_node hover_window_xml)
|
||||
if (hover_window_xml.has_sub_node("maximizer")) return Type::MAXIMIZER;
|
||||
if (hover_window_xml.has_sub_node("minimizer")) return Type::MINIMIZER;
|
||||
|
||||
|
||||
return Type::UNDEFINED;
|
||||
}
|
||||
|
||||
|
||||
void Floating_window_layouter::Main::initiate_window_drag(Window &window)
|
||||
{
|
||||
window.initiate_drag_operation(hovered_element);
|
||||
|
||||
drag_init_done = true;
|
||||
|
||||
/* bring focused window to front */
|
||||
if (&window != windows.first()) {
|
||||
windows.remove(&window);
|
||||
windows.insert(&window);
|
||||
}
|
||||
|
||||
window.topped();
|
||||
}
|
||||
|
||||
|
||||
void Floating_window_layouter::Main::handle_window_list_update(unsigned)
|
||||
{
|
||||
window_list.update();
|
||||
@ -701,7 +492,7 @@ void Floating_window_layouter::Main::_apply_focus_request()
|
||||
if (at == nullptr) {
|
||||
w->topped();
|
||||
|
||||
focused_window_id = w->id();
|
||||
_user_state.focused_window_id(w->id());
|
||||
generate_focus_model();
|
||||
}
|
||||
|
||||
@ -733,249 +524,33 @@ void Floating_window_layouter::Main::handle_hover_update(unsigned)
|
||||
|
||||
try {
|
||||
Xml_node const hover_xml(hover.local_addr<char>());
|
||||
|
||||
Xml_node const hover_window_xml = hover_xml.sub_node("window");
|
||||
|
||||
unsigned const id = attribute(hover_window_xml, "id", 0UL);
|
||||
_user_state.hover(attribute(hover_window_xml, "id", 0UL),
|
||||
element_from_hover_model(hover_window_xml));
|
||||
|
||||
hovered_element_now = element_from_hover_model(hover_window_xml);
|
||||
}
|
||||
|
||||
/*
|
||||
* An exception may occur during the 'Xml_node' construction if the hover
|
||||
* model is missing or malformed. Under this condition, we invalidate
|
||||
* the hover state.
|
||||
*/
|
||||
catch (...) {
|
||||
|
||||
_user_state.reset_hover();
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* Don't generate a focus-model update here. In a situation where the
|
||||
* pointer has moved over a native nitpicker view (outside the realm of
|
||||
* the window manager), the hover model as generated by the decorator
|
||||
* naturally becomes empty. If we posted a focus update, this would
|
||||
* steal the focus away from the native nitpicker view.
|
||||
*/
|
||||
if (id && !drag_init_done && dragged_window_id == 0)
|
||||
{
|
||||
dragged_window_id = id;
|
||||
hovered_window_id = id;
|
||||
focused_window_id = id;
|
||||
|
||||
Window *window = lookup_window_by_id(id);
|
||||
if (window) {
|
||||
initiate_window_drag(*window);
|
||||
generate_window_layout_model();
|
||||
generate_focus_model();
|
||||
}
|
||||
}
|
||||
|
||||
if (!drag_state && (id != hovered_window_id || hovered_element_now != hovered_element)) {
|
||||
|
||||
hovered_window_id = id;
|
||||
hovered_element = hovered_element_now;
|
||||
|
||||
/* XXX read from config */
|
||||
bool const focus_follows_pointer = true;
|
||||
if (id && focus_follows_pointer) {
|
||||
focused_window_id = id;
|
||||
generate_focus_model();
|
||||
}
|
||||
|
||||
generate_window_layout_model();
|
||||
}
|
||||
} catch (...) {
|
||||
|
||||
/* reset focused window if pointer does not hover over any window */
|
||||
if (!drag_state) {
|
||||
hovered_element = Window::Element::UNDEFINED;
|
||||
hovered_window_id = 0;
|
||||
generate_window_layout_model();
|
||||
|
||||
/*
|
||||
* Don't generate a focus-model update here. In a situation where
|
||||
* the pointer has moved over a native nitpicker view (outside
|
||||
* the realm of the window manager), the hover model as generated
|
||||
* by the decorator naturally becomes empty. If we posted a
|
||||
* focus update, this would steal the focus away from the native
|
||||
* nitpicker view.
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Floating_window_layouter::Main::handle_input(unsigned)
|
||||
{
|
||||
bool need_regenerate_window_layout_model = false;
|
||||
bool need_regenerate_resize_request_model = false;
|
||||
|
||||
Window *hovered_window = lookup_window_by_id(hovered_window_id);
|
||||
|
||||
while (input.is_pending()) {
|
||||
|
||||
size_t const num_events = input.flush();
|
||||
|
||||
Input::Event const * const ev = input_ds.local_addr<Input::Event>();
|
||||
|
||||
for (size_t i = 0; i < num_events; i++) {
|
||||
|
||||
Input::Event e = ev[i];
|
||||
|
||||
if (e.type() == Input::Event::MOTION
|
||||
|| e.type() == Input::Event::FOCUS)
|
||||
pointer_curr = Point(e.ax(), e.ay());
|
||||
|
||||
/* 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) {
|
||||
|
||||
/*
|
||||
* Toggle maximized state
|
||||
*/
|
||||
if (hovered_element == Window::Element::MAXIMIZER) {
|
||||
|
||||
if (hovered_window) {
|
||||
|
||||
dragged_window_id = hovered_window_id;
|
||||
hovered_window->is_maximized(!hovered_window->is_maximized());
|
||||
|
||||
/* bring focused window to front */
|
||||
if (hovered_window != windows.first()) {
|
||||
windows.remove(hovered_window);
|
||||
windows.insert(hovered_window);
|
||||
}
|
||||
hovered_window->topped();
|
||||
|
||||
focused_window_id = hovered_window_id;
|
||||
|
||||
need_regenerate_window_layout_model = true;
|
||||
need_regenerate_resize_request_model = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool const hovered_window_is_maximized =
|
||||
hovered_window ? hovered_window->is_maximized() : false;
|
||||
|
||||
/*
|
||||
* Change window geometry unless the window is in maximized
|
||||
* state.
|
||||
*/
|
||||
if (hovered_element != Window::Element::MAXIMIZER) {
|
||||
|
||||
if (!hovered_window_is_maximized) {
|
||||
|
||||
drag_state = true;
|
||||
drag_init_done = false;
|
||||
dragged_window_id = hovered_window_id;
|
||||
pointer_clicked = pointer_curr;
|
||||
pointer_last = pointer_clicked;
|
||||
|
||||
/*
|
||||
* If the hovered window is known at the time of the press
|
||||
* event, we can initiate the drag operation immediately.
|
||||
* Otherwise, we the initiation is deferred to the next
|
||||
* update of the hover model.
|
||||
*/
|
||||
if (hovered_window)
|
||||
initiate_window_drag(*hovered_window);
|
||||
}
|
||||
|
||||
if (hovered_window) {
|
||||
if (focused_window_id != hovered_window_id) {
|
||||
focused_window_id = hovered_window_id;
|
||||
|
||||
/* bring focused window to front */
|
||||
if (hovered_window != windows.first()) {
|
||||
windows.remove(hovered_window);
|
||||
windows.insert(hovered_window);
|
||||
}
|
||||
|
||||
hovered_window->topped();
|
||||
|
||||
generate_focus_model();
|
||||
}
|
||||
need_regenerate_window_layout_model = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* detect end of drag operation */
|
||||
if (e.type() == Input::Event::RELEASE) {
|
||||
if (key_cnt == 0) {
|
||||
drag_state = false;
|
||||
generate_focus_model();
|
||||
|
||||
bool const manipulate_geometry =
|
||||
hovered_element != Window::Element::CLOSER;
|
||||
|
||||
Window *dragged_window = lookup_window_by_id(dragged_window_id);
|
||||
|
||||
if (dragged_window && manipulate_geometry) {
|
||||
|
||||
Area const last_requested_size = dragged_window->requested_size();
|
||||
dragged_window->finalize_drag_operation();
|
||||
|
||||
if (last_requested_size != dragged_window->requested_size())
|
||||
need_regenerate_resize_request_model = true;
|
||||
|
||||
/*
|
||||
* Update window layout because highlighting may have
|
||||
* changed after the drag operation. E.g., if the
|
||||
* window has not kept up with the dragging of a
|
||||
* resize handle, the resize handle is no longer
|
||||
* hovered.
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Issue resize to 0x0 when releasing the the window closer
|
||||
*/
|
||||
if (dragged_window && hovered_element == Window::Element::CLOSER) {
|
||||
|
||||
if (hovered_element_now == hovered_element) {
|
||||
dragged_window->close();
|
||||
need_regenerate_resize_request_model = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (dragged_window) {
|
||||
handle_hover_update(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (drag_state && (pointer_curr != pointer_last)) {
|
||||
|
||||
pointer_last = pointer_curr;
|
||||
|
||||
bool const manipulate_geometry =
|
||||
hovered_element != Window::Element::CLOSER;
|
||||
|
||||
Window *dragged_window = lookup_window_by_id(dragged_window_id);
|
||||
if (dragged_window && manipulate_geometry) {
|
||||
|
||||
Point const last_pos = dragged_window->position();
|
||||
Area const last_requested_size = dragged_window->requested_size();
|
||||
|
||||
dragged_window->apply_drag_operation(pointer_curr - pointer_clicked);
|
||||
|
||||
if (last_pos != dragged_window->position())
|
||||
need_regenerate_window_layout_model = true;
|
||||
|
||||
if (last_requested_size != dragged_window->requested_size())
|
||||
need_regenerate_resize_request_model = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (need_regenerate_window_layout_model)
|
||||
generate_window_layout_model();
|
||||
|
||||
if (need_regenerate_resize_request_model)
|
||||
generate_resize_request_model();
|
||||
/* propagate changed hovering to the decorator */
|
||||
generate_window_layout_model();
|
||||
}
|
||||
|
||||
|
||||
|
33
repos/gems/src/app/floating_window_layouter/operations.h
Normal file
33
repos/gems/src/app/floating_window_layouter/operations.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* \brief Floating window layouter
|
||||
* \author Norman Feske
|
||||
* \date 2015-12-31
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 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 _OPERATIONS_H_
|
||||
#define _OPERATIONS_H_
|
||||
|
||||
/* local includes */
|
||||
#include "window.h"
|
||||
|
||||
namespace Floating_window_layouter { struct Operations; }
|
||||
|
||||
|
||||
struct Floating_window_layouter::Operations
|
||||
{
|
||||
virtual void close(Window_id) = 0;
|
||||
virtual void toggle_fullscreen(Window_id) = 0;
|
||||
virtual void focus(Window_id) = 0;
|
||||
virtual void to_front(Window_id) = 0;
|
||||
virtual void drag(Window_id, Window::Element, Point clicked, Point curr) = 0;
|
||||
virtual void finalize_drag(Window_id, Window::Element, Point clicked, Point final) = 0;
|
||||
};
|
||||
|
||||
#endif /* _OPERATIONS_H_ */
|
54
repos/gems/src/app/floating_window_layouter/types.h
Normal file
54
repos/gems/src/app/floating_window_layouter/types.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* \brief Floating window layouter
|
||||
* \author Norman Feske
|
||||
* \date 2015-12-31
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 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 _TYPES_H_
|
||||
#define _TYPES_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <decorator/types.h>
|
||||
|
||||
namespace Floating_window_layouter {
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
typedef Decorator::Point Point;
|
||||
typedef Decorator::Area Area;
|
||||
typedef Decorator::Rect Rect;
|
||||
|
||||
using Decorator::attribute;
|
||||
using Decorator::string_attribute;
|
||||
using Decorator::area_attribute;
|
||||
using Decorator::point_attribute;
|
||||
|
||||
struct Window_id
|
||||
{
|
||||
unsigned value = 0;
|
||||
|
||||
Window_id() { }
|
||||
Window_id(unsigned value) : value(value) { }
|
||||
|
||||
bool valid() const { return value != 0; }
|
||||
|
||||
bool operator != (Window_id const &other) const
|
||||
{
|
||||
return other.value != value;
|
||||
}
|
||||
|
||||
bool operator == (Window_id const &other) const
|
||||
{
|
||||
return other.value == value;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* _TYPES_H_ */
|
266
repos/gems/src/app/floating_window_layouter/user_state.h
Normal file
266
repos/gems/src/app/floating_window_layouter/user_state.h
Normal file
@ -0,0 +1,266 @@
|
||||
/*
|
||||
* \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_ */
|
334
repos/gems/src/app/floating_window_layouter/window.h
Normal file
334
repos/gems/src/app/floating_window_layouter/window.h
Normal file
@ -0,0 +1,334 @@
|
||||
/*
|
||||
* \brief Floating window layouter
|
||||
* \author Norman Feske
|
||||
* \date 2013-02-14
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013-2014 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 _WINDOW_H_
|
||||
#define _WINDOW_H_
|
||||
|
||||
/* local includes */
|
||||
#include "types.h"
|
||||
|
||||
namespace Floating_window_layouter { class Window; }
|
||||
|
||||
|
||||
class Floating_window_layouter::Window : public List<Window>::Element
|
||||
{
|
||||
public:
|
||||
|
||||
typedef String<256> Title;
|
||||
typedef String<256> Label;
|
||||
|
||||
struct Element
|
||||
{
|
||||
enum Type { UNDEFINED, TITLE, LEFT, RIGHT, TOP, BOTTOM,
|
||||
TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT,
|
||||
CLOSER, MAXIMIZER, MINIMIZER };
|
||||
|
||||
Type type;
|
||||
|
||||
char const *name() const
|
||||
{
|
||||
switch (type) {
|
||||
case UNDEFINED: return "";
|
||||
case TITLE: return "title";
|
||||
case LEFT: return "left";
|
||||
case RIGHT: return "right";
|
||||
case TOP: return "top";
|
||||
case BOTTOM: return "bottom";
|
||||
case TOP_LEFT: return "top_left";
|
||||
case TOP_RIGHT: return "top_right";
|
||||
case BOTTOM_LEFT: return "bottom_left";
|
||||
case BOTTOM_RIGHT: return "bottom_right";
|
||||
case CLOSER: return "closer";
|
||||
case MAXIMIZER: return "maximizer";
|
||||
case MINIMIZER: return "minimizer";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
Element(Type type) : type(type) { }
|
||||
|
||||
bool operator != (Element const &other) const { return other.type != type; }
|
||||
bool operator == (Element const &other) const { return other.type == type; }
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
Window_id const _id;
|
||||
|
||||
Title _title;
|
||||
|
||||
Label _label;
|
||||
|
||||
Rect _geometry;
|
||||
|
||||
/**
|
||||
* Window geometry at the start of the current drag operation
|
||||
*/
|
||||
Rect _orig_geometry;
|
||||
|
||||
/**
|
||||
* Size as desired by the user during resize drag operations
|
||||
*/
|
||||
Area _requested_size;
|
||||
|
||||
/**
|
||||
* Backup of the original geometry while the window is maximized
|
||||
*/
|
||||
Rect _unmaximized_geometry;
|
||||
|
||||
Rect const _maximized_geometry;
|
||||
|
||||
/**
|
||||
* Window may be partially transparent
|
||||
*/
|
||||
bool _has_alpha = false;
|
||||
|
||||
/**
|
||||
* Window is temporarily not visible
|
||||
*/
|
||||
bool _is_hidden = false;
|
||||
|
||||
bool _is_resizeable = false;
|
||||
|
||||
bool _is_maximized = false;
|
||||
|
||||
bool _is_dragged = false;
|
||||
|
||||
/*
|
||||
* Number of times the window has been topped. This value is used by
|
||||
* the decorator to detect the need for bringing the window to the
|
||||
* front of nitpicker's global view stack even if the stacking order
|
||||
* stays the same within the decorator instance. This is important in
|
||||
* the presence of more than a single decorator.
|
||||
*/
|
||||
unsigned _topped_cnt = 0;
|
||||
|
||||
bool _drag_left_border = false;
|
||||
bool _drag_right_border = false;
|
||||
bool _drag_top_border = false;
|
||||
bool _drag_bottom_border = false;
|
||||
|
||||
/**
|
||||
* Called when the user starts dragging a window element
|
||||
*/
|
||||
void _initiate_drag_operation(Window::Element element)
|
||||
{
|
||||
_drag_left_border = (element.type == Window::Element::LEFT)
|
||||
|| (element.type == Window::Element::TOP_LEFT)
|
||||
|| (element.type == Window::Element::BOTTOM_LEFT);
|
||||
|
||||
_drag_right_border = (element.type == Window::Element::RIGHT)
|
||||
|| (element.type == Window::Element::TOP_RIGHT)
|
||||
|| (element.type == Window::Element::BOTTOM_RIGHT);
|
||||
|
||||
_drag_top_border = (element.type == Window::Element::TOP)
|
||||
|| (element.type == Window::Element::TOP_LEFT)
|
||||
|| (element.type == Window::Element::TOP_RIGHT);
|
||||
|
||||
_drag_bottom_border = (element.type == Window::Element::BOTTOM)
|
||||
|| (element.type == Window::Element::BOTTOM_LEFT)
|
||||
|| (element.type == Window::Element::BOTTOM_RIGHT);
|
||||
|
||||
_orig_geometry = _geometry;
|
||||
|
||||
_requested_size = _geometry.area();
|
||||
|
||||
_is_dragged = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called each time the pointer moves while the window is dragged
|
||||
*/
|
||||
void _apply_drag_operation(Point offset)
|
||||
{
|
||||
if (!_drag_border())
|
||||
position(_orig_geometry.p1() + offset);
|
||||
|
||||
int requested_w = _orig_geometry.w(),
|
||||
requested_h = _orig_geometry.h();
|
||||
|
||||
if (_drag_left_border) requested_w -= offset.x();
|
||||
if (_drag_right_border) requested_w += offset.x();
|
||||
if (_drag_top_border) requested_h -= offset.y();
|
||||
if (_drag_bottom_border) requested_h += offset.y();
|
||||
|
||||
_requested_size = Area(max(1, requested_w), max(1, requested_h));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if user drags a window border
|
||||
*/
|
||||
bool _drag_border() const
|
||||
{
|
||||
return _drag_left_border || _drag_right_border
|
||||
|| _drag_top_border || _drag_bottom_border;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Window(Window_id id, Rect maximized_geometry)
|
||||
:
|
||||
_id(id), _maximized_geometry(maximized_geometry)
|
||||
{ }
|
||||
|
||||
bool has_id(Window_id id) const { return id == _id; }
|
||||
|
||||
Window_id id() const { return _id; }
|
||||
|
||||
void title(Title const &title) { _title = title; }
|
||||
|
||||
void label(Label const &label) { _label = label; }
|
||||
|
||||
void geometry(Rect geometry) { _geometry = geometry; }
|
||||
|
||||
Point position() const { return _geometry.p1(); }
|
||||
|
||||
void position(Point pos) { _geometry = Rect(pos, _geometry.area()); }
|
||||
|
||||
void has_alpha(bool has_alpha) { _has_alpha = has_alpha; }
|
||||
|
||||
void is_hidden(bool is_hidden) { _is_hidden = is_hidden; }
|
||||
|
||||
void is_resizeable(bool is_resizeable) { _is_resizeable = is_resizeable; }
|
||||
|
||||
bool label_matches(Label const &label) const { return label == _label; }
|
||||
|
||||
/**
|
||||
* Define window size
|
||||
*
|
||||
* This function is called when the window-list model changes.
|
||||
*/
|
||||
void size(Area size)
|
||||
{
|
||||
if (_is_maximized) {
|
||||
_geometry = Rect(_maximized_geometry.p1(), size);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_drag_border()) {
|
||||
_geometry = Rect(_geometry.p1(), size);
|
||||
return;
|
||||
}
|
||||
|
||||
Point p1 = _geometry.p1(), p2 = _geometry.p2();
|
||||
|
||||
if (_drag_left_border)
|
||||
p1 = Point(p2.x() - size.w() + 1, p1.y());
|
||||
|
||||
if (_drag_right_border)
|
||||
p2 = Point(p1.x() + size.w() - 1, p2.y());
|
||||
|
||||
if (_drag_top_border)
|
||||
p1 = Point(p1.x(), p2.y() - size.h() + 1);
|
||||
|
||||
if (_drag_bottom_border)
|
||||
p2 = Point(p2.x(), p1.y() + size.h() - 1);
|
||||
|
||||
_geometry = Rect(p1, p2);
|
||||
}
|
||||
|
||||
Area size() const { return _geometry.area(); }
|
||||
|
||||
Area requested_size() const { return _requested_size; }
|
||||
|
||||
void serialize(Xml_generator &xml, bool focused, Element highlight)
|
||||
{
|
||||
/* omit window from the layout if hidden */
|
||||
if (_is_hidden)
|
||||
return;
|
||||
|
||||
xml.node("window", [&]() {
|
||||
|
||||
xml.attribute("id", _id.value);
|
||||
|
||||
/* present concatenation of label and title in the window's title bar */
|
||||
{
|
||||
bool const has_title = Genode::strlen(_title.string()) > 0;
|
||||
|
||||
char buf[Label::capacity()];
|
||||
Genode::snprintf(buf, sizeof(buf), "%s%s%s",
|
||||
_label.string(),
|
||||
has_title ? " " : "",
|
||||
_title.string());
|
||||
|
||||
xml.attribute("title", buf);
|
||||
}
|
||||
|
||||
xml.attribute("xpos", _geometry.x1());
|
||||
xml.attribute("ypos", _geometry.y1());
|
||||
xml.attribute("width", _geometry.w());
|
||||
xml.attribute("height", _geometry.h());
|
||||
xml.attribute("topped", _topped_cnt);
|
||||
|
||||
if (focused)
|
||||
xml.attribute("focused", "yes");
|
||||
|
||||
if (highlight.type != Element::UNDEFINED) {
|
||||
xml.node("highlight", [&] () {
|
||||
xml.node(highlight.name());
|
||||
});
|
||||
}
|
||||
|
||||
if (_has_alpha)
|
||||
xml.attribute("has_alpha", "yes");
|
||||
|
||||
if (_is_resizeable) {
|
||||
xml.attribute("maximizer", "yes");
|
||||
xml.attribute("closer", "yes");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void drag(Window::Element element, Point clicked, Point curr)
|
||||
{
|
||||
/* prevent maximized windows from being dragged */
|
||||
if (is_maximized())
|
||||
return;
|
||||
|
||||
if (!_is_dragged)
|
||||
_initiate_drag_operation(element);
|
||||
|
||||
_apply_drag_operation(curr - clicked);
|
||||
}
|
||||
|
||||
void finalize_drag_operation()
|
||||
{
|
||||
_requested_size = _geometry.area();
|
||||
_is_dragged = false;
|
||||
}
|
||||
|
||||
void topped() { _topped_cnt++; }
|
||||
|
||||
void close() { _requested_size = Area(0, 0); }
|
||||
|
||||
bool is_maximized() const { return _is_maximized; }
|
||||
|
||||
void is_maximized(bool is_maximized)
|
||||
{
|
||||
/* enter maximized state */
|
||||
if (!_is_maximized && is_maximized) {
|
||||
_unmaximized_geometry = _geometry;
|
||||
_requested_size = _maximized_geometry.area();
|
||||
}
|
||||
|
||||
/* leave maximized state */
|
||||
if (_is_maximized && !is_maximized) {
|
||||
_requested_size = _unmaximized_geometry.area();
|
||||
_geometry = Rect(_unmaximized_geometry.p1(), _geometry.area());
|
||||
}
|
||||
|
||||
_is_maximized = is_maximized;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _WINDOW_H_ */
|
Loading…
Reference in New Issue
Block a user