genode/repos/gems/src/app/floating_window_layouter/window.h

335 lines
8.2 KiB
C++

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