7#include "text_field_delegate.hpp"
9#include "../text/editable_text.hpp"
10#include "../format.hpp"
11#include "../label.hpp"
62 template<
typename Value = observable<value_type>>
67 Value &&value = {})
noexcept :
69 value(std::forward<Value>(value)),
71 _field(theme::global->labelStyle),
74 _value_callback = this->value.subscribe([
this](
auto...) {
75 ttlet lock = std::scoped_lock(gui_system_mutex);
76 _request_relayout =
true;
80 template<
typename Value = observable<value_type>>
95 ttlet lock = std::scoped_lock(gui_system_mutex);
105 ttlet lock = std::scoped_lock(gui_system_mutex);
116 ttlet &text_font = font_book::global->get_font(text_font_id);
117 ttlet text_digit_width = text_font.description.DigitWidth *
text_style.scaled_size();
119 if (
auto delegate = _delegate.lock()) {
120 _text_width =
std::ceil(text_digit_width * narrow_cast<float>(delegate->text_width(*
this)));
126 extent2{_text_width + theme::global->margin * 2.0f, theme::global->smallSize + theme::global->margin * 2.0f},
128 _width_resistance = 2;
140 if (_focus && display_time_point >= _next_redraw_time_point) {
144 need_layout |= std::exchange(_request_relayout,
false);
146 _text_field_rectangle =
aarectangle{
extent2{_text_width + theme::global->margin * 2.0f, _size.height()}};
150 _text_field_clipping_rectangle = intersect(_clipping_rectangle, _text_field_rectangle);
152 _text_rectangle = shrink(_text_field_rectangle, theme::global->
margin);
154 ttlet field_str =
static_cast<std::string>(_field);
156 if (
auto delegate = _delegate.lock()) {
160 delegate->from_string(*
this, field_str, _error);
164 _field = delegate->to_string(*
this, *value);
170 _error =
l10n(
"system error: delegate missing");
175 _shaped_text = _field.shapedText();
178 _last_update_time_point = display_time_point;
188 _next_redraw_time_point = display_time_point + _blink_interval;
190 if (overlaps(context, this->_clipping_rectangle)) {
193 draw_background_box(context);
198 context.set_clipping_rectangle(_text_field_clipping_rectangle);
199 draw_selection_rectangles(context);
200 draw_partial_grapheme_caret(context);
201 draw_caret(context, display_time_point);
210 ttlet lock = std::scoped_lock(gui_system_mutex);
211 _request_relayout =
true;
215 case command::text_edit_paste:
224 case command::gui_escape: revert(
true);
return true;
226 case command::gui_enter:
229 this->
shared_from_this(), keyboard_focus_group::normal, keyboard_focus_direction::forward);
232 case command::gui_keyboard_enter:
237 case command::gui_keyboard_exit:
243 if (_field.handle_event(command)) {
255 ttlet lock = std::scoped_lock(gui_system_mutex);
259 _drag_scroll_speed_x = 0.0f;
260 _drag_click_count =
event.clickCount;
261 _drag_select_position =
event.position;
263 if (event.cause.leftButton) {
270 switch (event.type) {
271 using enum mouse_event::Type;
273 if (_text_rectangle.
contains(event.position)) {
274 ttlet mouseInTextPosition = _text_inv_translate *
event.position;
276 switch (event.clickCount) {
278 if (event.down.shiftKey) {
279 _field.dragmouse_cursorAtCoordinate(mouseInTextPosition);
281 _field.setmouse_cursorAtCoordinate(mouseInTextPosition);
284 case 2: _field.selectWordAtCoordinate(mouseInTextPosition);
break;
285 case 3: _field.selectParagraphAtCoordinate(mouseInTextPosition);
break;
290 _last_update_time_point =
event.timePoint;
299 if (event.position.x() > _text_rectangle.right()) {
301 _drag_select_position.
x() = _text_rectangle.right();
304 _drag_scroll_speed_x = 50.0f;
306 }
else if (event.position.x() < _text_rectangle.left()) {
308 _drag_select_position.
x() = _text_rectangle.left();
311 _drag_scroll_speed_x = -50.0f;
327 ttlet lock = std::scoped_lock(gui_system_mutex);
332 switch (event.type) {
341 case Partialgrapheme:
351 _request_relayout =
true;
359 if (_visible_rectangle.contains(position)) {
360 return hit_box{weak_from_this(), _draw_layer, *
enabled ? hit_box::Type::TextEdit : hit_box::Type::Default};
369 return is_normal(group) && *
enabled;
372 [[nodiscard]]
color focus_color() const noexcept
override
375 return theme::global->errorLabelStyle.color;
377 return super::focus_color();
382 typename decltype(value)::callback_ptr_type _value_callback;
386 bool _continues =
false;
392 float _text_width = 0.0f;
393 aarectangle _text_rectangle = {};
395 aarectangle _text_field_rectangle;
396 aarectangle _text_field_clipping_rectangle;
398 editable_text _field;
399 shaped_text _shaped_text;
400 aarectangle _left_to_right_caret = {};
405 float _drag_scroll_speed_x = 0.0f;
409 int _drag_click_count = 0;
411 point2 _drag_select_position = {};
415 float _text_scroll_x = 0.0f;
417 translate2 _text_translate;
418 translate2 _text_inv_translate;
420 static constexpr hires_utc_clock::duration _blink_interval = 500ms;
421 hires_utc_clock::time_point _next_redraw_time_point;
422 hires_utc_clock::time_point _last_update_time_point;
424 void revert(
bool force)
noexcept
426 if (
auto delegate = _delegate.
lock()) {
427 _field = delegate->to_string(*
this, *value);
431 _error = l10n(
"missing delegate");
435 void commit(
bool force)
noexcept
438 if (_continues || force) {
439 if (
auto delegate = _delegate.
lock()) {
440 auto optional_value = delegate->from_string(*
this,
static_cast<std::string>(_field), _error);
441 if (optional_value) {
442 value = *optional_value;
448 void drag_select() noexcept
452 ttlet mouseInTextPosition = _text_inv_translate * _drag_select_position;
453 switch (_drag_click_count) {
454 case 1: _field.dragmouse_cursorAtCoordinate(mouseInTextPosition);
break;
455 case 2: _field.dragWordAtCoordinate(mouseInTextPosition);
break;
456 case 3: _field.dragParagraphAtCoordinate(mouseInTextPosition);
break;
461 void scroll_text() noexcept
463 if (_drag_scroll_speed_x != 0.0f) {
464 _text_scroll_x += _drag_scroll_speed_x * (1.0f / 60.0f);
470 }
else if (_drag_click_count == 0) {
475 if (_left_to_right_caret.left() - _text_scroll_x > _text_rectangle.width()) {
476 _text_scroll_x = _left_to_right_caret.left() - _text_rectangle.width() * 0.75f;
481 while (_left_to_right_caret.left() - _text_scroll_x < 0.0f) {
482 _text_scroll_x = _left_to_right_caret.left() - _text_rectangle.width() * 0.25f;
487 ttlet max_scroll_width =
std::max(0.0f, _shaped_text.preferred_extent.width() - _text_rectangle.width());
488 _text_scroll_x = std::clamp(_text_scroll_x, 0.0f, max_scroll_width);
491 _text_translate = translate2{-_text_scroll_x, 0.0f} *
492 _shaped_text.translate_base_line(point2{_text_rectangle.left(),
rectangle().middle()});
493 _text_inv_translate = ~_text_translate;
496 void draw_background_box(draw_context context)
const noexcept
498 ttlet corner_shapes =
tt::corner_shapes{0.0f, 0.0f, theme::global->roundingRadius, theme::global->roundingRadius};
499 context.draw_box(_text_field_rectangle, background_color(), corner_shapes);
501 ttlet line_rectangle = aarectangle{get<0>(_text_field_rectangle), extent2{_text_field_rectangle.
width(), 1.0f}};
502 context.draw_filled_quad(translate3{0.0f, 0.0f, 0.1f} * line_rectangle, focus_color());
505 void draw_selection_rectangles(draw_context context)
const noexcept
507 ttlet selection_rectangles = _field.selectionRectangles();
508 for (ttlet selection_rectangle : selection_rectangles) {
509 context.draw_filled_quad(_text_translate * translate_z(0.1f) * selection_rectangle, theme::global->textSelectColor);
513 void draw_partial_grapheme_caret(draw_context context)
const noexcept
515 ttlet partial_grapheme_caret = _field.partialgraphemeCaret();
516 if (partial_grapheme_caret) {
517 context.draw_filled_quad(
518 _text_translate * translate_z(0.1f) * partial_grapheme_caret, theme::global->incompleteGlyphColor);
522 void draw_caret(draw_context context, hires_utc_clock::time_point display_time_point)
noexcept
525 ttlet duration_since_last_update = display_time_point - _last_update_time_point;
526 ttlet nr_half_blinks =
static_cast<int64_t
>(duration_since_last_update / _blink_interval);
528 ttlet blink_is_on = nr_half_blinks % 2 == 0;
529 _left_to_right_caret = _field.leftToRightCaret();
530 if (_left_to_right_caret && blink_is_on && _focus &&
window.
active) {
531 context.draw_filled_quad(_text_translate * translate_z(0.1f) * _left_to_right_caret, theme::global->cursorColor);
535 void draw_text(draw_context context)
const noexcept
537 context.draw_text(_shaped_text, label_color(), _text_translate * translate_z(0.2f));
This is a RGBA floating point color.
Definition color.hpp:39
Class which represents an axis-aligned rectangle.
Definition axis_aligned_rectangle.hpp:18
bool contains(point2 const &rhs) const noexcept
Check if a 2D coordinate is inside the rectangle.
Definition axis_aligned_rectangle.hpp:222
Definition corner_shapes.hpp:9
constexpr float & width() noexcept
Access the x-as-width element from the extent.
Definition extent.hpp:91
constexpr float & x() noexcept
Access the x element from the point.
Definition point.hpp:85
Draw context for drawing using the TTauri shaders.
Definition draw_context.hpp:33
Definition gui_window.hpp:37
std::atomic< bool > active
Definition gui_window.hpp:69
virtual void set_text_on_clipboard(std::string str) noexcept=0
Place a text string on the operating system's clip-board.
void update_keyboard_target(std::shared_ptr< tt::widget > widget, keyboard_focus_group group=keyboard_focus_group::normal) noexcept
Change the keyboard focus to the given widget.
virtual std::string get_text_from_clipboard() const noexcept=0
Retrieve a text string from the operating system's clip-board.
Definition hit_box.hpp:15
Definition keyboard_event.hpp:40
Type
Definition keyboard_event.hpp:41
Definition mouse_event.hpp:15
A localizable string.
Definition l10n.hpp:12
Definition observable.hpp:20
void setStyleOfAll(text_style style) noexcept
Change the text style of all graphemes.
Definition editable_text.hpp:113
void insertgrapheme(grapheme character) noexcept
Definition editable_text.hpp:338
void insertPartialgrapheme(grapheme character) noexcept
Definition editable_text.hpp:320
Definition grapheme.hpp:21
Definition text_style.hpp:16
int recurse_lock_count() const noexcept
This function should be used in tt_axiom() to check if the lock is held by current thread.
Definition unfair_recursive_mutex.hpp:60
A single line text field.
Definition text_field_widget.hpp:54
bool handle_event(keyboard_event const &event) noexcept override
Handle keyboard event.
Definition text_field_widget.hpp:325
bool handle_event(mouse_event const &event) noexcept override
Definition text_field_widget.hpp:253
void draw(draw_context context, hires_utc_clock::time_point display_time_point) noexcept override
Draw the widget.
Definition text_field_widget.hpp:184
void update_layout(hires_utc_clock::time_point display_time_point, bool need_layout) noexcept override
Update the internal layout of the widget.
Definition text_field_widget.hpp:136
bool update_constraints(hires_utc_clock::time_point display_time_point, bool need_reconstrain) noexcept override
Update the constraints of the widget.
Definition text_field_widget.hpp:109
void set_delegate(std::weak_ptr< delegate_type > &&delegate) noexcept
Set the delegate The delegate is used to convert between the value type and the string the user sees ...
Definition text_field_widget.hpp:93
hit_box hitbox_test(point2 position) const noexcept override
Find the widget that is under the mouse cursor.
Definition text_field_widget.hpp:355
void set_continues(bool flag) noexcept
Set or unset continues mode.
Definition text_field_widget.hpp:103
bool handle_event(command command) noexcept override
Handle command.
Definition text_field_widget.hpp:208
bool accepts_keyboard_focus(keyboard_focus_group group) const noexcept override
Check if the widget will accept keyboard focus.
Definition text_field_widget.hpp:366
Definition text_field_delegate.hpp:21
float margin() const noexcept
Get the margin around the Widget.
Definition widget.hpp:128
virtual void update_layout(hires_utc_clock::time_point display_time_point, bool need_layout) noexcept
Update the internal layout of the widget.
observable< bool > enabled
The widget is enabled.
Definition widget.hpp:105
widget(gui_window &window, std::shared_ptr< abstract_container_widget > parent) noexcept
aarectangle rectangle() const noexcept
Get the rectangle in local coordinates.
Definition widget.hpp:342
virtual bool handle_event(command command) noexcept
Handle command.
gui_window & window
Convenient reference to the Window.
Definition widget.hpp:101
virtual void draw(draw_context context, hires_utc_clock::time_point display_time_point) noexcept
Draw the widget.
Definition widget.hpp:462
virtual bool update_constraints(hires_utc_clock::time_point display_time_point, bool need_reconstrain) noexcept
Update the constraints of the widget.
abstract_container_widget const & parent() const noexcept
Get a reference to the parent.
T shared_from_this(T... args)