diff --git a/repos/gems/src/app/menu_view/label_widget.h b/repos/gems/src/app/menu_view/label_widget.h index 4a3d8df1c..bc93dc104 100644 --- a/repos/gems/src/app/menu_view/label_widget.h +++ b/repos/gems/src/app/menu_view/label_widget.h @@ -16,7 +16,7 @@ /* local includes */ #include -#include +#include namespace Menu_view { struct Label_widget; } @@ -29,18 +29,23 @@ struct Menu_view::Label_widget : Widget, Cursor::Glyph_position typedef String<200> Text; Text _text { }; - Cursor::Model_update_policy _cursor_update_policy; + Cursor::Model_update_policy _cursor_update_policy; + Text_selection::Model_update_policy _selection_update_policy; - List_model _cursors { }; + List_model _cursors { }; + List_model _selections { }; Label_widget(Widget_factory &factory, Xml_node node, Unique_id unique_id) : - Widget(factory, node, unique_id), _cursor_update_policy(factory, *this) + Widget(factory, node, unique_id), + _cursor_update_policy(factory, *this), + _selection_update_policy(factory.alloc, *this) { } ~Label_widget() { - _cursors.destroy_all_elements(_cursor_update_policy); + _cursors .destroy_all_elements(_cursor_update_policy); + _selections.destroy_all_elements(_selection_update_policy); } void update(Xml_node node) override @@ -49,7 +54,8 @@ struct Menu_view::Label_widget : Widget, Cursor::Glyph_position _text = node.attribute_value("text", Text("")); _text = Xml_unquoted(_text); - _cursors.update_from_xml(_cursor_update_policy, node); + _cursors .update_from_xml(_cursor_update_policy, node); + _selections.update_from_xml(_selection_update_policy, node); } Area min_size() const override @@ -74,6 +80,9 @@ struct Menu_view::Label_widget : Widget, Cursor::Glyph_position Point const centered = at + Point(dx/2, dy/2); + _selections.for_each([&] (Text_selection const &selection) { + selection.draw(pixel_surface, alpha_surface, at, text_size.h()); }); + Text_painter::paint(pixel_surface, Text_painter::Position(centered.x(), centered.y()), *_font, Color(0, 0, 0), _text.string()); diff --git a/repos/gems/src/app/menu_view/text_selection.h b/repos/gems/src/app/menu_view/text_selection.h new file mode 100644 index 000000000..9e29e9ff5 --- /dev/null +++ b/repos/gems/src/app/menu_view/text_selection.h @@ -0,0 +1,125 @@ +/* + * \brief Text selection + * \author Norman Feske + * \date 2020-01-15 + */ + +/* + * Copyright (C) 2020 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 _TEXT_SELECTION_H_ +#define _TEXT_SELECTION_H_ + +/* Genode includes */ +#include +#include + +/* local includes */ +#include +#include +#include + +namespace Menu_view { struct Text_selection; } + +class Menu_view::Text_selection : List_model::Element +{ + public: + + using Glyph_position = Cursor::Glyph_position; + + private: + + friend class List_model; + friend class List; + + Glyph_position &_glyph_position; + + enum { NAME_MAX_LEN = 32 }; + typedef String Name; + + Name const _name; + + struct Range + { + unsigned xpos_px; + unsigned width_px; + }; + + Range _range { 0, 0 }; + + static Name _node_name(Xml_node node) + { + return node.attribute_value("name", Name(node.type())); + } + + Range _range_from_xml_node(Xml_node node) + { + unsigned const at = node.attribute_value("at", 0u), + length = node.attribute_value("length", 0u); + + unsigned const x1 = _glyph_position.xpos_of_glyph(at), + x2 = _glyph_position.xpos_of_glyph(at + length); + + unsigned const width = (x2 >= x1 ? x2 - x1 : 0); + + return { .xpos_px = x1, .width_px = width }; + } + + public: + + Text_selection(Xml_node node, Glyph_position &glyph_position) + : + _glyph_position(glyph_position), _name(_node_name(node)) + { } + + void draw(Surface &pixel_surface, + Surface &, + Point at, unsigned height) const + { + Color const color(0xcf, 0x69, 0x69, 180); + Box_painter::paint(pixel_surface, + Rect(at + Point(_range.xpos_px, 0), + Area(_range.width_px, height)), + color); + } + + struct Model_update_policy : List_model::Update_policy + { + Allocator &_alloc; + Glyph_position &_glyph_position; + + Model_update_policy(Allocator &alloc, Glyph_position &glyph_position) + : + _alloc(alloc), _glyph_position(glyph_position) + { } + + 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"); + } + }; +}; + +#endif /* _TEXT_SELECTION_ */