mirror of
https://github.com/genodelabs/genode.git
synced 2025-03-11 23:14:13 +00:00
base: add util/list_model.h
The new 'List_model' utility helps with the implementation of component-internal data models created and updated from XML. Fixes #2653
This commit is contained in:
parent
cde8163770
commit
10f1616c28
220
repos/base/include/util/list_model.h
Normal file
220
repos/base/include/util/list_model.h
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
/*
|
||||||
|
* \brief List-based data model created and updated from XML
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2017-08-09
|
||||||
|
*
|
||||||
|
* The 'List_model' stores a component-internal representation of XML-node
|
||||||
|
* content. The XML information is imported according to an 'Update_policy',
|
||||||
|
* which specifies how the elements of the data model are created, destroyed,
|
||||||
|
* and updated. The elements are ordered according to the order of XML nodes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 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 _INCLUDE__UTIL__LIST_MODEL_H_
|
||||||
|
#define _INCLUDE__UTIL__LIST_MODEL_H_
|
||||||
|
|
||||||
|
#include <util/xml_node.h>
|
||||||
|
|
||||||
|
namespace Genode { template <typename> class List_model; }
|
||||||
|
|
||||||
|
|
||||||
|
template <typename ELEM>
|
||||||
|
class Genode::List_model
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
List<ELEM> _elements { };
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
struct Update_policy;
|
||||||
|
|
||||||
|
class Element : private List<ELEM>::Element
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
/* used by 'List_model::update_from_xml' only */
|
||||||
|
friend class List_model;
|
||||||
|
friend class List<ELEM>;
|
||||||
|
|
||||||
|
ELEM *_next() const { return List<ELEM>::Element::next(); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the element's neighbor if present, otherwise nullptr
|
||||||
|
*/
|
||||||
|
ELEM const *next() const { return List<ELEM>::Element::next(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Unknown_element_type : Exception { };
|
||||||
|
|
||||||
|
~List_model()
|
||||||
|
{
|
||||||
|
if (_elements.first())
|
||||||
|
warning("list model not empty at destruction time");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update data model according to XML structure 'node'
|
||||||
|
*
|
||||||
|
* \throw Unknown_element_type
|
||||||
|
*/
|
||||||
|
template <typename POLICY>
|
||||||
|
inline void update_from_xml(POLICY &policy, Xml_node node);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call functor 'fn' for each const element
|
||||||
|
*/
|
||||||
|
template <typename FN>
|
||||||
|
void for_each(FN const &fn) const
|
||||||
|
{
|
||||||
|
for (Element const *e = _elements.first(); e; e = e->next())
|
||||||
|
fn(static_cast<ELEM const &>(*e));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call functor 'fn' for each non-const element
|
||||||
|
*/
|
||||||
|
template <typename FN>
|
||||||
|
void for_each(FN const &fn)
|
||||||
|
{
|
||||||
|
Element *next = nullptr;
|
||||||
|
for (Element *e = _elements.first(); e; e = next) {
|
||||||
|
next = e->_next();
|
||||||
|
fn(static_cast<ELEM &>(*e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all elements from the data model
|
||||||
|
*
|
||||||
|
* This method should be called at the destruction time of the
|
||||||
|
* 'List_model'.
|
||||||
|
*
|
||||||
|
* List-model elements are not implicitly destroyed by the destructor
|
||||||
|
* because the 'policy' needed to destruct elements is not kept as
|
||||||
|
* member of the list model (multiple policies may applied to the same
|
||||||
|
* list model).
|
||||||
|
*/
|
||||||
|
template <typename POLICY>
|
||||||
|
void destroy_all_elements(POLICY &policy)
|
||||||
|
{
|
||||||
|
Element *next = nullptr;
|
||||||
|
for (Element *e = _elements.first(); e; e = next) {
|
||||||
|
next = e->_next();
|
||||||
|
ELEM &elem = static_cast<ELEM &>(*e);
|
||||||
|
_elements.remove(&elem);
|
||||||
|
policy.destroy_element(elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename ELEM>
|
||||||
|
template <typename POLICY>
|
||||||
|
void Genode::List_model<ELEM>::update_from_xml(POLICY &policy, Xml_node node)
|
||||||
|
{
|
||||||
|
typedef typename POLICY::Element Element;
|
||||||
|
|
||||||
|
List<Element> updated_list;
|
||||||
|
|
||||||
|
Element *last_updated = nullptr; /* used for appending to 'updated_list' */
|
||||||
|
|
||||||
|
node.for_each_sub_node([&] (Xml_node sub_node) {
|
||||||
|
|
||||||
|
/* skip XML nodes that are unrelated to the data model */
|
||||||
|
if (!policy.node_is_element(sub_node))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* look up corresponding element in original list */
|
||||||
|
Element *curr = _elements.first();
|
||||||
|
while (curr && !policy.element_matches_xml_node(*curr, sub_node))
|
||||||
|
curr = curr->_next();
|
||||||
|
|
||||||
|
/* consume existing element or create new one */
|
||||||
|
if (curr) {
|
||||||
|
_elements.remove(curr);
|
||||||
|
} else {
|
||||||
|
/* \throw Unknown_element_type */
|
||||||
|
curr = &policy.create_element(sub_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* append current element to 'updated_list' */
|
||||||
|
updated_list.insert(curr, last_updated);
|
||||||
|
last_updated = curr;
|
||||||
|
|
||||||
|
policy.update_element(*curr, sub_node);
|
||||||
|
});
|
||||||
|
|
||||||
|
/* remove stale elements */
|
||||||
|
Element *next = nullptr;
|
||||||
|
for (Element *e = _elements.first(); e; e = next) {
|
||||||
|
next = e->_next();
|
||||||
|
policy.destroy_element(*e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* use 'updated_list' list new data model */
|
||||||
|
_elements = updated_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Policy interface to be supplied to 'List_model::update_from_xml'
|
||||||
|
*
|
||||||
|
* \param ELEM element type, must be a list element
|
||||||
|
*
|
||||||
|
* This class template is merely a blue print of a policy to document the
|
||||||
|
* interface.
|
||||||
|
*/
|
||||||
|
template <typename ELEM>
|
||||||
|
struct Genode::List_model<ELEM>::Update_policy
|
||||||
|
{
|
||||||
|
typedef List_model<ELEM>::Unknown_element_type Unknown_element_type;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Type that needs to be supplied by the policy implementation
|
||||||
|
*/
|
||||||
|
typedef ELEM Element;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy element
|
||||||
|
*
|
||||||
|
* When this function is called, the element is no longer contained
|
||||||
|
* in the model's list.
|
||||||
|
*/
|
||||||
|
void destroy_element(ELEM &elem);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create element of the type given in the 'elem_node'
|
||||||
|
*
|
||||||
|
* \throw List_model::Unknown_element_type
|
||||||
|
*/
|
||||||
|
ELEM &create_element(Xml_node elem_node);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import element properties from XML node
|
||||||
|
*/
|
||||||
|
void update_element(ELEM &elem, Xml_node elem_node);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if element corresponds to XML node
|
||||||
|
*/
|
||||||
|
static bool element_matches_xml_node(Element const &, Xml_node);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if XML node should be imported
|
||||||
|
*
|
||||||
|
* This method allows the policy to disregard certain XML node types from
|
||||||
|
* building the data model.
|
||||||
|
*/
|
||||||
|
static bool node_is_element(Xml_node node) { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _INCLUDE__UTIL__LIST_MODEL_H_ */
|
@ -44,31 +44,31 @@ struct Menu_view::Box_layout_widget : Widget
|
|||||||
|
|
||||||
/* determine largest size among our children */
|
/* determine largest size among our children */
|
||||||
unsigned largest_size = 0;
|
unsigned largest_size = 0;
|
||||||
for (Widget *w = _children.first(); w; w = w->next())
|
_children.for_each([&] (Widget const &w) {
|
||||||
largest_size =
|
largest_size =
|
||||||
max(largest_size, _direction == VERTICAL ? w->min_size().w()
|
max(largest_size, _direction == VERTICAL ? w.min_size().w()
|
||||||
: w->min_size().h());
|
: w.min_size().h()); });
|
||||||
|
|
||||||
/* position children on one row/column */
|
/* position children on one row/column */
|
||||||
Point position(0, 0);
|
Point position(0, 0);
|
||||||
for (Widget *w = _children.first(); w; w = w->next()) {
|
_children.for_each([&] (Widget &w) {
|
||||||
|
|
||||||
Area const child_min_size = w->min_size();
|
Area const child_min_size = w.min_size();
|
||||||
|
|
||||||
if (_direction == VERTICAL) {
|
if (_direction == VERTICAL) {
|
||||||
w->geometry(Rect(position, Area(largest_size, child_min_size.h())));
|
w.geometry(Rect(position, Area(largest_size, child_min_size.h())));
|
||||||
unsigned const next_top_margin = w->next() ? w->next()->margin.top : 0;
|
unsigned const next_top_margin = w.next() ? w.next()->margin.top : 0;
|
||||||
unsigned const dy = child_min_size.h() - min(w->margin.bottom, next_top_margin);
|
unsigned const dy = child_min_size.h() - min(w.margin.bottom, next_top_margin);
|
||||||
position = position + Point(0, dy);
|
position = position + Point(0, dy);
|
||||||
} else {
|
} else {
|
||||||
w->geometry(Rect(position, Area(child_min_size.w(), largest_size)));
|
w.geometry(Rect(position, Area(child_min_size.w(), largest_size)));
|
||||||
unsigned const next_left_margin = w->next() ? w->next()->margin.left : 0;
|
unsigned const next_left_margin = w.next() ? w.next()->margin.left : 0;
|
||||||
unsigned const dx = child_min_size.w() - min(w->margin.right, next_left_margin);
|
unsigned const dx = child_min_size.w() - min(w.margin.right, next_left_margin);
|
||||||
position = position + Point(dx, 0);
|
position = position + Point(dx, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
_min_size = Area(w->geometry().x2() + 1, w->geometry().y2() + 1);
|
_min_size = Area(w.geometry().x2() + 1, w.geometry().y2() + 1);
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Area min_size() const override
|
Area min_size() const override
|
||||||
@ -85,12 +85,12 @@ struct Menu_view::Box_layout_widget : Widget
|
|||||||
|
|
||||||
void _layout() override
|
void _layout() override
|
||||||
{
|
{
|
||||||
for (Widget *w = _children.first(); w; w = w->next()) {
|
_children.for_each([&] (Widget &w) {
|
||||||
if (_direction == VERTICAL)
|
if (_direction == VERTICAL)
|
||||||
w->size(Area(geometry().w(), w->min_size().h()));
|
w.size(Area(geometry().w(), w.min_size().h()));
|
||||||
else
|
else
|
||||||
w->size(Area(w->min_size().w(), geometry().h()));
|
w.size(Area(w.min_size().w(), geometry().h()));
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -85,17 +85,19 @@ struct Menu_view::Button_widget : Widget, Animator::Item
|
|||||||
|
|
||||||
bool const dy = selected ? 1 : 0;
|
bool const dy = selected ? 1 : 0;
|
||||||
|
|
||||||
if (Widget *child = _children.first())
|
_children.for_each([&] (Widget &child) {
|
||||||
child->geometry(Rect(Point(margin.left + padding.left,
|
child.geometry(Rect(Point(margin.left + padding.left,
|
||||||
margin.top + padding.top + dy),
|
margin.top + padding.top + dy),
|
||||||
child->min_size()));
|
child.min_size()));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Area min_size() const override
|
Area min_size() const override
|
||||||
{
|
{
|
||||||
/* determine minimum child size */
|
/* determine minimum child size */
|
||||||
Widget const * const child = _children.first();
|
Area child_min_size(300, 10);
|
||||||
Area const child_min_size = child ? child->min_size() : Area(300, 10);
|
_children.for_each([&] (Widget const &child) {
|
||||||
|
child_min_size = child.min_size(); });
|
||||||
|
|
||||||
/* don't get smaller than the background texture */
|
/* don't get smaller than the background texture */
|
||||||
Area const texture_size = default_texture->size();
|
Area const texture_size = default_texture->size();
|
||||||
@ -140,9 +142,9 @@ struct Menu_view::Button_widget : Widget, Animator::Item
|
|||||||
|
|
||||||
void _layout() override
|
void _layout() override
|
||||||
{
|
{
|
||||||
for (Widget *w = _children.first(); w; w = w->next())
|
_children.for_each([&] (Widget &w) {
|
||||||
w->size(Area(geometry().w() - _space().w(),
|
w.size(Area(geometry().w() - _space().w(),
|
||||||
geometry().h() - _space().h()));
|
geometry().h() - _space().h())); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -377,7 +377,7 @@ struct Menu_view::Depgraph_widget : Widget
|
|||||||
* Customized model-update policy that augments the list of child widgets
|
* Customized model-update policy that augments the list of child widgets
|
||||||
* with their graph-node topology
|
* with their graph-node topology
|
||||||
*/
|
*/
|
||||||
struct Model_update_policy : List_model_update_policy<Widget>
|
struct Model_update_policy : List_model<Widget>::Update_policy
|
||||||
{
|
{
|
||||||
Widget::Model_update_policy &_generic_model_update_policy;
|
Widget::Model_update_policy &_generic_model_update_policy;
|
||||||
Allocator &_alloc;
|
Allocator &_alloc;
|
||||||
@ -440,10 +440,7 @@ struct Menu_view::Depgraph_widget : Widget
|
|||||||
|
|
||||||
~Depgraph_widget()
|
~Depgraph_widget()
|
||||||
{
|
{
|
||||||
while (Widget *w = _children.first()) {
|
_children.destroy_all_elements(_model_update_policy);
|
||||||
_children.remove(w);
|
|
||||||
_model_update_policy.destroy_element(*w);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void update(Xml_node node) override
|
void update(Xml_node node) override
|
||||||
@ -459,7 +456,7 @@ struct Menu_view::Depgraph_widget : Widget
|
|||||||
if (dir_name == "west") _depth_direction = { Depth_direction::WEST };
|
if (dir_name == "west") _depth_direction = { Depth_direction::WEST };
|
||||||
}
|
}
|
||||||
|
|
||||||
update_list_model_from_xml(_model_update_policy, _children, node);
|
_children.update_from_xml(_model_update_policy, node);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Import dependencies
|
* Import dependencies
|
||||||
@ -511,9 +508,9 @@ struct Menu_view::Depgraph_widget : Widget
|
|||||||
*
|
*
|
||||||
* The computation depends on the order of '_children'.
|
* The computation depends on the order of '_children'.
|
||||||
*/
|
*/
|
||||||
for (Widget *w = _children.first(); w; w = w->next()) {
|
_children.for_each([&] (Widget &w) {
|
||||||
_nodes.for_each([&] (Registered_node &node) {
|
_nodes.for_each([&] (Registered_node &node) {
|
||||||
if (!node.belongs_to(*w))
|
if (!node.belongs_to(w))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
apply_to_primary_dependency(node, [&] (Node &parent) {
|
apply_to_primary_dependency(node, [&] (Node &parent) {
|
||||||
@ -525,16 +522,15 @@ struct Menu_view::Depgraph_widget : Widget
|
|||||||
node.breadth_size(_depth_direction);
|
node.breadth_size(_depth_direction);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Apply layout to the children, determine _min_size
|
* Apply layout to the children, determine _min_size
|
||||||
*/
|
*/
|
||||||
Rect bounding_box(Point(0, 0), Area(0, 0));
|
Rect bounding_box(Point(0, 0), Area(0, 0));
|
||||||
for (Widget *w = _children.first(); w; w = w->next()) {
|
_children.for_each([&] (Widget &w) {
|
||||||
|
|
||||||
_nodes.for_each([&] (Registered_node &node) {
|
_nodes.for_each([&] (Registered_node &node) {
|
||||||
if (!node.belongs_to(*w))
|
if (!node.belongs_to(w))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int const depth_pos = node.depth_pos(_depth_direction),
|
int const depth_pos = node.depth_pos(_depth_direction),
|
||||||
@ -548,11 +544,11 @@ struct Menu_view::Depgraph_widget : Widget
|
|||||||
: Rect(Point(breadth_pos, depth_pos),
|
: Rect(Point(breadth_pos, depth_pos),
|
||||||
Area(breadth_size, depth_size));
|
Area(breadth_size, depth_size));
|
||||||
|
|
||||||
w->geometry(Rect(node_rect.center(w->min_size()), w->min_size()));
|
w.geometry(Rect(node_rect.center(w.min_size()), w.min_size()));
|
||||||
|
|
||||||
bounding_box = Rect::compound(bounding_box, w->geometry());
|
bounding_box = Rect::compound(bounding_box, w.geometry());
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Mirror coordinates if graph grows towards north or west
|
* Mirror coordinates if graph grows towards north or west
|
||||||
@ -560,18 +556,18 @@ struct Menu_view::Depgraph_widget : Widget
|
|||||||
if (_depth_direction.value == Depth_direction::NORTH
|
if (_depth_direction.value == Depth_direction::NORTH
|
||||||
|| _depth_direction.value == Depth_direction::WEST) {
|
|| _depth_direction.value == Depth_direction::WEST) {
|
||||||
|
|
||||||
for (Widget *w = _children.first(); w; w = w->next()) {
|
_children.for_each([&] (Widget &w) {
|
||||||
|
|
||||||
int x = w->geometry().x1(), y = w->geometry().y1();
|
int x = w.geometry().x1(), y = w.geometry().y1();
|
||||||
|
|
||||||
if (_depth_direction.value == Depth_direction::NORTH)
|
if (_depth_direction.value == Depth_direction::NORTH)
|
||||||
y = (int)bounding_box.h() - y - w->geometry().h();
|
y = (int)bounding_box.h() - y - w.geometry().h();
|
||||||
|
|
||||||
if (_depth_direction.value == Depth_direction::WEST)
|
if (_depth_direction.value == Depth_direction::WEST)
|
||||||
x = (int)bounding_box.w() - x - w->geometry().w();
|
x = (int)bounding_box.w() - x - w.geometry().w();
|
||||||
|
|
||||||
w->geometry(Rect(Point(x, y), w->geometry().area()));
|
w.geometry(Rect(Point(x, y), w.geometry().area()));
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
_min_size = bounding_box.area();
|
_min_size = bounding_box.area();
|
||||||
}
|
}
|
||||||
@ -657,8 +653,8 @@ struct Menu_view::Depgraph_widget : Widget
|
|||||||
|
|
||||||
void _layout() override
|
void _layout() override
|
||||||
{
|
{
|
||||||
for (Widget *w = _children.first(); w; w = w->next())
|
_children.for_each([&] (Widget &w) {
|
||||||
w->size(w->geometry().area());
|
w.size(w.geometry().area()); });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -55,15 +55,19 @@ struct Menu_view::Float_widget : Widget
|
|||||||
_east = node.attribute_value("east", false),
|
_east = node.attribute_value("east", false),
|
||||||
_west = node.attribute_value("west", false);
|
_west = node.attribute_value("west", false);
|
||||||
|
|
||||||
if (Widget *child = _children.first())
|
_children.for_each([&] (Widget &child) {
|
||||||
_place_child(*child);
|
_place_child(child); });
|
||||||
}
|
}
|
||||||
|
|
||||||
Area min_size() const override
|
Area min_size() const override
|
||||||
{
|
{
|
||||||
|
Area result(0, 0);
|
||||||
|
|
||||||
/* determine minimum child size */
|
/* determine minimum child size */
|
||||||
Widget const * const child = _children.first();
|
_children.for_each([&] (Widget const &child) {
|
||||||
return child ? child->min_size() : Area(0, 0);
|
result = child.min_size(); });
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw(Surface<Pixel_rgb888> &pixel_surface,
|
void draw(Surface<Pixel_rgb888> &pixel_surface,
|
||||||
@ -75,10 +79,10 @@ struct Menu_view::Float_widget : Widget
|
|||||||
|
|
||||||
void _layout() override
|
void _layout() override
|
||||||
{
|
{
|
||||||
if (Widget *child = _children.first()) {
|
_children.for_each([&] (Widget &child) {
|
||||||
_place_child(*child);
|
_place_child(child);
|
||||||
child->size(child->geometry().area());
|
child.size(child.geometry().area());
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -47,17 +47,18 @@ struct Menu_view::Frame_widget : Widget
|
|||||||
/*
|
/*
|
||||||
* layout
|
* layout
|
||||||
*/
|
*/
|
||||||
if (Widget *child = _children.first())
|
_children.for_each([&] (Widget &child) {
|
||||||
child->geometry(Rect(Point(margin.left + padding.left,
|
child.geometry(Rect(Point(margin.left + padding.left,
|
||||||
margin.top + padding.top),
|
margin.top + padding.top),
|
||||||
child->min_size()));
|
child.min_size())); });
|
||||||
}
|
}
|
||||||
|
|
||||||
Area min_size() const override
|
Area min_size() const override
|
||||||
{
|
{
|
||||||
/* determine minimum child size */
|
/* determine minimum child size (there is only one child) */
|
||||||
Widget const * const child = _children.first();
|
Area child_min_size(0, 0);
|
||||||
Area const child_min_size = child ? child->min_size() : Area(0, 0);
|
_children.for_each([&] (Widget const &child) {
|
||||||
|
child_min_size = child.min_size(); });
|
||||||
|
|
||||||
/* don't get smaller than the background texture */
|
/* don't get smaller than the background texture */
|
||||||
Area const texture_size = texture ? texture->size() : Area(0, 0);
|
Area const texture_size = texture ? texture->size() : Area(0, 0);
|
||||||
@ -81,9 +82,9 @@ struct Menu_view::Frame_widget : Widget
|
|||||||
|
|
||||||
void _layout() override
|
void _layout() override
|
||||||
{
|
{
|
||||||
if (Widget *child = _children.first())
|
_children.for_each([&] (Widget &child) {
|
||||||
child->size(Area(geometry().w() - _space().w(),
|
child.size(Area(geometry().w() - _space().w(),
|
||||||
geometry().h() - _space().h()));
|
geometry().h() - _space().h())); });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,138 +0,0 @@
|
|||||||
/*
|
|
||||||
* \brief Utility for updating an internal data model from an XML structure
|
|
||||||
* \author Norman Feske
|
|
||||||
* \date 2017-08-09
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (C) 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 _INCLUDE__UTIL__LIST_MODEL_FROM_XML_H_
|
|
||||||
#define _INCLUDE__UTIL__LIST_MODEL_FROM_XML_H_
|
|
||||||
|
|
||||||
#include <util/xml_node.h>
|
|
||||||
|
|
||||||
namespace Genode {
|
|
||||||
|
|
||||||
template <typename POLICY>
|
|
||||||
static inline void
|
|
||||||
update_list_model_from_xml(POLICY &policy,
|
|
||||||
List<typename POLICY::Element> &list,
|
|
||||||
Xml_node node);
|
|
||||||
|
|
||||||
template <typename> struct List_model_update_policy;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Policy interface to be supplied to 'update_list_model_from_xml'
|
|
||||||
*
|
|
||||||
* \param ELEM element type, must be a list element
|
|
||||||
*
|
|
||||||
* This class template is merely a blue print of a policy to document the
|
|
||||||
* interface.
|
|
||||||
*/
|
|
||||||
template <typename ELEM>
|
|
||||||
struct Genode::List_model_update_policy
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Types that need to be supplied by the policy implementation
|
|
||||||
*/
|
|
||||||
typedef ELEM Element;
|
|
||||||
|
|
||||||
struct Unknown_element_type : Exception { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy element
|
|
||||||
*
|
|
||||||
* When this function is called, the element is no longer contained
|
|
||||||
* in the model's list.
|
|
||||||
*/
|
|
||||||
void destroy_element(ELEM &elem);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create element of the type given in the 'elem_node'
|
|
||||||
*
|
|
||||||
* \throw Unknown_element_type
|
|
||||||
*/
|
|
||||||
ELEM &create_element(Xml_node elem_node);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Import element properties from XML node
|
|
||||||
*/
|
|
||||||
void update_element(ELEM &elem, Xml_node elem_node);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if element corresponds to XML node
|
|
||||||
*/
|
|
||||||
static bool element_matches_xml_node(Element const &, Xml_node);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if XML node should be imported
|
|
||||||
*
|
|
||||||
* This method allows the policy to disregard certain XML node types from
|
|
||||||
* building the data model.
|
|
||||||
*/
|
|
||||||
static bool node_is_element(Xml_node node) { return true; }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update 'list' data model according to XML structure 'node'
|
|
||||||
*
|
|
||||||
* \throw Unknown_element_type
|
|
||||||
*/
|
|
||||||
template <typename POLICY>
|
|
||||||
static inline void
|
|
||||||
Genode::update_list_model_from_xml(POLICY &policy,
|
|
||||||
List<typename POLICY::Element> &list,
|
|
||||||
Xml_node node)
|
|
||||||
{
|
|
||||||
typedef typename POLICY::Element Element;
|
|
||||||
|
|
||||||
List<Element> updated_list;
|
|
||||||
|
|
||||||
Element *last_updated = nullptr; /* used for appending to 'updated_list' */
|
|
||||||
|
|
||||||
node.for_each_sub_node([&] (Xml_node sub_node) {
|
|
||||||
|
|
||||||
/* skip XML nodes that are unrelated to the data model */
|
|
||||||
if (!policy.node_is_element(sub_node))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* look up corresponding element in original list */
|
|
||||||
Element *curr = list.first();
|
|
||||||
while (curr && !policy.element_matches_xml_node(*curr, sub_node))
|
|
||||||
curr = curr->next();
|
|
||||||
|
|
||||||
/* consume existing element or create new one */
|
|
||||||
if (curr) {
|
|
||||||
list.remove(curr);
|
|
||||||
} else {
|
|
||||||
/* \throw Unknown_element_type */
|
|
||||||
curr = &policy.create_element(sub_node);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* append current element to 'updated_list' */
|
|
||||||
updated_list.insert(curr, last_updated);
|
|
||||||
last_updated = curr;
|
|
||||||
|
|
||||||
policy.update_element(*curr, sub_node);
|
|
||||||
});
|
|
||||||
|
|
||||||
/* remove stale elements */
|
|
||||||
Element *next = nullptr;
|
|
||||||
for (Element *e = list.first(); e; e = next) {
|
|
||||||
next = e->next();
|
|
||||||
policy.destroy_element(*e);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* use 'updated_list' list new data model */
|
|
||||||
list = updated_list;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* _INCLUDE__UTIL__LIST_MODEL_FROM_XML_H_ */
|
|
@ -29,10 +29,12 @@ struct Menu_view::Root_widget : Widget
|
|||||||
|
|
||||||
Area animated_size() const
|
Area animated_size() const
|
||||||
{
|
{
|
||||||
if (Widget const * const child = _children.first())
|
Area result(1, 1);
|
||||||
return child->animated_geometry().area();
|
|
||||||
|
|
||||||
return Area(1, 1);
|
_children.for_each([&] (Widget const &child) {
|
||||||
|
result = child.animated_geometry().area(); });
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void update(Xml_node node) override
|
void update(Xml_node node) override
|
||||||
@ -51,16 +53,18 @@ struct Menu_view::Root_widget : Widget
|
|||||||
|
|
||||||
_update_children(node);
|
_update_children(node);
|
||||||
|
|
||||||
if (Widget *child = _children.first())
|
_children.for_each([&] (Widget &child) {
|
||||||
child->geometry(Rect(Point(0, 0), child->min_size()));
|
child.geometry(Rect(Point(0, 0), child.min_size())); });
|
||||||
}
|
}
|
||||||
|
|
||||||
Area min_size() const override
|
Area min_size() const override
|
||||||
{
|
{
|
||||||
if (Widget const * const child = _children.first())
|
Area result(1, 1);
|
||||||
return child->min_size();
|
|
||||||
|
|
||||||
return Area(1, 1);
|
_children.for_each([&] (Widget const &child) {
|
||||||
|
result = child.min_size(); });
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw(Surface<Pixel_rgb888> &pixel_surface,
|
void draw(Surface<Pixel_rgb888> &pixel_surface,
|
||||||
@ -72,10 +76,10 @@ struct Menu_view::Root_widget : Widget
|
|||||||
|
|
||||||
void _layout() override
|
void _layout() override
|
||||||
{
|
{
|
||||||
if (Widget *child = _children.first()) {
|
_children.for_each([&] (Widget &child) {
|
||||||
child->size(geometry().area());
|
child.size(geometry().area());
|
||||||
child->position(Point(0, 0));
|
child.position(Point(0, 0));
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -16,10 +16,10 @@
|
|||||||
|
|
||||||
/* Genode includes */
|
/* Genode includes */
|
||||||
#include <util/xml_generator.h>
|
#include <util/xml_generator.h>
|
||||||
|
#include <util/list_model.h>
|
||||||
|
|
||||||
/* local includes */
|
/* local includes */
|
||||||
#include <widget_factory.h>
|
#include <widget_factory.h>
|
||||||
#include <list_model_from_xml.h>
|
|
||||||
#include <animated_geometry.h>
|
#include <animated_geometry.h>
|
||||||
|
|
||||||
namespace Menu_view {
|
namespace Menu_view {
|
||||||
@ -45,7 +45,7 @@ struct Menu_view::Margin
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class Menu_view::Widget : public List<Widget>::Element
|
class Menu_view::Widget : public List_model<Widget>::Element
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@ -99,9 +99,9 @@ class Menu_view::Widget : public List<Widget>::Element
|
|||||||
|
|
||||||
Widget_factory &_factory;
|
Widget_factory &_factory;
|
||||||
|
|
||||||
List<Widget> _children;
|
List_model<Widget> _children;
|
||||||
|
|
||||||
struct Model_update_policy : List_model_update_policy<Widget>
|
struct Model_update_policy : List_model<Widget>::Update_policy
|
||||||
{
|
{
|
||||||
Widget_factory &_factory;
|
Widget_factory &_factory;
|
||||||
|
|
||||||
@ -129,15 +129,15 @@ class Menu_view::Widget : public List<Widget>::Element
|
|||||||
|
|
||||||
inline void _update_children(Xml_node node)
|
inline void _update_children(Xml_node node)
|
||||||
{
|
{
|
||||||
update_list_model_from_xml(_model_update_policy, _children, node);
|
_children.update_from_xml(_model_update_policy, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _draw_children(Surface<Pixel_rgb888> &pixel_surface,
|
void _draw_children(Surface<Pixel_rgb888> &pixel_surface,
|
||||||
Surface<Pixel_alpha8> &alpha_surface,
|
Surface<Pixel_alpha8> &alpha_surface,
|
||||||
Point at) const
|
Point at) const
|
||||||
{
|
{
|
||||||
for (Widget const *w = _children.first(); w; w = w->next())
|
_children.for_each([&] (Widget const &w) {
|
||||||
w->draw(pixel_surface, alpha_surface, at + w->_animated_geometry.p1());
|
w.draw(pixel_surface, alpha_surface, at + w._animated_geometry.p1()); });
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void _layout() { }
|
virtual void _layout() { }
|
||||||
@ -192,10 +192,7 @@ class Menu_view::Widget : public List<Widget>::Element
|
|||||||
|
|
||||||
virtual ~Widget()
|
virtual ~Widget()
|
||||||
{
|
{
|
||||||
while (Widget *w = _children.first()) {
|
_children.destroy_all_elements(_model_update_policy);
|
||||||
_children.remove(w);
|
|
||||||
_model_update_policy.destroy_element(*w);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool has_name(Name const &name) const { return name == _name; }
|
bool has_name(Name const &name) const { return name == _name; }
|
||||||
@ -230,11 +227,12 @@ class Menu_view::Widget : public List<Widget>::Element
|
|||||||
if (!_inner_geometry().contains(at))
|
if (!_inner_geometry().contains(at))
|
||||||
return Unique_id();
|
return Unique_id();
|
||||||
|
|
||||||
for (Widget const *w = _children.first(); w; w = w->next()) {
|
Unique_id result { };
|
||||||
Unique_id res = w->hovered(at - w->geometry().p1());
|
_children.for_each([&] (Widget const &w) {
|
||||||
if (res.valid())
|
Unique_id const id = w.hovered(at - w.geometry().p1());
|
||||||
return res;
|
if (id.valid())
|
||||||
}
|
result = id;
|
||||||
|
});
|
||||||
|
|
||||||
return _unique_id;
|
return _unique_id;
|
||||||
}
|
}
|
||||||
@ -256,9 +254,8 @@ class Menu_view::Widget : public List<Widget>::Element
|
|||||||
xml.attribute("width", geometry().w());
|
xml.attribute("width", geometry().w());
|
||||||
xml.attribute("height", geometry().h());
|
xml.attribute("height", geometry().h());
|
||||||
|
|
||||||
for (Widget const *w = _children.first(); w; w = w->next()) {
|
_children.for_each([&] (Widget const &w) {
|
||||||
w->gen_hover_model(xml, at - w->geometry().p1());
|
w.gen_hover_model(xml, at - w.geometry().p1()); });
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user