@@ -2596,6 +2596,11 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
25962596 deselect ();
25972597 }
25982598 }
2599+
2600+ if (!selection.drag_attempt ) {
2601+ is_selecting_text = true ;
2602+ click_select_held->start ();
2603+ }
25992604 }
26002605 }
26012606 } else if (b->is_pressed () && b->is_double_click () && selection.enabled ) {
@@ -2677,6 +2682,9 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
26772682 }
26782683 }
26792684 }
2685+
2686+ is_selecting_text = false ;
2687+ click_select_held->stop ();
26802688 }
26812689 }
26822690
@@ -2865,95 +2873,119 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
28652873
28662874 Ref<InputEventMouseMotion> m = p_event;
28672875 if (m.is_valid ()) {
2868- ItemFrame *c_frame = nullptr ;
2869- int c_line = 0 ;
2870- Item *c_item = nullptr ;
2871- int c_index = 0 ;
2872- bool outside;
2873-
2874- _find_click (main, m->get_position (), &c_frame, &c_line, &c_item, &c_index, &outside, false );
2875- if (selection.click_item && c_item) {
2876- selection.from_frame = selection.click_frame ;
2877- selection.from_line = selection.click_line ;
2878- selection.from_item = selection.click_item ;
2879- selection.from_char = selection.click_char ;
2880-
2881- selection.to_frame = c_frame;
2882- selection.to_line = c_line;
2883- selection.to_item = c_item;
2884- selection.to_char = c_index;
2885-
2886- bool swap = false ;
2887- if (selection.click_frame && c_frame) {
2888- const Line &l1 = c_frame->lines [c_line];
2889- const Line &l2 = selection.click_frame ->lines [selection.click_line ];
2890- if (l1.char_offset + c_index < l2.char_offset + selection.click_char ) {
2891- swap = true ;
2892- } else if (l1.char_offset + c_index == l2.char_offset + selection.click_char && !selection.double_click ) {
2893- deselect ();
2894- return ;
2895- }
2896- }
2876+ local_mouse_pos = get_local_mouse_position ();
2877+ last_clamped_mouse_pos = local_mouse_pos.clamp (Vector2 (), get_size ());
2878+ }
2879+ }
28972880
2898- if (swap) {
2899- SWAP (selection.from_frame , selection.to_frame );
2900- SWAP (selection.from_line , selection.to_line );
2901- SWAP (selection.from_item , selection.to_item );
2902- SWAP (selection.from_char , selection.to_char );
2903- }
2881+ void RichTextLabel::_update_selection () {
2882+ ItemFrame *c_frame = nullptr ;
2883+ int c_line = 0 ;
2884+ Item *c_item = nullptr ;
2885+ int c_index = 0 ;
2886+ bool outside;
2887+
2888+ // Handle auto scrolling.
2889+ const Size2 size = get_size ();
2890+ if (!(local_mouse_pos.x >= 0.0 && local_mouse_pos.y >= 0.0 &&
2891+ local_mouse_pos.x < size.x && local_mouse_pos.y < size.y )) {
2892+ real_t scroll_delta = 0.0 ;
2893+ if (local_mouse_pos.y < 0 ) {
2894+ scroll_delta = -auto_scroll_speed * (1 - (local_mouse_pos.y / 15.0 ));
2895+ } else if (local_mouse_pos.y > size.y ) {
2896+ scroll_delta = auto_scroll_speed * (1 + (local_mouse_pos.y - size.y ) / 15.0 );
2897+ }
29042898
2905- if (selection.double_click && c_frame) {
2906- // Expand the selection to word edges.
2899+ if (scroll_delta != 0.0 ) {
2900+ vscroll->scroll (scroll_delta);
2901+ queue_redraw ();
2902+ }
2903+ }
29072904
2908- Line *l = &selection.from_frame ->lines [selection.from_line ];
2909- MutexLock lock (l->text_buf ->get_mutex ());
2910- PackedInt32Array words = TS->shaped_text_get_word_breaks (l->text_buf ->get_rid ());
2911- for (int i = 0 ; i < words.size (); i = i + 2 ) {
2912- if (selection.from_char > words[i] && selection.from_char < words[i + 1 ]) {
2913- selection.from_char = words[i];
2914- break ;
2915- }
2916- }
2917- l = &selection.to_frame ->lines [selection.to_line ];
2918- lock = MutexLock (l->text_buf ->get_mutex ());
2919- words = TS->shaped_text_get_word_breaks (l->text_buf ->get_rid ());
2920- for (int i = 0 ; i < words.size (); i = i + 2 ) {
2921- if (selection.to_char > words[i] && selection.to_char < words[i + 1 ]) {
2922- selection.to_char = words[i + 1 ];
2923- break ;
2924- }
2925- }
2905+ // Update selection area.
2906+ _find_click (main, last_clamped_mouse_pos, &c_frame, &c_line, &c_item, &c_index, &outside, false );
2907+ if (selection.click_item && c_item) {
2908+ selection.from_frame = selection.click_frame ;
2909+ selection.from_line = selection.click_line ;
2910+ selection.from_item = selection.click_item ;
2911+ selection.from_char = selection.click_char ;
2912+
2913+ selection.to_frame = c_frame;
2914+ selection.to_line = c_line;
2915+ selection.to_item = c_item;
2916+ selection.to_char = c_index;
2917+
2918+ bool swap = false ;
2919+ if (selection.click_frame && c_frame) {
2920+ const Line &l1 = c_frame->lines [c_line];
2921+ const Line &l2 = selection.click_frame ->lines [selection.click_line ];
2922+ if (l1.char_offset + c_index < l2.char_offset + selection.click_char ) {
2923+ swap = true ;
2924+ } else if (l1.char_offset + c_index == l2.char_offset + selection.click_char && !selection.double_click ) {
2925+ deselect ();
2926+ return ;
29262927 }
2928+ }
29272929
2928- selection.active = true ;
2929- queue_accessibility_update ();
2930- queue_redraw ();
2930+ if (swap) {
2931+ SWAP (selection.from_frame , selection.to_frame );
2932+ SWAP (selection.from_line , selection.to_line );
2933+ SWAP (selection.from_item , selection.to_item );
2934+ SWAP (selection.from_char , selection.to_char );
29312935 }
29322936
2933- _find_click (main, m->get_position (), nullptr , nullptr , &c_item, nullptr , &outside, true );
2934- Variant meta;
2935- ItemMeta *item_meta;
2936- ItemMeta *prev_meta = meta_hovering;
2937- if (c_item && !outside && _find_meta (c_item, &meta, &item_meta)) {
2938- if (meta_hovering != item_meta) {
2939- if (meta_hovering) {
2940- emit_signal (SNAME (" meta_hover_ended" ), current_meta);
2937+ if (selection.double_click && c_frame) {
2938+ // Expand the selection to word edges.
2939+
2940+ Line *l = &selection.from_frame ->lines [selection.from_line ];
2941+ MutexLock lock (l->text_buf ->get_mutex ());
2942+ PackedInt32Array words = TS->shaped_text_get_word_breaks (l->text_buf ->get_rid ());
2943+ for (int i = 0 ; i < words.size (); i = i + 2 ) {
2944+ if (selection.from_char > words[i] && selection.from_char < words[i + 1 ]) {
2945+ selection.from_char = words[i];
2946+ break ;
29412947 }
2942- meta_hovering = item_meta;
2943- current_meta = meta;
2944- emit_signal (SNAME (" meta_hover_started" ), meta);
2945- if ((item_meta && item_meta->underline == META_UNDERLINE_ON_HOVER) || (prev_meta && prev_meta->underline == META_UNDERLINE_ON_HOVER)) {
2946- queue_redraw ();
2948+ }
2949+ l = &selection.to_frame ->lines [selection.to_line ];
2950+ lock = MutexLock (l->text_buf ->get_mutex ());
2951+ words = TS->shaped_text_get_word_breaks (l->text_buf ->get_rid ());
2952+ for (int i = 0 ; i < words.size (); i = i + 2 ) {
2953+ if (selection.to_char > words[i] && selection.to_char < words[i + 1 ]) {
2954+ selection.to_char = words[i + 1 ];
2955+ break ;
29472956 }
29482957 }
2949- } else if (meta_hovering) {
2950- meta_hovering = nullptr ;
2951- emit_signal (SNAME (" meta_hover_ended" ), current_meta);
2952- current_meta = false ;
2953- if (prev_meta->underline == META_UNDERLINE_ON_HOVER) {
2958+ }
2959+
2960+ selection.active = true ;
2961+ queue_accessibility_update ();
2962+ queue_redraw ();
2963+ }
2964+
2965+ // Update meta hovering.
2966+ _find_click (main, local_mouse_pos, nullptr , nullptr , &c_item, nullptr , &outside, true );
2967+ Variant meta;
2968+ ItemMeta *item_meta;
2969+ ItemMeta *prev_meta = meta_hovering;
2970+ if (c_item && !outside && _find_meta (c_item, &meta, &item_meta)) {
2971+ if (meta_hovering != item_meta) {
2972+ if (meta_hovering) {
2973+ emit_signal (SNAME (" meta_hover_ended" ), current_meta);
2974+ }
2975+ meta_hovering = item_meta;
2976+ current_meta = meta;
2977+ emit_signal (SNAME (" meta_hover_started" ), meta);
2978+ if ((item_meta && item_meta->underline == META_UNDERLINE_ON_HOVER) || (prev_meta && prev_meta->underline == META_UNDERLINE_ON_HOVER)) {
29542979 queue_redraw ();
29552980 }
29562981 }
2982+ } else if (meta_hovering) {
2983+ meta_hovering = nullptr ;
2984+ emit_signal (SNAME (" meta_hover_ended" ), current_meta);
2985+ current_meta = false ;
2986+ if (prev_meta->underline == META_UNDERLINE_ON_HOVER) {
2987+ queue_redraw ();
2988+ }
29572989 }
29582990}
29592991
@@ -7783,6 +7815,11 @@ RichTextLabel::RichTextLabel(const String &p_text) {
77837815 parsing_bbcode.store (false );
77847816
77857817 set_clip_contents (true );
7818+
7819+ click_select_held = memnew (Timer);
7820+ add_child (click_select_held, false , INTERNAL_MODE_FRONT);
7821+ click_select_held->set_wait_time (0.05 );
7822+ click_select_held->connect (" timeout" , callable_mp (this , &RichTextLabel::_update_selection));
77867823}
77877824
77887825RichTextLabel::~RichTextLabel () {
0 commit comments