/* * \brief Common base class for all widgets * \author Norman Feske * \date 2009-09-11 */ /* * Copyright (C) 2014-2017 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU Affero General Public License version 3. */ #ifndef _WIDGET_H_ #define _WIDGET_H_ /* Genode includes */ #include #include #include /* local includes */ #include namespace Menu_view { struct Margin; struct Widget; typedef Margin Padding; } struct Menu_view::Margin { unsigned left, right, top, bottom; Margin(unsigned left, unsigned right, unsigned top, unsigned bottom) : left(left), right(right), top(top), bottom(bottom) { } unsigned horizontal() const { return left + right; } unsigned vertical() const { return top + bottom; } }; class Menu_view::Widget : List_model::Element { private: friend class List_model; friend class List; public: using List_model::Element::next; enum { NAME_MAX_LEN = 32 }; typedef String Name; typedef Name Type_name; typedef String<10> Version; struct Unique_id { unsigned value = 0; /** * Constructor * * Only to be called by widget factory. */ Unique_id(unsigned value) : value(value) { } /** * Default constructor creates invalid ID */ Unique_id() { } bool operator != (Unique_id const &other) const { return other.value != value; } bool valid() const { return value != 0; } }; struct Hovered { /* widget */ Unique_id unique_id; /* widget-local detail */ using Detail = String<16>; Detail detail; bool operator != (Hovered const &other) const { return (unique_id != other.unique_id) || (detail != other.detail); } }; static Name node_name(Xml_node node) { return node.attribute_value("name", Name(node.type())); } static Animated_rect::Steps motion_steps() { return { 60 }; }; protected: Type_name const _type_name; Name const _name; Version const _version { }; Unique_id const _unique_id; Widget_factory &_factory; List_model _children { }; struct Model_update_policy : List_model::Update_policy { Widget_factory &_factory; Model_update_policy(Widget_factory &factory) : _factory(factory) { } void destroy_element(Widget &w) { _factory.destroy(&w); } Widget &create_element(Xml_node elem_node) { if (Widget *w = _factory.create(elem_node)) return *w; throw Unknown_element_type(); } void update_element(Widget &w, Xml_node node) { w.update(node); } static bool element_matches_xml_node(Widget const &w, Xml_node node) { return node.has_type(w._type_name.string()) && Widget::node_name(node) == w._name && node.attribute_value("version", Version()) == w._version; } } _model_update_policy { _factory }; inline void _update_children(Xml_node node) { _children.update_from_xml(_model_update_policy, node); } void _draw_children(Surface &pixel_surface, Surface &alpha_surface, Point at) const { _children.for_each([&] (Widget const &w) { w.draw(pixel_surface, alpha_surface, at + w._animated_geometry.p1()); }); } virtual void _layout() { } Rect _inner_geometry() const { return Rect(Point(margin.left, margin.top), Area(geometry().w() - margin.horizontal(), geometry().h() - margin.vertical())); } /* * Position relative to the parent widget and actual size, defined by * the parent */ Rect _geometry { Point(0, 0), Area(0, 0) }; Animated_rect _animated_geometry { _factory.animator }; void _trigger_geometry_animation() { bool const changed = (_geometry.p1() != _animated_geometry.p1() || _geometry.p2() != _animated_geometry.p2()); if (changed) _animated_geometry.move_to(_geometry, motion_steps()); } void _gen_common_hover_attr(Xml_generator &xml) const { xml.attribute("name", _name.string()); xml.attribute("xpos", geometry().x1()); xml.attribute("ypos", geometry().y1()); xml.attribute("width", geometry().w()); xml.attribute("height", geometry().h()); } public: Margin margin { 0, 0, 0, 0 }; Rect geometry() const { return _geometry; } Rect animated_geometry() const { return _animated_geometry.rect(); } /* * Return x/y positions of the edges of the widget with the margin * applied */ Rect edges() const { Rect const r = _animated_geometry.rect(); return Rect(Point(r.x1() + margin.left, r.y1() + margin.top), Point(r.x2() - margin.right, r.y2() - margin.bottom)); } Widget(Widget_factory &factory, Xml_node node, Unique_id unique_id) : _type_name(node.type()), _name(node_name(node)), _unique_id(unique_id), _factory(factory) { } virtual ~Widget() { _children.destroy_all_elements(_model_update_policy); } bool has_name(Name const &name) const { return name == _name; } virtual void update(Xml_node node) = 0; virtual Area min_size() const = 0; virtual void draw(Surface &pixel_surface, Surface &alpha_surface, Point at) const = 0; /** * Set widget size and update the widget tree's layout accordingly */ void size(Area size) { _geometry = Rect(_geometry.p1(), size); _layout(); _trigger_geometry_animation(); } void position(Point position) { _geometry = Rect(position, _geometry.area()); } /** * Return unique ID of inner-most hovered widget * * This function is used to track changes of the hover model. */ virtual Hovered hovered(Point at) const { if (!_inner_geometry().contains(at)) return { }; Hovered result { .unique_id = _unique_id, .detail = { } }; _children.for_each([&] (Widget const &w) { Hovered const hovered = w.hovered(at - w.geometry().p1()); if (hovered.unique_id.valid()) result = hovered; }); return result; } void print(Output &out) const { Genode::print(out, _name); } virtual void gen_hover_model(Xml_generator &xml, Point at) const { if (_inner_geometry().contains(at)) { xml.node(_type_name.string(), [&]() { _gen_common_hover_attr(xml); _children.for_each([&] (Widget const &w) { w.gen_hover_model(xml, at - w.geometry().p1()); }); }); } } }; #endif /* _WIDGET_H_ */