Merge pull request #14973 from poke1024/docs-word-selection
Double-click word selection for RichTextLabel (i.e. docs)
This commit is contained in:
commit
a6328011d8
5 changed files with 76 additions and 39 deletions
|
@ -56,6 +56,40 @@
|
||||||
#define IS_DIGIT(m_d) ((m_d) >= '0' && (m_d) <= '9')
|
#define IS_DIGIT(m_d) ((m_d) >= '0' && (m_d) <= '9')
|
||||||
#define IS_HEX_DIGIT(m_d) (((m_d) >= '0' && (m_d) <= '9') || ((m_d) >= 'a' && (m_d) <= 'f') || ((m_d) >= 'A' && (m_d) <= 'F'))
|
#define IS_HEX_DIGIT(m_d) (((m_d) >= '0' && (m_d) <= '9') || ((m_d) >= 'a' && (m_d) <= 'f') || ((m_d) >= 'A' && (m_d) <= 'F'))
|
||||||
|
|
||||||
|
bool is_symbol(CharType c) {
|
||||||
|
return c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t' || c == ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
bool select_word(const String &p_s, int p_col, int &r_beg, int &r_end) {
|
||||||
|
|
||||||
|
const String &s = p_s;
|
||||||
|
int beg = CLAMP(p_col, 0, s.length());
|
||||||
|
int end = beg;
|
||||||
|
|
||||||
|
if (s[beg] > 32 || beg == s.length()) {
|
||||||
|
|
||||||
|
bool symbol = beg < s.length() && is_symbol(s[beg]);
|
||||||
|
|
||||||
|
while (beg > 0 && s[beg - 1] > 32 && (symbol == is_symbol(s[beg - 1]))) {
|
||||||
|
beg--;
|
||||||
|
}
|
||||||
|
while (end < s.length() && s[end + 1] > 32 && (symbol == is_symbol(s[end + 1]))) {
|
||||||
|
end++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end < s.length())
|
||||||
|
end += 1;
|
||||||
|
|
||||||
|
r_beg = beg;
|
||||||
|
r_end = end;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** STRING **/
|
/** STRING **/
|
||||||
|
|
||||||
bool CharString::operator<(const CharString &p_right) const {
|
bool CharString::operator<(const CharString &p_right) const {
|
||||||
|
|
|
@ -318,4 +318,7 @@ String TTR(const String &);
|
||||||
//tool or regular translate
|
//tool or regular translate
|
||||||
String RTR(const String &);
|
String RTR(const String &);
|
||||||
|
|
||||||
|
bool is_symbol(CharType c);
|
||||||
|
bool select_word(const String &p_s, int p_col, int &r_beg, int &r_end);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -613,7 +613,7 @@ void EditorHelp::_class_desc_input(const Ref<InputEvent> &p_input) {
|
||||||
|
|
||||||
Ref<InputEventMouseButton> mb = p_input;
|
Ref<InputEventMouseButton> mb = p_input;
|
||||||
|
|
||||||
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == 1) {
|
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == 1 && !mb->is_doubleclick()) {
|
||||||
class_desc->set_selection_enabled(false);
|
class_desc->set_selection_enabled(false);
|
||||||
class_desc->set_selection_enabled(true);
|
class_desc->set_selection_enabled(true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -815,7 +815,35 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (b->is_pressed() && b->is_doubleclick() && selection.enabled) {
|
||||||
|
|
||||||
|
//doubleclick: select word
|
||||||
|
int line = 0;
|
||||||
|
Item *item = NULL;
|
||||||
|
bool outside;
|
||||||
|
|
||||||
|
_find_click(main, b->get_position(), &item, &line, &outside);
|
||||||
|
|
||||||
|
while (item && item->type != ITEM_TEXT) {
|
||||||
|
|
||||||
|
item = _get_next_item(item, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item && item->type == ITEM_TEXT) {
|
||||||
|
|
||||||
|
String itext = static_cast<ItemText *>(item)->text;
|
||||||
|
|
||||||
|
int beg, end;
|
||||||
|
if (select_word(itext, line, beg, end)) {
|
||||||
|
|
||||||
|
selection.from = item;
|
||||||
|
selection.to = item;
|
||||||
|
selection.from_char = beg;
|
||||||
|
selection.to_char = end - 1;
|
||||||
|
selection.active = true;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if (!b->is_pressed()) {
|
} else if (!b->is_pressed()) {
|
||||||
|
|
||||||
selection.click = NULL;
|
selection.click = NULL;
|
||||||
|
|
|
@ -42,16 +42,16 @@
|
||||||
|
|
||||||
#define TAB_PIXELS
|
#define TAB_PIXELS
|
||||||
|
|
||||||
|
inline bool _is_symbol(CharType c) {
|
||||||
|
|
||||||
|
return is_symbol(c);
|
||||||
|
}
|
||||||
|
|
||||||
static bool _is_text_char(CharType c) {
|
static bool _is_text_char(CharType c) {
|
||||||
|
|
||||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
|
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool _is_symbol(CharType c) {
|
|
||||||
|
|
||||||
return c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t' || c == ' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool _is_whitespace(CharType c) {
|
static bool _is_whitespace(CharType c) {
|
||||||
return c == '\t' || c == ' ';
|
return c == '\t' || c == ' ';
|
||||||
}
|
}
|
||||||
|
@ -1956,7 +1956,7 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
|
||||||
|
|
||||||
} else if (mb->is_doubleclick() && text[cursor.line].length()) {
|
} else if (mb->is_doubleclick() && text[cursor.line].length()) {
|
||||||
|
|
||||||
//doubleclick select world
|
//doubleclick select word
|
||||||
selection.selecting_mode = Selection::MODE_WORD;
|
selection.selecting_mode = Selection::MODE_WORD;
|
||||||
_update_selection_mode_word();
|
_update_selection_mode_word();
|
||||||
last_dblclk = OS::get_singleton()->get_ticks_msec();
|
last_dblclk = OS::get_singleton()->get_ticks_msec();
|
||||||
|
@ -5212,12 +5212,8 @@ String TextEdit::get_word_at_pos(const Vector2 &p_pos) const {
|
||||||
String s = text[row];
|
String s = text[row];
|
||||||
if (s.length() == 0)
|
if (s.length() == 0)
|
||||||
return "";
|
return "";
|
||||||
int beg = CLAMP(col, 0, s.length());
|
int beg, end;
|
||||||
int end = beg;
|
if (select_word(s, col, beg, end)) {
|
||||||
|
|
||||||
if (s[beg] > 32 || beg == s.length()) {
|
|
||||||
|
|
||||||
bool symbol = beg < s.length() && _is_symbol(s[beg]); //not sure if right but most editors behave like this
|
|
||||||
|
|
||||||
bool inside_quotes = false;
|
bool inside_quotes = false;
|
||||||
int qbegin = 0, qend = 0;
|
int qbegin = 0, qend = 0;
|
||||||
|
@ -5236,16 +5232,6 @@ String TextEdit::get_word_at_pos(const Vector2 &p_pos) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (beg > 0 && s[beg - 1] > 32 && (symbol == _is_symbol(s[beg - 1]))) {
|
|
||||||
beg--;
|
|
||||||
}
|
|
||||||
while (end < s.length() && s[end + 1] > 32 && (symbol == _is_symbol(s[end + 1]))) {
|
|
||||||
end++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (end < s.length())
|
|
||||||
end += 1;
|
|
||||||
|
|
||||||
return s.substr(beg, end - beg);
|
return s.substr(beg, end - beg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5262,22 +5248,8 @@ String TextEdit::get_tooltip(const Point2 &p_pos) const {
|
||||||
String s = text[row];
|
String s = text[row];
|
||||||
if (s.length() == 0)
|
if (s.length() == 0)
|
||||||
return Control::get_tooltip(p_pos);
|
return Control::get_tooltip(p_pos);
|
||||||
int beg = CLAMP(col, 0, s.length());
|
int beg, end;
|
||||||
int end = beg;
|
if (select_word(s, col, beg, end)) {
|
||||||
|
|
||||||
if (s[beg] > 32 || beg == s.length()) {
|
|
||||||
|
|
||||||
bool symbol = beg < s.length() && _is_symbol(s[beg]); //not sure if right but most editors behave like this
|
|
||||||
|
|
||||||
while (beg > 0 && s[beg - 1] > 32 && (symbol == _is_symbol(s[beg - 1]))) {
|
|
||||||
beg--;
|
|
||||||
}
|
|
||||||
while (end < s.length() && s[end + 1] > 32 && (symbol == _is_symbol(s[end + 1]))) {
|
|
||||||
end++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (end < s.length())
|
|
||||||
end += 1;
|
|
||||||
|
|
||||||
String tt = tooltip_obj->call(tooltip_func, s.substr(beg, end - beg), tooltip_ud);
|
String tt = tooltip_obj->call(tooltip_func, s.substr(beg, end - beg), tooltip_ud);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue