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 f32x4{_text_width + theme::global->margin * 2.0f, theme::global->smallSize + theme::global->margin * 2.0f},
129 _width_resistance = 2;
141 if (_focus && display_time_point >= _next_redraw_time_point) {
145 need_layout |= std::exchange(_request_relayout,
false);
147 _text_field_rectangle =
149 auto _text_field_window_rectangle =
aarect{
150 _window_rectangle.x() + theme::global->borderWidth * 2.0f,
151 _window_rectangle.y(),
152 _text_width + theme::global->margin * 2.0f - theme::global->borderWidth * 4.0f,
153 _window_rectangle.height()};
159 _text_rectangle = shrink(_text_field_rectangle, theme::global->
margin);
161 ttlet field_str =
static_cast<std::string>(_field);
163 if (
auto delegate = _delegate.lock()) {
167 delegate->from_string(*
this, field_str, _error);
171 _field = delegate->to_string(*
this, *value);
177 _error =
l10n(
"system error: delegate missing");
182 _shaped_text = _field.shapedText();
185 _last_update_time_point = display_time_point;
195 _next_redraw_time_point = display_time_point + _blink_interval;
200 draw_background_box(context);
205 context.clipping_rectangle = _text_field_clipping_rectangle;
206 context.transform = (
translate3{0.0, 0.0, 0.1f} * _text_translate) * context.transform;
208 draw_selection_rectangles(context);
209 draw_partial_grapheme_caret(context);
210 draw_caret(context, display_time_point);
219 ttlet lock = std::scoped_lock(gui_system_mutex);
220 _request_relayout =
true;
224 case command::text_edit_paste:
233 case command::gui_escape: revert(
true);
return true;
235 case command::gui_enter:
238 this->
shared_from_this(), keyboard_focus_group::normal, keyboard_focus_direction::forward);
241 case command::gui_keyboard_enter:
246 case command::gui_keyboard_exit:
252 if (_field.handle_event(command)) {
264 ttlet lock = std::scoped_lock(gui_system_mutex);
268 ttlet position = _from_window_transform *
event.position;
269 _drag_scroll_speed_x = 0.0f;
270 _drag_click_count =
event.clickCount;
271 _drag_select_position = position;
273 if (event.cause.leftButton) {
280 switch (event.type) {
281 using enum mouse_event::Type;
283 if (_text_rectangle.
contains(position)) {
284 ttlet mouseInTextPosition = _text_inv_translate * position;
286 switch (event.clickCount) {
288 if (event.down.shiftKey) {
289 _field.dragmouse_cursorAtCoordinate(mouseInTextPosition);
291 _field.setmouse_cursorAtCoordinate(mouseInTextPosition);
294 case 2: _field.selectWordAtCoordinate(mouseInTextPosition);
break;
295 case 3: _field.selectParagraphAtCoordinate(mouseInTextPosition);
break;
300 _last_update_time_point =
event.timePoint;
309 if (position.x() > _text_rectangle.p3().x()) {
311 _drag_select_position.x() = _text_rectangle.p3().x();
314 _drag_scroll_speed_x = 50.0f;
316 }
else if (position.x() < _text_rectangle.x()) {
318 _drag_select_position.x() = _text_rectangle.x();
321 _drag_scroll_speed_x = -50.0f;
337 ttlet lock = std::scoped_lock(gui_system_mutex);
342 switch (event.type) {
351 case Partialgrapheme:
361 _request_relayout =
true;
367 ttlet lock = std::scoped_lock(gui_system_mutex);
370 return hit_box{weak_from_this(), _draw_layer, *
enabled ? hit_box::Type::TextEdit : hit_box::Type::Default};
379 return is_normal(group) && *
enabled;
383 typename decltype(value)::callback_ptr_type _value_callback;
387 bool _continues =
false;
393 float _text_width = 0.0f;
394 aarect _text_rectangle = {};
396 aarect _text_field_rectangle;
397 aarect _text_field_clipping_rectangle;
399 editable_text _field;
400 shaped_text _shaped_text;
401 aarect _left_to_right_caret = {};
406 float _drag_scroll_speed_x = 0.0f;
410 int _drag_click_count = 0;
412 f32x4 _drag_select_position = {};
416 float _text_scroll_x = 0.0f;
418 translate2 _text_translate;
419 translate2 _text_inv_translate;
421 static constexpr hires_utc_clock::duration _blink_interval = 500ms;
422 hires_utc_clock::time_point _next_redraw_time_point;
423 hires_utc_clock::time_point _last_update_time_point;
425 void revert(
bool force)
noexcept
427 if (
auto delegate = _delegate.
lock()) {
428 _field = delegate->to_string(*
this, *value);
432 _error = l10n(
"missing delegate");
436 void commit(
bool force)
noexcept
439 if (_continues || force) {
440 if (
auto delegate = _delegate.
lock()) {
441 auto optional_value = delegate->from_string(*
this,
static_cast<std::string>(_field), _error);
442 if (optional_value) {
443 value = *optional_value;
449 void drag_select() noexcept
453 ttlet mouseInTextPosition = _text_inv_translate * _drag_select_position;
454 switch (_drag_click_count) {
455 case 1: _field.dragmouse_cursorAtCoordinate(mouseInTextPosition);
break;
456 case 2: _field.dragWordAtCoordinate(mouseInTextPosition);
break;
457 case 3: _field.dragParagraphAtCoordinate(mouseInTextPosition);
break;
462 void scroll_text() noexcept
464 if (_drag_scroll_speed_x != 0.0f) {
465 _text_scroll_x += _drag_scroll_speed_x * (1.0f / 60.0f);
471 }
else if (_drag_click_count == 0) {
476 if (_left_to_right_caret.x() - _text_scroll_x > _text_rectangle.width()) {
477 _text_scroll_x = _left_to_right_caret.x() - _text_rectangle.width() * 0.75f;
482 while (_left_to_right_caret.x() - _text_scroll_x < 0.0f) {
483 _text_scroll_x = _left_to_right_caret.x() - _text_rectangle.width() * 0.25f;
488 ttlet max_scroll_width =
std::max(0.0f, _shaped_text.preferred_extent.width() - _text_rectangle.width());
489 _text_scroll_x = std::clamp(_text_scroll_x, 0.0f, max_scroll_width);
492 _text_translate = translate2{-_text_scroll_x, 0.0f} * _shaped_text.translate_base_line(f32x4{_text_rectangle.x(),
base_line()});
493 _text_inv_translate = ~_text_translate;
496 void draw_background_box(draw_context context)
const noexcept
498 ttlet line_color = context.line_color;
500 context.line_color = context.fill_color;
501 context.corner_shapes = {0.0f, 0.0f, theme::global->roundingRadius, theme::global->roundingRadius};
502 context.draw_box_with_border_inside(_text_field_rectangle);
504 ttlet line_rectangle = aarect{_text_field_rectangle.p0(), f32x4{_text_field_rectangle.width(), context.line_width}};
505 context.transform = context.transform * translate3{0.0f, 0.0f, 0.1f};
507 context.fill_color = theme::global->errorLabelStyle.color;
509 context.fill_color = line_color;
511 context.draw_filled_quad(line_rectangle);
514 void draw_selection_rectangles(draw_context context)
const noexcept
516 ttlet selection_rectangles = _field.selectionRectangles();
517 for (ttlet selection_rectangle : selection_rectangles) {
518 context.fill_color = theme::global->textSelectColor;
519 context.draw_filled_quad(selection_rectangle);
523 void draw_partial_grapheme_caret(draw_context context)
const noexcept
525 ttlet partial_grapheme_caret = _field.partialgraphemeCaret();
526 if (partial_grapheme_caret) {
527 context.fill_color = theme::global->incompleteGlyphColor;
528 context.draw_filled_quad(partial_grapheme_caret);
532 void draw_caret(draw_context context, hires_utc_clock::time_point display_time_point)
noexcept
535 ttlet duration_since_last_update = display_time_point - _last_update_time_point;
536 ttlet nr_half_blinks =
static_cast<int64_t
>(duration_since_last_update / _blink_interval);
538 ttlet blink_is_on = nr_half_blinks % 2 == 0;
539 _left_to_right_caret = _field.leftToRightCaret();
540 if (_left_to_right_caret && blink_is_on && _focus &&
window.
active) {
541 context.fill_color = theme::global->cursorColor;
542 context.draw_filled_quad(_left_to_right_caret);
546 void draw_text(draw_context context)
const noexcept
548 context.transform = translate3{0.0f, 0.0f, 0.2f} * context.transform;
549 context.draw_text(_shaped_text);
bool contains(numeric_array< T, 4 > const &rhs) const noexcept
Check if a 2D coordinate is inside the rectangle.
Definition aarect.hpp:300
Definition alignment.hpp:104
Definition translate.hpp:14
Draw context for drawing using the TTauri shaders.
Definition draw_context.hpp:33
Definition gui_window.hpp:39
std::atomic< bool > active
Definition gui_window.hpp:71
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.
void request_redraw(aarect rectangle=aarect::infinity()) noexcept
Request a rectangle on the window to be redrawn.
Definition gui_window.hpp:115
Definition hit_box.hpp:15
Definition keyboard_event.hpp:39
Type
Definition keyboard_event.hpp:40
Definition mouse_event.hpp:13
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:332
void insertPartialgrapheme(grapheme character) noexcept
Definition editable_text.hpp:314
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:335
bool handle_event(mouse_event const &event) noexcept override
Definition text_field_widget.hpp:262
void draw(draw_context context, hires_utc_clock::time_point display_time_point) noexcept override
Draw the widget.
Definition text_field_widget.hpp:191
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:137
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
hit_box hitbox_test(f32x4 window_position) const noexcept override
Find the widget that is under the mouse cursor.
Definition text_field_widget.hpp:365
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
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:217
bool accepts_keyboard_focus(keyboard_focus_group group) const noexcept override
Check if the widget will accept keyboard focus.
Definition text_field_widget.hpp:376
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
virtual bool handle_event(command command) noexcept
Handle command.
gui_window & window
Convenient reference to the Window.
Definition widget.hpp:100
virtual void draw(draw_context context, hires_utc_clock::time_point display_time_point) noexcept
Draw the widget.
Definition widget.hpp:460
float base_line() const noexcept
Get the base-line in local coordinates.
Definition widget.hpp:350
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.
virtual aarect window_clipping_rectangle() const noexcept
Get the clipping-rectangle in window coordinates.
Definition widget.hpp:320
aarect rectangle() const noexcept
Get the rectangle in local coordinates.
Definition widget.hpp:340
T shared_from_this(T... args)