diff --git a/os/include/nitpicker_session/client.h b/os/include/nitpicker_session/client.h index 897adfeb5a..b66c746008 100644 --- a/os/include/nitpicker_session/client.h +++ b/os/include/nitpicker_session/client.h @@ -31,8 +31,8 @@ struct Nitpicker::Session_client : public Genode::Rpc_client Input::Session_capability input_session() { return call(); } - View_capability create_view() { - return call(); } + View_capability create_view(View_capability parent = View_capability()) { + return call(parent); } void destroy_view(View_capability view) { call(view); } diff --git a/os/include/nitpicker_session/nitpicker_session.h b/os/include/nitpicker_session/nitpicker_session.h index 40e3e16c9b..96be3a9b95 100644 --- a/os/include/nitpicker_session/nitpicker_session.h +++ b/os/include/nitpicker_session/nitpicker_session.h @@ -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); diff --git a/os/src/server/loader/nitpicker.h b/os/src/server/loader/nitpicker.h index 003cb286c5..7f6ab3aa2f 100644 --- a/os/src/server/loader/nitpicker.h +++ b/os/src/server/loader/nitpicker.h @@ -225,7 +225,7 @@ namespace Nitpicker { return _proxy_input_cap; } - View_capability create_view() + View_capability create_view(View_capability) { return _proxy_view_cap; } diff --git a/os/src/server/nitpicker/background.h b/os/src/server/nitpicker/background.h index 069c1a5da9..8355d0182c 100644 --- a/os/src/server/nitpicker/background.h +++ b/os/src/server/nitpicker/background.h @@ -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); } }; diff --git a/os/src/server/nitpicker/chunky_menubar.h b/os/src/server/nitpicker/chunky_menubar.h index c8515e30a3..6e2ddbc59e 100644 --- a/os/src/server/nitpicker/chunky_menubar.h +++ b/os/src/server/nitpicker/chunky_menubar.h @@ -37,9 +37,10 @@ class Chunky_menubar : public Texture, Texture(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, 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, 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; diff --git a/os/src/server/nitpicker/main.cc b/os/src/server/nitpicker/main.cc index d581ffa78a..beacbfd99e 100644 --- a/os/src/server/nitpicker/main.cc +++ b/os/src/server/nitpicker/main.cc @@ -418,11 +418,11 @@ class View_component : public Genode::List::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::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, 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(_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); diff --git a/os/src/server/nitpicker/mouse_cursor.h b/os/src/server/nitpicker/mouse_cursor.h index fcf96774e5..f4d01174f1 100644 --- a/os/src/server/nitpicker/mouse_cursor.h +++ b/os/src/server/nitpicker/mouse_cursor.h @@ -39,8 +39,7 @@ class Mouse_cursor : public Texture, : Texture(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, 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); } }; diff --git a/os/src/server/nitpicker/view.cc b/os/src/server/nitpicker/view.cc index a1d8c80c37..b79b9c70df 100644 --- a/os/src/server/nitpicker/view.cc +++ b/os/src/server/nitpicker/view.cc @@ -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; diff --git a/os/src/server/nitpicker/view.h b/os/src/server/nitpicker/view.h index 276caf0f60..46357957a3 100644 --- a/os/src/server/nitpicker/view.h +++ b/os/src/server/nitpicker/view.h @@ -33,9 +33,15 @@ struct Same_buffer_list_elem : Genode::List::Element { }; struct View_stack_elem : Genode::List::Element { }; +/* + * If a view has a parent, it is a list element of its parent view + */ +struct View_parent_elem : Genode::List::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 _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(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 + void for_each_child(FN const &fn) const { + for (View_parent_elem const *e = _children.first(); e; e = e->next()) + fn(*static_cast(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; } diff --git a/os/src/server/nitpicker/view_stack.cc b/os/src/server/nitpicker/view_stack.cc index 0c1db59684..a72e2343e1 100644 --- a/os/src/server/nitpicker/view_stack.cc +++ b/os/src/server/nitpicker/view_stack.cc @@ -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(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)); } diff --git a/os/src/server/nitpicker/view_stack.h b/os/src/server/nitpicker/view_stack.h index cc1450f7f9..7851e3a011 100644 --- a/os/src/server/nitpicker/view_stack.h +++ b/os/src/server/nitpicker/view_stack.h @@ -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); } } diff --git a/os/src/test/nitpicker/test.cc b/os/src/test/nitpicker/test.cc index 048b688906..b674d347be 100644 --- a/os/src/test/nitpicker/test.cc +++ b/os/src/test/nitpicker/test.cc @@ -29,18 +29,23 @@ class Test_view : public List::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::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 *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