extends RefCounted const non_typing_paragraph := "¶" const non_typing_space := "●" const whitespace_chars := [ 32, # " " 44, # "," 58, # ":" 45, # "-" 59, # ";" 40, # "(" 41, # ")" 46, # "." 182, # "¶" Linefeed 10, # "\n" Actual Linefeed 967, # "●" Whitespace ] 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.unicode_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 get_caret_movement_from_key(keycode : int) -> int: match keycode: KEY_LEFT: return -1 KEY_RIGHT: return +1 KEY_HOME: return -2 KEY_END: return +2 return 0 static func multi_move_caret(offset : int, edited_cells_text : Array, edit_caret_positions : Array, whole_word : bool) -> bool: if offset == -1: for i in edit_caret_positions.size(): edit_caret_positions[i] = _step_cursor(edited_cells_text[i], edit_caret_positions[i], -1, whole_word) elif offset == +1: for i in edit_caret_positions.size(): edit_caret_positions[i] = _step_cursor(edited_cells_text[i], edit_caret_positions[i], +1, whole_word) elif offset < -1: for i in edit_caret_positions.size(): edit_caret_positions[i] = 0 elif offset > +1: for i in edit_caret_positions.size(): edit_caret_positions[i] = edited_cells_text[i].length() return offset != 0 static func multi_erase_right(values : Array, cursor_positions : Array, whole_word : bool): for i in values.size(): var start_pos : int = cursor_positions[i] cursor_positions[i] = _step_cursor(values[i], cursor_positions[i], 1, whole_word) cursor_positions[i] = min( cursor_positions[i], values[i].length() ) values[i] = ( values[i].left(start_pos) + values[i].substr(cursor_positions[i]) ) cursor_positions[i] = start_pos return values static func multi_erase_left(values : Array, cursor_positions : Array, whole_word : bool): for i in values.size(): var start_pos : int = cursor_positions[i] cursor_positions[i] = _step_cursor(values[i], cursor_positions[i], -1, whole_word) values[i] = ( values[i].substr(0, cursor_positions[i]) + values[i].substr(start_pos) ) return values static func multi_paste(values : Array, cursor_positions : Array): var pasted_lines := DisplayServer.clipboard_get().replace("\r", "").split("\n") var paste_each_line := pasted_lines.size() == values.size() for i in values.size(): if paste_each_line: cursor_positions[i] += pasted_lines[i].length() else: cursor_positions[i] += DisplayServer.clipboard_get().length() values[i] = ( values[i].left(cursor_positions[i]) + (pasted_lines[i] if paste_each_line else DisplayServer.clipboard_get()) + values[i].substr(cursor_positions[i]) ) return values static func multi_copy(values : Array): DisplayServer.clipboard_set("\n".join(values)) static func multi_input(input_char : String, values : Array, cursor_positions : Array): for i in values.size(): values[i] = ( values[i].left(cursor_positions[i]) + input_char + values[i].substr(cursor_positions[i]) ) cursor_positions[i] = min(cursor_positions[i] + 1, values[i].length()) return values static func get_caret_rect(cell_text : String, caret_position : int, font : Font, font_size : int, label_padding_left : float, caret_width : float = 2.0) -> Rect2: var font_height := font.get_height(font_size) var char_size := Vector2(0, font_height) var result_pos := Vector2(label_padding_left, 0) for j in max(caret_position, 0) + 1: if j == 0: continue if cell_text.unicode_at(j - 1) == 10: # If "\n" found, next line. # The 2.0 is ACTUALLY not 2.0 and varies per character. # Since get_char_size() does not return the correct size, this will cause problems with non-Latin characters result_pos.x = label_padding_left result_pos.y += font_height - 2.0 font_height = 0.0 continue char_size = font.get_char_size(cell_text.unicode_at(j - 1), font_size) font_height = maxf(char_size.y, font_height) result_pos.x += char_size.x return Rect2(result_pos + Vector2(1.0, 0.0), Vector2(caret_width, char_size.y)) static func _step_cursor(text : String, start : int, step : int = 1, whole_word : bool = false) -> int: var cur := start if whole_word and is_character_whitespace(text, cur + step): cur += step while true: cur += step if !whole_word or is_character_whitespace(text, cur): if cur > text.length(): return text.length() if cur <= 0: return 0 if whole_word and step < 0: return cur + 1 return cur return 0