@@ -2670,6 +2670,11 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
26702670 deselect ();
26712671 }
26722672 }
2673+
2674+ if (!selection.drag_attempt ) {
2675+ is_selecting_text = true ;
2676+ click_select_held->start ();
2677+ }
26732678 }
26742679 }
26752680 } else if (b->is_pressed () && b->is_double_click () && selection.enabled ) {
@@ -2751,6 +2756,9 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
27512756 }
27522757 }
27532758 }
2759+
2760+ is_selecting_text = false ;
2761+ click_select_held->stop ();
27542762 }
27552763 }
27562764
@@ -2939,95 +2947,119 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
29392947
29402948 Ref<InputEventMouseMotion> m = p_event;
29412949 if (m.is_valid ()) {
2942- ItemFrame *c_frame = nullptr ;
2943- int c_line = 0 ;
2944- Item *c_item = nullptr ;
2945- int c_index = 0 ;
2946- bool outside;
2947-
2948- _find_click (main, m->get_position (), &c_frame, &c_line, &c_item, &c_index, &outside, false );
2949- if (selection.click_item && c_item) {
2950- selection.from_frame = selection.click_frame ;
2951- selection.from_line = selection.click_line ;
2952- selection.from_item = selection.click_item ;
2953- selection.from_char = selection.click_char ;
2954-
2955- selection.to_frame = c_frame;
2956- selection.to_line = c_line;
2957- selection.to_item = c_item;
2958- selection.to_char = c_index;
2959-
2960- bool swap = false ;
2961- if (selection.click_frame && c_frame) {
2962- const Line &l1 = c_frame->lines [c_line];
2963- const Line &l2 = selection.click_frame ->lines [selection.click_line ];
2964- if (l1.char_offset + c_index < l2.char_offset + selection.click_char ) {
2965- swap = true ;
2966- } else if (l1.char_offset + c_index == l2.char_offset + selection.click_char && !selection.double_click ) {
2967- deselect ();
2968- return ;
2969- }
2970- }
2950+ local_mouse_pos = get_local_mouse_position ();
2951+ last_clamped_mouse_pos = local_mouse_pos.clamp (Vector2 (), get_size ());
2952+ }
2953+ }
29712954
2972- if (swap) {
2973- SWAP (selection.from_frame , selection.to_frame );
2974- SWAP (selection.from_line , selection.to_line );
2975- SWAP (selection.from_item , selection.to_item );
2976- SWAP (selection.from_char , selection.to_char );
2977- }
2955+ void RichTextLabel::_update_selection () {
2956+ ItemFrame *c_frame = nullptr ;
2957+ int c_line = 0 ;
2958+ Item *c_item = nullptr ;
2959+ int c_index = 0 ;
2960+ bool outside;
2961+
2962+ // Handle auto scrolling.
2963+ const Size2 size = get_size ();
2964+ if (!(local_mouse_pos.x >= 0.0 && local_mouse_pos.y >= 0.0 &&
2965+ local_mouse_pos.x < size.x && local_mouse_pos.y < size.y )) {
2966+ real_t scroll_delta = 0.0 ;
2967+ if (local_mouse_pos.y < 0 ) {
2968+ scroll_delta = -auto_scroll_speed * (1 - (local_mouse_pos.y / 15.0 ));
2969+ } else if (local_mouse_pos.y > size.y ) {
2970+ scroll_delta = auto_scroll_speed * (1 + (local_mouse_pos.y - size.y ) / 15.0 );
2971+ }
29782972
2979- if (selection.double_click && c_frame) {
2980- // Expand the selection to word edges.
2973+ if (scroll_delta != 0.0 ) {
2974+ vscroll->scroll (scroll_delta);
2975+ queue_redraw ();
2976+ }
2977+ }
29812978
2982- Line *l = &selection.from_frame ->lines [selection.from_line ];
2983- MutexLock lock (l->text_buf ->get_mutex ());
2984- PackedInt32Array words = TS->shaped_text_get_word_breaks (l->text_buf ->get_rid ());
2985- for (int i = 0 ; i < words.size (); i = i + 2 ) {
2986- if (selection.from_char > words[i] && selection.from_char < words[i + 1 ]) {
2987- selection.from_char = words[i];
2988- break ;
2989- }
2990- }
2991- l = &selection.to_frame ->lines [selection.to_line ];
2992- lock = MutexLock (l->text_buf ->get_mutex ());
2993- words = TS->shaped_text_get_word_breaks (l->text_buf ->get_rid ());
2994- for (int i = 0 ; i < words.size (); i = i + 2 ) {
2995- if (selection.to_char > words[i] && selection.to_char < words[i + 1 ]) {
2996- selection.to_char = words[i + 1 ];
2997- break ;
2998- }
2999- }
2979+ // Update selection area.
2980+ _find_click (main, last_clamped_mouse_pos, &c_frame, &c_line, &c_item, &c_index, &outside, false );
2981+ if (selection.click_item && c_item) {
2982+ selection.from_frame = selection.click_frame ;
2983+ selection.from_line = selection.click_line ;
2984+ selection.from_item = selection.click_item ;
2985+ selection.from_char = selection.click_char ;
2986+
2987+ selection.to_frame = c_frame;
2988+ selection.to_line = c_line;
2989+ selection.to_item = c_item;
2990+ selection.to_char = c_index;
2991+
2992+ bool swap = false ;
2993+ if (selection.click_frame && c_frame) {
2994+ const Line &l1 = c_frame->lines [c_line];
2995+ const Line &l2 = selection.click_frame ->lines [selection.click_line ];
2996+ if (l1.char_offset + c_index < l2.char_offset + selection.click_char ) {
2997+ swap = true ;
2998+ } else if (l1.char_offset + c_index == l2.char_offset + selection.click_char && !selection.double_click ) {
2999+ deselect ();
3000+ return ;
30003001 }
3002+ }
30013003
3002- selection.active = true ;
3003- queue_accessibility_update ();
3004- queue_redraw ();
3004+ if (swap) {
3005+ SWAP (selection.from_frame , selection.to_frame );
3006+ SWAP (selection.from_line , selection.to_line );
3007+ SWAP (selection.from_item , selection.to_item );
3008+ SWAP (selection.from_char , selection.to_char );
30053009 }
30063010
3007- _find_click (main, m->get_position (), nullptr , nullptr , &c_item, nullptr , &outside, true );
3008- Variant meta;
3009- ItemMeta *item_meta;
3010- ItemMeta *prev_meta = meta_hovering;
3011- if (c_item && !outside && _find_meta (c_item, &meta, &item_meta)) {
3012- if (meta_hovering != item_meta) {
3013- if (meta_hovering) {
3014- emit_signal (SNAME (" meta_hover_ended" ), current_meta);
3011+ if (selection.double_click && c_frame) {
3012+ // Expand the selection to word edges.
3013+
3014+ Line *l = &selection.from_frame ->lines [selection.from_line ];
3015+ MutexLock lock (l->text_buf ->get_mutex ());
3016+ PackedInt32Array words = TS->shaped_text_get_word_breaks (l->text_buf ->get_rid ());
3017+ for (int i = 0 ; i < words.size (); i = i + 2 ) {
3018+ if (selection.from_char > words[i] && selection.from_char < words[i + 1 ]) {
3019+ selection.from_char = words[i];
3020+ break ;
30153021 }
3016- meta_hovering = item_meta;
3017- current_meta = meta;
3018- emit_signal (SNAME (" meta_hover_started" ), meta);
3019- if ((item_meta && item_meta->underline == META_UNDERLINE_ON_HOVER) || (prev_meta && prev_meta->underline == META_UNDERLINE_ON_HOVER)) {
3020- queue_redraw ();
3022+ }
3023+ l = &selection.to_frame ->lines [selection.to_line ];
3024+ lock = MutexLock (l->text_buf ->get_mutex ());
3025+ words = TS->shaped_text_get_word_breaks (l->text_buf ->get_rid ());
3026+ for (int i = 0 ; i < words.size (); i = i + 2 ) {
3027+ if (selection.to_char > words[i] && selection.to_char < words[i + 1 ]) {
3028+ selection.to_char = words[i + 1 ];
3029+ break ;
30213030 }
30223031 }
3023- } else if (meta_hovering) {
3024- meta_hovering = nullptr ;
3025- emit_signal (SNAME (" meta_hover_ended" ), current_meta);
3026- current_meta = false ;
3027- if (prev_meta->underline == META_UNDERLINE_ON_HOVER) {
3032+ }
3033+
3034+ selection.active = true ;
3035+ queue_accessibility_update ();
3036+ queue_redraw ();
3037+ }
3038+
3039+ // Update meta hovering.
3040+ _find_click (main, local_mouse_pos, nullptr , nullptr , &c_item, nullptr , &outside, true );
3041+ Variant meta;
3042+ ItemMeta *item_meta;
3043+ ItemMeta *prev_meta = meta_hovering;
3044+ if (c_item && !outside && _find_meta (c_item, &meta, &item_meta)) {
3045+ if (meta_hovering != item_meta) {
3046+ if (meta_hovering) {
3047+ emit_signal (SNAME (" meta_hover_ended" ), current_meta);
3048+ }
3049+ meta_hovering = item_meta;
3050+ current_meta = meta;
3051+ emit_signal (SNAME (" meta_hover_started" ), meta);
3052+ if ((item_meta && item_meta->underline == META_UNDERLINE_ON_HOVER) || (prev_meta && prev_meta->underline == META_UNDERLINE_ON_HOVER)) {
30283053 queue_redraw ();
30293054 }
30303055 }
3056+ } else if (meta_hovering) {
3057+ meta_hovering = nullptr ;
3058+ emit_signal (SNAME (" meta_hover_ended" ), current_meta);
3059+ current_meta = false ;
3060+ if (prev_meta->underline == META_UNDERLINE_ON_HOVER) {
3061+ queue_redraw ();
3062+ }
30313063 }
30323064}
30333065
@@ -8076,6 +8108,11 @@ RichTextLabel::RichTextLabel(const String &p_text) {
80768108 parsing_bbcode.store (false );
80778109
80788110 set_clip_contents (true );
8111+
8112+ click_select_held = memnew (Timer);
8113+ add_child (click_select_held, false , INTERNAL_MODE_FRONT);
8114+ click_select_held->set_wait_time (0.05 );
8115+ click_select_held->connect (" timeout" , callable_mp (this , &RichTextLabel::_update_selection));
80798116}
80808117
80818118RichTextLabel::~RichTextLabel () {
0 commit comments