nitpicker: Parent-child relation between views

Fixes #1020
This commit is contained in:
Norman Feske 2014-02-14 10:36:04 +01:00 committed by Christian Helmuth
parent 93605c2b15
commit 1498bb740b
12 changed files with 216 additions and 80 deletions

View File

@ -31,8 +31,8 @@ struct Nitpicker::Session_client : public Genode::Rpc_client<Session>
Input::Session_capability input_session() {
return call<Rpc_input_session>(); }
View_capability create_view() {
return call<Rpc_create_view>(); }
View_capability create_view(View_capability parent = View_capability()) {
return call<Rpc_create_view>(parent); }
void destroy_view(View_capability view) {
call<Rpc_destroy_view>(view); }

View File

@ -51,9 +51,16 @@ struct Nitpicker::Session : Genode::Session
/**
* Create a new view at the buffer
*
* \param parent parent view
*
* \return capability to a new view
*
* The 'parent' argument allows the client to use the location of an
* existing view as the coordinate origin for the to-be-created view.
* If an invalid capability is specified (default), the view will be a
* top-level view.
*/
virtual View_capability create_view() = 0;
virtual View_capability create_view(View_capability parent = View_capability()) = 0;
/**
* Destroy view
@ -97,7 +104,7 @@ struct Nitpicker::Session : Genode::Session
GENODE_RPC(Rpc_framebuffer_session, Framebuffer::Session_capability, framebuffer_session);
GENODE_RPC(Rpc_input_session, Input::Session_capability, input_session);
GENODE_RPC(Rpc_create_view, View_capability, create_view);
GENODE_RPC(Rpc_create_view, View_capability, create_view, View_capability);
GENODE_RPC(Rpc_destroy_view, void, destroy_view, View_capability);
GENODE_RPC(Rpc_background, int, background, View_capability);
GENODE_RPC(Rpc_mode, Framebuffer::Mode, mode);

View File

@ -225,7 +225,7 @@ namespace Nitpicker {
return _proxy_input_cap;
}
View_capability create_view()
View_capability create_view(View_capability)
{
return _proxy_view_cap;
}

View File

@ -31,10 +31,11 @@ struct Background : private Texture_base, Session, View
Background(Area size)
:
Texture_base(Area(0, 0)), Session(Genode::Session_label(""), 0, false),
View(*this, View::NOT_STAY_TOP, View::NOT_TRANSPARENT,
View::BACKGROUND, Rect(Point(0, 0), size)),
View(*this, View::NOT_STAY_TOP, View::NOT_TRANSPARENT, View::BACKGROUND, 0),
color(25, 37, 50)
{ }
{
View::geometry(Rect(Point(0, 0), size));
}
/***********************
@ -53,8 +54,9 @@ struct Background : private Texture_base, Session, View
void draw(Canvas_base &canvas, Mode const &mode) const
{
Clip_guard clip_guard(canvas, *this);
canvas.draw_box(*this, color);
Rect const view_rect = abs_geometry();
Clip_guard clip_guard(canvas, view_rect);
canvas.draw_box(view_rect, color);
}
};

View File

@ -37,9 +37,10 @@ class Chunky_menubar : public Texture<PT>,
Texture<PT>(pixels, 0, size),
Session(Genode::Session_label(""), 0, false),
View(*this, View::STAY_TOP, View::NOT_TRANSPARENT,
View::NOT_BACKGROUND, Rect(Point(0, 0), size)),
View::NOT_BACKGROUND, 0),
_canvas(pixels, size)
{
View::geometry(Rect(Point(0, 0), size));
Session::texture(this, false);
}
@ -62,7 +63,7 @@ class Chunky_menubar : public Texture<PT>,
Clip_guard clip_guard(canvas, *this);
/* draw menubar content */
canvas.draw_texture(p1(), *this, Texture_painter::SOLID, BLACK, false);
canvas.draw_texture(abs_position(), *this, Texture_painter::SOLID, BLACK, false);
}
@ -79,25 +80,28 @@ class Chunky_menubar : public Texture<PT>,
int g = (mode.kill()) ? 70 : (mode.xray()) ? session_color.g : (session_color.g + 100) >> 1;
int b = (mode.kill()) ? 70 : (mode.xray()) ? session_color.b : (session_color.b + 100) >> 1;
Rect const view_rect = abs_geometry();
/* highlight first line with slightly brighter color */
_canvas.draw_box(Rect(Point(0, 0), Area(View::w(), 1)),
_canvas.draw_box(Rect(Point(0, 0), Area(view_rect.w(), 1)),
Color(r + (r / 2), g + (g / 2), b + (b / 2)));
/* draw slightly shaded background */
for (unsigned i = 1; i < View::h() - 1; i++) {
for (unsigned i = 1; i < view_rect.h() - 1; i++) {
r -= r > 3 ? 4 : 0;
g -= g > 3 ? 4 : 0;
b -= b > 4 ? 4 : 0;
_canvas.draw_box(Rect(Point(0, i), Area(View::w(), 1)), Color(r, g, b));
_canvas.draw_box(Rect(Point(0, i), Area(view_rect.w(), 1)), Color(r, g, b));
}
/* draw last line darker */
_canvas.draw_box(Rect(Point(0, View::h() - 1), Area(View::w(), 1)),
_canvas.draw_box(Rect(Point(0, view_rect.h() - 1), Area(view_rect.w(), 1)),
Color(r / 4, g / 4, b / 4));
/* draw label */
draw_label(_canvas, center(label_size(session_label.string(), view_title.string())),
session_label.string(), WHITE, view_title.string(), session_color);
draw_label(_canvas, view_rect.center(label_size(session_label.string(),
view_title.string())), session_label.string(),
WHITE, view_title.string(), session_color);
}
using Menubar::state;

View File

@ -418,11 +418,11 @@ class View_component : public Genode::List<View_component>::Element,
*/
View_component(::Session &session, View_stack &view_stack,
Canvas_accessor &canvas_accessor,
Rpc_entrypoint &ep):
Rpc_entrypoint &ep, ::View *parent_view):
_view_stack(view_stack),
_view(session,
session.stay_top() ? ::View::STAY_TOP : ::View::NOT_STAY_TOP,
::View::NOT_TRANSPARENT, ::View::NOT_BACKGROUND, Rect()),
::View::NOT_TRANSPARENT, ::View::NOT_BACKGROUND, parent_view),
_canvas_accessor(canvas_accessor),
_ep(ep)
{ }
@ -437,8 +437,9 @@ class View_component : public Genode::List<View_component>::Element,
int viewport(int x, int y, int w, int h,
int buf_x, int buf_y, bool redraw)
{
/* transpose y position by vertical session offset */
y += _view.session().v_offset();
/* transpose y position of top-level views by vertical session offset */
if (_view.top_level())
y += _view.session().v_offset();
_view_stack.viewport(_canvas(), _view,
Rect(Point(x, y), Area(w, h)),
@ -605,15 +606,25 @@ class Nitpicker::Session_component : public Genode::Rpc_object<Session>,
Input::Session_capability input_session() {
return _input_session_cap; }
View_capability create_view()
View_capability create_view(View_capability parent_cap)
{
/**
/* lookup parent view */
View_component *parent_view =
dynamic_cast<View_component *>(_ep.lookup_and_lock(parent_cap));
/*
* FIXME: Do not allocate View meta data from Heap!
* Use a heap partition!
*/
View_component *view = new (env()->heap())
View_component(*this, _view_stack,
_canvas_accessor, _ep);
View_component(*this, _view_stack, _canvas_accessor, _ep,
parent_view ? &parent_view->view() : 0);
if (parent_view)
parent_view->view().add_child(&view->view());
if (parent_view)
parent_view->release();
_view_list.insert(view);
return _ep.manage(view);

View File

@ -39,8 +39,7 @@ class Mouse_cursor : public Texture<PT>,
:
Texture<PT>(pixels, 0, size),
Session(Genode::Session_label(""), 0, false),
View(*this, View::STAY_TOP, View::TRANSPARENT, View::NOT_BACKGROUND,
Rect()),
View(*this, View::STAY_TOP, View::TRANSPARENT, View::NOT_BACKGROUND, 0),
_view_stack(view_stack)
{ }
@ -66,13 +65,15 @@ class Mouse_cursor : public Texture<PT>,
void draw(Canvas_base &canvas, Mode const &mode) const
{
Clip_guard clip_guard(canvas, *this);
Rect const view_rect = abs_geometry();
Clip_guard clip_guard(canvas, view_rect);
/* draw area behind the mouse cursor */
_view_stack.draw_rec(canvas, view_stack_next(), 0, 0, *this);
_view_stack.draw_rec(canvas, view_stack_next(), 0, 0, view_rect);
/* draw mouse cursor */
canvas.draw_texture(p1(), *this, Texture_painter::MASKED, BLACK, true);
canvas.draw_texture(view_rect.p1(), *this, Texture_painter::MASKED, BLACK, true);
}
};

View File

@ -73,7 +73,7 @@ void View::frame(Canvas_base &canvas, Mode const &mode) const
/* do not draw frame in flat mode */
if (mode.flat()) return;
draw_frame(canvas, *this, _session.color(), frame_size(mode));
draw_frame(canvas, abs_geometry(), _session.color(), frame_size(mode));
}
@ -92,13 +92,15 @@ void View::draw(Canvas_base &canvas, Mode const &mode) const
Texture_painter::Mode const op = mode.flat() || (mode.xray() && view_is_focused)
? Texture_painter::SOLID : Texture_painter::MIXED;
Rect const view_rect = abs_geometry();
/*
* The view content and label should never overdraw the
* frame of the view in non-flat Nitpicker modes. The frame
* is located outside the view area. By shrinking the
* clipping area to the view area, we protect the frame.
*/
Clip_guard clip_guard(canvas, *this);
Clip_guard clip_guard(canvas, view_rect);
/*
* If the clipping area shrinked to zero, we do not process drawing
@ -116,8 +118,8 @@ void View::draw(Canvas_base &canvas, Mode const &mode) const
_session.color().b >> 1);
if (_session.texture())
canvas.draw_texture(_buffer_off + p1(), *_session.texture(), op,
mix_color, allow_alpha);
canvas.draw_texture(_buffer_off + view_rect.p1(), *_session.texture(),
op, mix_color, allow_alpha);
if (mode.flat()) return;

View File

@ -33,9 +33,15 @@ struct Same_buffer_list_elem : Genode::List<Same_buffer_list_elem>::Element { };
struct View_stack_elem : Genode::List<View_stack_elem>::Element { };
/*
* If a view has a parent, it is a list element of its parent view
*/
struct View_parent_elem : Genode::List<View_parent_elem>::Element { };
class View : public Same_buffer_list_elem,
public View_stack_elem,
public Rect
public View_parent_elem
{
public:
@ -51,21 +57,76 @@ class View : public Same_buffer_list_elem,
Transparent const _transparent; /* background is partly visible */
Background _background; /* view is a background view */
Rect _label_rect; /* position and size of label */
Point _buffer_off; /* offset to the visible buffer area */
Session &_session; /* session that created the view */
View *_parent; /* parent view */
Rect _geometry; /* position and size relative to parent */
Rect _label_rect; /* position and size of label */
Point _buffer_off; /* offset to the visible buffer area */
Session &_session; /* session that created the view */
char _title[TITLE_LEN];
Genode::List<View_parent_elem> _children;
public:
View(Session &session, Stay_top stay_top, Transparent transparent,
Background background, Rect rect)
Background bg, View *parent)
:
Rect(rect), _stay_top(stay_top), _transparent(transparent),
_background(background), _session(session)
_stay_top(stay_top), _transparent(transparent), _background(bg),
_parent(parent), _session(session)
{ title(""); }
virtual ~View() { }
virtual ~View()
{
/* break link to our parent */
if (_parent)
_parent->remove_child(this);
/* break links to our children */
while (View_parent_elem *e = _children.first())
static_cast<View *>(e)->dissolve_from_parent();
}
/**
* Return absolute view position
*/
Point abs_position() const
{
return _parent ? _geometry.p1() + _parent->abs_position()
: _geometry.p1();
}
/**
* Return absolute view geometry
*/
Rect abs_geometry() const
{
return Rect(abs_position(), _geometry.area());
}
/**
* Break the relationship of a child view from its parent
*
* This function is called when a parent view gets destroyed.
*/
void dissolve_from_parent()
{
_parent = 0;
_geometry = Rect();
}
Rect geometry() const { return _geometry; }
void geometry(Rect geometry) { _geometry = geometry; }
void add_child(View const *child) { _children.insert(child); }
void remove_child(View const *child) { _children.remove(child); }
template <typename FN>
void for_each_child(FN const &fn) const {
for (View_parent_elem const *e = _children.first(); e; e = e->next())
fn(*static_cast<View const *>(e));
}
/**
* Return thickness of frame that surrounds the view
@ -119,12 +180,13 @@ class View : public Same_buffer_list_elem,
bool belongs_to(Session const &session) const { return &session == &_session; }
bool same_session_as(View const &other) const { return &_session == &other._session; }
bool stay_top() const { return _stay_top; }
bool transparent() const { return _transparent || _session.uses_alpha(); }
bool background() const { return _background; }
Rect label_rect() const { return _label_rect; }
bool uses_alpha() const { return _session.uses_alpha(); }
Point buffer_off() const { return _buffer_off; }
bool top_level() const { return _parent == 0; }
bool stay_top() const { return _stay_top; }
bool transparent() const { return _transparent || _session.uses_alpha(); }
bool background() const { return _background; }
Rect label_rect() const { return _label_rect; }
bool uses_alpha() const { return _session.uses_alpha(); }
Point buffer_off() const { return _buffer_off; }
char const *title() const { return _title; }
@ -137,14 +199,16 @@ class View : public Same_buffer_list_elem,
*/
bool input_response_at(Point p, Mode const &mode) const
{
Rect const view_rect = abs_geometry();
/* check if point lies outside view geometry */
if ((p.x() < x1()) || (p.x() > x2())
|| (p.y() < y1()) || (p.y() > y2()))
if ((p.x() < view_rect.x1()) || (p.x() > view_rect.x2())
|| (p.y() < view_rect.y1()) || (p.y() > view_rect.y2()))
return false;
/* if view uses an alpha channel, check the input mask */
if (mode.flat() && session().uses_alpha())
return session().input_mask_at(p - p1() + _buffer_off);
return session().input_mask_at(p - view_rect.p1() + _buffer_off);
return true;
}

View File

@ -65,13 +65,15 @@ VIEW *View_stack::_next_view(VIEW *view) const
Rect View_stack::_outline(View const &view) const
{
if (_mode.flat()) return view;
Rect const rect = view.abs_geometry();
if (_mode.flat()) return rect;
/* request thickness of view frame */
int const frame_size = view.frame_size(_mode);
return Rect(Point(view.x1() - frame_size, view.y1() - frame_size),
Point(view.x2() + frame_size, view.y2() + frame_size));
return Rect(Point(rect.x1() - frame_size, rect.y1() - frame_size),
Point(rect.x2() + frame_size, rect.y2() + frame_size));
}
@ -157,13 +159,15 @@ void View_stack::_place_labels(Canvas_base &canvas, Rect rect)
View const *start = _next_view(_first_view());
View *view = _next_view(_first_view());
for (; view && _next_view(view); view = _next_view(view))
if (Rect::intersect(*view, rect).valid()) {
for (; view && _next_view(view); view = _next_view(view)) {
Rect const view_rect = view->abs_geometry();
if (Rect::intersect(view_rect, rect).valid()) {
Rect old = view->label_rect(), best;
/* calculate best visible label position */
Rect rect = Rect::intersect(Rect(Point(), canvas.size()), *view);
Rect rect = Rect::intersect(Rect(Point(), canvas.size()), view_rect);
if (start) _optimize_label_rec(start, view, rect, &best);
/*
@ -180,6 +184,7 @@ void View_stack::_place_labels(Canvas_base &canvas, Rect rect)
refresh_view(canvas, *view, view, old);
refresh_view(canvas, *view, view, view->label_rect());
}
}
}
@ -235,15 +240,17 @@ void View_stack::refresh_view(Canvas_base &canvas, View const &view,
}
void View_stack::viewport(Canvas_base &canvas, View &view, Rect pos,
void View_stack::viewport(Canvas_base &canvas, View &view, Rect rect,
Point buffer_off, bool do_redraw)
{
Rect old = _outline(view);
Rect const old_compound = _compound_outline(view);
static_cast<Rect &>(view) = Rect(pos);
view.geometry(Rect(rect));
view.buffer_off(buffer_off);
Rect compound = Rect::compound(old, _outline(view));
Rect const new_compound = _compound_outline(view);
Rect const compound = Rect::compound(old_compound, new_compound);
/* update labels (except when moving the mouse cursor) */
if (&view != _first_view())
@ -263,7 +270,7 @@ void View_stack::stack(Canvas_base &canvas, View const &view,
_views.remove(&view);
_views.insert(&view, _target_stack_position(neighbor, behind));
_place_labels(canvas, view);
_place_labels(canvas, view.abs_geometry());
/* refresh affected screen area */
refresh_view(canvas, view, 0, _outline(view));
@ -273,7 +280,7 @@ void View_stack::stack(Canvas_base &canvas, View const &view,
void View_stack::title(Canvas_base &canvas, View &view, const char *title)
{
view.title(title);
_place_labels(canvas, view);
_place_labels(canvas, view.abs_geometry());
refresh_view(canvas, view, 0, _outline(view));
}

View File

@ -57,6 +57,19 @@ class View_stack
*/
void _place_labels(Canvas_base &, Rect);
/**
* Return compound rectangle covering the view and all of its children
*/
Rect _compound_outline(View const &view)
{
Rect rect = _outline(view);
view.for_each_child([&] (View const &child) {
rect = Rect::compound(_outline(child), rect); });
return rect;
}
/**
* Return view following the specified view in the view stack
*
@ -123,9 +136,10 @@ class View_stack
* Determine view portion that displays the buffer portion
* specified by 'rect'.
*/
Point offset = view->p1() + view->buffer_off();
Rect r = Rect::intersect(Rect(rect.p1() + offset,
rect.p2() + offset), *view);
Point const offset = view->abs_position() + view->buffer_off();
Rect const r = Rect::intersect(Rect(rect.p1() + offset,
rect.p2() + offset),
view->abs_geometry());
refresh_view(canvas, *view, view, r);
}
}

View File

@ -29,18 +29,23 @@ class Test_view : public List<Test_view>::Element
Nitpicker::View_capability _cap;
int _x, _y, _w, _h;
const char *_title;
Test_view const *_parent_view;
public:
Test_view(Nitpicker::Session *nitpicker,
int x, int y, int w, int h,
const char *title)
const char *title,
Test_view *parent_view = 0)
:
_x(x), _y(y), _w(w), _h(h), _title(title)
_x(x), _y(y), _w(w), _h(h), _title(title), _parent_view(parent_view)
{
using namespace Nitpicker;
_cap = nitpicker->create_view();
View_capability parent_cap = parent_view ? parent_view->_cap
: View_capability();
_cap = nitpicker->create_view(parent_cap);
View_client(_cap).viewport(_x, _y, _w, _h, 0, 0, true);
View_client(_cap).stack(Nitpicker::View_capability(), true, true);
View_client(_cap).title(_title);
@ -51,21 +56,30 @@ class Test_view : public List<Test_view>::Element
Nitpicker::View_client(_cap).stack(Nitpicker::View_capability(), true, true);
}
/**
* Move view to absolute position
*/
void move(int x, int y)
{
_x = x;
_y = y;
/*
* If the view uses a parent view as corrdinate origin, we need to
* transform the absolute coordinates to parent-relative
* coordinates.
*/
_x = _parent_view ? x - _parent_view->x() : x;
_y = _parent_view ? y - _parent_view->y() : y;
Nitpicker::View_client(_cap).viewport(_x, _y, _w, _h, 0, 0, true);
}
/**
* Accessors
*/
const char *title() { return _title; }
int x() { return _x; }
int y() { return _y; }
int w() { return _w; }
int h() { return _h; }
const char *title() const { return _title; }
int x() const { return _parent_view ? _parent_view->x() + _x : _x; }
int y() const { return _parent_view ? _parent_view->y() + _y : _y; }
int w() const { return _w; }
int h() const { return _h; }
};
@ -83,6 +97,7 @@ class Test_view_stack : public List<Test_view>
Test_view *find(int x, int y)
{
for (Test_view *tv = first(); tv; tv = tv->next()) {
if (x < tv->x() || x >= tv->x() + tv->w()
|| y < tv->y() || y >= tv->y() + tv->h())
continue;
@ -151,9 +166,18 @@ int main(int argc, char **argv)
*/
Test_view_stack tvs(input_mask, scr_w);
tvs.insert(new (env()->heap()) Test_view(&nitpicker, 150, 100, 230, 200, "Eins"));
tvs.insert(new (env()->heap()) Test_view(&nitpicker, 170, 120, 230, 210, "Zwei"));
tvs.insert(new (env()->heap()) Test_view(&nitpicker, 190, 140, 230, 220, "Drei"));
{
/*
* View 'v1' is used as coordinate origin of 'v2' and 'v3'.
*/
static Test_view v1(&nitpicker, 150, 100, 230, 200, "Eins");
static Test_view v2(&nitpicker, 20, 20, 230, 210, "Zwei", &v1);
static Test_view v3(&nitpicker, 40, 40, 230, 220, "Drei", &v1);
tvs.insert(&v1);
tvs.insert(&v2);
tvs.insert(&v3);
}
/*
* Handle input events