scroll_text: markup and link awareness support
support for copying and partial editing only. this does not make multiline_text a rich text editor.
This commit is contained in:
parent
6023eb338c
commit
54535fca3b
8 changed files with 140 additions and 85 deletions
|
@ -57,6 +57,8 @@
|
|||
inactive_color = ["{GUI__FONT_COLOR_DISABLED__DEFAULT}"]
|
||||
)"
|
||||
text = "(text)"
|
||||
text_link_aware = "(text_link_aware)"
|
||||
text_markup = "(text_markup)"
|
||||
highlight_color = "21, 53, 80"
|
||||
highlight_start = "(highlight_start)"
|
||||
highlight_end = "(highlight_end)"
|
||||
|
@ -96,6 +98,8 @@
|
|||
reg_color = [{COLOR}]
|
||||
)"
|
||||
|
||||
text_link_aware = "(text_link_aware)"
|
||||
text_markup = "(text_markup)"
|
||||
text = "(
|
||||
if(text = '' and hint_text != '', hint_text, text))"
|
||||
[/text]
|
||||
|
@ -149,8 +153,6 @@
|
|||
text_y_offset = 2
|
||||
text_extra_width = {EXTRA_WIDTH}
|
||||
|
||||
#functions = "(def show_hint_text() (text = '' and hint_text != '');)"
|
||||
|
||||
[state_enabled]
|
||||
|
||||
[draw]
|
||||
|
|
|
@ -17,8 +17,12 @@
|
|||
|
||||
#include "gui/widgets/multiline_text.hpp"
|
||||
|
||||
#include "cursor.hpp"
|
||||
#include "desktop/clipboard.hpp"
|
||||
#include "desktop/open.hpp"
|
||||
#include "gui/core/log.hpp"
|
||||
#include "gui/core/register_widget.hpp"
|
||||
#include "gui/dialogs/message.hpp"
|
||||
#include "gui/widgets/window.hpp"
|
||||
#include "serialization/unicode.hpp"
|
||||
#include "font/text.hpp"
|
||||
|
@ -37,7 +41,7 @@ namespace gui2
|
|||
|
||||
REGISTER_WIDGET(multiline_text)
|
||||
|
||||
multiline_text::multiline_text(const implementation::builder_styled_widget& builder)
|
||||
multiline_text::multiline_text(const implementation::builder_multiline_text& builder)
|
||||
: text_box_base(builder, type())
|
||||
, history_()
|
||||
, max_input_length_(0)
|
||||
|
@ -45,6 +49,7 @@ multiline_text::multiline_text(const implementation::builder_styled_widget& buil
|
|||
, text_y_offset_(0)
|
||||
, text_height_(0)
|
||||
, dragging_(false)
|
||||
, link_aware_(builder.link_aware)
|
||||
{
|
||||
set_wants_mouse_left_double_click();
|
||||
|
||||
|
@ -66,6 +71,15 @@ multiline_text::multiline_text(const implementation::builder_styled_widget& buil
|
|||
update_offsets();
|
||||
}
|
||||
|
||||
void multiline_text::set_link_aware(bool link_aware)
|
||||
{
|
||||
if(link_aware != link_aware_) {
|
||||
link_aware_ = link_aware;
|
||||
update_canvas();
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
void multiline_text::place(const point& origin, const point& size)
|
||||
{
|
||||
// Inherited.
|
||||
|
@ -124,16 +138,22 @@ void multiline_text::update_canvas()
|
|||
|
||||
const int max_width = get_text_maximum_width();
|
||||
const int max_height = get_text_maximum_height();
|
||||
const point cpos = get_cursor_pos_from_index(start + length);
|
||||
unsigned byte_pos = start + length;
|
||||
if (get_use_markup() && (start + length > utf8::size(plain_text()) + 1)) {
|
||||
byte_pos = utf8::size(plain_text());
|
||||
}
|
||||
const point cpos = get_cursor_pos_from_index(byte_pos);
|
||||
|
||||
for(auto & tmp : get_canvases())
|
||||
{
|
||||
|
||||
tmp.set_variable("text", wfl::variant(get_value()));
|
||||
tmp.set_variable("text_markup", wfl::variant(get_use_markup()));
|
||||
tmp.set_variable("text_x_offset", wfl::variant(text_x_offset_));
|
||||
tmp.set_variable("text_y_offset", wfl::variant(text_y_offset_));
|
||||
tmp.set_variable("text_maximum_width", wfl::variant(max_width));
|
||||
tmp.set_variable("text_maximum_height", wfl::variant(max_height));
|
||||
tmp.set_variable("text_link_aware", wfl::variant(get_link_aware()));
|
||||
tmp.set_variable("text_wrap_mode", wfl::variant(PANGO_ELLIPSIZE_NONE));
|
||||
|
||||
tmp.set_variable("editable", wfl::variant(is_editable()));
|
||||
|
@ -191,26 +211,22 @@ void multiline_text::delete_selection()
|
|||
|
||||
void multiline_text::handle_mouse_selection(point mouse, const bool start_selection)
|
||||
{
|
||||
mouse.x -= get_x();
|
||||
mouse.y -= get_y();
|
||||
mouse -= get_origin();
|
||||
point text_offset(text_x_offset_, text_y_offset_);
|
||||
// FIXME we don't test for overflow in width
|
||||
if(mouse.x < static_cast<int>(text_x_offset_)
|
||||
|| mouse.y < static_cast<int>(text_y_offset_)
|
||||
|| mouse.y >= static_cast<int>(text_y_offset_ + get_lines_count() * font::get_line_spacing_factor() * text_height_)) {
|
||||
if(mouse < text_offset
|
||||
|| mouse.y >= static_cast<int>(text_y_offset_ + get_lines_count() * font::get_line_spacing_factor() * text_height_))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
point cursor_pos = get_column_line(point(mouse.x - text_x_offset_, mouse.y - text_y_offset_));
|
||||
int offset = cursor_pos.x;
|
||||
int line = cursor_pos.y;
|
||||
const auto& [offset, line] = get_column_line(mouse - text_offset);
|
||||
|
||||
if(offset < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
offset += get_line_start_offset(line);
|
||||
|
||||
set_cursor(offset, !start_selection);
|
||||
set_cursor(offset + get_line_start_offset(line), !start_selection);
|
||||
|
||||
update_canvas();
|
||||
queue_redraw();
|
||||
|
@ -319,10 +335,10 @@ void multiline_text::handle_key_down_arrow(SDL_Keymod modifier, bool& handled)
|
|||
|
||||
handled = true;
|
||||
|
||||
size_t offset = get_selection_start();
|
||||
unsigned offset = get_selection_start();
|
||||
const unsigned line_num = get_line_number(offset);
|
||||
|
||||
if (line_num == get_lines_count()) {
|
||||
if (line_num == get_lines_count()-1) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -330,15 +346,7 @@ void multiline_text::handle_key_down_arrow(SDL_Keymod modifier, bool& handled)
|
|||
const unsigned next_line_start = get_line_start_offset(line_num+1);
|
||||
const unsigned next_line_end = get_line_end_offset(line_num+1);
|
||||
|
||||
if (line_num < get_lines_count()-1) {
|
||||
offset = offset - line_start + next_line_start;
|
||||
|
||||
if (offset > next_line_end) {
|
||||
offset = next_line_end;
|
||||
}
|
||||
}
|
||||
|
||||
offset += get_selection_length();
|
||||
offset = std::min(offset - line_start + next_line_start, next_line_end) + get_selection_length();
|
||||
|
||||
if (offset <= get_length()) {
|
||||
set_cursor(offset, (modifier & KMOD_SHIFT) != 0);
|
||||
|
@ -354,7 +362,7 @@ void multiline_text::handle_key_up_arrow(SDL_Keymod modifier, bool& handled)
|
|||
|
||||
handled = true;
|
||||
|
||||
size_t offset = get_selection_start();
|
||||
unsigned offset = get_selection_start();
|
||||
const unsigned line_num = get_line_number(offset);
|
||||
|
||||
if (line_num == 0) {
|
||||
|
@ -365,15 +373,7 @@ void multiline_text::handle_key_up_arrow(SDL_Keymod modifier, bool& handled)
|
|||
const unsigned prev_line_start = get_line_start_offset(line_num-1);
|
||||
const unsigned prev_line_end = get_line_end_offset(line_num-1);
|
||||
|
||||
if (line_num > 0) {
|
||||
offset = offset - line_start + prev_line_start;
|
||||
|
||||
if (offset > prev_line_end) {
|
||||
offset = prev_line_end;
|
||||
}
|
||||
}
|
||||
|
||||
offset += get_selection_length();
|
||||
offset = std::min(offset - line_start + prev_line_start, prev_line_end) + get_selection_length();
|
||||
|
||||
/* offset is unsigned int */
|
||||
if (offset <= get_length()) {
|
||||
|
@ -392,6 +392,17 @@ void multiline_text::signal_handler_mouse_motion(const event::ui_event event,
|
|||
|
||||
if(dragging_) {
|
||||
handle_mouse_selection(coordinate, false);
|
||||
} else {
|
||||
if(!get_link_aware()) {
|
||||
return; // without marking event as "handled"
|
||||
}
|
||||
|
||||
point mouse = coordinate - get_origin();
|
||||
if (!get_label_link(mouse).empty()) {
|
||||
cursor::set(cursor::HYPERLINK);
|
||||
} else {
|
||||
cursor::set(cursor::IBEAM);
|
||||
}
|
||||
}
|
||||
|
||||
handled = true;
|
||||
|
@ -405,7 +416,27 @@ void multiline_text::signal_handler_left_button_down(const event::ui_event event
|
|||
get_window()->keyboard_capture(this);
|
||||
get_window()->mouse_capture();
|
||||
|
||||
handle_mouse_selection(get_mouse_position(), true);
|
||||
point mouse_pos = get_mouse_position();
|
||||
|
||||
if (get_link_aware()) {
|
||||
std::string link = get_label_link(mouse_pos - get_origin());
|
||||
DBG_GUI_E << "Clicked Link:\"" << link << "\"";
|
||||
|
||||
if (!link.empty()) {
|
||||
if (desktop::open_object_is_supported()) {
|
||||
if(show_message(_("Open link?"), link, dialogs::message::yes_no_buttons) == gui2::retval::OK) {
|
||||
desktop::open_object(link);
|
||||
}
|
||||
} else {
|
||||
desktop::clipboard::copy_to_clipboard(link, true);
|
||||
show_message("", _("Opening links is not supported, contact your packager. Link URL has been copied to the clipboard."), dialogs::message::auto_close);
|
||||
}
|
||||
} else {
|
||||
handle_mouse_selection(mouse_pos, true);
|
||||
}
|
||||
} else {
|
||||
handle_mouse_selection(mouse_pos, true);
|
||||
}
|
||||
|
||||
handled = true;
|
||||
}
|
||||
|
@ -464,6 +495,7 @@ builder_multiline_text::builder_multiline_text(const config& cfg)
|
|||
, hint_image(cfg["hint_image"])
|
||||
, editable(cfg["editable"].to_bool(true))
|
||||
, wrap(cfg["wrap"].to_bool(true))
|
||||
, link_aware(cfg["link_aware"].to_bool(false))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ class multiline_text : public text_box_base
|
|||
friend struct implementation::builder_multiline_text;
|
||||
|
||||
public:
|
||||
explicit multiline_text(const implementation::builder_styled_widget& builder);
|
||||
explicit multiline_text(const implementation::builder_multiline_text& builder);
|
||||
|
||||
/** See @ref widget::can_wrap. */
|
||||
bool can_wrap() const override
|
||||
|
@ -143,6 +143,14 @@ public:
|
|||
update_layout();
|
||||
}
|
||||
|
||||
/** See @ref styled_widget::get_link_aware. */
|
||||
virtual bool get_link_aware() const override
|
||||
{
|
||||
return link_aware_;
|
||||
}
|
||||
|
||||
void set_link_aware(bool l);
|
||||
|
||||
private:
|
||||
/** Inherited from text_box_base. */
|
||||
void paste_selection(const bool mouse) override
|
||||
|
@ -194,6 +202,12 @@ private:
|
|||
/** Is the mouse in dragging mode, this affects selection in mouse move */
|
||||
bool dragging_;
|
||||
|
||||
/**
|
||||
* Whether the text area is link aware, rendering links with special formatting
|
||||
* and handling click events.
|
||||
*/
|
||||
bool link_aware_;
|
||||
|
||||
/** Helper text to display (such as "Search") if the text box is empty. */
|
||||
std::string hint_text_;
|
||||
|
||||
|
@ -351,6 +365,7 @@ public:
|
|||
|
||||
bool editable;
|
||||
bool wrap;
|
||||
bool link_aware;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
Copyright (C) 2023 - 2024
|
||||
by babaissarkar(Subhraman Sarkar) <suvrax@gmail.com>
|
||||
by Subhraman Sarkar (babaissarkar) <suvrax@gmail.com>
|
||||
Part of the Battle for Wesnoth Project https://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
@ -41,8 +41,9 @@ scroll_text::scroll_text(const implementation::builder_scroll_text& builder)
|
|||
, state_(ENABLED)
|
||||
, wrap_on_(false)
|
||||
, text_alignment_(builder.text_alignment)
|
||||
, editable_(true)
|
||||
, editable_(builder.editable)
|
||||
, max_size_(point(0,0))
|
||||
, link_aware_(builder.link_aware)
|
||||
{
|
||||
connect_signal<event::LEFT_BUTTON_DOWN>(
|
||||
std::bind(&scroll_text::signal_handler_left_button_down, this, std::placeholders::_2),
|
||||
|
@ -81,6 +82,15 @@ std::string scroll_text::get_value()
|
|||
}
|
||||
}
|
||||
|
||||
void scroll_text::set_link_aware(bool l)
|
||||
{
|
||||
link_aware_ = l;
|
||||
|
||||
if(multiline_text* widget = get_internal_text_box()) {
|
||||
widget->set_link_aware(l);
|
||||
}
|
||||
}
|
||||
|
||||
void scroll_text::set_text_alignment(const PangoAlignment text_alignment)
|
||||
{
|
||||
// Inherit.
|
||||
|
@ -93,16 +103,6 @@ void scroll_text::set_text_alignment(const PangoAlignment text_alignment)
|
|||
}
|
||||
}
|
||||
|
||||
void scroll_text::set_use_markup(bool use_markup)
|
||||
{
|
||||
// Inherit.
|
||||
styled_widget::set_use_markup(use_markup);
|
||||
|
||||
if(multiline_text* widget = get_internal_text_box()) {
|
||||
widget->set_use_markup(use_markup);
|
||||
}
|
||||
}
|
||||
|
||||
void scroll_text::set_self_active(const bool active)
|
||||
{
|
||||
state_ = active ? ENABLED : DISABLED;
|
||||
|
@ -126,6 +126,7 @@ void scroll_text::finalize_subclass()
|
|||
text->set_editable(is_editable());
|
||||
text->set_label(get_label());
|
||||
text->set_text_alignment(text_alignment_);
|
||||
text->set_link_aware(link_aware_);
|
||||
text->set_use_markup(get_use_markup());
|
||||
}
|
||||
|
||||
|
@ -244,6 +245,7 @@ builder_scroll_text::builder_scroll_text(const config& cfg)
|
|||
, horizontal_scrollbar_mode(get_scrollbar_mode(cfg["horizontal_scrollbar_mode"]))
|
||||
, text_alignment(decode_text_alignment(cfg["text_alignment"]))
|
||||
, editable(cfg["editable"].to_bool(true))
|
||||
, link_aware(cfg["link_aware"].to_bool(false))
|
||||
{
|
||||
// Scrollbar default to auto. AUTO_VISIBLE_FIRST_RUN doesn't work.
|
||||
if (horizontal_scrollbar_mode == scrollbar_container::AUTO_VISIBLE_FIRST_RUN) {
|
||||
|
@ -261,8 +263,6 @@ std::unique_ptr<widget> builder_scroll_text::build() const
|
|||
|
||||
widget->set_vertical_scrollbar_mode(vertical_scrollbar_mode);
|
||||
widget->set_horizontal_scrollbar_mode(horizontal_scrollbar_mode);
|
||||
widget->set_editable(editable);
|
||||
widget->set_text_alignment(text_alignment);
|
||||
|
||||
const auto conf = widget->cast_config_to<scroll_text_definition>();
|
||||
assert(conf);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
Copyright (C) 2023 - 2024
|
||||
by babaissarkar(Subhraman Sarkar) <suvrax@gmail.com>
|
||||
by Subhraman Sarkar (babaissarkar) <suvrax@gmail.com>
|
||||
Part of the Battle for Wesnoth Project https://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
|
@ -53,9 +53,6 @@ public:
|
|||
/** See @ref styled_widget::set_text_alignment. */
|
||||
virtual void set_text_alignment(const PangoAlignment text_alignment) override;
|
||||
|
||||
/** See @ref styled_widget::set_use_markup. */
|
||||
virtual void set_use_markup(bool use_markup) override;
|
||||
|
||||
/** See @ref container_base::set_self_active. */
|
||||
virtual void set_self_active(const bool active) override;
|
||||
|
||||
|
@ -70,6 +67,14 @@ public:
|
|||
bool can_wrap() const override;
|
||||
void set_can_wrap(bool can_wrap);
|
||||
|
||||
void set_link_aware(bool l);
|
||||
|
||||
/** See @ref styled_widget::get_link_aware. */
|
||||
virtual bool get_link_aware() const override
|
||||
{
|
||||
return link_aware_;
|
||||
}
|
||||
|
||||
void set_editable(bool editable)
|
||||
{
|
||||
editable_ = editable;
|
||||
|
@ -110,6 +115,8 @@ private:
|
|||
|
||||
point max_size_;
|
||||
|
||||
bool link_aware_;
|
||||
|
||||
void finalize_subclass() override;
|
||||
|
||||
/** Used for moving scrollbars.
|
||||
|
@ -183,6 +190,7 @@ struct builder_scroll_text : public builder_styled_widget
|
|||
scrollbar_container::scrollbar_mode horizontal_scrollbar_mode;
|
||||
const PangoAlignment text_alignment;
|
||||
bool editable;
|
||||
bool link_aware;
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
|
|
|
@ -487,7 +487,7 @@ point styled_widget::get_best_text_size(point minimum_size, point maximum_size)
|
|||
<< "Status:\n"
|
||||
<< "minimum_size: " << minimum_size << "\n"
|
||||
<< "maximum_size: " << maximum_size << "\n"
|
||||
<< "text_maximum_width_: " << text_maximum_width_ << "\n"
|
||||
<< "maximum width of text: " << text_maximum_width_ << "\n"
|
||||
<< "can_wrap: " << can_wrap() << "\n"
|
||||
<< "characters_per_line: " << get_characters_per_line() << "\n"
|
||||
<< "truncated: " << renderer_.is_truncated() << "\n"
|
||||
|
|
|
@ -123,7 +123,7 @@ void text_box_base::set_maximum_length(const std::size_t maximum_length)
|
|||
void text_box_base::set_value(const std::string& text)
|
||||
{
|
||||
if(text != text_.text()) {
|
||||
text_.set_text(text, false);
|
||||
text_.set_text(text, get_use_markup());
|
||||
|
||||
// default to put the cursor at the end of the buffer.
|
||||
selection_start_ = text_.get_length();
|
||||
|
@ -138,31 +138,18 @@ void text_box_base::set_cursor(const std::size_t offset, const bool select)
|
|||
reset_cursor_state();
|
||||
|
||||
if(select) {
|
||||
|
||||
if(selection_start_ == offset) {
|
||||
selection_length_ = 0;
|
||||
} else {
|
||||
selection_length_ = -static_cast<int>(selection_start_ - offset);
|
||||
}
|
||||
|
||||
selection_length_ = (selection_start_ == offset) ? 0 : -static_cast<int>(selection_start_ - offset);
|
||||
#ifdef __unix__
|
||||
// selecting copies on UNIX systems.
|
||||
copy_selection(true);
|
||||
#endif
|
||||
update_canvas();
|
||||
queue_redraw();
|
||||
|
||||
} else {
|
||||
if (offset <= text_.get_length()) {
|
||||
selection_start_ = offset;
|
||||
} else {
|
||||
selection_start_ = 0;
|
||||
}
|
||||
selection_start_ = (offset <= text_.get_length()) ? offset : 0;
|
||||
selection_length_ = 0;
|
||||
|
||||
update_canvas();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
update_canvas();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
void text_box_base::insert_char(const std::string& unicode)
|
||||
|
@ -174,10 +161,14 @@ void text_box_base::insert_char(const std::string& unicode)
|
|||
|
||||
delete_selection();
|
||||
|
||||
if(text_.insert_text(selection_start_, unicode)) {
|
||||
|
||||
if(text_.insert_text(selection_start_, unicode, get_use_markup())) {
|
||||
// Update status
|
||||
set_cursor(selection_start_ + utf8::size(unicode), false);
|
||||
size_t plain_text_len = utf8::size(plain_text());
|
||||
size_t cursor_pos = selection_start_ + utf8::size(unicode);
|
||||
if (get_use_markup() && (selection_start_ + utf8::size(unicode) > plain_text_len + 1)) {
|
||||
cursor_pos = plain_text_len;
|
||||
}
|
||||
set_cursor(cursor_pos, false);
|
||||
update_canvas();
|
||||
queue_redraw();
|
||||
}
|
||||
|
@ -213,7 +204,7 @@ void text_box_base::copy_selection(const bool mouse)
|
|||
}
|
||||
|
||||
unsigned end, start = selection_start_;
|
||||
const std::string txt = text_.text();
|
||||
const std::string txt = get_use_markup() ? plain_text() : text_.text();
|
||||
|
||||
if(selection_length_ > 0) {
|
||||
end = utf8::index(txt, start + selection_length_);
|
||||
|
@ -240,7 +231,7 @@ void text_box_base::paste_selection(const bool mouse)
|
|||
|
||||
delete_selection();
|
||||
|
||||
selection_start_ += text_.insert_text(selection_start_, text);
|
||||
selection_start_ += text_.insert_text(selection_start_, text, get_use_markup());
|
||||
|
||||
update_canvas();
|
||||
queue_redraw();
|
||||
|
@ -383,7 +374,7 @@ void text_box_base::handle_key_right_arrow(SDL_Keymod modifier, bool& handled)
|
|||
|
||||
handled = true;
|
||||
const std::size_t offset = selection_start_ + 1 + selection_length_;
|
||||
if(offset <= text_.get_length()) {
|
||||
if(offset <= (get_use_markup() ? utf8::size(plain_text()) : text_.get_length())) {
|
||||
set_cursor(offset, (modifier & KMOD_SHIFT) != 0);
|
||||
}
|
||||
}
|
||||
|
@ -498,13 +489,13 @@ void text_box_base::handle_editing(bool& handled, const std::string& unicode, in
|
|||
// SDL_TextEditingEvent.
|
||||
// start is start position of the separated event in entire composition text
|
||||
if(start == 0) {
|
||||
text_.set_text(text_cached_, false);
|
||||
text_.set_text(text_cached_, get_use_markup());
|
||||
}
|
||||
text_.insert_text(ime_start_point_ + start, unicode);
|
||||
text_.insert_text(ime_start_point_ + start, unicode, get_use_markup());
|
||||
#else
|
||||
std::string new_text(text_cached_);
|
||||
utf8::insert(new_text, ime_start_point_, unicode);
|
||||
text_.set_text(new_text, false);
|
||||
text_.set_text(new_text, get_use_markup());
|
||||
|
||||
#endif
|
||||
int maximum_length = text_.get_length();
|
||||
|
|
|
@ -147,6 +147,13 @@ public:
|
|||
return text_.text();
|
||||
}
|
||||
|
||||
std::string plain_text()
|
||||
{
|
||||
char* plain_text = nullptr;
|
||||
pango_parse_markup(text().c_str(), text().size(), 0, nullptr, &plain_text, nullptr, nullptr);
|
||||
return plain_text ? std::string(plain_text) : std::string();
|
||||
}
|
||||
|
||||
/** Set the text_changed callback. */
|
||||
void set_text_changed_callback(
|
||||
std::function<void(text_box_base* textbox, const std::string text)> cb)
|
||||
|
|
Loading…
Add table
Reference in a new issue