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

392 lines
9.5 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
*/
/*
* Copyright (C) 2006-2017 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 Affero General Public License version 3.
2011-12-22 16:19:25 +01:00
*/
#include "view_stack.h"
#include "clip_guard.h"
using namespace Nitpicker;
2011-12-22 16:19:25 +01:00
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
{
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->owner().visible()) continue;
if (!next_view->background()) return next_view;
2011-12-22 16:19:25 +01:00
if (is_default_background(*next_view) || _focus.focused_background(*next_view))
return next_view;
2011-12-22 16:19:25 +01:00
/* view is a background view belonging to a non-focused session */
}
return 0;
}
Nitpicker::Rect View_stack::_outline(View_component const &view) const
2011-12-22 16:19:25 +01:00
{
Rect const rect = view.abs_geometry();
2011-12-22 16:19:25 +01:00
/* request thickness of view frame */
int const frame_size = view.frame_size(_focus);
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
}
View_component const *View_stack::_target_stack_position(View_component const *neighbor, bool behind)
2011-12-22 16:19:25 +01:00
{
if (behind) {
2011-12-22 16:19:25 +01:00
if (!neighbor)
return nullptr;
2011-12-22 16:19:25 +01:00
/* find target position behind neighbor */
for (View_component const *cv = _first_view(); cv; cv = _next_view(*cv))
if (cv == neighbor)
return cv;
2011-12-22 16:19:25 +01:00
} else {
2011-12-22 16:19:25 +01:00
if (neighbor == _first_view())
return nullptr;
2011-12-22 16:19:25 +01:00
/* find target position in front of neighbor */
for (View_component const *cv = _first_view(), *next = nullptr; cv; cv = next) {
next = _next_view(*cv);
if (!next || next == neighbor || next->background())
return cv;
}
2011-12-22 16:19:25 +01:00
}
/* we should never reach this point */
return nullptr;
2011-12-22 16:19:25 +01:00
}
void View_stack::_optimize_label_rec(View_component const *cv, View_component 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
{
/*
* XXX We may skip this if none of the domains have the labeling enabled.
*/
2011-12-22 16:19:25 +01:00
/* ignore mouse cursor */
View_component const *start = _next_view(*_first_view());
View_component *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_component 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_component 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 */
view->dirty_rect().flush([&] (Rect const &dirty_rect) {
Clip_guard clip_guard(canvas, Rect::intersect(clipped, dirty_rect));
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, _focus);
view->draw(canvas, _focus);
});
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_component &view, Rect const rect)
{
/* rectangle constrained to view geometry */
Rect const view_rect = Rect::intersect(rect, _outline(view));
for (View_component *v = _first_view(); v; v = v->view_stack_next()) {
Rect const intersection = Rect::intersect(view_rect, _outline(*v));
if (intersection.valid())
_mark_view_as_dirty(*v, intersection);
}
view.for_each_child([&] (View_component &child) { refresh_view(child, rect); });
}
void View_stack::refresh(Rect const rect)
2011-12-22 16:19:25 +01:00
{
for (View_component *v = _first_view(); v; v = v->view_stack_next()) {
Rect const intersection = Rect::intersect(rect, _outline(*v));
2011-12-22 16:19:25 +01:00
if (intersection.valid())
refresh_view(*v, intersection);
}
2011-12-22 16:19:25 +01:00
}
void View_stack::geometry(View_component &view, Rect const rect)
2011-12-22 16:19:25 +01:00
{
Rect const old_outline = _outline(view);
/*
* Refresh area covered by the original view geometry.
*
* We specify the whole geometry to also cover the refresh of child
* views. The 'refresh_view' function takes care to constrain the
* refresh to the actual view geometry.
*/
refresh_view(view, Rect(Point(), _size));
2011-12-22 16:19:25 +01:00
/* change geometry */
view.geometry(Rect(rect));
2011-12-22 16:19:25 +01:00
/* refresh new view geometry */
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::buffer_offset(View_component &view, Point const buffer_off)
{
view.buffer_off(buffer_off);
refresh_view(view, Rect(Point(), _size));
}
void View_stack::stack(View_component &view, View_component 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
/* enforce stacking constrains dictated by domain layers */
sort_views_by_layer();
_place_labels(view.abs_geometry());
2011-12-22 16:19:25 +01:00
refresh_view(view, _outline(view));
2011-12-22 16:19:25 +01:00
}
void View_stack::title(View_component &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());
_mark_view_as_dirty(view, _outline(view));
2011-12-22 16:19:25 +01:00
}
View_component *View_stack::find_view(Point p)
2011-12-22 16:19:25 +01:00
{
View_component *view = _first_view();
2011-12-22 16:19:25 +01:00
for ( ; view; view = _next_view(*view))
if (view->input_response_at(p))
2011-12-22 16:19:25 +01:00
return view;
return nullptr;
2011-12-22 16:19:25 +01:00
}
Follow practices suggested by "Effective C++" The patch adjust the code of the base, base-<kernel>, and os repository. To adapt existing components to fix violations of the best practices suggested by "Effective C++" as reported by the -Weffc++ compiler argument. The changes follow the patterns outlined below: * A class with virtual functions can no longer publicly inherit base classed without a vtable. The inherited object may either be moved to a member variable, or inherited privately. The latter would be used for classes that inherit 'List::Element' or 'Avl_node'. In order to enable the 'List' and 'Avl_tree' to access the meta data, the 'List' must become a friend. * Instead of adding a virtual destructor to abstract base classes, we inherit the new 'Interface' class, which contains a virtual destructor. This way, single-line abstract base classes can stay as compact as they are now. The 'Interface' utility resides in base/include/util/interface.h. * With the new warnings enabled, all member variables must be explicitly initialized. Basic types may be initialized with '='. All other types are initialized with braces '{ ... }' or as class initializers. If basic types and non-basic types appear in a row, it is nice to only use the brace syntax (also for basic types) and align the braces. * If a class contains pointers as members, it must now also provide a copy constructor and assignment operator. In the most cases, one would make them private, effectively disallowing the objects to be copied. Unfortunately, this warning cannot be fixed be inheriting our existing 'Noncopyable' class (the compiler fails to detect that the inheriting class cannot be copied and still gives the error). For now, we have to manually add declarations for both the copy constructor and assignment operator as private class members. Those declarations should be prepended with a comment like this: /* * Noncopyable */ Thread(Thread const &); Thread &operator = (Thread const &); In the future, we should revisit these places and try to replace the pointers with references. In the presence of at least one reference member, the compiler would no longer implicitly generate a copy constructor. So we could remove the manual declaration. Issue #465
2017-12-21 15:42:15 +01:00
void View_stack::remove_view(View_component const &view, bool /* redraw */)
2011-12-22 16:19:25 +01:00
{
view.for_each_const_child([&] (View_component const &child) { remove_view(child); });
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
refresh(rect);
2011-12-22 16:19:25 +01:00
}
void View_stack::sort_views_by_layer()
{
Genode::List<View_stack_elem> sorted;
/* last element of the sorted list */
View_stack_elem *at = nullptr;
while (_views.first()) {
/* find view with the lowest layer */
unsigned lowest_layer = ~0U;
View_stack_elem *lowest_view = nullptr;
for (View_stack_elem *v = _views.first(); v; v = v->next()) {
unsigned const layer = static_cast<View_component *>(v)->owner().layer();
if (layer < lowest_layer) {
lowest_layer = layer;
lowest_view = v;
}
}
if (!lowest_view)
lowest_view = _views.first();
/*
* Move lowest view from unsorted list to the end of the sorted
* list.
*/
_views.remove(lowest_view);
sorted.insert(lowest_view, at);
at = lowest_view;
}
/* replace empty source list by newly sorted list */
_views = sorted;
}
void View_stack::to_front(char const *selector)
{
/*
* Move all views that match the selector to the front while
* maintaining their ordering.
*/
View_component *at = nullptr;
for (View_component *v = _first_view(); v; v = v->view_stack_next()) {
if (!v->owner().matches_session_label(selector))
continue;
if (v->background())
continue;
/*
* Move view to behind the previous view that we moved to
* front. If 'v' is the first view that matches the selector,
* move it to the front ('at' argument of 'insert' is 0).
*/
_views.remove(v);
_views.insert(v, at);
at = v;
/* mark view geometry as to be redrawn */
refresh(_outline(*v));
}
/* reestablish domain layering */
sort_views_by_layer();
}