mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-21 22:47:50 +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 */
|
||||
unsigned largest_size = 0;
|
||||
for (Widget *w = _children.first(); w; w = w->next())
|
||||
_children.for_each([&] (Widget const &w) {
|
||||
largest_size =
|
||||
max(largest_size, _direction == VERTICAL ? w->min_size().w()
|
||||
: w->min_size().h());
|
||||
max(largest_size, _direction == VERTICAL ? w.min_size().w()
|
||||
: w.min_size().h()); });
|
||||
|
||||
/* position children on one row/column */
|
||||
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) {
|
||||
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 dy = child_min_size.h() - min(w->margin.bottom, next_top_margin);
|
||||
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 dy = child_min_size.h() - min(w.margin.bottom, next_top_margin);
|
||||
position = position + Point(0, dy);
|
||||
} else {
|
||||
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 dx = child_min_size.w() - min(w->margin.right, next_left_margin);
|
||||
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 dx = child_min_size.w() - min(w.margin.right, next_left_margin);
|
||||
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
|
||||
@ -85,12 +85,12 @@ struct Menu_view::Box_layout_widget : Widget
|
||||
|
||||
void _layout() override
|
||||
{
|
||||
for (Widget *w = _children.first(); w; w = w->next()) {
|
||||
_children.for_each([&] (Widget &w) {
|
||||
if (_direction == VERTICAL)
|
||||
w->size(Area(geometry().w(), w->min_size().h()));
|
||||
w.size(Area(geometry().w(), w.min_size().h()));
|
||||
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;
|
||||
|
||||
if (Widget *child = _children.first())
|
||||
child->geometry(Rect(Point(margin.left + padding.left,
|
||||
margin.top + padding.top + dy),
|
||||
child->min_size()));
|
||||
_children.for_each([&] (Widget &child) {
|
||||
child.geometry(Rect(Point(margin.left + padding.left,
|
||||
margin.top + padding.top + dy),
|
||||
child.min_size()));
|
||||
});
|
||||
}
|
||||
|
||||
Area min_size() const override
|
||||
{
|
||||
/* determine minimum child size */
|
||||
Widget const * const child = _children.first();
|
||||
Area const child_min_size = child ? child->min_size() : Area(300, 10);
|
||||
Area child_min_size(300, 10);
|
||||
_children.for_each([&] (Widget const &child) {
|
||||
child_min_size = child.min_size(); });
|
||||
|
||||
/* don't get smaller than the background texture */
|
||||
Area const texture_size = default_texture->size();
|
||||
@ -140,9 +142,9 @@ struct Menu_view::Button_widget : Widget, Animator::Item
|
||||
|
||||
void _layout() override
|
||||
{
|
||||
for (Widget *w = _children.first(); w; w = w->next())
|
||||
w->size(Area(geometry().w() - _space().w(),
|
||||
geometry().h() - _space().h()));
|
||||
_children.for_each([&] (Widget &w) {
|
||||
w.size(Area(geometry().w() - _space().w(),
|
||||
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
|
||||
* 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;
|
||||
Allocator &_alloc;
|
||||
@ -440,10 +440,7 @@ struct Menu_view::Depgraph_widget : Widget
|
||||
|
||||
~Depgraph_widget()
|
||||
{
|
||||
while (Widget *w = _children.first()) {
|
||||
_children.remove(w);
|
||||
_model_update_policy.destroy_element(*w);
|
||||
}
|
||||
_children.destroy_all_elements(_model_update_policy);
|
||||
}
|
||||
|
||||
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 };
|
||||
}
|
||||
|
||||
update_list_model_from_xml(_model_update_policy, _children, node);
|
||||
_children.update_from_xml(_model_update_policy, node);
|
||||
|
||||
/*
|
||||
* Import dependencies
|
||||
@ -511,9 +508,9 @@ struct Menu_view::Depgraph_widget : Widget
|
||||
*
|
||||
* 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) {
|
||||
if (!node.belongs_to(*w))
|
||||
if (!node.belongs_to(w))
|
||||
return;
|
||||
|
||||
apply_to_primary_dependency(node, [&] (Node &parent) {
|
||||
@ -525,16 +522,15 @@ struct Menu_view::Depgraph_widget : Widget
|
||||
node.breadth_size(_depth_direction);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* Apply layout to the children, determine _min_size
|
||||
*/
|
||||
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) {
|
||||
if (!node.belongs_to(*w))
|
||||
if (!node.belongs_to(w))
|
||||
return;
|
||||
|
||||
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),
|
||||
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
|
||||
@ -560,18 +556,18 @@ struct Menu_view::Depgraph_widget : Widget
|
||||
if (_depth_direction.value == Depth_direction::NORTH
|
||||
|| _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)
|
||||
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)
|
||||
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();
|
||||
}
|
||||
@ -657,8 +653,8 @@ struct Menu_view::Depgraph_widget : Widget
|
||||
|
||||
void _layout() override
|
||||
{
|
||||
for (Widget *w = _children.first(); w; w = w->next())
|
||||
w->size(w->geometry().area());
|
||||
_children.for_each([&] (Widget &w) {
|
||||
w.size(w.geometry().area()); });
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -55,15 +55,19 @@ struct Menu_view::Float_widget : Widget
|
||||
_east = node.attribute_value("east", false),
|
||||
_west = node.attribute_value("west", false);
|
||||
|
||||
if (Widget *child = _children.first())
|
||||
_place_child(*child);
|
||||
_children.for_each([&] (Widget &child) {
|
||||
_place_child(child); });
|
||||
}
|
||||
|
||||
Area min_size() const override
|
||||
{
|
||||
Area result(0, 0);
|
||||
|
||||
/* determine minimum child size */
|
||||
Widget const * const child = _children.first();
|
||||
return child ? child->min_size() : Area(0, 0);
|
||||
_children.for_each([&] (Widget const &child) {
|
||||
result = child.min_size(); });
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void draw(Surface<Pixel_rgb888> &pixel_surface,
|
||||
@ -75,10 +79,10 @@ struct Menu_view::Float_widget : Widget
|
||||
|
||||
void _layout() override
|
||||
{
|
||||
if (Widget *child = _children.first()) {
|
||||
_place_child(*child);
|
||||
child->size(child->geometry().area());
|
||||
}
|
||||
_children.for_each([&] (Widget &child) {
|
||||
_place_child(child);
|
||||
child.size(child.geometry().area());
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -47,17 +47,18 @@ struct Menu_view::Frame_widget : Widget
|
||||
/*
|
||||
* layout
|
||||
*/
|
||||
if (Widget *child = _children.first())
|
||||
child->geometry(Rect(Point(margin.left + padding.left,
|
||||
margin.top + padding.top),
|
||||
child->min_size()));
|
||||
_children.for_each([&] (Widget &child) {
|
||||
child.geometry(Rect(Point(margin.left + padding.left,
|
||||
margin.top + padding.top),
|
||||
child.min_size())); });
|
||||
}
|
||||
|
||||
Area min_size() const override
|
||||
{
|
||||
/* determine minimum child size */
|
||||
Widget const * const child = _children.first();
|
||||
Area const child_min_size = child ? child->min_size() : Area(0, 0);
|
||||
/* determine minimum child size (there is only one child) */
|
||||
Area child_min_size(0, 0);
|
||||
_children.for_each([&] (Widget const &child) {
|
||||
child_min_size = child.min_size(); });
|
||||
|
||||
/* don't get smaller than the background texture */
|
||||
Area const texture_size = texture ? texture->size() : Area(0, 0);
|
||||
@ -81,9 +82,9 @@ struct Menu_view::Frame_widget : Widget
|
||||
|
||||
void _layout() override
|
||||
{
|
||||
if (Widget *child = _children.first())
|
||||
child->size(Area(geometry().w() - _space().w(),
|
||||
geometry().h() - _space().h()));
|
||||
_children.for_each([&] (Widget &child) {
|
||||
child.size(Area(geometry().w() - _space().w(),
|
||||
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
|
||||
{
|
||||
if (Widget const * const child = _children.first())
|
||||
return child->animated_geometry().area();
|
||||
Area result(1, 1);
|
||||
|
||||
return Area(1, 1);
|
||||
_children.for_each([&] (Widget const &child) {
|
||||
result = child.animated_geometry().area(); });
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void update(Xml_node node) override
|
||||
@ -51,16 +53,18 @@ struct Menu_view::Root_widget : Widget
|
||||
|
||||
_update_children(node);
|
||||
|
||||
if (Widget *child = _children.first())
|
||||
child->geometry(Rect(Point(0, 0), child->min_size()));
|
||||
_children.for_each([&] (Widget &child) {
|
||||
child.geometry(Rect(Point(0, 0), child.min_size())); });
|
||||
}
|
||||
|
||||
Area min_size() const override
|
||||
{
|
||||
if (Widget const * const child = _children.first())
|
||||
return child->min_size();
|
||||
Area result(1, 1);
|
||||
|
||||
return Area(1, 1);
|
||||
_children.for_each([&] (Widget const &child) {
|
||||
result = child.min_size(); });
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void draw(Surface<Pixel_rgb888> &pixel_surface,
|
||||
@ -72,10 +76,10 @@ struct Menu_view::Root_widget : Widget
|
||||
|
||||
void _layout() override
|
||||
{
|
||||
if (Widget *child = _children.first()) {
|
||||
child->size(geometry().area());
|
||||
child->position(Point(0, 0));
|
||||
}
|
||||
_children.for_each([&] (Widget &child) {
|
||||
child.size(geometry().area());
|
||||
child.position(Point(0, 0));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -16,10 +16,10 @@
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/xml_generator.h>
|
||||
#include <util/list_model.h>
|
||||
|
||||
/* local includes */
|
||||
#include <widget_factory.h>
|
||||
#include <list_model_from_xml.h>
|
||||
#include <animated_geometry.h>
|
||||
|
||||
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:
|
||||
|
||||
@ -99,9 +99,9 @@ class Menu_view::Widget : public List<Widget>::Element
|
||||
|
||||
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;
|
||||
|
||||
@ -129,15 +129,15 @@ class Menu_view::Widget : public List<Widget>::Element
|
||||
|
||||
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,
|
||||
Surface<Pixel_alpha8> &alpha_surface,
|
||||
Point at) const
|
||||
{
|
||||
for (Widget const *w = _children.first(); w; w = w->next())
|
||||
w->draw(pixel_surface, alpha_surface, at + w->_animated_geometry.p1());
|
||||
_children.for_each([&] (Widget const &w) {
|
||||
w.draw(pixel_surface, alpha_surface, at + w._animated_geometry.p1()); });
|
||||
}
|
||||
|
||||
virtual void _layout() { }
|
||||
@ -192,10 +192,7 @@ class Menu_view::Widget : public List<Widget>::Element
|
||||
|
||||
virtual ~Widget()
|
||||
{
|
||||
while (Widget *w = _children.first()) {
|
||||
_children.remove(w);
|
||||
_model_update_policy.destroy_element(*w);
|
||||
}
|
||||
_children.destroy_all_elements(_model_update_policy);
|
||||
}
|
||||
|
||||
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))
|
||||
return Unique_id();
|
||||
|
||||
for (Widget const *w = _children.first(); w; w = w->next()) {
|
||||
Unique_id res = w->hovered(at - w->geometry().p1());
|
||||
if (res.valid())
|
||||
return res;
|
||||
}
|
||||
Unique_id result { };
|
||||
_children.for_each([&] (Widget const &w) {
|
||||
Unique_id const id = w.hovered(at - w.geometry().p1());
|
||||
if (id.valid())
|
||||
result = id;
|
||||
});
|
||||
|
||||
return _unique_id;
|
||||
}
|
||||
@ -256,9 +254,8 @@ class Menu_view::Widget : public List<Widget>::Element
|
||||
xml.attribute("width", geometry().w());
|
||||
xml.attribute("height", geometry().h());
|
||||
|
||||
for (Widget const *w = _children.first(); w; w = w->next()) {
|
||||
w->gen_hover_model(xml, at - w->geometry().p1());
|
||||
}
|
||||
_children.for_each([&] (Widget const &w) {
|
||||
w.gen_hover_model(xml, at - w.geometry().p1()); });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user