genode/repos/os/include/decorator/window_stack.h
Norman Feske afac1e86bb decorator: window colors and controls
This patch improves the decorator in two ways. First, it enables the
assignment of window colors depending on the window labels. This
configuration can be changed dynamically. Second, it adds the handling
of window controls for closing, maximizing, minimizing windows.

Issue #1689
Fixes #1688
2015-10-06 12:18:54 +02:00

339 lines
8.4 KiB
C++

/*
* \brief Window-stack handling for decorator
* \author Norman Feske
* \date 2014-01-09
*/
/*
* Copyright (C) 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 _INCLUDE__DECORATOR__WINDOW_STACK_H_
#define _INCLUDE__DECORATOR__WINDOW_STACK_H_
/* Genode includes */
#include <nitpicker_session/nitpicker_session.h>
#include <base/printf.h>
/* local includes */
#include <decorator/types.h>
#include <decorator/window.h>
#include <decorator/window_factory.h>
#include <decorator/xml_utils.h>
namespace Decorator { class Window_stack; }
class Decorator::Window_stack : public Window_base::Draw_behind_fn
{
private:
Window_list _windows;
Window_factory_base &_window_factory;
Dirty_rect mutable _dirty_rect;
inline void _draw_rec(Canvas_base &canvas, Window_base const *win,
Rect rect) const;
Window_base *_lookup_by_id(unsigned const id)
{
for (Window_base *win = _windows.first(); win; win = win->next())
if (win->id() == id)
return win;
return 0;
}
static inline
Xml_node _xml_node_by_window_id(Genode::Xml_node node, unsigned id)
{
for (node = node.sub_node("window"); ; node = node.next()) {
if (node.has_type("window") && attribute(node, "id", 0UL) == id)
return node;
if (node.is_last()) break;
}
throw Xml_node::Nonexistent_sub_node();
}
void _destroy(Window_base &window)
{
_windows.remove(&window);
_window_factory.destroy(&window);
}
/**
* Generate window list in reverse order
*
* After calling this method, the '_windows' list is empty.
*/
Window_list _reversed_window_list()
{
Window_list reversed;
while (Window_base *w = _windows.first()) {
_windows.remove(w);
reversed.insert(w);
}
return reversed;
}
public:
Window_stack(Window_factory_base &window_factory)
:
_window_factory(window_factory)
{ }
Dirty_rect draw(Canvas_base &canvas) const
{
Dirty_rect result = _dirty_rect;
_dirty_rect.flush([&] (Rect const &rect) {
_draw_rec(canvas, _windows.first(), rect); });
return result;
}
inline void update_model(Xml_node root_node);
bool schedule_animated_windows()
{
bool redraw_needed = false;
for (Window_base *win = _windows.first(); win; win = win->next()) {
if (win->animated()) {
_dirty_rect.mark_as_dirty(win->outer_geometry());
redraw_needed = true;
}
}
return redraw_needed;
}
/**
* Apply functor to each window
*
* The functor is called with 'Window_base &' as argument.
*/
template <typename FUNC>
void for_each_window(FUNC const &func)
{
for (Window_base *win = _windows.first(); win; win = win->next())
func(*win);
}
void update_nitpicker_views()
{
/*
* Update nitpicker views in reverse order (back-most first). The
* reverse order is important because the stacking position of a
* view is propagated by referring to the neighbor the view is in
* front of. By starting with the back-most view, we make sure that
* each view is always at its final stacking position when
* specified as neighbor of another view.
*/
Window_list reversed = _reversed_window_list();
while (Window_base *win = reversed.first()) {
win->update_nitpicker_views();
reversed.remove(win);
_windows.insert(win);
}
}
void flush()
{
while (Window_base *window = _windows.first())
_destroy(*window);
}
Window_base::Hover hover(Point pos) const
{
for (Window_base const *win = _windows.first(); win; win = win->next())
if (win->outer_geometry().contains(pos))
return win->hover(pos);
return Window_base::Hover();
}
/**************************************
** Window::Draw_behind_fn interface **
**************************************/
void draw_behind(Canvas_base &canvas, Window_base const &window, Rect clip) const override
{
_draw_rec(canvas, window.next(), clip);
}
};
void Decorator::Window_stack::_draw_rec(Decorator::Canvas_base &canvas,
Decorator::Window_base const *win,
Decorator::Rect rect) const
{
Rect clipped;
/* find next window that intersects with the rectangle */
for ( ; win && !(clipped = Rect::intersect(win->outer_geometry(), rect)).valid(); )
win = win->next();;
/* check if we hit the bottom of the window stack */
if (!win) return;
/* draw areas around the current window */
if (Window_base const * const next = win->next()) {
Rect top, left, right, bottom;
rect.cut(clipped, &top, &left, &right, &bottom);
if (top.valid()) _draw_rec(canvas, next, top);
if (left.valid()) _draw_rec(canvas, next, left);
if (right.valid()) _draw_rec(canvas, next, right);
if (bottom.valid()) _draw_rec(canvas, next, bottom);
}
/* draw current window */
win->draw(canvas, clipped, *this);
}
void Decorator::Window_stack::update_model(Genode::Xml_node root_node)
{
/*
* Step 1: Remove windows that are no longer present.
*/
for (Window_base *window = _windows.first(), *next = 0; window; window = next) {
next = window->next();
try {
_xml_node_by_window_id(root_node, window->id());
}
catch (Xml_node::Nonexistent_sub_node) {
_dirty_rect.mark_as_dirty(window->outer_geometry());
_destroy(*window);
};
}
/*
* Step 2: Update window properties of already present windows.
*/
for (Window_base *window = _windows.first(); window; window = window->next()) {
/*
* After step 1, a Xml_node::Nonexistent_sub_node exception can no
* longer occur. All windows remaining in the window stack are present
* in the XML model.
*/
try {
Rect const orig_geometry = window->outer_geometry();
if (window->update(_xml_node_by_window_id(root_node, window->id()))) {
_dirty_rect.mark_as_dirty(orig_geometry);
_dirty_rect.mark_as_dirty(window->outer_geometry());
}
}
catch (Xml_node::Nonexistent_sub_node) {
PERR("could not look up window %ld in XML model", window->id()); }
}
/*
* Step 3: Add new appearing windows to the window stack.
*/
for_each_sub_node(root_node, "window", [&] (Xml_node window_node) {
unsigned long const id = attribute(window_node, "id", 0UL);
if (!_lookup_by_id(id)) {
Window_base *new_window = _window_factory.create(window_node);
if (new_window) {
new_window->update(window_node);
/*
* Insert new window in front of all other windows.
*
* Immediately propagate the new stacking position of the new
* window to nitpicker ('update_nitpicker_views'). Otherwise,
*/
new_window->stack(_windows.first()
? _windows.first()->frontmost_view()
: Nitpicker::Session::View_handle());
_windows.insert(new_window);
_dirty_rect.mark_as_dirty(new_window->outer_geometry());
}
}
});
/*
* Step 4: Adjust window order.
*/
Window_base *previous_window = 0;
Window_base *window = _windows.first();
for_each_sub_node(root_node, "window", [&] (Xml_node window_node) {
if (!window) {
PERR("unexpected end of window list during re-ordering");
return;
}
unsigned long const id = attribute(window_node, "id", 0UL);
if (window->id() != id) {
window = _lookup_by_id(id);
if (!window) {
PERR("window lookup unexpectedly failed during re-ordering");
return;
}
_windows.remove(window);
_windows.insert(window, previous_window);
_dirty_rect.mark_as_dirty(window->outer_geometry());
}
previous_window = window;
window = window->next();
});
/*
* Propagate changed stacking order to nitpicker
*
* First, we reverse the window list. The 'reversed' list starts with
* the back-most window. We then go throuh each window back to front
* and check if its neighbor is consistent with its position in the
* window list.
*/
Window_list reversed = _reversed_window_list();
if (Window_base * const back_most = reversed.first()) {
/* keep back-most window as is */
reversed.remove(back_most);
_windows.insert(back_most);
/* check consistency between window list order and view stacking */
while (Window_base *w = reversed.first()) {
Window_base * const neighbor = _windows.first();
reversed.remove(w);
_windows.insert(w);
/* propagate change stacking order to nitpicker */
if (w->is_in_front_of(*neighbor))
continue;
w->stack(neighbor->frontmost_view());
}
}
}
#endif /* _INCLUDE__DECORATOR__WINDOW_STACK_H_ */