/* * \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::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_ */