menu_view: use modern list-model interface

Issue #4317
This commit is contained in:
Norman Feske 2023-10-27 15:23:36 +02:00 committed by Christian Helmuth
parent 3e24a86b87
commit c9938e424b
7 changed files with 163 additions and 185 deletions

View File

@ -92,7 +92,6 @@ class Menu_view::Cursor : List_model<Cursor>::Element
_move_to(_position_from_xml_node(node), Steps{0});
}
void draw(Surface<Pixel_rgb888> &pixel_surface,
Surface<Pixel_alpha8> &alpha_surface,
Point at, unsigned height) const
@ -111,36 +110,20 @@ class Menu_view::Cursor : List_model<Cursor>::Element
}
}
struct Model_update_policy : List_model<Cursor>::Update_policy
bool matches(Xml_node const &node) const
{
Widget_factory &_factory;
Glyph_position &_glyph_position;
return _node_name(node) == _name;
}
Model_update_policy(Widget_factory &factory, Glyph_position &glyph_position)
:
_factory(factory), _glyph_position(glyph_position)
{ }
static bool type_matches(Xml_node const &node)
{
return node.has_type("cursor");
}
void destroy_element(Cursor &c) { destroy(_factory.alloc, &c); }
Cursor &create_element(Xml_node node)
{
return *new (_factory.alloc)
Cursor(node, _factory.animator, _glyph_position, _factory.styles);
}
void update_element(Cursor &c, Xml_node node)
{
c._move_to(c._position_from_xml_node(node), Steps{12});
}
static bool element_matches_xml_node(Cursor const &c, Xml_node node)
{
return node.has_type("cursor") && _node_name(node) == c._name;
}
static bool node_is_element(Xml_node node) { return node.has_type("cursor"); }
};
void update(Xml_node const &node)
{
_move_to(_position_from_xml_node(node), Steps{12});
}
};
#endif /* _CURSOR_H_ */

View File

@ -442,79 +442,12 @@ struct Menu_view::Depgraph_widget : Widget
fn(_root_node);
}
/**
* Customized model-update policy that augments the list of child widgets
* with their graph-node topology
*/
struct Model_update_policy : List_model<Widget>::Update_policy
{
Widget::Model_update_policy &_generic_model_update_policy;
Allocator &_alloc;
Animator &_animator;
Node_registry &_nodes;
Model_update_policy(Widget::Model_update_policy &policy,
Allocator &alloc, Animator &animator,
Node_registry &nodes)
:
_generic_model_update_policy(policy),
_alloc(alloc), _animator(animator), _nodes(nodes)
{ }
void _destroy_node(Registered_node &node)
{
/*
* If a server node vanishes, disconnect all client nodes. The
* nodes will be reconnected - if possible - after the model
* update.
*/
node.for_each_dependent_node([&] (Node &dependent) {
dependent.cut_dependencies(); });
Widget &w = node._widget;
destroy(_alloc, &node);
_generic_model_update_policy.destroy_element(w);
}
void destroy_element(Widget &w)
{
_nodes.for_each([&] (Registered_node &node) {
if (node.belongs_to(w))
_destroy_node(node); });
}
/* do not import <dep> nodes as widgets */
bool node_is_element(Xml_node node) { return !node.has_type("dep"); }
Widget &create_element(Xml_node elem_node)
{
Widget &w = _generic_model_update_policy.create_element(elem_node);
new (_alloc) Registered_node(_nodes, _alloc, w, _animator);
return w;
}
void update_element(Widget &w, Xml_node elem_node)
{
_generic_model_update_policy.update_element(w, elem_node);
}
static bool element_matches_xml_node(Widget const &w, Xml_node node)
{
return Widget::Model_update_policy::element_matches_xml_node(w, node);
}
} _model_update_policy { Widget::_model_update_policy,
_factory.alloc, _factory.animator, _nodes };
Depgraph_widget(Widget_factory &factory, Xml_node node, Unique_id unique_id)
:
Widget(factory, node, unique_id)
{ }
~Depgraph_widget()
{
_children.destroy_all_elements(_model_update_policy);
}
~Depgraph_widget() { _update_children(Xml_node("<empty/>")); }
void update(Xml_node node) override
{
@ -529,7 +462,49 @@ struct Menu_view::Depgraph_widget : Widget
if (dir_name == "west") _depth_direction = { Depth_direction::WEST };
}
_children.update_from_xml(_model_update_policy, node);
_update_children(node);
}
void _update_children(Xml_node const &node)
{
Allocator &alloc = _factory.alloc;
update_list_model_from_xml(_children, node,
/* create */
[&] (Xml_node const &node) -> Widget &
{
Widget &w = _factory.create(node);
new (alloc) Registered_node(_nodes, alloc, w, _factory.animator);
return w;
},
/* destroy */
[&] (Widget &w)
{
auto destroy_node = [&] (Registered_node &node)
{
/*
* If a server node vanishes, disconnect all client nodes. The
* nodes will be reconnected - if possible - after the model
* update.
*/
node.for_each_dependent_node([&] (Node &dependent) {
dependent.cut_dependencies(); });
Widget &w = node._widget;
destroy(alloc, &node);
_factory.destroy(&w);
};
_nodes.for_each([&] (Registered_node &node) {
if (node.belongs_to(w))
destroy_node(node); });
},
/* update */
[&] (Widget &w, Xml_node const &node) { w.update(node); }
);
/*
* Import dependencies

View File

@ -37,24 +37,47 @@ struct Menu_view::Label_widget : Widget, Cursor::Glyph_position
int _min_width = 0;
int _min_height = 0;
Cursor::Model_update_policy _cursor_update_policy;
Text_selection::Model_update_policy _selection_update_policy;
List_model<Cursor> _cursors { };
List_model<Text_selection> _selections { };
Label_widget(Widget_factory &factory, Xml_node node, Unique_id unique_id)
:
Widget(factory, node, unique_id),
_color(factory.animator),
_cursor_update_policy(factory, *this),
_selection_update_policy(factory.alloc, *this)
Widget(factory, node, unique_id), _color(factory.animator)
{ }
~Label_widget()
~Label_widget() { _update_children(Xml_node("<empty/>")); }
void _update_children(Xml_node const &node)
{
_cursors .destroy_all_elements(_cursor_update_policy);
_selections.destroy_all_elements(_selection_update_policy);
update_list_model_from_xml(_cursors, node,
/* create */
[&] (Xml_node const &node) -> Cursor & {
return *new (_factory.alloc)
Cursor(node, _factory.animator, *this, _factory.styles); },
/* destroy */
[&] (Cursor &cursor) { destroy(_factory.alloc, &cursor); },
/* update */
[&] (Cursor &cursor, Xml_node const &node) {
cursor.update(node); }
);
update_list_model_from_xml(_selections, node,
/* create */
[&] (Xml_node const &node) -> Text_selection & {
return *new (_factory.alloc)
Text_selection(node, *this); },
/* destroy */
[&] (Text_selection &t) { destroy(_factory.alloc, &t); },
/* update */
[&] (Text_selection &t, Xml_node const &node) {
t.update(node); }
);
}
void update(Xml_node node) override
@ -81,8 +104,7 @@ struct Menu_view::Label_widget : Widget, Cursor::Glyph_position
_min_width = min_w_px.decimal();
}
_cursors .update_from_xml(_cursor_update_policy, node);
_selections.update_from_xml(_selection_update_policy, node);
_update_children(node);
}
Area min_size() const override

View File

@ -22,6 +22,7 @@
#include "depgraph_widget.h"
/* Genode includes */
#include <base/sleep.h>
#include <input/event.h>
#include <os/reporter.h>
#include <timer_session/connection.h>
@ -419,27 +420,37 @@ void Menu_view::Main::_handle_frame_timer()
}
Menu_view::Widget *
Menu_view::Widget_factory::create(Xml_node node)
Menu_view::Widget &
Menu_view::Widget_factory::create(Xml_node const &node)
{
Widget *w = nullptr;
Widget::Unique_id const unique_id(++_unique_id_cnt);
if (node.has_type("label")) w = new (alloc) Label_widget (*this, node, unique_id);
if (node.has_type("button")) w = new (alloc) Button_widget (*this, node, unique_id);
if (node.has_type("vbox")) w = new (alloc) Box_layout_widget (*this, node, unique_id);
if (node.has_type("hbox")) w = new (alloc) Box_layout_widget (*this, node, unique_id);
if (node.has_type("frame")) w = new (alloc) Frame_widget (*this, node, unique_id);
if (node.has_type("float")) w = new (alloc) Float_widget (*this, node, unique_id);
if (node.has_type("depgraph")) w = new (alloc) Depgraph_widget (*this, node, unique_id);
if (node.has_type("label")) return *new (alloc) Label_widget (*this, node, unique_id);
if (node.has_type("button")) return *new (alloc) Button_widget (*this, node, unique_id);
if (node.has_type("vbox")) return *new (alloc) Box_layout_widget (*this, node, unique_id);
if (node.has_type("hbox")) return *new (alloc) Box_layout_widget (*this, node, unique_id);
if (node.has_type("frame")) return *new (alloc) Frame_widget (*this, node, unique_id);
if (node.has_type("float")) return *new (alloc) Float_widget (*this, node, unique_id);
if (node.has_type("depgraph")) return *new (alloc) Depgraph_widget (*this, node, unique_id);
if (!w) {
Genode::error("unknown widget type '", node.type(), "'");
return 0;
}
/*
* This cannot occur because the 'List_model' ensures that 'create' is only
* called for nodes that passed 'node_type_known'.
*/
error("unknown widget type '", node.type(), "'");
sleep_forever();
}
return w;
bool Menu_view::Widget_factory::node_type_known(Xml_node const &node)
{
return node.has_type("label")
|| node.has_type("button")
|| node.has_type("vbox")
|| node.has_type("hbox")
|| node.has_type("frame")
|| node.has_type("float")
|| node.has_type("depgraph");
}

View File

@ -87,39 +87,20 @@ class Menu_view::Text_selection : List_model<Text_selection>::Element
color);
}
struct Model_update_policy : List_model<Text_selection>::Update_policy
bool matches(Xml_node const &node) const
{
Allocator &_alloc;
Glyph_position &_glyph_position;
return _node_name(node) == _name;
}
Model_update_policy(Allocator &alloc, Glyph_position &glyph_position)
:
_alloc(alloc), _glyph_position(glyph_position)
{ }
static bool type_matches(Xml_node const &node)
{
return node.has_type("selection");
}
void destroy_element(Text_selection &t) { destroy(_alloc, &t); }
Text_selection &create_element(Xml_node node)
{
return *new (_alloc)
Text_selection(node, _glyph_position);
}
void update_element(Text_selection &t, Xml_node node)
{
t._range = t._range_from_xml_node(node);
}
static bool element_matches_xml_node(Text_selection const &t, Xml_node node)
{
return node.has_type("selection") && _node_name(node) == t._name;
}
static bool node_is_element(Xml_node node)
{
return node.has_type("selection");
}
};
void update(Xml_node const &node)
{
_range = _range_from_xml_node(node);
}
};
#endif /* _TEXT_SELECTION_ */

View File

@ -126,36 +126,22 @@ class Menu_view::Widget : List_model<Widget>::Element
List_model<Widget> _children { };
struct Model_update_policy : List_model<Widget>::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_version(node) == w._version;
}
} _model_update_policy { _factory };
inline void _update_children(Xml_node node)
{
_children.update_from_xml(_model_update_policy, node);
update_list_model_from_xml(_children, node,
/* create */
[&] (Xml_node const &node) -> Widget & {
return _factory.create(node); },
/* destroy */
[&] (Widget &w) {
_factory.destroy(&w); },
/* update */
[&] (Widget &w, Xml_node const &node) {
w.update(node); }
);
}
void _draw_children(Surface<Pixel_rgb888> &pixel_surface,
@ -230,7 +216,7 @@ class Menu_view::Widget : List_model<Widget>::Element
virtual ~Widget()
{
_children.destroy_all_elements(_model_update_policy);
_update_children(Xml_node("<empty/>"));
}
bool has_name(Name const &name) const { return name == _name; }
@ -320,6 +306,24 @@ class Menu_view::Widget : List_model<Widget>::Element
{
Genode::print(out, _name);
}
/**
* List_model::Element
*/
bool matches(Xml_node const &node) const
{
return node.has_type(_type_name.string())
&& Widget::node_name(node) == _name
&& node_version(node) == _version;
}
/**
* List_model::Element
*/
static bool type_matches(Xml_node const &node)
{
return Widget_factory::node_type_known(node);
}
};
#endif /* _WIDGET_H_ */

View File

@ -44,9 +44,11 @@ class Menu_view::Widget_factory
alloc(alloc), styles(styles), animator(animator)
{ }
Widget *create(Xml_node node);
Widget &create(Xml_node const &);
void destroy(Widget *widget) { Genode::destroy(alloc, widget); }
static bool node_type_known(Xml_node const &);
};
#endif /* _WIDGET_FACTORY_H_ */