lxgui
Loading...
Searching...
No Matches
gui_edit_box.cpp
1#include "lxgui/gui_edit_box.hpp"
2
3#include "lxgui/gui_alive_checker.hpp"
4#include "lxgui/gui_font_string.hpp"
5#include "lxgui/gui_localizer.hpp"
6#include "lxgui/gui_manager.hpp"
7#include "lxgui/gui_out.hpp"
8#include "lxgui/gui_quad.hpp"
9#include "lxgui/gui_region_tpl.hpp"
10#include "lxgui/gui_texture.hpp"
11#include "lxgui/input_window.hpp"
12#include "lxgui/utils_range.hpp"
13
14#include <lxgui/extern_sol2_state.hpp>
15
16using namespace lxgui::input;
17
18namespace lxgui::gui {
19
20edit_box::edit_box(utils::control_block& block, manager& mgr, const frame_core_attributes& attr) :
21 frame(block, mgr, attr),
22 carret_timer_(blink_time_, utils::periodic_timer::start_type::first_tick, false) {
23
24 initialize_(*this, attr);
25
27
31}
32
33bool edit_box::can_use_script(const std::string& script_name) const {
34 return base::can_use_script(script_name) || script_name == "OnCursorChanged" ||
35 script_name == "OnEnterPressed" || script_name == "OnEscapePressed" ||
36 script_name == "OnSpacePressed" || script_name == "OnTabPressed" ||
37 script_name == "OnUpPressed" || script_name == "OnDownPressed" ||
38 script_name == "OnTextChanged" || script_name == "OnTextSet";
39}
40
41void edit_box::copy_from(const region& obj) {
42 base::copy_from(obj);
43
44 const edit_box* box_obj = down_cast<edit_box>(&obj);
45 if (!box_obj)
46 return;
47
48 this->set_max_letters(box_obj->get_max_letters());
49 this->set_blink_time(box_obj->get_blink_time());
50 this->set_numeric_only(box_obj->is_numeric_only());
51 this->set_positive_only(box_obj->is_positive_only());
52 this->set_integer_only(box_obj->is_integer_only());
54 this->set_multi_line(box_obj->is_multi_line());
56 this->set_text_insets(box_obj->get_text_insets());
57
58 if (const font_string* fs = box_obj->get_font_string().get()) {
60 attr.name = fs->get_raw_name();
61 attr.inheritance = {box_obj->get_font_string()};
62
63 auto fnt = this->create_layered_region<font_string>(fs->get_draw_layer(), std::move(attr));
64
65 if (fnt) {
66 fnt->set_manually_inherited(true);
67 fnt->notify_loaded();
68 this->set_font_string(fnt);
69 }
70 }
71}
72
73void edit_box::update_(float delta) {
74 alive_checker checker(*this);
75
76 base::update_(delta);
77 if (!checker.is_alive())
78 return;
79
80 if (has_focus()) {
81 carret_timer_.update(delta);
82
83 if (carret_timer_.ticks()) {
84 if (!carret_)
86
87 if (carret_) {
88 if (carret_->is_shown())
89 carret_->hide();
90 else
91 carret_->show();
92 }
93 }
94 }
95}
96
97void edit_box::fire_script(const std::string& script_name, const event_data& data) {
98 alive_checker checker(*this);
99
100 // Do not fire OnKeyUp/OnKeyRepeat/OnKeyDown events when typing
101 bool bypass_event = false;
102 if (has_focus() &&
103 (script_name == "OnKeyUp" || script_name == "OnKeyDown" || script_name == "OnKeyRepeat")) {
104 bypass_event = true;
105 }
106 if (!has_focus() && (script_name == "OnChar")) {
107 bypass_event = true;
108 }
109
110 if (!bypass_event) {
111 base::fire_script(script_name, data);
112 if (!checker.is_alive())
113 return;
114 }
115
116 if ((script_name == "OnKeyDown" || script_name == "OnKeyRepeat") && has_focus()) {
117 key key_id = data.get<key>(0);
118 bool shift_is_pressed = data.get<bool>(1);
119 bool ctrl_is_pressed = data.get<bool>(2);
120
121 if (key_id == key::k_return || key_id == key::k_numpadenter) {
122 fire_script("OnEnterPressed");
123 if (!checker.is_alive())
124 return;
125 } else if (key_id == key::k_tab) {
126 fire_script("OnTabPressed");
127 if (!checker.is_alive())
128 return;
129 } else if (key_id == key::k_up) {
130 fire_script("OnUpPressed");
131 if (!checker.is_alive())
132 return;
133 } else if (key_id == key::k_down) {
134 fire_script("OnDownPressed");
135 if (!checker.is_alive())
136 return;
137 } else if (key_id == key::k_space) {
138 fire_script("OnSpacePressed");
139 if (!checker.is_alive())
140 return;
141 } else if (key_id == key::k_escape) {
142 fire_script("OnEscapePressed");
143 if (!checker.is_alive())
144 return;
145 }
146
147 process_key_(key_id, shift_is_pressed, ctrl_is_pressed);
148
149 if (!checker.is_alive())
150 return;
151 } else if (script_name == "OnChar" && has_focus()) {
152 std::uint32_t c = data.get<std::uint32_t>(1);
153 if (add_char_(c)) {
154 fire_script("OnTextChanged");
155 if (!checker.is_alive())
156 return;
157
158 fire_script("OnCursorChanged");
159 if (!checker.is_alive())
160 return;
161 }
162 } else if (script_name == "OnSizeChanged") {
166 } else if (script_name == "OnDragStart") {
168 get_letter_id_at_(vector2f(data.get<float>(2), data.get<float>(3)));
169 } else if (script_name == "OnDragMove") {
170 std::size_t pos = get_letter_id_at_(vector2f(data.get<float>(2), data.get<float>(3)));
171 if (pos != selection_end_pos_) {
172 if (pos != std::numeric_limits<std::size_t>::max()) {
174 iter_carret_pos_ = unicode_text_.begin() + pos;
176 } else {
177 std::size_t temp = selection_start_pos_;
178 unlight_text();
182 }
183
184 fire_script("OnCursorChanged");
185 if (!checker.is_alive())
186 return;
187 }
188 } else if (script_name == "OnMouseDown") {
189 set_focus(true);
190 if (!checker.is_alive())
191 return;
192
193 unlight_text();
194
195 if (move_carret_at_({data.get<float>(2), data.get<float>(3)})) {
196 fire_script("OnCursorChanged");
197 if (!checker.is_alive())
198 return;
199 }
200 }
201}
202
203void edit_box::set_text(const utils::ustring& content) {
204 if (content == unicode_text_)
205 return;
206
207 unlight_text();
208 unicode_text_ = content;
209 check_text_();
214
215 alive_checker checker(*this);
216
217 fire_script("OnTextSet");
218 if (!checker.is_alive())
219 return;
220
221 fire_script("OnTextChanged");
222 if (!checker.is_alive())
223 return;
224
225 fire_script("OnCursorChanged");
226 if (!checker.is_alive())
227 return;
228}
229
230const utils::ustring& edit_box::get_text() const {
231 return unicode_text_;
232}
233
237 is_text_selected_ = false;
238
239 if (highlight_)
240 highlight_->hide();
241}
242
243void edit_box::highlight_text(std::size_t start, std::size_t end, bool force_update) {
244 if (!highlight_)
246
247 if (!highlight_)
248 return;
249
250 std::size_t left = std::min(start, end);
251 std::size_t right = std::max(start, end);
252
253 if (selection_start_pos_ != start || selection_end_pos_ != end || force_update) {
254 if (left != right) {
255 is_text_selected_ = true;
256
257 if (right >= display_pos_ && left < display_pos_ + displayed_text_.size() &&
258 font_string_ && font_string_->get_text_object()) {
259 text* text = font_string_->get_text_object();
260
261 if (left < display_pos_)
262 left = 0;
263 else
265
266 float left_pos = text_insets_.left;
267 if (left < text->get_letter_count())
268 left_pos += text->get_letter_quad(left)[0].pos.x;
269
271 float right_pos = text_insets_.left;
272 if (right < displayed_text_.size()) {
273 if (right < text->get_letter_count())
274 right_pos += text->get_letter_quad(right)[0].pos.x;
275 } else {
276 right = displayed_text_.size() - 1;
277 if (right < text->get_letter_count())
278 right_pos += text->get_letter_quad(right)[2].pos.x;
279 }
280
281 highlight_->set_anchor(point::left, name_, vector2f(left_pos, 0));
282 highlight_->set_anchor(point::right, name_, point::left, vector2f(right_pos, 0));
283
284 highlight_->show();
285 } else
286 highlight_->hide();
287 } else {
288 is_text_selected_ = false;
289 highlight_->hide();
290 }
291 }
292
293 selection_start_pos_ = start;
294 selection_end_pos_ = end;
295}
296
298 if (highlight_color_ == c)
299 return;
300
302
303 if (!highlight_)
305
306 if (!highlight_)
307 return;
308
309 highlight_->set_solid_color(highlight_color_);
310}
311
312void edit_box::insert_after_cursor(const utils::ustring& content) {
313 if (content.empty())
314 return;
315
316 if (is_numeric_only_ && !utils::is_number(content))
317 return;
318
319 if (unicode_text_.size() + content.size() <= max_letters_) {
320 unlight_text();
321 unicode_text_.insert(iter_carret_pos_, content.begin(), content.end());
322 iter_carret_pos_ += content.size();
323
327
328 alive_checker checker(*this);
329 fire_script("OnTextChanged");
330 if (!checker.is_alive())
331 return;
332
333 fire_script("OnCursorChanged");
334 if (!checker.is_alive())
335 return;
336 }
337}
338
339std::size_t edit_box::get_cursor_position() const {
340 return iter_carret_pos_ - unicode_text_.begin();
341}
342
343void edit_box::set_cursor_position(std::size_t pos) {
344 if (pos == get_cursor_position())
345 return;
346
347 iter_carret_pos_ = unicode_text_.begin() + pos;
349
350 alive_checker checker(*this);
351 fire_script("OnCursorChanged");
352 if (!checker.is_alive())
353 return;
354}
355
356void edit_box::set_max_letters(std::size_t max_letters) {
357 if (max_letters == 0) {
358 max_letters_ = std::numeric_limits<std::size_t>::max();
359 return;
360 }
361
362 if (max_letters_ != max_letters) {
363 max_letters_ = max_letters;
364
365 const std::size_t carret_pos = iter_carret_pos_ - unicode_text_.begin();
366
367 if (check_text_()) {
368 bool cursor_changed = false;
369 if (carret_pos > max_letters_) {
374 cursor_changed = true;
375 } else {
376 iter_carret_pos_ = unicode_text_.begin() + carret_pos;
377 }
378
379 alive_checker checker(*this);
380 fire_script("OnTextChanged");
381 if (!checker.is_alive())
382 return;
383
384 if (cursor_changed) {
385 fire_script("OnCursorChanged");
386 if (!checker.is_alive())
387 return;
388 }
389 }
390 }
391}
392
393std::size_t edit_box::get_max_letters() const {
394 return max_letters_;
395}
396
397std::size_t edit_box::get_letter_count() const {
398 return unicode_text_.size();
399}
400
401void edit_box::set_blink_time(double blink_time) {
402 if (blink_time_ == blink_time)
403 return;
404
405 blink_time_ = blink_time;
408}
409
411 return blink_time_;
412}
413
414void edit_box::set_numeric_only(bool numeric_only) {
415 if (is_numeric_only_ == numeric_only)
416 return;
417
418 is_numeric_only_ = numeric_only;
419
420 if (is_numeric_only_ && check_text_()) {
424
425 alive_checker checker(*this);
426 fire_script("OnTextChanged");
427 if (!checker.is_alive())
428 return;
429
430 fire_script("OnCursorChanged");
431 if (!checker.is_alive())
432 return;
433 }
434}
435
436void edit_box::set_positive_only(bool positive_only) {
437 if (is_positive_only_ == positive_only)
438 return;
439
440 is_positive_only_ = positive_only;
441
446
447 alive_checker checker(*this);
448 fire_script("OnTextChanged");
449 if (!checker.is_alive())
450 return;
451
452 fire_script("OnCursorChanged");
453 if (!checker.is_alive())
454 return;
455 }
456}
457
458void edit_box::set_integer_only(bool integer_only) {
459 if (is_integer_only_ == integer_only)
460 return;
461
462 is_integer_only_ = integer_only;
463
468
469 alive_checker checker(*this);
470 fire_script("OnTextChanged");
471 if (!checker.is_alive())
472 return;
473
474 fire_script("OnCursorChanged");
475 if (!checker.is_alive())
476 return;
477 }
478}
479
481 return is_numeric_only_;
482}
483
485 return is_positive_only_;
486}
487
489 return is_integer_only_;
490}
491
493 if (is_password_mode_ == enable)
494 return;
495
496 is_password_mode_ = enable;
497
501}
502
506
507void edit_box::set_multi_line(bool multi_line) {
508 if (is_multi_line_ == multi_line)
509 return;
510
511 is_multi_line_ = multi_line;
512
513 if (font_string_) {
514 font_string_->set_word_wrap_enabled(is_multi_line_);
515 font_string_->set_word_ellipsis_enabled(is_multi_line_);
516 }
517
518 bool text_changed = check_text_();
519 if (text_changed) {
521 }
522
526
527 if (text_changed) {
528 alive_checker checker(*this);
529 fire_script("OnTextChanged");
530 if (!checker.is_alive())
531 return;
532
533 fire_script("OnCursorChanged");
534 if (!checker.is_alive())
535 return;
536 }
537}
538
540 return is_multi_line_;
541}
542
543void edit_box::set_max_history_lines(std::size_t max_history_lines) {
544 if (max_history_lines == 0) {
545 max_history_lines_ = std::numeric_limits<std::size_t>::max();
546 return;
547 }
548
549 if (max_history_lines_ != max_history_lines) {
550 max_history_lines_ = max_history_lines;
551
553 history_line_list_.erase(
554 history_line_list_.begin(),
556
557 current_history_line_ = std::numeric_limits<std::size_t>::max();
558 }
559 }
560}
561
563 return max_history_lines_;
564}
565
566void edit_box::add_history_line(const utils::ustring& history_line) {
567 if (is_multi_line_)
568 return;
569
570 history_line_list_.push_back(history_line);
571
573 history_line_list_.erase(
574 history_line_list_.begin(),
576 }
577
578 current_history_line_ = std::numeric_limits<std::size_t>::max();
579}
580
581const std::vector<utils::ustring>& edit_box::get_history_lines() const {
582 return history_line_list_;
583}
584
586 history_line_list_.clear();
587 current_history_line_ = std::numeric_limits<std::size_t>::max();
588}
589
590void edit_box::set_arrows_ignored(bool arrows_ignored) {
591 are_arrows_ignored_ = arrows_ignored;
592}
593
595 text_insets_ = insets;
596
597 if (font_string_) {
598 font_string_->clear_all_anchors();
601
605 }
606}
607
609 return text_insets_;
610}
611
612void edit_box::notify_focus(bool focus) {
613 if (has_focus() == focus)
614 return;
615
616 if (focus) {
617 if (!carret_)
619
620 if (carret_)
621 carret_->show();
622
624 } else {
625 if (carret_)
626 carret_->hide();
627
628 unlight_text();
629 }
630
631 alive_checker checker(*this);
632 base::notify_focus(focus);
633 if (!checker.is_alive())
634 return;
635
636 if (check_text_()) {
640
641 fire_script("OnTextChanged");
642 if (!checker.is_alive())
643 return;
644
645 fire_script("OnCursorChanged");
646 if (!checker.is_alive())
647 return;
648 }
649}
650
653
654 if (font_string_) {
655 font_string_->notify_scaling_factor_updated();
657 }
658}
659
660void edit_box::set_font_string(utils::observer_ptr<font_string> fstr) {
661 font_string_ = std::move(fstr);
662 if (!font_string_)
663 return;
664
665 font_string_->set_word_wrap_enabled(is_multi_line_);
666 font_string_->set_word_ellipsis_enabled(is_multi_line_);
667
668 font_string_->set_dimensions(vector2f(0, 0));
669 font_string_->clear_all_anchors();
670
673
674 font_string_->disable_formatting();
675
677}
678
679void edit_box::set_font(const std::string& font_name, float height) {
681
682 if (font_string_)
683 font_string_->set_font(font_name, height);
684
686}
687
689 if (font_string_)
690 return;
691
692 auto fnt = create_layered_region<font_string>(layer::artwork, "$parentFontString");
693 if (!fnt)
694 return;
695
696 fnt->set_manually_inherited(true);
697 fnt->notify_loaded();
698 set_font_string(fnt);
699}
700
702 if (highlight_ || is_virtual())
703 return;
704
705 auto highlight = create_layered_region<texture>(layer::highlight, "$parentHighlight");
706 if (!highlight)
707 return;
708
709 highlight->set_manually_inherited(true);
710
711 highlight->set_anchor(point::top, vector2f(0.0f, text_insets_.top));
712 highlight->set_anchor(point::bottom, vector2f(0.0f, -text_insets_.bottom));
713
714 highlight->set_solid_color(highlight_color_);
715
716 highlight->notify_loaded();
718}
719
721 if (!font_string_ || !font_string_->get_text_object() || is_virtual())
722 return;
723
724 if (!carret_) {
725 auto carret = create_layered_region<texture>(layer::highlight, "$parentCarret");
726 if (!carret)
727 return;
728
729 carret->set_manually_inherited(true);
730
731 carret->set_anchor(point::center, point::left, vector2f(text_insets_.left - 1.0f, 0.0f));
732
733 carret->notify_loaded();
734 carret_ = carret;
735 }
736
737 quad quad = font_string_->get_text_object()->create_letter_quad(U'|');
738 for (std::size_t i = 0; i < 4; ++i)
739 quad.v[i].col = font_string_->get_text_color();
740
741 carret_->set_quad(quad);
742
744}
745
747 bool modified = false;
748 if (unicode_text_.size() > max_letters_) {
750 modified = true;
751 }
752
753 if (is_numeric_only_) {
754 const auto& locale = get_manager().get_localizer().get_locale();
755 if (!utils::is_number(locale, unicode_text_)) {
756 unicode_text_.clear();
757 return true;
758 }
759
760 if (is_integer_only_ && !utils::is_integer(locale, unicode_text_)) {
761 unicode_text_.clear();
762 return true;
763 }
764
765 if (is_positive_only_ &&
766 utils::from_string<double>(locale, unicode_text_).value_or(-1.0) < 0.0) {
767 unicode_text_.clear();
768 return true;
769 }
770 }
771
772 return modified;
773}
774
776 if (!font_string_ || !font_string_->get_text_object())
777 return;
778
780 displayed_text_ = utils::ustring(unicode_text_.size(), U'*');
781 else
783
784 if (!is_multi_line_) {
785 text* text_object = font_string_->get_text_object();
786
787 if (!std::isinf(text_object->get_box_width())) {
789
790 while (!displayed_text_.empty() &&
791 text_object->get_string_width(displayed_text_) > text_object->get_box_width()) {
792 displayed_text_.erase(displayed_text_.size() - 1, 1);
793 }
794 }
795 } else {
796 // TODO: implement for multiline edit box
797 // https://github.com/cschreib/lxgui/issues/39
798 }
799}
800
810
812 if (!font_string_ || !font_string_->get_text_object() || !carret_)
813 return;
814
815 if (unicode_text_.empty()) {
816 point p;
817 float offset = 0.0f;
818
819 switch (font_string_->get_alignment_x()) {
821 p = point::left;
822 offset = text_insets_.left - 1;
823 break;
824 case alignment_x::center: p = point::center; break;
826 p = point::right;
827 offset = -text_insets_.right - 1;
828 break;
829 default: p = point::left; break;
830 }
831
832 carret_->set_anchor(point::center, p, vector2f(offset, 0.0f));
833 } else {
834 text* text = font_string_->get_text_object();
835 utils::ustring::iterator iter_display_carret;
836
837 if (!is_multi_line_) {
838 std::size_t global_pos = iter_carret_pos_ - unicode_text_.begin();
839
840 if (display_pos_ > global_pos) {
841 // The carret has been positioned before the start of the displayed string
842 float box_width = text->get_box_width();
843 float left_string_max_size = box_width * 0.25f;
844 float left_string_size = 0.0f;
845 utils::ustring left_string;
846
847 utils::ustring::iterator iter = iter_carret_pos_;
848 while ((iter != unicode_text_.begin()) &&
849 (left_string_size < left_string_max_size)) {
850 --iter;
851 left_string.insert(left_string.begin(), *iter);
852 left_string_size = text->get_string_width(left_string);
853 }
854
855 display_pos_ = iter - unicode_text_.begin();
858 }
859
860 std::size_t carret_pos = global_pos - display_pos_;
861 if (carret_pos > displayed_text_.size()) {
862 // The carret has been positioned after the end of the displayed string
863 float box_width = text->get_box_width();
864 float left_string_max_size = box_width * 0.75f;
865 float left_string_size = 0.0f;
866 utils::ustring left_string;
867
868 utils::ustring::iterator iter = iter_carret_pos_;
869 while ((iter_carret_pos_ != unicode_text_.begin()) &&
870 (left_string_size < left_string_max_size)) {
871 --iter;
872 left_string.insert(left_string.begin(), *iter);
873 left_string_size = text->get_string_width(left_string);
874 }
875
876 display_pos_ = iter - unicode_text_.begin();
879
880 carret_pos = global_pos - display_pos_;
881 }
882
883 iter_display_carret = displayed_text_.begin() + carret_pos;
884 } else {
885 iter_display_carret =
887 }
888
889 float y_offset = static_cast<float>((text->get_line_count() - 1)) *
891
892 std::size_t index = iter_display_carret - displayed_text_.begin();
893
894 float x_offset = text_insets_.left;
895 if (index < displayed_text_.size()) {
896 if (index < text->get_letter_count())
897 x_offset += text->get_letter_quad(index)[0].pos.x;
898 } else {
899 index = displayed_text_.size() - 1;
900 if (index < text->get_letter_count())
901 x_offset += text->get_letter_quad(index)[2].pos.x;
902 }
903
904 carret_->set_anchor(point::center, point::left, vector2f(x_offset, y_offset));
905 }
906
908 if (has_focus())
909 carret_->show();
910 else
911 carret_->hide();
912}
913
914bool edit_box::add_char_(char32_t c) {
916 remove_char_();
917
919 return false;
920
922
926
927 if (carret_)
928 carret_->show();
929
931
932 return true;
933}
934
936 if (is_text_selected_) {
938 std::size_t left = std::min(selection_start_pos_, selection_end_pos_);
939 std::size_t right = std::max(selection_start_pos_, selection_end_pos_);
940
941 unicode_text_.erase(left, right - left);
942
944 }
945
946 unlight_text();
947 } else {
948 if (iter_carret_pos_ == unicode_text_.end())
949 return false;
950
952 }
953
957
958 if (carret_)
959 carret_->show();
960
962
963 return true;
964}
965
966std::size_t edit_box::get_letter_id_at_(const vector2f& position) const {
967 if (!font_string_ || !font_string_->get_text_object())
968 return std::numeric_limits<std::size_t>::max();
969
970 if (displayed_text_.empty())
971 return display_pos_;
972
973 const text* text = font_string_->get_text_object();
974
975 float local_x = position.x - borders_.left - text_insets_.left;
976 // float local_y = position.y - borders_.top - text_insets_.top;
977
978 if (!is_multi_line_) {
979 if (position.x < borders_.left + text_insets_.left)
980 return display_pos_;
981 else if (position.x > borders_.right - text_insets_.right)
982 return displayed_text_.size() + display_pos_;
983
984 std::size_t num_letters =
985 std::min<std::size_t>(text->get_letter_count(), displayed_text_.size());
986
987 for (std::size_t index = 0u; index < num_letters; ++index) {
988 const auto& quad = text->get_letter_quad(index);
989 if (local_x < 0.5f * (quad[0].pos.x + quad[2].pos.x))
990 return index + display_pos_;
991 }
992
993 return displayed_text_.size() + display_pos_;
994 } else {
995 // TODO: Implement for multi line edit_box
996 // https://github.com/cschreib/lxgui/issues/39
997 return display_pos_;
998 }
999}
1000
1002 std::size_t pos = get_letter_id_at_(position);
1003 if (pos != std::numeric_limits<std::size_t>::max()) {
1004 iter_carret_pos_ = unicode_text_.begin() + pos;
1006 return true;
1007 } else
1008 return false;
1009}
1010
1012 if (forward) {
1013 if (iter_carret_pos_ != unicode_text_.end()) {
1017
1018 if (carret_)
1019 carret_->show();
1020
1022
1023 return true;
1024 } else
1025 return false;
1026 } else {
1027 if (iter_carret_pos_ != unicode_text_.begin()) {
1031
1032 if (carret_)
1033 carret_->show();
1034
1036
1037 return true;
1038 } else
1039 return false;
1040 }
1041}
1042
1044 if (is_multi_line_) {
1045 // TODO: Implement for multi line edit_box
1046 // https://github.com/cschreib/lxgui/issues/39
1047 return false;
1048 } else {
1049 utils::ustring::iterator iter_old = iter_carret_pos_;
1050
1051 if (down)
1053 else
1055
1056 if (iter_old != iter_carret_pos_) {
1059
1060 if (carret_)
1061 carret_->show();
1062
1064
1065 return true;
1066 }
1067
1068 return false;
1069 }
1070}
1071
1072void edit_box::process_key_(key key_id, bool shift_is_pressed, bool ctrl_is_pressed) {
1073 alive_checker checker(*this);
1074
1075 if (key_id == key::k_return || key_id == key::k_numpadenter) {
1076 if (is_multi_line_) {
1077 event_data key_event;
1078 key_event.add(std::string("\n"));
1079 fire_script("OnChar", key_event);
1080 if (!checker.is_alive())
1081 return;
1082 }
1083 } else if (key_id == key::k_end) {
1084 std::size_t previous_carret_pos = get_cursor_position();
1086
1087 if (shift_is_pressed) {
1090 else
1091 highlight_text(previous_carret_pos, iter_carret_pos_ - unicode_text_.begin());
1092 } else
1093 unlight_text();
1094
1095 return;
1096 } else if (key_id == key::k_home) {
1097 std::size_t previous_carret_pos = get_cursor_position();
1099
1100 if (shift_is_pressed) {
1103 else
1104 highlight_text(previous_carret_pos, iter_carret_pos_ - unicode_text_.begin());
1105 } else
1106 unlight_text();
1107
1108 return;
1109 } else if (key_id == key::k_back || key_id == key::k_delete) {
1110 if (is_text_selected_ || key_id == key::k_delete || move_carret_horizontally_(false)) {
1111 if (remove_char_()) {
1112 fire_script("OnTextChanged");
1113 if (!checker.is_alive())
1114 return;
1115
1116 fire_script("OnCursorChanged");
1117 if (!checker.is_alive())
1118 return;
1119 }
1120 }
1121 } else if (
1122 key_id == key::k_left || key_id == key::k_right ||
1123 (is_multi_line_ && (key_id == key::k_up || key_id == key::k_down))) {
1124 if (!are_arrows_ignored_) {
1125 const std::size_t previous_carret_pos = iter_carret_pos_ - unicode_text_.begin();
1126
1127 if (key_id == key::k_left || key_id == key::k_right) {
1128 if (is_text_selected_ && !shift_is_pressed) {
1129 std::size_t offset = 0;
1130 if (key_id == key::k_left)
1131 offset = std::min(selection_start_pos_, selection_end_pos_);
1132 else
1133 offset = std::max(selection_start_pos_, selection_end_pos_);
1134
1135 iter_carret_pos_ = unicode_text_.begin() + offset;
1137
1138 fire_script("OnCursorChanged");
1139 if (!checker.is_alive())
1140 return;
1141 } else {
1142 if (move_carret_horizontally_(key_id == key::k_right)) {
1143 fire_script("OnCursorChanged");
1144 if (!checker.is_alive())
1145 return;
1146 }
1147 }
1148 } else {
1149 if (is_multi_line_) {
1150 if (move_carret_vertically_(key_id == key::k_down)) {
1151 fire_script("OnCursorChanged");
1152 if (!checker.is_alive())
1153 return;
1154 }
1155 }
1156 }
1157
1158 if (shift_is_pressed) {
1159 if (is_text_selected_) {
1160 std::size_t new_end_pos = iter_carret_pos_ - unicode_text_.begin();
1161 if (new_end_pos != selection_start_pos_)
1163 else
1164 unlight_text();
1165 } else
1166 highlight_text(previous_carret_pos, iter_carret_pos_ - unicode_text_.begin());
1167 } else
1168 unlight_text();
1169 }
1170 } else if (
1171 !is_multi_line_ && (key_id == key::k_up || key_id == key::k_down) &&
1172 !history_line_list_.empty()) {
1173 if (key_id == key::k_up) {
1174 if (current_history_line_ != 0u) {
1175 if (current_history_line_ == std::numeric_limits<std::size_t>::max())
1177 else
1179
1181 if (!checker.is_alive())
1182 return;
1183 }
1184 } else {
1185 if (current_history_line_ != std::numeric_limits<std::size_t>::max()) {
1186 if (current_history_line_ + 1 == history_line_list_.size()) {
1187 current_history_line_ = std::numeric_limits<std::size_t>::max();
1188 set_text(U"");
1189 if (!checker.is_alive())
1190 return;
1191 } else {
1194 if (!checker.is_alive())
1195 return;
1196 }
1197 }
1198 }
1199 } else if (key_id == key::k_c && ctrl_is_pressed) {
1201 std::size_t min_pos = std::min(selection_start_pos_, selection_end_pos_);
1202 std::size_t max_pos = std::max(selection_start_pos_, selection_end_pos_);
1203 utils::ustring selected = unicode_text_.substr(min_pos, max_pos - min_pos);
1205 }
1206 } else if (key_id == key::k_v && ctrl_is_pressed) {
1207 bool text_added = false;
1208 for (char32_t c : get_manager().get_window().get_clipboard_content()) {
1209 if (!add_char_(c))
1210 break;
1211
1212 text_added = true;
1213 }
1214
1215 if (text_added) {
1216 fire_script("OnTextChanged");
1217 if (!checker.is_alive())
1218 return;
1219
1220 fire_script("OnCursorChanged");
1221 if (!checker.is_alive())
1222 return;
1223 }
1224 }
1225}
1226
1227const std::vector<std::string>& edit_box::get_type_list_() const {
1228 return get_type_list_impl_<edit_box>();
1229}
1230
1231} // namespace lxgui::gui
Utility class for safe checking of region validity.
bool is_alive() const
Check if the wrapped region is still alive.
Holds a single color (float RGBA, 128 bits)
Definition gui_color.hpp:13
A frame with an editable text box.
void set_integer_only(bool integer_only)
Makes this edit_box allow integer numbers only.
bool add_char_(char32_t c)
const bounds2f & get_text_insets() const
Returns the text insets.
std::size_t get_max_letters() const
Returns the maximum number of letters to allow in this edit_box.
void notify_scaling_factor_updated() override
Tells this region that the global interface scaling factor has changed.
void set_max_letters(std::size_t max_letters)
Sets the maximum number of letters to allow in this edit_box.
void set_text_insets(const bounds2f &insets)
Sets the insets used to render the content text.
const std::vector< std::string > & get_type_list_() const override
void add_history_line(const utils::ustring &history_line)
Adds a new history line to the history line list.
utils::ustring::iterator iter_carret_pos_
void clear_history_lines()
Clears the history line list.
void process_key_(input::key key_id, bool shift_is_pressed, bool ctrl_is_pressed)
void set_font_string(utils::observer_ptr< font_string > fstr)
Sets the font_string to use to render the content.
bool move_carret_vertically_(bool down=true)
double get_blink_time() const
Returns the carret's blink time.
void set_highlight_color(const color &c)
Sets the color of the highlight quad.
bool is_positive_only() const
Checks if this edit_box allows positive numbers only.
utils::periodic_timer carret_timer_
std::size_t selection_start_pos_
void highlight_text(std::size_t start=0u, std::size_t end=std::numeric_limits< std::size_t >::max(), bool force_update=false)
Selects a portion of the content.
void set_cursor_position(std::size_t pos)
Moves the cursor to a chosen position.
void set_positive_only(bool positive_only)
Makes this edit_box allow positive numbers only.
std::size_t get_letter_count() const
Returns the number of letters in the content.
const utils::ustring & get_text() const
Returns the content of this edit_box.
std::size_t max_history_lines_
void set_font(const std::string &font_name, float height)
Sets the font (file and size) to render the content.
void set_text(const utils::ustring &content)
Sets the content of this edit_box.
bool move_carret_horizontally_(bool forward=true)
edit_box(utils::control_block &block, manager &mgr, const frame_core_attributes &attr)
Constructor.
const std::vector< utils::ustring > & get_history_lines() const
Returns the history line list.
std::size_t get_max_history_lines() const
Returns the maximum number of history lines this edit_box can keep.
bool is_numeric_only() const
Checks if this edit_box allows numeric characters only.
std::size_t get_cursor_position() const
Returns the current position of the cursor.
utils::observer_ptr< font_string > font_string_
void insert_after_cursor(const utils::ustring &content)
Inserts some text after the cursor.
bool is_password_mode_enabled() const
Checks if this edit_box is in password mode.
bool is_integer_only() const
Checks if this edit_box allows integer numbers only.
bool move_carret_at_(const vector2f &position)
void copy_from(const region &obj) override
Copies a region's parameters into this edit_box (inheritance).
void set_password_mode_enabled(bool enable)
Enables or disables password mode.
std::size_t selection_end_pos_
const utils::observer_ptr< font_string > & get_font_string()
Returns the font_string used to render the content.
void notify_focus(bool focus) override
Notifies this frame that it has received or lost focus.
void set_numeric_only(bool numeric_only)
Makes this edit_box allow numeric characters only.
std::size_t get_letter_id_at_(const vector2f &position) const
std::vector< utils::ustring > history_line_list_
void set_arrows_ignored(bool arrows_ignored)
Sets whether keyboard arrows move the carret or not.
void unlight_text()
Deselects the selected text, if any.
void set_blink_time(double blink_time)
Sets the carret's blink time.
utils::observer_ptr< texture > highlight_
void set_multi_line(bool multi_line)
Allows this edit_box to have several lines in it.
bool can_use_script(const std::string &script_name) const override
Returns 'true' if this edit_box can use a script.
std::size_t current_history_line_
utils::ustring displayed_text_
void update_(float delta) override
void fire_script(const std::string &script_name, const event_data &data=event_data{}) override
Calls a script.
utils::observer_ptr< texture > carret_
bool is_multi_line() const
Checks if this edit_box can have several lines in it.
void set_max_history_lines(std::size_t max_history_lines)
Sets the maximum number of history lines this edit_box can keep.
utils::ustring unicode_text_
Stores a variable number of arguments for an event.
const utils::variant & get(std::size_t index) const
Returns a parameter of this event.
void add(T &&value)
Adds a parameter to this event.
A layered_region that can draw text on the screen.
A region that can contain other regions and react to events.
void enable_keyboard()
Marks this frame as able to receive any keyboard input.
void copy_from(const region &obj) override
Copies a region's parameters into this frame (inheritance).
void enable_mouse()
Marks this frame as able to receive mouse input (click & move).
virtual void fire_script(const std::string &script_name, const event_data &data=event_data{})
Calls a script.
virtual void update_(float delta)
virtual bool can_use_script(const std::string &script_name) const
Returns 'true' if this frame can use a script.
bool has_focus() const
Check if this frame currently has focus.
virtual void notify_focus(bool focus)
Notifies this frame that it has received or lost focus.
void set_focus(bool focus)
Asks for focus for this frame.
void notify_scaling_factor_updated() override
Tells this region that the global interface scaling factor has changed.
void enable_drag(const std::string &button_name)
Tells this frame to react to mouse drag.
const std::locale & get_locale() const
Returns the current locale (used to format numbers).
Manages the user interface.
localizer & get_localizer()
Returns the object used for localizing strings.
const input::window & get_window() const
Returns the window in which this gui is being displayed.
The base class of all elements in the GUI.
manager & get_manager()
Returns this region's manager.
void initialize_(T &self, const region_core_attributes &attr)
Set up function to call in all derived class constructors.
bool is_virtual() const
Checks if this region is virtual.
Used to draw some text on the screen.
Definition gui_text.hpp:29
float get_line_spacing() const
Returns this text's line spacing.
Definition gui_text.cpp:484
float get_box_width() const
Returns the width of the text box.
Definition gui_text.cpp:384
float get_string_width(const std::string &content) const
Returns the length of a provided string.
Definition gui_text.cpp:411
const std::array< vertex, 4 > & get_letter_quad(std::size_t index) const
Returns the quad for the letter at the provided index (position, texture coords, color).
std::size_t get_line_count() const
Returns the number of text lines.
Definition gui_text.cpp:406
float get_line_height() const
Returns the height of one line (constant).
Definition gui_text.cpp:287
void set_text(const utils::ustring &content)
Sets the text to render (unicode character set).
Definition gui_text.cpp:306
std::size_t get_letter_count() const
Returns the number of letters currently displayed.
utils::ustring get_clipboard_content()
Retrieve a copy of the clipboard content.
void set_clipboard_content(const utils::ustring &content)
Replace the content of the clipboard.
@ first_tick
The timer will start when you first call ticks()
void zero()
Resets the timer but doesn't pause it.
bool ticks()
Checks if the timer's period has been reached.
void update(double delta)
Updates this timer (adds time).
vector2< float > vector2f
Holds 2D coordinates (as floats)
vector2< T > bottom_right() const noexcept
vector2< T > top_left() const noexcept
Struct holding all the core information about a frame necessary for its creation.
Simple structure holding four vertices and a material.
Definition gui_quad.hpp:18
std::array< vertex, 4 > v
Definition gui_quad.hpp:19
Struct holding all the core information about a region necessary for its creation.
std::vector< utils::observer_ptr< const region > > inheritance