Expose and cleanup TextEdit line wrap API

This commit is contained in:
Paulb23 2021-07-09 11:52:49 +01:00
parent d5dcaee4c5
commit 7e70f9e0b9
9 changed files with 256 additions and 179 deletions

View file

@ -213,6 +213,35 @@
<description>
</description>
</method>
<method name="get_line_wrap_count" qualifiers="const">
<return type="int">
</return>
<argument index="0" name="line" type="int">
</argument>
<description>
Returns the number of the time given line is wrapped.
</description>
</method>
<method name="get_line_wrap_index_at_column" qualifiers="const">
<return type="int">
</return>
<argument index="0" name="line" type="int">
</argument>
<argument index="1" name="column" type="int">
</argument>
<description>
Returns the wrap index of the given line column.
</description>
</method>
<method name="get_line_wrapped_text" qualifiers="const">
<return type="PackedStringArray">
</return>
<argument index="0" name="line" type="int">
</argument>
<description>
Returns an array of [String] repersenting each wrapped index.
</description>
</method>
<method name="get_menu" qualifiers="const">
<return type="PopupMenu" />
<description>
@ -346,6 +375,15 @@
Returns whether the menu is visible. Use this instead of [code]get_menu().visible[/code] to improve performance (so the creation of the menu is avoided).
</description>
</method>
<method name="is_line_wrapped" qualifiers="const">
<return type="bool">
</return>
<argument index="0" name="line" type="int">
</argument>
<description>
Returns if the given line is wrapped.
</description>
</method>
<method name="is_selection_active" qualifiers="const">
<return type="bool" />
<description>
@ -686,8 +724,8 @@
<member name="virtual_keyboard_enabled" type="bool" setter="set_virtual_keyboard_enabled" getter="is_virtual_keyboard_enabled" default="true">
If [code]true[/code], the native virtual keyboard is shown when focused on platforms that support it.
</member>
<member name="wrap_enabled" type="bool" setter="set_wrap_enabled" getter="is_wrap_enabled" default="false">
If [code]true[/code], enables text wrapping when it goes beyond the edge of what is visible.
<member name="wrap_mode" type="int" setter="set_line_wrapping_mode" getter="get_line_wrapping_mode" enum="TextEdit.LineWrappingMode" default="0">
Sets the line wrapping mode to use.
</member>
</members>
<signals>
@ -748,6 +786,12 @@
</constant>
<constant name="SELECTION_MODE_LINE" value="4" enum="SelectionMode">
</constant>
<constant name="LINE_WRAPPING_NONE" value="0" enum="LineWrappingMode">
Line wrapping is disabled.
</constant>
<constant name="LINE_WRAPPING_BOUNDARY" value="1" enum="LineWrappingMode">
Line wrapping occurs at the control boundary, beyond what would normally be visible.
</constant>
<constant name="GUTTER_TYPE_STRING" value="0" enum="GutterType">
</constant>
<constant name="GUTTER_TYPE_ICON" value="1" enum="GutterType">

View file

@ -950,7 +950,7 @@ void CodeTextEditor::update_editor_settings() {
text_editor->set_draw_bookmarks_gutter(EditorSettings::get_singleton()->get("text_editor/appearance/show_bookmark_gutter"));
text_editor->set_line_folding_enabled(EditorSettings::get_singleton()->get("text_editor/appearance/code_folding"));
text_editor->set_draw_fold_gutter(EditorSettings::get_singleton()->get("text_editor/appearance/code_folding"));
text_editor->set_wrap_enabled(EditorSettings::get_singleton()->get("text_editor/appearance/word_wrap"));
text_editor->set_line_wrapping_mode((TextEdit::LineWrappingMode)EditorSettings::get_singleton()->get("text_editor/appearance/word_wrap").operator int());
text_editor->set_scroll_pass_end_of_file(EditorSettings::get_singleton()->get("text_editor/cursor/scroll_past_end_of_file"));
text_editor->set_caret_type((TextEdit::CaretType)EditorSettings::get_singleton()->get("text_editor/cursor/type").operator int());
text_editor->set_caret_blink_enabled(EditorSettings::get_singleton()->get("text_editor/cursor/caret_blink"));

View file

@ -121,7 +121,7 @@ void EditorPropertyMultilineText::_open_big_text() {
if (!big_text_dialog) {
big_text = memnew(TextEdit);
big_text->connect("text_changed", callable_mp(this, &EditorPropertyMultilineText::_big_text_changed));
big_text->set_wrap_enabled(true);
big_text->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY);
big_text_dialog = memnew(AcceptDialog);
big_text_dialog->add_child(big_text);
big_text_dialog->set_title(TTR("Edit Text:"));
@ -166,7 +166,7 @@ EditorPropertyMultilineText::EditorPropertyMultilineText() {
set_bottom_editor(hb);
text = memnew(TextEdit);
text->connect("text_changed", callable_mp(this, &EditorPropertyMultilineText::_text_changed));
text->set_wrap_enabled(true);
text->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY);
add_focusable(text);
hb->add_child(text);
text->set_h_size_flags(SIZE_EXPAND_FILL);

View file

@ -525,7 +525,9 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("text_editor/appearance/show_bookmark_gutter", true);
_initial_set("text_editor/appearance/show_info_gutter", true);
_initial_set("text_editor/appearance/code_folding", true);
_initial_set("text_editor/appearance/word_wrap", false);
_initial_set("text_editor/appearance/word_wrap", 0);
hints["text_editor/appearance/word_wrap"] = PropertyInfo(Variant::INT, "text_editor/appearance/word_wrap", PROPERTY_HINT_ENUM, "None,Boundary");
_initial_set("text_editor/appearance/show_line_length_guidelines", true);
_initial_set("text_editor/appearance/line_length_guideline_soft_column", 80);
hints["text_editor/appearance/line_length_guideline_soft_column"] = PropertyInfo(Variant::INT, "text_editor/appearance/line_length_guideline_soft_column", PROPERTY_HINT_RANGE, "20, 160, 1");

View file

@ -282,7 +282,7 @@ PluginConfigDialog::PluginConfigDialog() {
desc_edit = memnew(TextEdit);
desc_edit->set_custom_minimum_size(Size2(400, 80) * EDSCALE);
desc_edit->set_wrap_enabled(true);
desc_edit->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY);
grid->add_child(desc_edit);
// Author

View file

@ -484,7 +484,7 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() {
commit_message->set_h_grow_direction(Control::GrowDirection::GROW_DIRECTION_BEGIN);
commit_message->set_v_grow_direction(Control::GrowDirection::GROW_DIRECTION_END);
commit_message->set_custom_minimum_size(Size2(200, 100));
commit_message->set_wrap_enabled(true);
commit_message->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY);
commit_message->connect("text_changed", callable_mp(this, &VersionControlEditorPlugin::_update_commit_button));
commit_message->connect("gui_input", callable_mp(this, &VersionControlEditorPlugin::_commit_message_gui_input));
commit_box_vbc->add_child(commit_message);

View file

@ -295,8 +295,8 @@ void CodeEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
if (is_line_folded(line)) {
int wrap_index = get_line_wrap_index_at_col(line, col);
if (wrap_index == times_line_wraps(line)) {
int wrap_index = get_line_wrap_index_at_column(line, col);
if (wrap_index == get_line_wrap_count(line)) {
int eol_icon_width = cache.folded_eol_icon->get_width();
int left_margin = get_total_gutter_width() + eol_icon_width + get_line_width(line, wrap_index) - get_h_scroll();
if (mpos.x > left_margin && mpos.x <= left_margin + eol_icon_width + 3) {
@ -531,8 +531,8 @@ Control::CursorShape CodeEdit::get_cursor_shape(const Point2 &p_pos) const {
_get_mouse_pos(p_pos, line, col);
if (is_line_folded(line)) {
int wrap_index = get_line_wrap_index_at_col(line, col);
if (wrap_index == times_line_wraps(line)) {
int wrap_index = get_line_wrap_index_at_column(line, col);
if (wrap_index == get_line_wrap_count(line)) {
int eol_icon_width = cache.folded_eol_icon->get_width();
int left_margin = get_total_gutter_width() + eol_icon_width + get_line_width(line, wrap_index) - get_h_scroll();
if (p_pos.x > left_margin && p_pos.x <= left_margin + eol_icon_width + 3) {

View file

@ -295,7 +295,7 @@ void TextEdit::_update_scrollbars() {
v_scroll->hide();
}
if (total_width > visible_width && !is_wrap_enabled()) {
if (total_width > visible_width && get_line_wrapping_mode() == LineWrappingMode::LINE_WRAPPING_NONE) {
h_scroll->show();
h_scroll->set_max(total_width);
h_scroll->set_page(visible_width);
@ -495,11 +495,11 @@ void TextEdit::_notification(int p_what) {
if (text_changed_dirty) {
MessageQueue::get_singleton()->push_call(this, "_text_changed_emit");
}
_update_wrap_at(true);
_update_wrap_at_column(true);
} break;
case NOTIFICATION_RESIZED: {
_update_scrollbars();
_update_wrap_at();
_update_wrap_at_column();
} break;
case NOTIFICATION_VISIBILITY_CHANGED: {
if (is_visible()) {
@ -511,7 +511,7 @@ void TextEdit::_notification(int p_what) {
case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_THEME_CHANGED: {
_update_caches();
_update_wrap_at(true);
_update_wrap_at_column(true);
} break;
case NOTIFICATION_WM_WINDOW_FOCUS_IN: {
window_has_focus = true;
@ -748,7 +748,7 @@ void TextEdit::_notification(int p_what) {
int first_visible_line = get_first_visible_line() - 1;
int draw_amount = visible_rows + (smooth_scroll_enabled ? 1 : 0);
draw_amount += times_line_wraps(first_visible_line + 1);
draw_amount += get_line_wrap_count(first_visible_line + 1);
// minimap
if (draw_minimap) {
@ -769,7 +769,7 @@ void TextEdit::_notification(int p_what) {
minimap_line -= num_lines_from_rows(first_visible_line, 0, -num_lines_before, wi);
minimap_line -= (minimap_line > 0 && smooth_scroll_enabled ? 1 : 0);
}
int minimap_draw_amount = minimap_visible_lines + times_line_wraps(minimap_line + 1);
int minimap_draw_amount = minimap_visible_lines + get_line_wrap_count(minimap_line + 1);
// draw the minimap
Color viewport_color = (cache.background_color.get_v() < 0.5) ? Color(1, 1, 1, 0.1) : Color(0, 0, 0, 0.1);
@ -805,8 +805,8 @@ void TextEdit::_notification(int p_what) {
current_color = cache.font_readonly_color;
}
Vector<String> wrap_rows = get_wrap_rows_text(minimap_line);
int line_wrap_amount = times_line_wraps(minimap_line);
Vector<String> wrap_rows = get_line_wrapped_text(minimap_line);
int line_wrap_amount = get_line_wrap_count(minimap_line);
int last_wrap_column = 0;
for (int line_wrap_index = 0; line_wrap_index < line_wrap_amount + 1; line_wrap_index++) {
@ -819,7 +819,7 @@ void TextEdit::_notification(int p_what) {
const String &str = wrap_rows[line_wrap_index];
int indent_px = line_wrap_index != 0 ? get_indent_level(minimap_line) : 0;
if (indent_px >= wrap_at) {
if (indent_px >= wrap_at_column) {
indent_px = 0;
}
indent_px = minimap_char_size.x * indent_px;
@ -947,8 +947,8 @@ void TextEdit::_notification(int p_what) {
const Ref<TextParagraph> ldata = text.get_line_data(line);
Vector<String> wrap_rows = get_wrap_rows_text(line);
int line_wrap_amount = times_line_wraps(line);
Vector<String> wrap_rows = get_line_wrapped_text(line);
int line_wrap_amount = get_line_wrap_count(line);
for (int line_wrap_index = 0; line_wrap_index <= line_wrap_amount; line_wrap_index++) {
if (line_wrap_index != 0) {
@ -1659,8 +1659,8 @@ void TextEdit::_move_caret_up(bool p_select) {
set_caret_column(0);
} else {
int new_line = caret.line - num_lines_from(caret.line - 1, -1);
if (line_wraps(new_line)) {
set_caret_line(new_line, true, false, times_line_wraps(new_line));
if (is_line_wrapped(new_line)) {
set_caret_line(new_line, true, false, get_line_wrap_count(new_line));
} else {
set_caret_line(new_line, true, false);
}
@ -1679,7 +1679,7 @@ void TextEdit::_move_caret_down(bool p_select) {
}
int cur_wrap_index = get_caret_wrap_index();
if (cur_wrap_index < times_line_wraps(caret.line)) {
if (cur_wrap_index < get_line_wrap_count(caret.line)) {
set_caret_line(caret.line, true, false, cur_wrap_index + 1);
} else if (caret.line == get_last_unhidden_line()) {
set_caret_column(text[caret.line].length());
@ -1701,7 +1701,7 @@ void TextEdit::_move_caret_to_line_start(bool p_select) {
}
// Move caret column to start of wrapped row and then to start of text.
Vector<String> rows = get_wrap_rows_text(caret.line);
Vector<String> rows = get_line_wrapped_text(caret.line);
int wi = get_caret_wrap_index();
int row_start_col = 0;
for (int i = 0; i < wi; i++) {
@ -1740,7 +1740,7 @@ void TextEdit::_move_caret_to_line_end(bool p_select) {
}
// Move caret column to end of wrapped row and then to end of text.
Vector<String> rows = get_wrap_rows_text(caret.line);
Vector<String> rows = get_line_wrapped_text(caret.line);
int wi = get_caret_wrap_index();
int row_end_col = -1;
for (int i = 0; i < wi + 1; i++) {
@ -1929,7 +1929,7 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co
int row = first_vis_line + Math::floor(rows);
int wrap_index = 0;
if (is_wrap_enabled() || is_hiding_enabled()) {
if (get_line_wrapping_mode() != LineWrappingMode::LINE_WRAPPING_NONE || is_hiding_enabled()) {
int f_ofs = num_lines_from_rows(first_vis_line, caret.wrap_ofs, rows + (1 * SGN(rows)), wrap_index) - 1;
if (rows < 0) {
row = first_vis_line - f_ofs;
@ -1951,9 +1951,9 @@ void TextEdit::_get_mouse_pos(const Point2i &p_mouse, int &r_row, int &r_col) co
int colx = p_mouse.x - (cache.style_normal->get_margin(SIDE_LEFT) + gutters_width + gutter_padding);
colx += caret.x_ofs;
col = get_char_pos_for_line(colx, row, wrap_index);
if (is_wrap_enabled() && wrap_index < times_line_wraps(row)) {
if (get_line_wrapping_mode() != LineWrappingMode::LINE_WRAPPING_NONE && wrap_index < get_line_wrap_count(row)) {
// Move back one if we are at the end of the row.
Vector<String> rows2 = get_wrap_rows_text(row);
Vector<String> rows2 = get_line_wrapped_text(row);
int row_end_col = 0;
for (int i = 0; i < wrap_index + 1; i++) {
row_end_col += rows2[i].length();
@ -1985,7 +1985,7 @@ void TextEdit::_get_minimap_mouse_row(const Point2i &p_mouse, int &r_row) const
int visible_rows = get_visible_rows() + 1;
int first_visible_line = get_first_visible_line() - 1;
int draw_amount = visible_rows + (smooth_scroll_enabled ? 1 : 0);
draw_amount += times_line_wraps(first_visible_line + 1);
draw_amount += get_line_wrap_count(first_visible_line + 1);
int minimap_line_height = (minimap_char_size.y + minimap_line_spacing);
// calculate viewport size and y offset
@ -2007,7 +2007,7 @@ void TextEdit::_get_minimap_mouse_row(const Point2i &p_mouse, int &r_row) const
int row = minimap_line + Math::floor(rows);
int wrap_index = 0;
if (is_wrap_enabled() || is_hiding_enabled()) {
if (get_line_wrapping_mode() != LineWrappingMode::LINE_WRAPPING_NONE || is_hiding_enabled()) {
int f_ofs = num_lines_from_rows(minimap_line, caret.wrap_ofs, rows + (1 * SGN(rows)), wrap_index) - 1;
if (rows < 0) {
row = minimap_line - f_ofs;
@ -2894,7 +2894,7 @@ int TextEdit::_get_minimap_visible_rows() const {
int TextEdit::get_total_visible_rows() const {
// Returns the total amount of rows we need in the editor.
// This skips hidden lines and counts each wrapping of a line.
if (!is_hiding_enabled() && !is_wrap_enabled()) {
if (!is_hiding_enabled() && get_line_wrapping_mode() == LineWrappingMode::LINE_WRAPPING_NONE) {
return text.size();
}
@ -2902,35 +2902,12 @@ int TextEdit::get_total_visible_rows() const {
for (int i = 0; i < text.size(); i++) {
if (!text.is_hidden(i)) {
total_rows++;
total_rows += times_line_wraps(i);
total_rows += get_line_wrap_count(i);
}
}
return total_rows;
}
void TextEdit::_update_wrap_at(bool p_force) {
int new_wrap_at = get_size().width - cache.style_normal->get_minimum_size().width - gutters_width - gutter_padding;
if (draw_minimap) {
new_wrap_at -= minimap_width;
}
if (v_scroll->is_visible_in_tree()) {
new_wrap_at -= v_scroll->get_combined_minimum_size().width;
}
new_wrap_at -= wrap_right_offset; // Give it a little more space.
if ((wrap_at != new_wrap_at) || p_force) {
wrap_at = new_wrap_at;
if (wrap_enabled) {
text.set_width(wrap_at);
} else {
text.set_width(-1);
}
text.invalidate_all_lines();
}
_update_caret_wrap_offset();
}
void TextEdit::adjust_viewport_to_caret() {
// Make sure Caret is visible on the screen.
scrolling = false;
@ -2958,7 +2935,7 @@ void TextEdit::adjust_viewport_to_caret() {
}
visible_width -= 20; // Give it a little more space.
if (!is_wrap_enabled()) {
if (get_line_wrapping_mode() == LineWrappingMode::LINE_WRAPPING_NONE) {
// Adjust x offset.
Vector2i caret_pos;
@ -3007,7 +2984,7 @@ void TextEdit::center_viewport_to_caret() {
}
visible_width -= 20; // Give it a little more space.
if (is_wrap_enabled()) {
if (get_line_wrapping_mode() != LineWrappingMode::LINE_WRAPPING_NONE) {
// Center x offset.
Vector2i caret_pos;
@ -3045,74 +3022,6 @@ void TextEdit::center_viewport_to_caret() {
update();
}
void TextEdit::_update_caret_wrap_offset() {
int first_vis_line = get_first_visible_line();
if (line_wraps(first_vis_line)) {
caret.wrap_ofs = MIN(caret.wrap_ofs, times_line_wraps(first_vis_line));
} else {
caret.wrap_ofs = 0;
}
set_line_as_first_visible(caret.line_ofs, caret.wrap_ofs);
}
bool TextEdit::line_wraps(int line) const {
ERR_FAIL_INDEX_V(line, text.size(), 0);
if (!is_wrap_enabled()) {
return false;
}
return text.get_line_wrap_amount(line) > 0;
}
int TextEdit::times_line_wraps(int line) const {
ERR_FAIL_INDEX_V(line, text.size(), 0);
if (!line_wraps(line)) {
return 0;
}
return text.get_line_wrap_amount(line);
}
Vector<String> TextEdit::get_wrap_rows_text(int p_line) const {
ERR_FAIL_INDEX_V(p_line, text.size(), Vector<String>());
Vector<String> lines;
if (!line_wraps(p_line)) {
lines.push_back(text[p_line]);
return lines;
}
const String &line_text = text[p_line];
Vector<Vector2i> line_ranges = text.get_line_wrap_ranges(p_line);
for (int i = 0; i < line_ranges.size(); i++) {
lines.push_back(line_text.substr(line_ranges[i].x, line_ranges[i].y - line_ranges[i].x));
}
return lines;
}
int TextEdit::get_line_wrap_index_at_col(int p_line, int p_column) const {
ERR_FAIL_INDEX_V(p_line, text.size(), 0);
if (!line_wraps(p_line)) {
return 0;
}
// Loop through wraps in the line text until we get to the column.
int wrap_index = 0;
int col = 0;
Vector<String> rows = get_wrap_rows_text(p_line);
for (int i = 0; i < rows.size(); i++) {
wrap_index = i;
String s = rows[wrap_index];
col += s.length();
if (col > p_column) {
break;
}
}
return wrap_index;
}
TextEdit::SelectionMode TextEdit::get_selection_mode() const {
return selection.selecting_mode;
}
@ -3158,14 +3067,14 @@ void TextEdit::_scroll_moved(double p_to_val) {
for (n_line = 0; n_line < text.size(); n_line++) {
if (!is_line_hidden(n_line)) {
sc++;
sc += times_line_wraps(n_line);
sc += get_line_wrap_count(n_line);
if (sc > v_scroll_i) {
break;
}
}
}
n_line = MIN(n_line, text.size() - 1);
int line_wrap_amount = times_line_wraps(n_line);
int line_wrap_amount = get_line_wrap_count(n_line);
int wi = line_wrap_amount - (sc - v_scroll_i - 1);
wi = CLAMP(wi, 0, line_wrap_amount);
@ -3460,17 +3369,6 @@ bool TextEdit::is_readonly() const {
return readonly;
}
void TextEdit::set_wrap_enabled(bool p_wrap_enabled) {
if (wrap_enabled != p_wrap_enabled) {
wrap_enabled = p_wrap_enabled;
_update_wrap_at(true);
}
}
bool TextEdit::is_wrap_enabled() const {
return wrap_enabled;
}
void TextEdit::_update_caches() {
/* Caret */
caret_color = get_theme_color(SNAME("caret_color"));
@ -3661,8 +3559,8 @@ void TextEdit::set_caret_line(int p_line, bool p_adjust_viewport, bool p_can_be_
caret.line = p_line;
int n_col = get_char_pos_for_line(caret.last_fit_x, p_line, p_wrap_index);
if (n_col != 0 && is_wrap_enabled() && p_wrap_index < times_line_wraps(p_line)) {
Vector<String> rows = get_wrap_rows_text(p_line);
if (n_col != 0 && get_line_wrapping_mode() != LineWrappingMode::LINE_WRAPPING_NONE && p_wrap_index < get_line_wrap_count(p_line)) {
Vector<String> rows = get_line_wrapped_text(p_line);
int row_end_col = 0;
for (int i = 0; i < p_wrap_index + 1; i++) {
row_end_col += rows[i].length();
@ -3720,7 +3618,77 @@ int TextEdit::get_caret_column() const {
}
int TextEdit::get_caret_wrap_index() const {
return get_line_wrap_index_at_col(caret.line, caret.column);
return get_line_wrap_index_at_column(caret.line, caret.column);
}
/* line wrapping. */
void TextEdit::set_line_wrapping_mode(LineWrappingMode p_wrapping_mode) {
if (line_wrapping_mode != p_wrapping_mode) {
line_wrapping_mode = p_wrapping_mode;
_update_wrap_at_column(true);
}
}
TextEdit::LineWrappingMode TextEdit::get_line_wrapping_mode() const {
return line_wrapping_mode;
}
bool TextEdit::is_line_wrapped(int p_line) const {
ERR_FAIL_INDEX_V(p_line, text.size(), 0);
if (get_line_wrapping_mode() == LineWrappingMode::LINE_WRAPPING_NONE) {
return false;
}
return text.get_line_wrap_amount(p_line) > 0;
}
int TextEdit::get_line_wrap_count(int p_line) const {
ERR_FAIL_INDEX_V(p_line, text.size(), 0);
if (!is_line_wrapped(p_line)) {
return 0;
}
return text.get_line_wrap_amount(p_line);
}
int TextEdit::get_line_wrap_index_at_column(int p_line, int p_column) const {
ERR_FAIL_INDEX_V(p_line, text.size(), 0);
if (!is_line_wrapped(p_line)) {
return 0;
}
/* Loop through wraps in the line text until we get to the column. */
int wrap_index = 0;
int col = 0;
Vector<String> lines = get_line_wrapped_text(p_line);
for (int i = 0; i < lines.size(); i++) {
wrap_index = i;
String s = lines[wrap_index];
col += s.length();
if (col > p_column) {
break;
}
}
return wrap_index;
}
Vector<String> TextEdit::get_line_wrapped_text(int p_line) const {
ERR_FAIL_INDEX_V(p_line, text.size(), Vector<String>());
Vector<String> lines;
if (!is_line_wrapped(p_line)) {
lines.push_back(text[p_line]);
return lines;
}
const String &line_text = text[p_line];
Vector<Vector2i> line_ranges = text.get_line_wrap_ranges(p_line);
for (int i = 0; i < line_ranges.size(); i++) {
lines.push_back(line_text.substr(line_ranges[i].x, line_ranges[i].y - line_ranges[i].x));
}
return lines;
}
/* Syntax Highlighting. */
@ -4384,7 +4352,7 @@ int TextEdit::num_lines_from_rows(int p_line_from, int p_wrap_index_from, int vi
wrap_index = 0;
ERR_FAIL_INDEX_V(p_line_from, text.size(), ABS(visible_amount));
if (!is_hiding_enabled() && !is_wrap_enabled()) {
if (!is_hiding_enabled() && get_line_wrapping_mode() == LineWrappingMode::LINE_WRAPPING_NONE) {
return ABS(visible_amount);
}
@ -4400,22 +4368,22 @@ int TextEdit::num_lines_from_rows(int p_line_from, int p_wrap_index_from, int vi
num_total++;
if (!is_line_hidden(i)) {
num_visible++;
num_visible += times_line_wraps(i);
num_visible += get_line_wrap_count(i);
}
if (num_visible >= visible_amount) {
break;
}
}
wrap_index = times_line_wraps(MIN(i, text.size() - 1)) - MAX(0, num_visible - visible_amount);
wrap_index = get_line_wrap_count(MIN(i, text.size() - 1)) - MAX(0, num_visible - visible_amount);
} else {
visible_amount = ABS(visible_amount);
int i;
num_visible -= times_line_wraps(p_line_from) - p_wrap_index_from;
num_visible -= get_line_wrap_count(p_line_from) - p_wrap_index_from;
for (i = p_line_from; i >= 0; i--) {
num_total++;
if (!is_line_hidden(i)) {
num_visible++;
num_visible += times_line_wraps(i);
num_visible += get_line_wrap_count(i);
}
if (num_visible >= visible_amount) {
break;
@ -4709,7 +4677,7 @@ void TextEdit::tag_saved_version() {
}
double TextEdit::get_scroll_pos_for_line(int p_line, int p_wrap_index) const {
if (!is_wrap_enabled() && !is_hiding_enabled()) {
if (get_line_wrapping_mode() == LineWrappingMode::LINE_WRAPPING_NONE && !is_hiding_enabled()) {
return p_line;
}
@ -4719,7 +4687,7 @@ double TextEdit::get_scroll_pos_for_line(int p_line, int p_wrap_index) const {
for (int i = 0; i < to; i++) {
if (!text.is_hidden(i)) {
new_line_scroll_pos++;
new_line_scroll_pos += times_line_wraps(i);
new_line_scroll_pos += get_line_wrap_count(i);
}
}
new_line_scroll_pos += p_wrap_index;
@ -4918,7 +4886,7 @@ void TextEdit::insert_at(const String &p_text, int at) {
void TextEdit::set_draw_minimap(bool p_draw) {
if (draw_minimap != p_draw) {
draw_minimap = p_draw;
_update_wrap_at();
_update_wrap_at_column();
}
update();
}
@ -4930,7 +4898,7 @@ bool TextEdit::is_drawing_minimap() const {
void TextEdit::set_minimap_width(int p_minimap_width) {
if (minimap_width != p_minimap_width) {
minimap_width = p_minimap_width;
_update_wrap_at();
_update_wrap_at_column();
}
update();
}
@ -5197,7 +5165,6 @@ void TextEdit::_get_property_list(List<PropertyInfo> *p_list) const {
void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("_gui_input"), &TextEdit::_gui_input);
ClassDB::bind_method(D_METHOD("_text_changed_emit"), &TextEdit::_text_changed_emit);
ClassDB::bind_method(D_METHOD("_update_wrap_at", "force"), &TextEdit::_update_wrap_at, DEFVAL(false));
BIND_ENUM_CONSTANT(SEARCH_MATCH_CASE);
BIND_ENUM_CONSTANT(SEARCH_WHOLE_WORDS);
@ -5248,8 +5215,6 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_readonly", "enable"), &TextEdit::set_readonly);
ClassDB::bind_method(D_METHOD("is_readonly"), &TextEdit::is_readonly);
ClassDB::bind_method(D_METHOD("set_wrap_enabled", "enable"), &TextEdit::set_wrap_enabled);
ClassDB::bind_method(D_METHOD("is_wrap_enabled"), &TextEdit::is_wrap_enabled);
ClassDB::bind_method(D_METHOD("set_context_menu_enabled", "enable"), &TextEdit::set_context_menu_enabled);
ClassDB::bind_method(D_METHOD("is_context_menu_enabled"), &TextEdit::is_context_menu_enabled);
ClassDB::bind_method(D_METHOD("set_shortcut_keys_enabled", "enable"), &TextEdit::set_shortcut_keys_enabled);
@ -5339,6 +5304,22 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_caret_wrap_index"), &TextEdit::get_caret_wrap_index);
/* line wrapping. */
BIND_ENUM_CONSTANT(LINE_WRAPPING_NONE);
BIND_ENUM_CONSTANT(LINE_WRAPPING_BOUNDARY);
// internal.
ClassDB::bind_method(D_METHOD("_update_wrap_at_column", "force"), &TextEdit::_update_wrap_at_column, DEFVAL(false));
ClassDB::bind_method(D_METHOD("set_line_wrapping_mode", "mode"), &TextEdit::set_line_wrapping_mode);
ClassDB::bind_method(D_METHOD("get_line_wrapping_mode"), &TextEdit::get_line_wrapping_mode);
ClassDB::bind_method(D_METHOD("is_line_wrapped", "line"), &TextEdit::is_line_wrapped);
ClassDB::bind_method(D_METHOD("get_line_wrap_count", "line"), &TextEdit::get_line_wrap_count);
ClassDB::bind_method(D_METHOD("get_line_wrap_index_at_column", "line", "column"), &TextEdit::get_line_wrap_index_at_column);
ClassDB::bind_method(D_METHOD("get_line_wrapped_text", "line"), &TextEdit::get_line_wrapped_text);
/* Syntax Highlighting. */
ClassDB::bind_method(D_METHOD("set_syntax_highlighter", "syntax_highlighter"), &TextEdit::set_syntax_highlighter);
ClassDB::bind_method(D_METHOD("get_syntax_highlighter"), &TextEdit::get_syntax_highlighter);
@ -5420,7 +5401,7 @@ void TextEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selecting_enabled"), "set_selecting_enabled", "is_selecting_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_scrolling"), "set_smooth_scroll_enable", "is_smooth_scroll_enabled");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "v_scroll_speed"), "set_v_scroll_speed", "get_v_scroll_speed");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "wrap_enabled"), "set_wrap_enabled", "is_wrap_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "wrap_mode", PROPERTY_HINT_ENUM, "None,Boundary"), "set_line_wrapping_mode", "get_line_wrapping_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scroll_vertical"), "set_v_scroll", "get_v_scroll");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_horizontal"), "set_h_scroll", "get_h_scroll");
@ -5716,6 +5697,41 @@ void TextEdit::_toggle_draw_caret() {
}
}
/* Line Wrapping */
void TextEdit::_update_wrap_at_column(bool p_force) {
int new_wrap_at = get_size().width - cache.style_normal->get_minimum_size().width - gutters_width - gutter_padding;
if (draw_minimap) {
new_wrap_at -= minimap_width;
}
if (v_scroll->is_visible_in_tree()) {
new_wrap_at -= v_scroll->get_combined_minimum_size().width;
}
/* Give it a little more space. */
new_wrap_at -= wrap_right_offset;
if ((wrap_at_column != new_wrap_at) || p_force) {
wrap_at_column = new_wrap_at;
if (line_wrapping_mode) {
text.set_width(wrap_at_column);
} else {
text.set_width(-1);
}
text.invalidate_all_lines();
}
_update_caret_wrap_offset();
}
void TextEdit::_update_caret_wrap_offset() {
int first_vis_line = get_first_visible_line();
if (is_line_wrapped(first_vis_line)) {
caret.wrap_ofs = MIN(caret.wrap_ofs, get_line_wrap_count(first_vis_line));
} else {
caret.wrap_ofs = 0;
}
set_line_as_first_visible(caret.line_ofs, caret.wrap_ofs);
}
TextEdit::TextEdit() {
clear();
set_focus_mode(FOCUS_ALL);

View file

@ -48,12 +48,6 @@ public:
CARET_TYPE_BLOCK
};
enum GutterType {
GUTTER_TYPE_STRING,
GUTTER_TYPE_ICON,
GUTTER_TYPE_CUSTOM
};
/* Selection */
enum SelectionMode {
SELECTION_MODE_NONE,
@ -63,6 +57,19 @@ public:
SELECTION_MODE_LINE
};
/* Line Wrapping.*/
enum LineWrappingMode {
LINE_WRAPPING_NONE,
LINE_WRAPPING_BOUNDARY
};
/* Gutters. */
enum GutterType {
GUTTER_TYPE_STRING,
GUTTER_TYPE_ICON,
GUTTER_TYPE_CUSTOM
};
private:
struct GutterInfo {
GutterType type = GutterType::GUTTER_TYPE_STRING;
@ -183,7 +190,7 @@ private:
/* Text manipulation */
String cut_copy_line = "";
/* Caret */
/* Caret. */
struct Caret {
Point2 draw_pos;
bool visible = false;
@ -217,6 +224,7 @@ private:
void _reset_caret_blink_timer();
void _toggle_draw_caret();
/* Selection. */
struct Selection {
SelectionMode selecting_mode = SelectionMode::SELECTION_MODE_NONE;
int selecting_line = 0;
@ -236,6 +244,17 @@ private:
bool shiftclick_left = false;
} selection;
/* line wrapping. */
LineWrappingMode line_wrapping_mode = LineWrappingMode::LINE_WRAPPING_NONE;
int wrap_at_column = 0;
int wrap_right_offset = 10;
void _update_wrap_at_column(bool p_force = false);
void _update_caret_wrap_offset();
/* Syntax highlighting. */
Map<int, Dictionary> syntax_highlighting_cache;
struct TextOperation {
@ -295,10 +314,6 @@ private:
bool window_has_focus = true;
bool wrap_enabled = false;
int wrap_at = 0;
int wrap_right_offset = 10;
bool first_draw = true;
bool draw_tabs = false;
bool draw_spaces = false;
@ -362,10 +377,6 @@ private:
int _get_minimap_visible_rows() const;
void _update_caret_wrap_offset();
void _update_wrap_at(bool p_force = false);
Vector<String> get_wrap_rows_text(int p_line) const;
double get_scroll_pos_for_line(int p_line, int p_wrap_index = 0) const;
void set_line_as_first_visible(int p_line, int p_wrap_index = 0);
void set_line_as_center_visible(int p_line, int p_wrap_index = 0);
@ -537,6 +548,16 @@ public:
int get_caret_wrap_index() const;
/* line wrapping. */
void set_line_wrapping_mode(LineWrappingMode p_wrapping_mode);
LineWrappingMode get_line_wrapping_mode() const;
bool is_line_wrapped(int p_line) const;
int get_line_wrap_count(int p_line) const;
int get_line_wrap_index_at_column(int p_line, int p_column) const;
Vector<String> get_line_wrapped_text(int p_line) const;
/* Syntax Highlighting. */
Ref<SyntaxHighlighter> get_syntax_highlighter();
void set_syntax_highlighter(Ref<SyntaxHighlighter> p_syntax_highlighter);
@ -664,7 +685,6 @@ public:
void insert_at(const String &p_text, int at);
int get_line_count() const;
int get_line_width(int p_line, int p_wrap_offset = -1) const;
int get_line_wrap_index_at_col(int p_line, int p_column) const;
void set_line_as_hidden(int p_line, bool p_hidden);
bool is_line_hidden(int p_line) const;
@ -698,11 +718,6 @@ public:
void set_readonly(bool p_readonly);
bool is_readonly() const;
void set_wrap_enabled(bool p_wrap_enabled);
bool is_wrap_enabled() const;
bool line_wraps(int line) const;
int times_line_wraps(int line) const;
void clear();
void delete_selection();
@ -799,8 +814,8 @@ public:
~TextEdit();
};
VARIANT_ENUM_CAST(TextEdit::CaretType);
VARIANT_ENUM_CAST(TextEdit::LineWrappingMode);
VARIANT_ENUM_CAST(TextEdit::SelectionMode);
VARIANT_ENUM_CAST(TextEdit::GutterType);
VARIANT_ENUM_CAST(TextEdit::MenuItems);