diff --git a/repos/gems/src/app/text_area/dialog.h b/repos/gems/include/dialog/text_area_widget.h
similarity index 69%
rename from repos/gems/src/app/text_area/dialog.h
rename to repos/gems/include/dialog/text_area_widget.h
index 298b79fead..f98c9e788a 100644
--- a/repos/gems/src/app/text_area/dialog.h
+++ b/repos/gems/include/dialog/text_area_widget.h
@@ -5,61 +5,40 @@
*/
/*
- * Copyright (C) 2020 Genode Labs GmbH
+ * Copyright (C) 2020-2023 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 _DIALOG_H_
-#define _DIALOG_H_
+#ifndef _INCLUDE__DIALOG__TEXT_AREA_WIDGET_H_
+#define _INCLUDE__DIALOG__TEXT_AREA_WIDGET_H_
/* Genode includes */
-#include
-#include
-#include
-#include
-#include
-#include
+#include
#include
+#include
+#include
-/* local includes */
-#include
-#include
-#include
-
-namespace Text_area { struct Dialog; }
+namespace Dialog { struct Text_area_widget; }
-struct Text_area::Dialog : private Dynamic_rom_session::Xml_producer
+struct Dialog::Text_area_widget : Widget
{
public:
- Dynamic_rom_session rom_session;
-
- struct Trigger_copy : Interface, Noncopyable
+ struct Action : Interface, Noncopyable
{
virtual void trigger_copy() = 0;
- };
-
- struct Trigger_paste : Interface, Noncopyable
- {
virtual void trigger_paste() = 0;
- };
-
- struct Trigger_save : Interface, Noncopyable
- {
virtual void trigger_save() = 0;
+ virtual void refresh_text_area() = 0;
};
private:
Allocator &_alloc;
- Trigger_copy &_trigger_copy;
- Trigger_paste &_trigger_paste;
- Trigger_save &_trigger_save;
-
struct Character : Codepoint
{
Character(Codepoint &codepoint) : Codepoint(codepoint) { }
@@ -85,16 +64,6 @@ struct Text_area::Dialog : private Dynamic_rom_session::Xml_producer
Line::Index x;
Text::Index y;
- Position(Line::Index x, Text::Index y) : x(x), y(y) { }
-
- Position(Position const &other) : x(other.x), y(other.y) { }
-
- Position &operator = (Position const &other)
- {
- x = other.x, y = other.y;
- return *this;
- }
-
bool operator != (Position const &other) const
{
return (x.value != other.x.value) || (y.value != other.y.value);
@@ -136,18 +105,15 @@ struct Text_area::Dialog : private Dynamic_rom_session::Xml_producer
void with_selection_at_line(Text::Index y, Line const &, FN const &) const;
/* generate dialog model */
- void gen_selected_line(Xml_generator &, Text::Index, Line const &) const;
+ void view_selected_line(Scope &, Text::Index, Line const &) const;
};
- bool _drag = false;
- bool _shift = false;
- bool _control = false;
- bool _text_hovered = false;
+ bool _drag = false;
+ bool _shift = false;
+ bool _control = false;
Selection _selection { };
- void produce_xml(Xml_generator &xml) override;
-
static bool _printable(Codepoint code)
{
if (!code.valid())
@@ -192,6 +158,7 @@ struct Text_area::Dialog : private Dynamic_rom_session::Xml_producer
- _max_lines;
}
+ void _sanitize_scroll_position();
void _move_characters(Line &, Line &);
void _delete_selection();
void _insert_printable(Codepoint);
@@ -208,10 +175,17 @@ struct Text_area::Dialog : private Dynamic_rom_session::Xml_producer
void _handle_home();
void _handle_end();
+ void _with_position_at(At const &, auto const &) const;
+
public:
- Dialog(Entrypoint &, Ram_allocator &, Region_map &, Allocator &,
- Trigger_copy &, Trigger_paste &, Trigger_save &);
+ Text_area_widget(Allocator &alloc) : _alloc(alloc) { clear(); }
+
+ void view(Scope &) const;
+
+ void click(Clicked_at const &);
+ void clack(Clacked_at const &, Action &);
+ void drag (Dragged_at const &);
void editable(bool editable) { _editable = editable; }
@@ -219,25 +193,15 @@ struct Text_area::Dialog : private Dynamic_rom_session::Xml_producer
void max_lines(unsigned max_lines) { _max_lines = max_lines; }
- void handle_input_event(Input::Event const &);
+ void handle_event(Event const &, Action &);
- void handle_hover(Xml_node const &hover);
+ void move_cursor_to(::Dialog::At const &);
void clear();
- void append_newline()
- {
- _text.append(_alloc);
- }
+ void append_newline() { _text.append(_alloc); }
- void append_character(Codepoint c)
- {
- if (_printable(c)) {
- Text::Index const y { _text.upper_bound().value - 1 };
- _text.apply(y, [&] (Line &line) {
- line.append(c); });
- }
- }
+ void append_character(Codepoint c);
/* insert character and advance cursor */
void insert_at_cursor_position(Codepoint);
@@ -257,4 +221,4 @@ struct Text_area::Dialog : private Dynamic_rom_session::Xml_producer
}
};
-#endif /* _DIALOG_H_ */
+#endif /* _INCLUDE__DIALOG__TEXT_AREA_WIDGET_H_ */
diff --git a/repos/gems/lib/mk/dialog.mk b/repos/gems/lib/mk/dialog.mk
index 34d832ca82..7b20a82d7f 100644
--- a/repos/gems/lib/mk/dialog.mk
+++ b/repos/gems/lib/mk/dialog.mk
@@ -1,4 +1,4 @@
-SRC_CC += sandboxed_runtime.cc
+SRC_CC += sandboxed_runtime.cc text_area_widget.cc
LIBS += sandbox
vpath %.cc $(REP_DIR)/src/lib/dialog
diff --git a/repos/gems/recipes/src/text_area/content.mk b/repos/gems/recipes/src/text_area/content.mk
index 28780d322b..074eb25eb9 100644
--- a/repos/gems/recipes/src/text_area/content.mk
+++ b/repos/gems/recipes/src/text_area/content.mk
@@ -1,3 +1,10 @@
SRC_DIR := src/app/text_area
+MIRROR_FROM_REP_DIR := lib/mk/dialog.mk src/lib/dialog include/dialog
+
+content: $(MIRROR_FROM_REP_DIR)
+
+$(MIRROR_FROM_REP_DIR):
+ $(mirror_from_rep_dir)
+
include $(GENODE_DIR)/repos/base/recipes/src/content.inc
diff --git a/repos/gems/recipes/src/text_area/used_apis b/repos/gems/recipes/src/text_area/used_apis
index 817b332130..fe8a8f9881 100644
--- a/repos/gems/recipes/src/text_area/used_apis
+++ b/repos/gems/recipes/src/text_area/used_apis
@@ -8,3 +8,4 @@ timer_session
gui_session
input_session
framebuffer_session
+gems
diff --git a/repos/gems/src/app/text_area/child_state.h b/repos/gems/src/app/text_area/child_state.h
deleted file mode 100644
index 49e04ea9db..0000000000
--- a/repos/gems/src/app/text_area/child_state.h
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * \brief Runtime state of a child hosted in the runtime subsystem
- * \author Norman Feske
- * \date 2018-09-03
- */
-
-/*
- * Copyright (C) 2018 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 _CHILD_STATE_H_
-#define _CHILD_STATE_H_
-
-/* Genode includes */
-#include
-#include
-#include
-#include
-#include
-
-/* local includes */
-#include "types.h"
-
-namespace Text_area { struct Child_state; }
-
-struct Text_area::Child_state : Noncopyable
-{
- private:
-
- using Start_name = String<128>;
-
- Registry::Element _element;
-
- Start_name const _name;
-
- Ram_quota const _initial_ram_quota;
- Cap_quota const _initial_cap_quota;
-
- Ram_quota _ram_quota = _initial_ram_quota;
- Cap_quota _cap_quota = _initial_cap_quota;
-
- struct Version { unsigned value; } _version { 0 };
-
- public:
-
- /**
- * Constructor
- *
- * \param ram_quota initial RAM quota
- * \param cap_quota initial capability quota
- */
- Child_state(Registry ®istry, Start_name const &name,
- Ram_quota ram_quota, Cap_quota cap_quota)
- :
- _element(registry, *this),
- _name(name),
- _initial_ram_quota(ram_quota), _initial_cap_quota(cap_quota)
- { }
-
- void trigger_restart()
- {
- _version.value++;
- _ram_quota = _initial_ram_quota;
- _cap_quota = _initial_cap_quota;
- }
-
- void gen_start_node_version(Xml_generator &xml) const
- {
- if (_version.value)
- xml.attribute("version", _version.value);
- }
-
- void gen_start_node_content(Xml_generator &xml) const
- {
- xml.attribute("name", _name);
-
- gen_start_node_version(xml);
-
- xml.attribute("caps", _cap_quota.value);
- xml.node("resource", [&] () {
- xml.attribute("name", "RAM");
- Number_of_bytes const bytes(_ram_quota.value);
- xml.attribute("quantum", String<64>(bytes)); });
- }
-
- /**
- * Adapt runtime state information to the child
- *
- * This method responds to RAM and cap-resource requests by increasing
- * the resource quotas as needed.
- *
- * \param child child node of the runtime'r state report
- * \return true if runtime must be reconfigured so that the changes
- * can take effect
- */
- bool apply_child_state_report(Xml_node child)
- {
- bool result = false;
-
- if (child.attribute_value("name", Start_name()) != _name)
- return false;
-
- if (child.has_sub_node("ram") && child.sub_node("ram").has_attribute("requested")) {
- _ram_quota.value *= 2;
- result = true;
- }
-
- if (child.has_sub_node("caps") && child.sub_node("caps").has_attribute("requested")) {
- _cap_quota.value += 100;
- result = true;
- }
-
- return result;
- }
-
- Ram_quota ram_quota() const { return _ram_quota; }
-
- Start_name name() const { return _name; }
-};
-
-#endif /* _CHILD_STATE_H_ */
diff --git a/repos/gems/src/app/text_area/gui.h b/repos/gems/src/app/text_area/gui.h
deleted file mode 100644
index 1b47d54dbb..0000000000
--- a/repos/gems/src/app/text_area/gui.h
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * \brief GUI wrapper for monitoring the user input of GUI components
- * \author Norman Feske
- * \date 2020-01-12
- */
-
-/*
- * 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 _GUI_H_
-#define _GUI_H_
-
-/* Genode includes */
-#include
-#include
-#include
-
-/* local includes */
-#include
-
-namespace Gui {
-
- using namespace Genode;
-
- struct Session_component;
-}
-
-
-struct Gui::Session_component : Session_object
-{
- Env &_env;
-
- Input_event_handler &_event_handler;
-
- Gui::Connection _connection;
-
- Input::Session_component _input_component { _env, _env.ram() };
-
- Signal_handler _input_handler {
- _env.ep(), *this, &Session_component::_handle_input };
-
- void _handle_input()
- {
- _connection.input()->for_each_event([&] (Input::Event ev) {
-
- /* handle event locally within the sculpt manager */
- _event_handler.handle_input_event(ev);
-
- _input_component.submit(ev);
- });
- }
-
- template
- Session_component(Env &env, Input_event_handler &event_handler, ARGS &&... args)
- :
- Session_object(args...),
- _env(env), _event_handler(event_handler),
- _connection(env, _label.string())
- {
- _connection.input()->sigh(_input_handler);
- _env.ep().manage(_input_component);
- _input_component.event_queue().enabled(true);
- }
-
- ~Session_component() { _env.ep().dissolve(_input_component); }
-
- void upgrade(Session::Resources const &resources)
- {
- _connection.upgrade(resources);
- }
-
- Framebuffer::Session_capability framebuffer_session() override {
- return _connection.framebuffer_session(); }
-
- Input::Session_capability input_session() override {
- return _input_component.cap(); }
-
- View_handle create_view(View_handle parent) override {
- return _connection.create_view(parent); }
-
- void destroy_view(View_handle view) override {
- _connection.destroy_view(view); }
-
- View_handle view_handle(View_capability view_cap, View_handle handle) override {
- return _connection.view_handle(view_cap, handle); }
-
- View_capability view_capability(View_handle view) override {
- return _connection.view_capability(view); }
-
- void release_view_handle(View_handle view) override {
- _connection.release_view_handle(view); }
-
- Dataspace_capability command_dataspace() override {
- return _connection.command_dataspace(); }
-
- void execute() override {
- _connection.execute(); }
-
- Framebuffer::Mode mode() override {
- return _connection.mode(); }
-
- void mode_sigh(Signal_context_capability sigh) override {
- _connection.mode_sigh(sigh); }
-
- void buffer(Framebuffer::Mode mode, bool use_alpha) override
- {
- /*
- * Do not call 'Connection::buffer' to avoid paying session quota
- * from our own budget.
- */
- _connection.Client::buffer(mode, use_alpha);
- }
-
- void focus(Capability session) override {
- _connection.focus(session); }
-};
-
-#endif /* _GUI_H_ */
diff --git a/repos/gems/src/app/text_area/input_event_handler.h b/repos/gems/src/app/text_area/input_event_handler.h
deleted file mode 100644
index 25329dcb52..0000000000
--- a/repos/gems/src/app/text_area/input_event_handler.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * \brief Interface for handling input events
- * \author Norman Feske
- * \date 2018-05-02
- */
-
-/*
- * Copyright (C) 2018 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 _INPUT_EVENT_HANDLER_H_
-#define _INPUT_EVENT_HANDLER_H_
-
-/* Genode includes */
-#include
-#include
-
-namespace Gui { struct Input_event_handler; }
-
-struct Gui::Input_event_handler : Genode::Interface
-{
- virtual void handle_input_event(Input::Event const &) = 0;
-};
-
-#endif /* _INPUT_EVENT_HANDLER_H_ */
diff --git a/repos/gems/src/app/text_area/main.cc b/repos/gems/src/app/text_area/main.cc
index b6c2112ab9..1b446b0833 100644
--- a/repos/gems/src/app/text_area/main.cc
+++ b/repos/gems/src/app/text_area/main.cc
@@ -5,7 +5,7 @@
*/
/*
- * Copyright (C) 2020 Genode Labs GmbH
+ * Copyright (C) 2020-2023 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.
@@ -13,28 +13,21 @@
/* Genode includes */
#include
-#include
#include
-#include
-#include
-#include
-#include
-#include
+#include
+#include
#include
+#include
-/* local includes */
-#include
-#include
-#include
-#include
+namespace Text_area {
-namespace Text_area { struct Main; }
+ using namespace Dialog;
-struct Text_area::Main : Sandbox::Local_service_base::Wakeup,
- Sandbox::State_handler,
- Gui::Input_event_handler,
- Dialog::Trigger_copy, Dialog::Trigger_paste,
- Dialog::Trigger_save
+ struct Main;
+}
+
+
+struct Text_area::Main : Text_area_widget::Action
{
Env &_env;
@@ -47,60 +40,69 @@ struct Text_area::Main : Sandbox::Local_service_base::Wakeup,
unsigned _min_width = 0;
unsigned _min_height = 0;
- Registry _children { };
+ Runtime _runtime { _env, _heap };
- Child_state _menu_view_child_state { _children, "menu_view",
- Ram_quota { 4*1024*1024 },
- Cap_quota { 200 } };
- /**
- * Sandbox::State_handler
- */
- void handle_sandbox_state() override
+ struct Main_dialog : Top_level_dialog
{
- /* obtain current sandbox state */
- Buffered_xml state(_heap, "state", [&] (Xml_generator &xml) {
- _sandbox.generate_state_report(xml);
+ Main &_main;
+
+ Hosted text { Id { "text" }, _main._heap };
+
+ Main_dialog(Main &main) : Top_level_dialog("text_area"), _main(main) { }
+
+ void view(Scope<> &s) const override
+ {
+ s.sub_scope ([&] (Scope &s) {
+ s.sub_scope([&] (Scope &s) {
+
+ if (s.hovered())
+ s.attribute("hovered", "yes");
+
+ s.sub_scope([&] (Scope &s) {
+ s.attribute("north", "yes");
+ s.attribute("east", "yes");
+ s.attribute("west", "yes");
+ s.widget(text);
+ });
+ });
+ });
+ }
+
+ void click(Clicked_at const &at) override { text.propagate(at); }
+ void clack(Clacked_at const &at) override { text.propagate(at, _main); }
+ void drag (Dragged_at const &at) override { text.propagate(at); }
+
+ } _dialog { *this };
+
+ Runtime::View _view { _runtime, _dialog };
+
+ /* handler used to respond to keyboard input */
+ Runtime::Event_handler _event_handler { _runtime, *this, &Main::_handle_event };
+
+ void _handle_event(::Dialog::Event const &event)
+ {
+ bool const orig_modified = _modified();
+
+ _dialog.text.handle_event(event, *this);
+
+ event.event.handle_press([&] (Input::Keycode const key, Codepoint) {
+
+ /* paste on middle mouse click */
+ bool const middle_click = (key == Input::BTN_MIDDLE);
+ if (middle_click) {
+ _view.if_hovered([&] (Hovered_at const &at) {
+ _dialog.text.move_cursor_to(at);
+ trigger_paste();
+ _view.refresh();
+ return true;
+ });
+ }
});
- bool reconfiguration_needed = false;
-
- state.with_xml_node([&] (Xml_node state) {
- state.for_each_sub_node("child", [&] (Xml_node const &child) {
- if (_menu_view_child_state.apply_child_state_report(child))
- reconfiguration_needed = true; }); });
-
- if (reconfiguration_needed)
- _update_sandbox_config();
+ if (_modified() != orig_modified)
+ _generate_saved_report();
}
- Sandbox _sandbox { _env, *this };
-
- typedef Sandbox::Local_service Gui_service;
-
- Gui_service _gui_service { _sandbox, *this };
-
- typedef Sandbox::Local_service Rom_service;
-
- Rom_service _rom_service { _sandbox, *this };
-
- typedef Sandbox::Local_service Report_service;
-
- Report_service _report_service { _sandbox, *this };
-
- void _handle_hover(Xml_node const &node)
- {
- if (!node.has_sub_node("dialog"))
- _dialog.handle_hover(Xml_node(" "));
-
- node.with_optional_sub_node("dialog", [&] (Xml_node const &dialog) {
- _dialog.handle_hover(dialog); });
- }
-
- Report::Session_component::Xml_handler
- _hover_handler { *this, &Main::_handle_hover };
-
- Dialog _dialog { _env.ep(), _env.ram(), _env.rm(), _heap, *this, *this, *this };
-
Constructible _saved_reporter { };
struct Saved_version { unsigned value; } _saved_version { 0 };
@@ -112,7 +114,7 @@ struct Text_area::Main : Sandbox::Local_service_base::Wakeup,
bool _modified() const
{
- return _dialog.modification_count() != _saved_modification_count.value;
+ return _dialog.text.modification_count() != _saved_modification_count.value;
}
void _generate_saved_report()
@@ -127,158 +129,6 @@ struct Text_area::Main : Sandbox::Local_service_base::Wakeup,
});
}
- void _generate_sandbox_config(Xml_generator &xml) const
- {
- xml.node("report", [&] () {
- xml.attribute("child_ram", "yes");
- xml.attribute("child_caps", "yes");
- xml.attribute("delay_ms", 20*1000);
- });
- xml.node("parent-provides", [&] () {
-
- auto service_node = [&] (char const *name) {
- xml.node("service", [&] () {
- xml.attribute("name", name); }); };
-
- service_node("ROM");
- service_node("CPU");
- service_node("PD");
- service_node("LOG");
- service_node("File_system");
- service_node("Gui");
- service_node("Timer");
- service_node("Report");
- });
-
- xml.node("start", [&] () {
- _menu_view_child_state.gen_start_node_content(xml);
-
- xml.node("config", [&] () {
- xml.attribute("xpos", "100");
- xml.attribute("ypos", "50");
-
- if (_min_width) xml.attribute("width", _min_width);
- if (_min_height) xml.attribute("height", _min_height);
-
- xml.node("report", [&] () {
- xml.attribute("hover", "yes"); });
-
- xml.node("libc", [&] () {
- xml.attribute("stderr", "/dev/log"); });
-
- xml.node("vfs", [&] () {
- xml.node("tar", [&] () {
- xml.attribute("name", "menu_view_styles.tar"); });
- xml.node("dir", [&] () {
- xml.attribute("name", "dev");
- xml.node("log", [&] () { });
- });
- xml.node("dir", [&] () {
- xml.attribute("name", "fonts");
- xml.node("fs", [&] () {
- xml.attribute("label", "fonts");
- });
- });
- });
- });
-
- xml.node("route", [&] () {
-
- xml.node("service", [&] () {
- xml.attribute("name", "ROM");
- xml.attribute("label", "dialog");
- xml.node("local", [&] () { });
- });
-
- xml.node("service", [&] () {
- xml.attribute("name", "Report");
- xml.attribute("label", "hover");
- xml.node("local", [&] () { });
- });
-
- xml.node("service", [&] () {
- xml.attribute("name", "Gui");
- xml.node("local", [&] () { });
- });
-
- xml.node("service", [&] () {
- xml.attribute("name", "File_system");
- xml.attribute("label", "fonts");
- xml.node("parent", [&] () {
- xml.attribute("label", "fonts"); });
- });
-
- xml.node("any-service", [&] () {
- xml.node("parent", [&] () { }); });
- });
- });
- }
-
- /**
- * Sandbox::Local_service_base::Wakeup interface
- */
- void wakeup_local_service() override
- {
- _rom_service.for_each_requested_session([&] (Rom_service::Request &request) {
-
- if (request.label == "menu_view -> dialog")
- request.deliver_session(_dialog.rom_session);
- else
- request.deny();
- });
-
- _report_service.for_each_requested_session([&] (Report_service::Request &request) {
-
- if (request.label == "menu_view -> hover") {
- Report::Session_component &session = *new (_heap)
- Report::Session_component(_env, _hover_handler,
- _env.ep(),
- request.resources, "", request.diag);
- request.deliver_session(session);
- }
- });
-
- _report_service.for_each_session_to_close([&] (Report::Session_component &session) {
-
- destroy(_heap, &session);
- return Report_service::Close_response::CLOSED;
- });
-
- _gui_service.for_each_requested_session([&] (Gui_service::Request &request) {
-
- Gui::Session_component &session = *new (_heap)
- Gui::Session_component(_env, *this, _env.ep(),
- request.resources, "", request.diag);
-
- request.deliver_session(session);
- });
-
- _gui_service.for_each_upgraded_session([&] (Gui::Session_component &session,
- Session::Resources const &amount) {
- session.upgrade(amount);
- return Gui_service::Upgrade_response::CONFIRMED;
- });
-
- _gui_service.for_each_session_to_close([&] (Gui::Session_component &session) {
-
- destroy(_heap, &session);
- return Gui_service::Close_response::CLOSED;
- });
- }
-
- /**
- * Gui::Input_event_handler interface
- */
- void handle_input_event(Input::Event const &event) override
- {
- bool const orig_modified = _modified();
-
- _dialog.handle_input_event(event);
-
- if (_modified() != orig_modified)
- _generate_saved_report();
- }
-
Directory::Path _path() const
{
return _config.xml().attribute_value("path", Directory::Path());
@@ -301,7 +151,7 @@ struct Text_area::Main : Sandbox::Local_service_base::Wakeup,
enum { MAX_LINE_LEN = 1000 };
typedef String Content_line;
- _dialog.clear();
+ _dialog.text.clear();
content.for_each_line([&] (Content_line const &line) {
if (line.length() == Content_line::capacity()) {
@@ -309,19 +159,19 @@ struct Text_area::Main : Sandbox::Local_service_base::Wakeup,
throw Max_line_len_exceeded();
}
- _dialog.append_newline();
+ _dialog.text.append_newline();
for (Utf8_ptr utf8(line.string()); utf8.complete(); utf8 = utf8.next())
- _dialog.append_character(utf8.codepoint());
+ _dialog.text.append_character(utf8.codepoint());
});
}
catch (...) {
warning("failed to load file ", _path());
- _dialog.clear();
+ _dialog.text.clear();
}
- _dialog.rom_session.trigger_update();
+ _view.refresh();
}
Constructible> _watch_handler { };
@@ -335,7 +185,7 @@ struct Text_area::Main : Sandbox::Local_service_base::Wakeup,
Constructible _clipboard_reporter { };
/**
- * Dialog::Trigger_copy interface
+ * Text_area::Dialog::Action interface
*/
void trigger_copy() override
{
@@ -343,7 +193,7 @@ struct Text_area::Main : Sandbox::Local_service_base::Wakeup,
return;
_clipboard_reporter->generate([&] (Xml_generator &xml) {
- _dialog.gen_clipboard_content(xml); });
+ _dialog.text.gen_clipboard_content(xml); });
}
/*
@@ -356,7 +206,7 @@ struct Text_area::Main : Sandbox::Local_service_base::Wakeup,
struct Paste_buffer { char buffer[PASTE_BUFFER_SIZE]; } _paste_buffer { };
/**
- * Dialog::Trigger_paste interface
+ * Text_area::Dialog::Action interface
*/
void trigger_paste() override
{
@@ -381,9 +231,9 @@ struct Text_area::Main : Sandbox::Local_service_base::Wakeup,
}
for (Utf8_ptr utf8(_paste_buffer.buffer); utf8.complete(); utf8 = utf8.next())
- _dialog.insert_at_cursor_position(utf8.codepoint());
+ _dialog.text.insert_at_cursor_position(utf8.codepoint());
- _dialog.rom_session.trigger_update();
+ _view.refresh();
}
/*
@@ -409,7 +259,7 @@ struct Text_area::Main : Sandbox::Local_service_base::Wakeup,
Buffered_output<1024, decltype(write)> output(write);
- _dialog.for_each_character([&] (Codepoint c) { print(output, c); });
+ _dialog.text.for_each_character([&] (Codepoint c) { print(output, c); });
}
catch (New_file::Create_failed) {
error("file creation failed while saving file"); }
@@ -419,13 +269,13 @@ struct Text_area::Main : Sandbox::Local_service_base::Wakeup,
return;
}
- _saved_modification_count.value = _dialog.modification_count();
+ _saved_modification_count.value = _dialog.text.modification_count();
_generate_saved_report();
}
/**
- * Dialog::Trigger_save interface
+ * Text_area::Dialog::Action interface
*/
void trigger_save() override
{
@@ -453,11 +303,11 @@ struct Text_area::Main : Sandbox::Local_service_base::Wakeup,
_clipboard_reporter.conditional(copy_enabled, _env, "clipboard", "clipboard");
_clipboard_rom .conditional(paste_enabled, _env, "clipboard");
- _dialog.max_lines(config.attribute_value("max_lines", ~0U));
+ _dialog.text.max_lines(config.attribute_value("max_lines", ~0U));
_watch(config.attribute_value("watch", false));
- _dialog.editable(_editable());
+ _dialog.text.editable(_editable());
if (_editable()) {
bool const orig_saved_reporter_enabled = _saved_reporter.constructed();
@@ -493,14 +343,10 @@ struct Text_area::Main : Sandbox::Local_service_base::Wakeup,
Signal_handler _config_handler {
_env.ep(), *this, &Main::_handle_config };
- void _update_sandbox_config()
- {
- Buffered_xml const config { _heap, "config", [&] (Xml_generator &xml) {
- _generate_sandbox_config(xml); } };
-
- config.with_xml_node([&] (Xml_node const &config) {
- _sandbox.apply_config(config); });
- }
+ /**
+ * Text_area::Dialog::Action interface
+ */
+ void refresh_text_area() override { _view.refresh(); }
Main(Env &env)
:
@@ -515,7 +361,6 @@ struct Text_area::Main : Sandbox::Local_service_base::Wakeup,
_config.sigh(_config_handler);
_handle_config();
- _update_sandbox_config();
}
};
diff --git a/repos/gems/src/app/text_area/report.h b/repos/gems/src/app/text_area/report.h
deleted file mode 100644
index f9e402c1ca..0000000000
--- a/repos/gems/src/app/text_area/report.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * \brief Report session provided to the sandbox
- * \author Norman Feske
- * \date 2020-01-14
- */
-
-/*
- * 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 _REPORT_H_
-#define _REPORT_H_
-
-/* Genode includes */
-#include
-#include
-
-namespace Report {
-
- using namespace Genode;
-
- struct Session_component;
-}
-
-
-class Report::Session_component : public Session_object
-{
- public:
-
- struct Handler_base : Interface, Genode::Noncopyable
- {
- virtual void handle_report(char const *, size_t) = 0;
- };
-
- template
- struct Xml_handler : Handler_base
- {
- T &_obj;
- void (T::*_member) (Xml_node const &);
-
- Xml_handler(T &obj, void (T::*member)(Xml_node const &))
- : _obj(obj), _member(member) { }
-
- void handle_report(char const *start, size_t length) override
- {
- (_obj.*_member)(Xml_node(start, length));
- }
- };
-
- private:
-
- Attached_ram_dataspace _ds;
-
- Handler_base &_handler;
-
-
- /*******************************
- ** Report::Session interface **
- *******************************/
-
- Dataspace_capability dataspace() override { return _ds.cap(); }
-
- void submit(size_t length) override
- {
- _handler.handle_report(_ds.local_addr(),
- min(_ds.size(), length));
- }
-
- void response_sigh(Signal_context_capability) override { }
-
- size_t obtain_response() override { return 0; }
-
- public:
-
- template
- Session_component(Env &env, Handler_base &handler,
- Entrypoint &ep, Resources const &resources,
- ARGS &&... args)
- :
- Session_object(ep, resources, args...),
- _ds(env.ram(), env.rm(), resources.ram_quota.value),
- _handler(handler)
- { }
-};
-
-#endif /* _REPORT_H_ */
diff --git a/repos/gems/src/app/text_area/target.mk b/repos/gems/src/app/text_area/target.mk
index b5643b1561..6a91dd8463 100644
--- a/repos/gems/src/app/text_area/target.mk
+++ b/repos/gems/src/app/text_area/target.mk
@@ -1,4 +1,4 @@
TARGET = text_area
-SRC_CC = main.cc dialog.cc
-LIBS += base sandbox vfs
+SRC_CC = main.cc
+LIBS += base sandbox vfs dialog
INC_DIR += $(PRG_DIR)
diff --git a/repos/gems/src/app/text_area/types.h b/repos/gems/src/app/text_area/types.h
deleted file mode 100644
index 303a626e24..0000000000
--- a/repos/gems/src/app/text_area/types.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * \brief Common types
- * \author Norman Feske
- * \date 2020-01-14
- */
-
-/*
- * Copyright (C) 2012 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 _TYPES_H_
-#define _TYPES_H_
-
-namespace Genode { }
-
-namespace Text_area { using namespace Genode; }
-
-#endif /* _TYPES_H_ */
diff --git a/repos/gems/src/app/text_area/dialog.cc b/repos/gems/src/lib/dialog/text_area_widget.cc
similarity index 60%
rename from repos/gems/src/app/text_area/dialog.cc
rename to repos/gems/src/lib/dialog/text_area_widget.cc
index 04a037111e..7dfeadf9aa 100644
--- a/repos/gems/src/app/text_area/dialog.cc
+++ b/repos/gems/src/lib/dialog/text_area_widget.cc
@@ -11,10 +11,9 @@
* under the terms of the GNU Affero General Public License version 3.
*/
-/* local includes */
-#include
+#include
-using namespace Text_area;
+using namespace Dialog;
enum {
@@ -53,8 +52,33 @@ template
static void swap(T &v1, T &v2) { auto tmp = v1; v1 = v2; v2 = tmp; };
-template
-void Dialog::Selection::for_each_selected_line(FN const &fn) const
+static unsigned unsigned_from_id(Dialog::Id const &id)
+{
+ unsigned value { };
+ ascii_to(id.value.string(), value);
+ return value;
+};
+
+
+void Text_area_widget::_with_position_at(::Dialog::At const &at, auto const &fn) const
+{
+ At::Narrowed::with_at(at, [&] (At const &at) {
+
+ Text::Index const y = { unsigned_from_id(at.id()) + _scroll.y.value };
+
+ _text.apply(y, [&] (Line const &line) {
+ Line::Index x = line.upper_bound();
+
+ At::Narrowed::with_at(at, [&] (At const &at) {
+ x = { at._location.attribute_value("at", 0u) }; });
+
+ fn(Position { x, y });
+ });
+ });
+}
+
+
+void Text_area_widget::Selection::for_each_selected_line(auto const &fn) const
{
if (!defined())
return;
@@ -72,9 +96,8 @@ void Dialog::Selection::for_each_selected_line(FN const &fn) const
}
-template
-void Dialog::Selection::with_selection_at_line(Text::Index y, Line const &line,
- FN const &fn) const
+void Text_area_widget::Selection::with_selection_at_line(Text::Index y, Line const &line,
+ auto const &fn) const
{
if (!defined())
return;
@@ -103,74 +126,80 @@ void Dialog::Selection::with_selection_at_line(Text::Index y, Line const &line,
}
-void Dialog::Selection::gen_selected_line(Xml_generator &xml,
- Text::Index y, Line const &line) const
+void Text_area_widget::Selection::view_selected_line(Scope &s,
+ Text::Index y, Line const &line) const
{
with_selection_at_line(y, line, [&] (Line::Index const start_x, const unsigned n) {
- xml.node("selection", [&] () {
- xml.attribute("at", start_x.value);
- xml.attribute("length", n);
- });
- });
+ s.sub_node("selection", [&] () {
+ s.attribute("at", start_x.value);
+ s.attribute("length", n); }); });
}
-void Dialog::produce_xml(Xml_generator &xml)
+void Text_area_widget::view(Scope &s) const
{
- auto gen_line = [&] (Text::Index at, Line const &line)
+ using namespace ::Dialog;
+
+ struct Line_widget : Widget
{
- xml.node("hbox", [&] () {
- xml.attribute("name", at.value - _scroll.y.value);
- xml.node("float", [&] () {
- xml.attribute("north", "yes");
- xml.attribute("south", "yes");
- xml.attribute("west", "yes");
- xml.node("label", [&] () {
- xml.attribute("font", "monospace/regular");
- xml.attribute("text", String<512>(line));
- xml.attribute("hover", "yes");
+ struct Attr
+ {
+ Text::Index const y;
+ Line const &line;
+ Position const &cursor;
+ Selection const &selection;
+ };
- if (_cursor.y.value == at.value)
- xml.node("cursor", [&] () {
- xml.attribute("name", "cursor");
- xml.attribute("at", _cursor.x.value); });
+ void view(Scope &s, Attr const &attr) const
+ {
+ bool const line_hovered = s.hovered();
- if (_hovered_position.constructed())
- if (_hovered_position->y.value == at.value)
- xml.node("cursor", [&] () {
- xml.attribute("name", "hover");
- xml.attribute("style", "hover");
- xml.attribute("at", _hovered_position->x.value); });
+ s.sub_scope([&] (Scope &s) {
+ s.attribute("north", "yes");
+ s.attribute("south", "yes");
+ s.attribute("west", "yes");
- _selection.gen_selected_line(xml, at, line);
+ s.sub_scope(String<512>(attr.line), [&] (Scope &s) {
+ s.attribute("font", "monospace/regular");
+ s.attribute("hover", "yes");
+
+ if (attr.cursor.y.value == attr.y.value)
+ s.sub_node("cursor", [&] {
+ s.attribute("name", "cursor");
+ s.attribute("at", attr.cursor.x.value); });
+
+ if (line_hovered) {
+ unsigned const hover_x =
+ s.hover._location.attribute_value("at", attr.line.upper_bound().value);
+
+ s.sub_node("cursor", [&] {
+ s.attribute("name", "hover");
+ s.attribute("style", "hover");
+ s.attribute("at", hover_x); });
+ }
+
+ attr.selection.view_selected_line(s, attr.y, attr.line);
});
});
- });
+ }
};
- xml.node("frame", [&] () {
- xml.node("button", [&] () {
- xml.attribute("name", "text");
-
- if (_text_hovered)
- xml.attribute("hovered", "yes");
-
- xml.node("float", [&] () {
- xml.attribute("north", "yes");
- xml.attribute("east", "yes");
- xml.attribute("west", "yes");
- xml.node("vbox", [&] () {
- Dynamic_array::Range const range { .at = _scroll.y,
- .length = _max_lines };
- _text.for_each(range, gen_line);
- });
- });
+ Dynamic_array::Range const range { .at = _scroll.y,
+ .length = _max_lines };
+ unsigned count = 0;
+ _text.for_each(range, [&] (Text::Index const &at, Line const &line) {
+ s.widget(Hosted { Id { { count } } }, Line_widget::Attr {
+ .y = at,
+ .line = line,
+ .cursor = _cursor,
+ .selection = _selection
});
+ count++;
});
}
-void Dialog::_delete_selection()
+void Text_area_widget::_delete_selection()
{
if (!_editable)
return;
@@ -246,7 +275,7 @@ void Dialog::_delete_selection()
}
-void Dialog::_insert_printable(Codepoint code)
+void Text_area_widget::_insert_printable(Codepoint code)
{
_tie_cursor_to_end_of_line();
@@ -257,7 +286,24 @@ void Dialog::_insert_printable(Codepoint code)
}
-void Dialog::_handle_printable(Codepoint code)
+void Text_area_widget::_sanitize_scroll_position()
+{
+ /* ensure that the cursor remains visible */
+ if (_cursor.y.value > 0)
+ if (_scroll.y.value > _cursor.y.value - 1)
+ _scroll.y.value = _cursor.y.value - 1;
+
+ if (_cursor.y.value == 0)
+ _scroll.y.value = 0;
+
+ if (_scroll.y.value + _max_lines < _cursor.y.value + 2)
+ _scroll.y.value = _cursor.y.value - _max_lines + 2;
+
+ _clamp_scroll_position_to_upper_bound();
+}
+
+
+void Text_area_widget::_handle_printable(Codepoint code)
{
if (!_editable)
return;
@@ -269,7 +315,7 @@ void Dialog::_handle_printable(Codepoint code)
}
-void Dialog::_move_characters(Line &from, Line &to)
+void Text_area_widget::_move_characters(Line &from, Line &to)
{
/* move all characters of line 'from' to the end of line 'to' */
Line::Index const first { 0 };
@@ -281,7 +327,7 @@ void Dialog::_move_characters(Line &from, Line &to)
}
-void Dialog::_handle_backspace()
+void Text_area_widget::_handle_backspace()
{
if (!_editable)
return;
@@ -323,7 +369,7 @@ void Dialog::_handle_backspace()
}
-void Dialog::_handle_delete()
+void Text_area_widget::_handle_delete()
{
if (!_editable)
return;
@@ -344,7 +390,7 @@ void Dialog::_handle_delete()
}
-void Dialog::_handle_newline()
+void Text_area_widget::_handle_newline()
{
if (!_editable)
return;
@@ -373,11 +419,11 @@ void Dialog::_handle_newline()
}
-void Dialog::_handle_left()
+void Text_area_widget::_handle_left()
{
_tie_cursor_to_end_of_line();
-if (_cursor.x.value == 0) {
+ if (_cursor.x.value == 0) {
if (_cursor.y.value > 0) {
_cursor.y.value--;
_text.apply(_cursor.y, [&] (Line &line) {
@@ -389,7 +435,7 @@ if (_cursor.x.value == 0) {
}
-void Dialog::_handle_right()
+void Text_area_widget::_handle_right()
{
if (!_cursor_at_end_of_line()) {
_cursor.x.value++;
@@ -403,21 +449,21 @@ void Dialog::_handle_right()
}
-void Dialog::_handle_up()
+void Text_area_widget::_handle_up()
{
if (_cursor.y.value > 0)
_cursor.y.value--;
}
-void Dialog::_handle_down()
+void Text_area_widget::_handle_down()
{
if (_cursor.y.value + 1 < _text.upper_bound().value)
_cursor.y.value++;
}
-void Dialog::_handle_pageup()
+void Text_area_widget::_handle_pageup()
{
if (_max_lines != ~0U) {
for (unsigned i = 0; i < _max_lines; i++)
@@ -428,7 +474,7 @@ void Dialog::_handle_pageup()
}
-void Dialog::_handle_pagedown()
+void Text_area_widget::_handle_pagedown()
{
if (_max_lines != ~0U) {
for (unsigned i = 0; i < _max_lines; i++)
@@ -439,35 +485,59 @@ void Dialog::_handle_pagedown()
}
-void Dialog::_handle_home()
+void Text_area_widget::_handle_home()
{
_cursor.x.value = 0;
}
-void Dialog::_handle_end()
+void Text_area_widget::_handle_end()
{
_text.apply(_cursor.y, [&] (Line &line) {
_cursor.x = line.upper_bound(); });
}
-void Dialog::handle_input_event(Input::Event const &event)
+void Text_area_widget::click(Clicked_at const &at)
+{
+ _with_position_at(at, [&] (Position const pos) {
+
+ if (_shift) {
+ _selection.end.construct(pos);
+ } else {
+ _selection.start.construct(pos);
+ _selection.end.destruct();
+ }
+
+ _drag = true;
+ });
+}
+
+
+void Text_area_widget::clack(Clacked_at const &at, Action &action)
+{
+ _with_position_at(at, [&] (Position const pos) {
+ _cursor = pos; });
+
+ _drag = false;
+
+ if (_selection.defined())
+ action.trigger_copy();
+}
+
+
+void Text_area_widget::drag(Dragged_at const &at)
+{
+ _with_position_at(at, [&] (Position const pos) {
+ _selection.end.construct(pos); });
+}
+
+
+void Text_area_widget::handle_event(Event const &event, Action &action)
{
bool update_dialog = false;
- Position const orig_cursor = _cursor;
-
- auto cursor_to_hovered_position = [&] ()
- {
- if (_hovered_position.constructed()) {
- _cursor.x = _hovered_position->x;
- _cursor.y = _hovered_position->y;
- update_dialog = true;
- }
- };
-
- event.handle_press([&] (Input::Keycode key, Codepoint code) {
+ event.event.handle_press([&] (Input::Keycode key, Codepoint code) {
bool key_has_visible_effect = true;
@@ -501,7 +571,7 @@ void Dialog::handle_input_event(Input::Event const &event)
else if (code.value == CODEPOINT_PAGEUP) { _handle_pageup(); }
else if (code.value == CODEPOINT_HOME) { _handle_home(); }
else if (code.value == CODEPOINT_END) { _handle_end(); }
- else if (code.value == CODEPOINT_INSERT) { _trigger_paste.trigger_paste(); }
+ else if (code.value == CODEPOINT_INSERT) { action.trigger_paste(); }
else {
key_has_visible_effect = false;
}
@@ -513,56 +583,25 @@ void Dialog::handle_input_event(Input::Event const &event)
if (_control) {
if (code.value == 'c')
- _trigger_copy.trigger_copy();
+ action.trigger_copy();
if (code.value == 'x') {
- _trigger_copy.trigger_copy();
+ action.trigger_copy();
_delete_selection();
}
if (code.value == 'v')
- _trigger_paste.trigger_paste();
+ action.trigger_paste();
if (code.value == 's')
- _trigger_save.trigger_save();
+ action.trigger_save();
}
if (key_has_visible_effect)
update_dialog = true;
-
- bool const click = (key == Input::BTN_LEFT);
- if (click && _hovered_position.constructed()) {
-
- if (_shift)
- _selection.end.construct(*_hovered_position);
- else
- _selection.start.construct(*_hovered_position);
-
- _drag = true;
- }
-
- bool const middle_click = (key == Input::BTN_MIDDLE);
- if (middle_click) {
- cursor_to_hovered_position();
- _trigger_paste.trigger_paste();
- }
});
- if (_drag && _hovered_position.constructed()) {
- _selection.end.construct(*_hovered_position);
- update_dialog = true;
- }
-
- bool const clack = event.key_release(Input::BTN_LEFT);
- if (clack) {
- cursor_to_hovered_position();
- _drag = false;
-
- if (_selection.defined())
- _trigger_copy.trigger_copy();
- }
-
- event.handle_release([&] (Input::Keycode key) {
+ event.event.handle_release([&] (Input::Keycode key) {
if (shift_key(key)) _shift = false;
if (control_key(key)) _control = false;
});
@@ -571,7 +610,7 @@ void Dialog::handle_input_event(Input::Event const &event)
(_max_lines == ~0U) || (_text.upper_bound().value <= _max_lines);
if (!all_lines_visible) {
- event.handle_wheel([&] (int, int y) {
+ event.event.handle_wheel([&] (int, int y) {
/* scroll at granulatory of 1/5th of vertical view size */
y *= max(1U, _max_lines / 5);
@@ -587,105 +626,26 @@ void Dialog::handle_input_event(Input::Event const &event)
}
/* adjust scroll position */
- if (all_lines_visible) {
+ if (all_lines_visible)
_scroll.y.value = 0;
- } else if (orig_cursor != _cursor) {
-
- /* ensure that the cursor remains visible */
- if (_cursor.y.value > 0)
- if (_scroll.y.value > _cursor.y.value - 1)
- _scroll.y.value = _cursor.y.value - 1;
-
- if (_cursor.y.value == 0)
- _scroll.y.value = 0;
-
- if (_scroll.y.value + _max_lines < _cursor.y.value + 2)
- _scroll.y.value = _cursor.y.value - _max_lines + 2;
- }
-
- _clamp_scroll_position_to_upper_bound();
+ _sanitize_scroll_position();
if (update_dialog)
- rom_session.trigger_update();
+ action.refresh_text_area();
}
-void Dialog::handle_hover(Xml_node const &hover)
+void Text_area_widget::move_cursor_to(::Dialog::At const &at)
{
- Constructible orig_pos { };
+ _with_position_at(at, [&] (Position pos) {
+ _cursor = pos; });
- if (_hovered_position.constructed())
- orig_pos.construct(*_hovered_position);
-
- _hovered_position.destruct();
-
- auto with_hovered_line = [&] (Xml_node node)
- {
- Text::Index const y {
- node.attribute_value("name", _text.upper_bound().value)
- + _scroll.y.value };
-
- _text.apply(y, [&] (Line const &line) {
-
- Line::Index const max_x = line.upper_bound();
-
- _hovered_position.construct(max_x, y);
-
- node.with_optional_sub_node("float", [&] (Xml_node node) {
- node.with_optional_sub_node("label", [&] (Xml_node node) {
-
- Line::Index const x {
- node.attribute_value("at", max_x.value) };
-
- _hovered_position.construct(x, y);
- });
- });
- });
- };
-
- bool const hover_changed =
- (orig_pos.constructed() != _hovered_position.constructed());
-
- bool const position_changed = orig_pos.constructed()
- && _hovered_position.constructed()
- && (*orig_pos != *_hovered_position);
-
- bool const orig_text_hovered = _text_hovered;
-
- _text_hovered = false;
-
- hover.with_optional_sub_node("frame", [&] (Xml_node node) {
- node.with_optional_sub_node("button", [&] (Xml_node node) {
-
- _text_hovered = true;
-
- node.with_optional_sub_node("float", [&] (Xml_node node) {
- node.with_optional_sub_node("vbox", [&] (Xml_node node) {
- node.with_optional_sub_node("hbox", [&] (Xml_node node) {
- with_hovered_line(node); }); }); }); }); });
-
- if (hover_changed || position_changed || (_text_hovered != orig_text_hovered))
- rom_session.trigger_update();
+ _sanitize_scroll_position();
}
-Dialog::Dialog(Entrypoint &ep, Ram_allocator &ram, Region_map &rm,
- Allocator &alloc, Trigger_copy &trigger_copy,
- Trigger_paste &trigger_paste, Trigger_save &trigger_save)
-:
- Xml_producer("dialog"),
- rom_session(ep, ram, rm, *this),
- _alloc(alloc),
- _trigger_copy(trigger_copy),
- _trigger_paste(trigger_paste),
- _trigger_save(trigger_save)
-{
- clear();
-}
-
-
-void Dialog::clear()
+void Text_area_widget::clear()
{
Text::Index const first { 0 };
@@ -697,7 +657,17 @@ void Dialog::clear()
}
-void Dialog::insert_at_cursor_position(Codepoint c)
+void Text_area_widget::append_character(Codepoint c)
+{
+ if (_printable(c)) {
+ Text::Index const y { _text.upper_bound().value - 1 };
+ _text.apply(y, [&] (Line &line) {
+ line.append(c); });
+ }
+}
+
+
+void Text_area_widget::insert_at_cursor_position(Codepoint c)
{
if (_printable(c)) {
_insert_printable(c);
@@ -710,7 +680,7 @@ void Dialog::insert_at_cursor_position(Codepoint c)
}
-void Dialog::gen_clipboard_content(Xml_generator &xml) const
+void Text_area_widget::gen_clipboard_content(Xml_generator &xml) const
{
if (!_selection.defined())
return;