mirror of
https://github.com/godotengine/godot.git
synced 2025-10-19 07:53:26 +00:00
Add auto-scroll behavior when selecting text outside the visible area in RichTextLabel
This commit is contained in:
parent
42c7f14422
commit
4774b57e4d
2 changed files with 132 additions and 80 deletions
|
@ -2596,6 +2596,11 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
|
|||
deselect();
|
||||
}
|
||||
}
|
||||
|
||||
if (!selection.drag_attempt) {
|
||||
is_selecting_text = true;
|
||||
click_select_held->start();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (b->is_pressed() && b->is_double_click() && selection.enabled) {
|
||||
|
@ -2677,6 +2682,9 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
is_selecting_text = false;
|
||||
click_select_held->stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2865,95 +2873,119 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
|
|||
|
||||
Ref<InputEventMouseMotion> m = p_event;
|
||||
if (m.is_valid()) {
|
||||
ItemFrame *c_frame = nullptr;
|
||||
int c_line = 0;
|
||||
Item *c_item = nullptr;
|
||||
int c_index = 0;
|
||||
bool outside;
|
||||
local_mouse_pos = get_local_mouse_position();
|
||||
last_clamped_mouse_pos = local_mouse_pos.clamp(Vector2(), get_size());
|
||||
}
|
||||
}
|
||||
|
||||
_find_click(main, m->get_position(), &c_frame, &c_line, &c_item, &c_index, &outside, false);
|
||||
if (selection.click_item && c_item) {
|
||||
selection.from_frame = selection.click_frame;
|
||||
selection.from_line = selection.click_line;
|
||||
selection.from_item = selection.click_item;
|
||||
selection.from_char = selection.click_char;
|
||||
void RichTextLabel::_update_selection() {
|
||||
ItemFrame *c_frame = nullptr;
|
||||
int c_line = 0;
|
||||
Item *c_item = nullptr;
|
||||
int c_index = 0;
|
||||
bool outside;
|
||||
|
||||
selection.to_frame = c_frame;
|
||||
selection.to_line = c_line;
|
||||
selection.to_item = c_item;
|
||||
selection.to_char = c_index;
|
||||
|
||||
bool swap = false;
|
||||
if (selection.click_frame && c_frame) {
|
||||
const Line &l1 = c_frame->lines[c_line];
|
||||
const Line &l2 = selection.click_frame->lines[selection.click_line];
|
||||
if (l1.char_offset + c_index < l2.char_offset + selection.click_char) {
|
||||
swap = true;
|
||||
} else if (l1.char_offset + c_index == l2.char_offset + selection.click_char && !selection.double_click) {
|
||||
deselect();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (swap) {
|
||||
SWAP(selection.from_frame, selection.to_frame);
|
||||
SWAP(selection.from_line, selection.to_line);
|
||||
SWAP(selection.from_item, selection.to_item);
|
||||
SWAP(selection.from_char, selection.to_char);
|
||||
}
|
||||
|
||||
if (selection.double_click && c_frame) {
|
||||
// Expand the selection to word edges.
|
||||
|
||||
Line *l = &selection.from_frame->lines[selection.from_line];
|
||||
MutexLock lock(l->text_buf->get_mutex());
|
||||
PackedInt32Array words = TS->shaped_text_get_word_breaks(l->text_buf->get_rid());
|
||||
for (int i = 0; i < words.size(); i = i + 2) {
|
||||
if (selection.from_char > words[i] && selection.from_char < words[i + 1]) {
|
||||
selection.from_char = words[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
l = &selection.to_frame->lines[selection.to_line];
|
||||
lock = MutexLock(l->text_buf->get_mutex());
|
||||
words = TS->shaped_text_get_word_breaks(l->text_buf->get_rid());
|
||||
for (int i = 0; i < words.size(); i = i + 2) {
|
||||
if (selection.to_char > words[i] && selection.to_char < words[i + 1]) {
|
||||
selection.to_char = words[i + 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selection.active = true;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
// Handle auto scrolling.
|
||||
const Size2 size = get_size();
|
||||
if (!(local_mouse_pos.x >= 0.0 && local_mouse_pos.y >= 0.0 &&
|
||||
local_mouse_pos.x < size.x && local_mouse_pos.y < size.y)) {
|
||||
real_t scroll_delta = 0.0;
|
||||
if (local_mouse_pos.y < 0) {
|
||||
scroll_delta = -auto_scroll_speed * (1 - (local_mouse_pos.y / 15.0));
|
||||
} else if (local_mouse_pos.y > size.y) {
|
||||
scroll_delta = auto_scroll_speed * (1 + (local_mouse_pos.y - size.y) / 15.0);
|
||||
}
|
||||
|
||||
_find_click(main, m->get_position(), nullptr, nullptr, &c_item, nullptr, &outside, true);
|
||||
Variant meta;
|
||||
ItemMeta *item_meta;
|
||||
ItemMeta *prev_meta = meta_hovering;
|
||||
if (c_item && !outside && _find_meta(c_item, &meta, &item_meta)) {
|
||||
if (meta_hovering != item_meta) {
|
||||
if (meta_hovering) {
|
||||
emit_signal(SNAME("meta_hover_ended"), current_meta);
|
||||
}
|
||||
meta_hovering = item_meta;
|
||||
current_meta = meta;
|
||||
emit_signal(SNAME("meta_hover_started"), meta);
|
||||
if ((item_meta && item_meta->underline == META_UNDERLINE_ON_HOVER) || (prev_meta && prev_meta->underline == META_UNDERLINE_ON_HOVER)) {
|
||||
queue_redraw();
|
||||
if (scroll_delta != 0.0) {
|
||||
vscroll->scroll(scroll_delta);
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
// Update selection area.
|
||||
_find_click(main, last_clamped_mouse_pos, &c_frame, &c_line, &c_item, &c_index, &outside, false);
|
||||
if (selection.click_item && c_item) {
|
||||
selection.from_frame = selection.click_frame;
|
||||
selection.from_line = selection.click_line;
|
||||
selection.from_item = selection.click_item;
|
||||
selection.from_char = selection.click_char;
|
||||
|
||||
selection.to_frame = c_frame;
|
||||
selection.to_line = c_line;
|
||||
selection.to_item = c_item;
|
||||
selection.to_char = c_index;
|
||||
|
||||
bool swap = false;
|
||||
if (selection.click_frame && c_frame) {
|
||||
const Line &l1 = c_frame->lines[c_line];
|
||||
const Line &l2 = selection.click_frame->lines[selection.click_line];
|
||||
if (l1.char_offset + c_index < l2.char_offset + selection.click_char) {
|
||||
swap = true;
|
||||
} else if (l1.char_offset + c_index == l2.char_offset + selection.click_char && !selection.double_click) {
|
||||
deselect();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (swap) {
|
||||
SWAP(selection.from_frame, selection.to_frame);
|
||||
SWAP(selection.from_line, selection.to_line);
|
||||
SWAP(selection.from_item, selection.to_item);
|
||||
SWAP(selection.from_char, selection.to_char);
|
||||
}
|
||||
|
||||
if (selection.double_click && c_frame) {
|
||||
// Expand the selection to word edges.
|
||||
|
||||
Line *l = &selection.from_frame->lines[selection.from_line];
|
||||
MutexLock lock(l->text_buf->get_mutex());
|
||||
PackedInt32Array words = TS->shaped_text_get_word_breaks(l->text_buf->get_rid());
|
||||
for (int i = 0; i < words.size(); i = i + 2) {
|
||||
if (selection.from_char > words[i] && selection.from_char < words[i + 1]) {
|
||||
selection.from_char = words[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (meta_hovering) {
|
||||
meta_hovering = nullptr;
|
||||
emit_signal(SNAME("meta_hover_ended"), current_meta);
|
||||
current_meta = false;
|
||||
if (prev_meta->underline == META_UNDERLINE_ON_HOVER) {
|
||||
l = &selection.to_frame->lines[selection.to_line];
|
||||
lock = MutexLock(l->text_buf->get_mutex());
|
||||
words = TS->shaped_text_get_word_breaks(l->text_buf->get_rid());
|
||||
for (int i = 0; i < words.size(); i = i + 2) {
|
||||
if (selection.to_char > words[i] && selection.to_char < words[i + 1]) {
|
||||
selection.to_char = words[i + 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selection.active = true;
|
||||
queue_accessibility_update();
|
||||
queue_redraw();
|
||||
}
|
||||
|
||||
// Update meta hovering.
|
||||
_find_click(main, local_mouse_pos, nullptr, nullptr, &c_item, nullptr, &outside, true);
|
||||
Variant meta;
|
||||
ItemMeta *item_meta;
|
||||
ItemMeta *prev_meta = meta_hovering;
|
||||
if (c_item && !outside && _find_meta(c_item, &meta, &item_meta)) {
|
||||
if (meta_hovering != item_meta) {
|
||||
if (meta_hovering) {
|
||||
emit_signal(SNAME("meta_hover_ended"), current_meta);
|
||||
}
|
||||
meta_hovering = item_meta;
|
||||
current_meta = meta;
|
||||
emit_signal(SNAME("meta_hover_started"), meta);
|
||||
if ((item_meta && item_meta->underline == META_UNDERLINE_ON_HOVER) || (prev_meta && prev_meta->underline == META_UNDERLINE_ON_HOVER)) {
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
} else if (meta_hovering) {
|
||||
meta_hovering = nullptr;
|
||||
emit_signal(SNAME("meta_hover_ended"), current_meta);
|
||||
current_meta = false;
|
||||
if (prev_meta->underline == META_UNDERLINE_ON_HOVER) {
|
||||
queue_redraw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7783,6 +7815,11 @@ RichTextLabel::RichTextLabel(const String &p_text) {
|
|||
parsing_bbcode.store(false);
|
||||
|
||||
set_clip_contents(true);
|
||||
|
||||
click_select_held = memnew(Timer);
|
||||
add_child(click_select_held, false, INTERNAL_MODE_FRONT);
|
||||
click_select_held->set_wait_time(0.05);
|
||||
click_select_held->connect("timeout", callable_mp(this, &RichTextLabel::_update_selection));
|
||||
}
|
||||
|
||||
RichTextLabel::~RichTextLabel() {
|
||||
|
|
|
@ -36,6 +36,10 @@
|
|||
#include "scene/gui/scroll_bar.h"
|
||||
#include "scene/resources/text_paragraph.h"
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "editor/themes/editor_scale.h"
|
||||
#endif
|
||||
|
||||
class CharFXTransform;
|
||||
class RichTextEffect;
|
||||
|
||||
|
@ -667,6 +671,17 @@ private:
|
|||
void _scroll_changed(double);
|
||||
int _find_first_line(int p_from, int p_to, int p_vofs) const;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
const real_t auto_scroll_speed = EDSCALE * 2.0f;
|
||||
#else
|
||||
const real_t auto_scroll_speed = 2.0f;
|
||||
#endif
|
||||
Vector2 last_clamped_mouse_pos;
|
||||
Timer *click_select_held = nullptr;
|
||||
Vector2 local_mouse_pos;
|
||||
bool is_selecting_text = false;
|
||||
void _update_selection();
|
||||
|
||||
_FORCE_INLINE_ float _calculate_line_vertical_offset(const Line &line) const;
|
||||
|
||||
virtual void gui_input(const Ref<InputEvent> &p_event) override;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue