genode/repos/os/src/server/nitpicker/view_stack.cc

302 lines
7.2 KiB
C++
Raw Normal View History

2011-12-22 16:19:25 +01:00
/*
* \brief Nitpicker view stack implementation
* \author Norman Feske
* \date 2006-08-09
*/
/*
2013-01-10 21:44:47 +01:00
* Copyright (C) 2006-2013 Genode Labs GmbH
2011-12-22 16:19:25 +01:00
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#include "view_stack.h"
#include "clip_guard.h"
/***************
** Utilities **
***************/
/**
* Get last view with the stay_top attribute
*
* \param view first view of the view stack
*/
2013-09-07 02:02:26 +02:00
static View const *last_stay_top_view(View const *view)
2011-12-22 16:19:25 +01:00
{
for (; view && view->view_stack_next() && view->view_stack_next()->stay_top(); )
view = view->view_stack_next();
return view;
}
/**************************
** View stack interface **
**************************/
2013-09-07 02:02:26 +02:00
template <typename VIEW>
VIEW *View_stack::_next_view(VIEW &view) const
2011-12-22 16:19:25 +01:00
{
Session * const focused_session = _mode.focused_session();
2011-12-22 16:19:25 +01:00
View * const active_background = focused_session ?
focused_session->background() : 0;
2011-12-22 16:19:25 +01:00
for (VIEW *next_view = &view; ;) {
next_view = next_view->view_stack_next();
2011-12-22 16:19:25 +01:00
/* check if we hit the bottom of the view stack */
if (!next_view) return 0;
2011-12-22 16:19:25 +01:00
if (!next_view->background()) return next_view;
2011-12-22 16:19:25 +01:00
if (is_default_background(*next_view) || next_view == active_background)
return next_view;
2011-12-22 16:19:25 +01:00
/* view is a background view belonging to a non-focused session */
}
return 0;
}
Rect View_stack::_outline(View const &view) const
2011-12-22 16:19:25 +01:00
{
Rect const rect = view.abs_geometry();
if (_mode.flat()) return rect;
2011-12-22 16:19:25 +01:00
/* request thickness of view frame */
2013-09-07 02:02:26 +02:00
int const frame_size = view.frame_size(_mode);
2011-12-22 16:19:25 +01:00
return Rect(Point(rect.x1() - frame_size, rect.y1() - frame_size),
Point(rect.x2() + frame_size, rect.y2() + frame_size));
2011-12-22 16:19:25 +01:00
}
2013-09-07 02:02:26 +02:00
View const *View_stack::_target_stack_position(View const *neighbor, bool behind)
2011-12-22 16:19:25 +01:00
{
/* find target position in view stack */
2013-09-07 02:02:26 +02:00
View const *cv = last_stay_top_view(_first_view());
2011-12-22 16:19:25 +01:00
for ( ; cv; cv = _next_view(*cv)) {
2011-12-22 16:19:25 +01:00
/* bring view to front? */
if (behind && !neighbor)
break;
/* insert view after cv? */
if (behind && (cv == neighbor))
break;
/* insert view in front of cv? */
if (!behind && (_next_view(*cv) == neighbor))
2011-12-22 16:19:25 +01:00
break;
/* insert view in front of the background? */
if (!behind && !neighbor && _next_view(*cv)->background())
2011-12-22 16:19:25 +01:00
break;
}
return cv ? cv : last_stay_top_view(_first_view());
}
2013-09-07 02:02:26 +02:00
void View_stack::_optimize_label_rec(View const *cv, View const *lv, Rect rect, Rect *optimal)
2011-12-22 16:19:25 +01:00
{
/* if label already fits in optimized rectangle, we are happy */
if (optimal->fits(lv->label_rect().area()))
return;
/* find next view that intersects with the rectangle or the target view */
Rect clipped;
2013-09-07 02:02:26 +02:00
while (cv && cv != lv && !(clipped = Rect::intersect(_outline(*cv), rect)).valid())
cv = _next_view(*cv);
2011-12-22 16:19:25 +01:00
/* reached end of view stack */
if (!cv) return;
if (cv != lv && _next_view(*cv)) {
2011-12-22 16:19:25 +01:00
/* cut current view from rectangle and go into sub rectangles */
Rect r[4];
rect.cut(clipped, &r[0], &r[1], &r[2], &r[3]);
for (int i = 0; i < 4; i++)
_optimize_label_rec(_next_view(*cv), lv, r[i], optimal);
2011-12-22 16:19:25 +01:00
return;
}
/*
* Now, cv equals lv and we must decide how to configure the
* optimal rectangle.
*/
/* stop if label does not fit vertically */
if (rect.h() < lv->label_rect().h())
return;
/*
* If label fits completely within current rectangle, we are done.
* If label's width is not fully visible, choose the widest rectangle.
*/
if (rect.fits(lv->label_rect().area()) || (rect.w() > optimal->w())) {
*optimal = rect;
return;
}
}
void View_stack::_place_labels(Rect rect)
2011-12-22 16:19:25 +01:00
{
/* do not calculate label positions in flat mode */
2013-09-07 02:02:26 +02:00
if (_mode.flat()) return;
2011-12-22 16:19:25 +01:00
/* ignore mouse cursor */
View const *start = _next_view(*_first_view());
View *view = _next_view(*_first_view());
2011-12-22 16:19:25 +01:00
for (; view && _next_view(*view); view = _next_view(*view)) {
Rect const view_rect = view->abs_geometry();
if (Rect::intersect(view_rect, rect).valid()) {
2011-12-22 16:19:25 +01:00
Rect old = view->label_rect(), best;
/* calculate best visible label position */
Rect rect = Rect::intersect(Rect(Point(), _size), view_rect);
2011-12-22 16:19:25 +01:00
if (start) _optimize_label_rec(start, view, rect, &best);
/*
* If label is not fully visible, we ensure to display the first
* (most important) part. Otherwise, we center the label horizontally.
*/
int x = best.x1();
if (best.fits(view->label_rect().area()))
x += (best.w() - view->label_rect().w()) / 2;
view->label_pos(Point(x, best.y1()));
/* refresh old and new label positions */
refresh_view(*view, old);
refresh_view(*view, view->label_rect());
2011-12-22 16:19:25 +01:00
}
}
2011-12-22 16:19:25 +01:00
}
void View_stack::draw_rec(Canvas_base &canvas, View const *view, Rect rect) const
2011-12-22 16:19:25 +01:00
{
Rect clipped;
/* find next view that intersects with the current clipping rectangle */
2013-09-07 02:02:26 +02:00
for ( ; view && !(clipped = Rect::intersect(_outline(*view), rect)).valid(); )
view = _next_view(*view);
2011-12-22 16:19:25 +01:00
/* check if we hit the bottom of the view stack */
if (!view) return;
Rect top, left, right, bottom;
rect.cut(clipped, &top, &left, &right, &bottom);
View const *next = _next_view(*view);
2011-12-22 16:19:25 +01:00
/* draw areas at the top/left of the current view */
if (next && top.valid()) draw_rec(canvas, next, top);
if (next && left.valid()) draw_rec(canvas, next, left);
2011-12-22 16:19:25 +01:00
/* draw current view */
{
Clip_guard clip_guard(canvas, clipped);
2011-12-22 16:19:25 +01:00
/* draw background if view is transparent */
2013-09-07 02:02:26 +02:00
if (view->uses_alpha())
draw_rec(canvas, _next_view(*view), clipped);
2011-12-22 16:19:25 +01:00
view->frame(canvas, _mode);
view->draw(canvas, _mode);
2011-12-22 16:19:25 +01:00
}
/* draw areas at the bottom/right of the current view */
if (next && right.valid()) draw_rec(canvas, next, right);
if (next && bottom.valid()) draw_rec(canvas, next, bottom);
2011-12-22 16:19:25 +01:00
}
void View_stack::refresh_view(View const &view, Rect const rect)
2011-12-22 16:19:25 +01:00
{
/* clip argument agains view outline */
Rect const intersection = Rect::intersect(rect, _outline(view));
_dirty_rect.mark_as_dirty(intersection);
2011-12-22 16:19:25 +01:00
view.for_each_child([&] (View const &child) { refresh_view(child, rect); });
2011-12-22 16:19:25 +01:00
}
void View_stack::viewport(View &view, Rect const rect, Point const buffer_off)
2011-12-22 16:19:25 +01:00
{
Rect const old_outline = _outline(view);
refresh_view(view, Rect(Point(), _size));
2011-12-22 16:19:25 +01:00
view.geometry(Rect(rect));
2013-09-07 02:02:26 +02:00
view.buffer_off(buffer_off);
2011-12-22 16:19:25 +01:00
refresh_view(view, Rect(Point(), _size));
Rect const compound = Rect::compound(old_outline, _outline(view));
2011-12-22 16:19:25 +01:00
/* update labels (except when moving the mouse cursor) */
2013-09-07 02:02:26 +02:00
if (&view != _first_view())
_place_labels(compound);
2011-12-22 16:19:25 +01:00
}
void View_stack::stack(View const &view, View const *neighbor, bool behind)
2011-12-22 16:19:25 +01:00
{
2013-09-07 02:02:26 +02:00
_views.remove(&view);
_views.insert(&view, _target_stack_position(neighbor, behind));
2011-12-22 16:19:25 +01:00
_place_labels(view.abs_geometry());
2011-12-22 16:19:25 +01:00
_dirty_rect.mark_as_dirty(_outline(view));
2011-12-22 16:19:25 +01:00
}
void View_stack::title(View &view, const char *title)
2011-12-22 16:19:25 +01:00
{
2013-09-07 02:02:26 +02:00
view.title(title);
_place_labels(view.abs_geometry());
_dirty_rect.mark_as_dirty(_outline(view));
2011-12-22 16:19:25 +01:00
}
View *View_stack::find_view(Point p)
{
/* skip mouse cursor */
View *view = _next_view(*_first_view());
2011-12-22 16:19:25 +01:00
for ( ; view; view = _next_view(*view))
2011-12-22 16:19:25 +01:00
if (view->input_response_at(p, _mode))
return view;
return 0;
}
void View_stack::remove_view(View const &view, bool redraw)
2011-12-22 16:19:25 +01:00
{
/* remember geometry of view to remove */
Rect rect = _outline(view);
/* exclude view from view stack */
2013-09-07 02:02:26 +02:00
_views.remove(&view);
2011-12-22 16:19:25 +01:00
_dirty_rect.mark_as_dirty(rect);
2011-12-22 16:19:25 +01:00
}