class_name TextEditingUtils extends Reference const non_typing_paragraph := "¶" const non_typing_space := "●" const whitespace_chars := [ ord(" "), ord(","), ord(":"), ord("-"), ord(";"), ord("("), ord(")"), ord("."), ord(non_typing_paragraph), ord(non_typing_space), ] static func is_character_whitespace(text : String, idx : int) -> bool: if idx <= 0: return true # Stop at the edges. if idx >= text.length(): return true return text.ord_at(idx) in whitespace_chars static func show_non_typing(text : String) -> String: text = text\ .replace(non_typing_paragraph, "\n")\ .replace(non_typing_space, " ") if text.ends_with("\n"): text = text.left(text.length() - 1) + non_typing_paragraph elif text.ends_with(" "): text = text.left(text.length() - 1) + non_typing_space return text static func revert_non_typing(text : String) -> String: if text.ends_with(non_typing_paragraph): text = text.left(text.length() - 1) + "\n" elif text.ends_with(non_typing_space): text = text.left(text.length() - 1) + " " return text static func multi_erase_right(edited_cells : Array, edit_cursor_positions : Array, cell_editor : Reference, callback_object : Object): for i in edited_cells.size(): var cell = edited_cells[i] var start_pos = edit_cursor_positions[i] while true: edit_cursor_positions[i] += 1 if !Input.is_key_pressed(KEY_CONTROL) or is_character_whitespace(_get_cell_text(cell, cell_editor), edit_cursor_positions[i]): break edit_cursor_positions[i] = min( edit_cursor_positions[i], edited_cells[i].text.length() ) callback_object.set_cell(cell, ( _get_cell_text(cell, cell_editor).left(start_pos) + _get_cell_text(cell, cell_editor).substr(edit_cursor_positions[i]) )) edit_cursor_positions[i] = start_pos static func multi_erase_left(edited_cells : Array, edit_cursor_positions : Array, cell_editor : Reference, callback_object : Object): for i in edited_cells.size(): var cell = edited_cells[i] var start_pos = edit_cursor_positions[i] edit_cursor_positions[i] = _step_cursor(_get_cell_text(cell, cell_editor), edit_cursor_positions[i], -1) var result_text = ( _get_cell_text(cell, cell_editor).substr(0, edit_cursor_positions[i]) + _get_cell_text(cell, cell_editor).substr(start_pos) ) callback_object.set_cell(cell, ( _get_cell_text(cell, cell_editor).substr(0, edit_cursor_positions[i]) + _get_cell_text(cell, cell_editor).substr(start_pos) )) static func multi_move_left(edited_cells : Array, edit_cursor_positions : Array, cell_editor : Reference): for i in edit_cursor_positions.size(): edit_cursor_positions[i] = _step_cursor(edited_cells[i].text, edit_cursor_positions[i], -1) static func multi_move_right(edited_cells : Array, edit_cursor_positions : Array, cell_editor : Reference): for i in edit_cursor_positions.size(): edit_cursor_positions[i] = _step_cursor(edited_cells[i].text, edit_cursor_positions[i], 1) static func multi_paste(edited_cells : Array, edit_cursor_positions : Array, cell_editor : Reference, callback_object : Object): var pasted_lines := OS.clipboard.split("\n") var paste_each_line := pasted_lines.size() == edited_cells.size() for i in edited_cells.size(): if paste_each_line: edit_cursor_positions[i] += pasted_lines[i].length() else: edit_cursor_positions[i] += OS.clipboard.length() var cell = edited_cells[i] callback_object.set_cell(cell, ( _get_cell_text(cell, cell_editor).left(edit_cursor_positions[i]) + (pasted_lines[i] if paste_each_line else OS.clipboard) + _get_cell_text(cell, cell_editor).substr(edit_cursor_positions[i]) )) static func multi_copy(edited_cells : Array): var copied_text := "" for i in edited_cells.size(): copied_text += "\n" + edited_cells[i].text # Cut the first \n out. OS.clipboard = copied_text.substr(1) static func multi_linefeed(edited_cells : Array, edit_cursor_positions : Array, cell_editor : Reference, callback_object : Object): for i in edited_cells.size(): var cell = edited_cells[i] callback_object.set_cell(cell, ( _get_cell_text(cell, cell_editor).left(edit_cursor_positions[i]) + "\n" + _get_cell_text(cell, cell_editor).substr(edit_cursor_positions[i]) )) edit_cursor_positions[i] = min(edit_cursor_positions[i] + 1, _get_cell_text(cell, cell_editor).length()) static func multi_input(input_char : String, edited_cells : Array, edit_cursor_positions : Array, cell_editor : Reference, callback_object : Object): for i in edited_cells.size(): var cell = edited_cells[i] callback_object.set_cell(cell, ( _get_cell_text(cell, cell_editor).left(edit_cursor_positions[i]) + input_char + _get_cell_text(cell, cell_editor).substr(edit_cursor_positions[i]) )) edit_cursor_positions[i] = min(edit_cursor_positions[i] + 1, _get_cell_text(cell, cell_editor).length()) static func _get_cell_text(cell : Control, cell_editor : Reference): return cell_editor.get_text_value(cell) static func _step_cursor(text : String, start : int, step : int = 1) -> int: while true: start += step if !Input.is_key_pressed(KEY_CONTROL) or is_character_whitespace(text, start): if start > text.length(): return text.length() if start < 0: return 0 return start return 0