From 6a05d7827cdd95fd3c52058993a9c63c2171c87e Mon Sep 17 00:00:00 2001 From: don-tnowe <67479453+don-tnowe@users.noreply.github.com> Date: Tue, 6 Jun 2023 13:21:04 +0300 Subject: [PATCH] Huge text cursor and cell selection improvements --- .../editor_view.tscn | 7 +- .../main_screen/input_handler.gd | 51 +++++++------ .../main_screen/selection_manager.gd | 72 +++++++++++++++---- .../text_editing_utils.gd | 18 +++-- 4 files changed, 103 insertions(+), 45 deletions(-) diff --git a/addons/resources_spreadsheet_view/editor_view.tscn b/addons/resources_spreadsheet_view/editor_view.tscn index 981a67d..fc1c6ce 100644 --- a/addons/resources_spreadsheet_view/editor_view.tscn +++ b/addons/resources_spreadsheet_view/editor_view.tscn @@ -466,10 +466,13 @@ text = "Edit Resources as Spreadsheet Possible inputs: - Ctrl + Click / Cmd + Click - Select multiple cells in one column - Shift + Click - Select all cells between A and B in one column +- Up / Down / Shift+Tab / Tab - move cell selection up/down/left/right + - Left/Right - Move cursor along cell text - Backspace/Delete - Erase text Left / Right from cursor - Home/End - Move cursor to start/end of cell - Ctrl + / Cmd + - Move through / Erase whole word + - Ctrl/Cmd + C/V - Copy cells / Paste text into cells - Ctrl/Cmd + (Shift) + Z - The Savior If clipboard contains as many lines as there are cells selected, each line is pasted into a separate cell. @@ -507,7 +510,9 @@ offset_bottom = 117.0 [node name="InputHandler" type="Node" parent="."] script = ExtResource("14_2t57a") -[node name="SelectionManager" type="Node" parent="." node_paths=PackedStringArray("node_property_editors")] +[node name="SelectionManager" type="Control" parent="." node_paths=PackedStringArray("node_property_editors")] +layout_mode = 2 +mouse_filter = 2 script = ExtResource("15_mx6qn") cell_editor_classes = Array[Script]([ExtResource("16_p7n52"), ExtResource("17_sofdw"), ExtResource("18_oeewr"), ExtResource("19_7x44x"), ExtResource("20_swsbn"), ExtResource("21_58wf8"), ExtResource("22_bni8r")]) node_property_editors = NodePath("../HeaderContentSplit/MarginContainer/FooterContentSplit/Footer/PropertyEditors") diff --git a/addons/resources_spreadsheet_view/main_screen/input_handler.gd b/addons/resources_spreadsheet_view/main_screen/input_handler.gd index 20e4c20..9d170c7 100644 --- a/addons/resources_spreadsheet_view/main_screen/input_handler.gd +++ b/addons/resources_spreadsheet_view/main_screen/input_handler.gd @@ -11,17 +11,14 @@ const SelectionManager = preload("res://addons/resources_spreadsheet_view/main_s func _on_cell_gui_input(event : InputEvent, cell : Control): if event is InputEventMouseButton: editor_view.grab_focus() - if event.button_index != MOUSE_BUTTON_LEFT: - if event.button_index == MOUSE_BUTTON_RIGHT && event.is_pressed(): - if !cell in selection.edited_cells: - selection.deselect_all_cells() - selection.select_cell(cell) + if event.button_index == MOUSE_BUTTON_RIGHT and event.pressed: + if !cell in selection.edited_cells: + selection.deselect_all_cells() + selection.select_cell(cell) - selection.rightclick_cells() + selection.rightclick_cells() - return - - if event.pressed: + if event.button_index == MOUSE_BUTTON_LEFT and event.pressed: if Input.is_key_pressed(KEY_CTRL): if cell in selection.edited_cells: selection.deselect_cell(cell) @@ -39,15 +36,13 @@ func _on_cell_gui_input(event : InputEvent, cell : Control): func _gui_input(event : InputEvent): if event is InputEventMouseButton: - if event.button_index != MOUSE_BUTTON_LEFT: - if event.button_index == MOUSE_BUTTON_RIGHT && event.is_pressed(): - selection.rightclick_cells() + if event.button_index == MOUSE_BUTTON_RIGHT and event.is_pressed(): + selection.rightclick_cells() - return - - editor_view.grab_focus() - if !event.pressed: - selection.deselect_all_cells() + if event.button_index == MOUSE_BUTTON_LEFT: + editor_view.grab_focus() + if !event.pressed: + selection.deselect_all_cells() func _input(event : InputEvent): @@ -128,7 +123,7 @@ func _key_specific_action(event : InputEvent): # Ctrl + C (so you can edit in a proper text editor instead of this wacky nonsense) elif ctrl_pressed and event.keycode == KEY_C: TextEditingUtils.multi_copy(selection.edited_cells_text) - get_tree().set_input_as_handled() + get_viewport().set_input_as_handled() # The following actions do not work on non-editable cells. if !selection.column_editors[column].is_text() or editor_view.columns[column] == "resource_path": @@ -139,7 +134,7 @@ func _key_specific_action(event : InputEvent): editor_view.set_edited_cells_values(TextEditingUtils.multi_paste( selection.edited_cells_text, selection.edit_cursor_positions )) - get_tree().set_input_as_handled() + get_viewport().set_input_as_handled() # ERASING elif event.keycode == KEY_BACKSPACE: @@ -164,14 +159,18 @@ func _key_specific_action(event : InputEvent): char(event.unicode), selection.edited_cells_text, selection.edit_cursor_positions )) + selection.queue_redraw() + func _move_selection_on_grid(move_h : int, move_v : int): - var cell = selection.edited_cells[0] + var selected_cells := selection.edited_cells.duplicate() + for i in selected_cells.size(): + selected_cells[i] = editor_view.node_table_root.get_child( + selected_cells[i].get_index() + + move_h + + move_v * editor_view.columns.size() + ) + editor_view.grab_focus() selection.deselect_all_cells() - selection.select_cell( - editor_view.node_table_root.get_child( - cell.get_index() - + move_h + move_v * editor_view.columns.size() - ) - ) + selection.select_cells(selected_cells) diff --git a/addons/resources_spreadsheet_view/main_screen/selection_manager.gd b/addons/resources_spreadsheet_view/main_screen/selection_manager.gd index 9e7ac46..5f82d90 100644 --- a/addons/resources_spreadsheet_view/main_screen/selection_manager.gd +++ b/addons/resources_spreadsheet_view/main_screen/selection_manager.gd @@ -1,5 +1,5 @@ @tool -extends Node +extends Control signal cells_selected(cells) signal cells_rightclicked(cells) @@ -9,12 +9,13 @@ const EditorView = preload("res://addons/resources_spreadsheet_view/editor_view. @export var cell_editor_classes : Array[Script] = [] @export @onready var node_property_editors : VBoxContainer = $"../HeaderContentSplit/MarginContainer/FooterContentSplit/Footer/PropertyEditors" +@export @onready var scrollbar : ScrollContainer = $"../HeaderContentSplit/MarginContainer/FooterContentSplit/Panel/Scroll" @onready var editor_view : EditorView = get_parent() -var edited_cells := [] -var edited_cells_text := [] -var edit_cursor_positions := [] +var edited_cells = [] +var edited_cells_text : Array[String] = [] +var edit_cursor_positions : Array[int] = [] var all_cell_editors : Array[Object] var column_editors := [] @@ -33,6 +34,38 @@ func _ready(): .property_edited\ .connect(_on_inspector_property_edited) + scrollbar.get_h_scroll_bar().value_changed.connect(queue_redraw.unbind(1), CONNECT_DEFERRED) + scrollbar.get_v_scroll_bar().value_changed.connect(queue_redraw.unbind(1), CONNECT_DEFERRED) + + +func _draw(): + var font := get_theme_font("font", "Label") + var font_size := get_theme_font_size("font", "Label") + var label_padding_left := 2.0 + var newline_char := 10 + for i in edited_cells.size(): + if edit_cursor_positions[i] >= edited_cells_text[i].length(): + continue + + var char_size := Vector2(0, font.get_ascent(font_size)) + var cursor_pos := Vector2(label_padding_left, 0) + var cell_text := edited_cells_text[i] + var cell : Control = edited_cells[i] + if cell is Label and cell.horizontal_alignment == HORIZONTAL_ALIGNMENT_RIGHT: + cursor_pos.x += cell.size.x - font.get_multiline_string_size(edited_cells[i].text, HORIZONTAL_ALIGNMENT_RIGHT, -1, font_size).x + + for j in max(edit_cursor_positions[i], 0) + 1: + if j == 0: continue + if cell_text.unicode_at(j - 1) == newline_char: + cursor_pos.x = label_padding_left + cursor_pos.y += font.get_ascent(font_size) + continue + + char_size = font.get_char_size(cell_text.unicode_at(j - 1), font_size) + cursor_pos.x += char_size.x + + draw_rect(Rect2(cursor_pos + cell.global_position - global_position, Vector2(2, char_size.y)), Color(1, 1, 1, 0.5)) + func initialize_editors(column_values, column_types, column_hints): deselect_all_cells() @@ -55,7 +88,7 @@ func deselect_all_cells(): edited_cells.clear() edited_cells_text.clear() edit_cursor_positions.clear() - cells_selected.emit([]) + _selection_changed() func deselect_cell(cell : Control): @@ -68,7 +101,7 @@ func deselect_cell(cell : Control): edited_cells_text.remove_at(idx) edit_cursor_positions.remove_at(idx) - cells_selected.emit(edited_cells) + _selection_changed() func select_cell(cell : Control): @@ -81,7 +114,18 @@ func select_cell(cell : Control): # inspector_resource.resource_path = "" editor_view.editor_plugin.get_editor_interface().edit_resource(inspector_resource) - cells_selected.emit(edited_cells) + _selection_changed() + + +func select_cells(cells : Array): + var last_selectible = null + for x in cells: + if can_select_cell(x): + _add_cell_to_selection(x) + last_selectible = x + + if last_selectible != null: + select_cell(last_selectible) func select_cells_to(cell : Control): @@ -108,7 +152,7 @@ func select_cells_to(cell : Control): edited_cells_text.append(str(cur_cell.text)) edit_cursor_positions.append(cur_cell.text.length()) - cells_selected.emit(edited_cells) + _selection_changed() func rightclick_cells(): @@ -118,16 +162,13 @@ func rightclick_cells(): func can_select_cell(cell : Control) -> bool: if edited_cells.size() == 0: return true - - if !Input.is_key_pressed(KEY_CTRL): - return false - + if ( get_cell_column(cell) != get_cell_column(edited_cells[0]) ): return false - + return !cell in edited_cells @@ -148,6 +189,11 @@ func get_edited_rows() -> Array[int]: return rows +func _selection_changed(): + queue_redraw() + cells_selected.emit(edited_cells) + + func _add_cell_to_selection(cell : Control): var column_editor = column_editors[get_cell_column(cell)] column_editor.set_selected(cell, true) diff --git a/addons/resources_spreadsheet_view/text_editing_utils.gd b/addons/resources_spreadsheet_view/text_editing_utils.gd index e811e8f..5e348cd 100644 --- a/addons/resources_spreadsheet_view/text_editing_utils.gd +++ b/addons/resources_spreadsheet_view/text_editing_utils.gd @@ -13,6 +13,7 @@ const whitespace_chars := [ 41, # ")" 46, # "." 182, # "¶" Linefeed + 10, # "\n" Actual Linefeed 967, # "●" Whitespace ] @@ -128,15 +129,22 @@ static func multi_input(input_char : String, values : Array, cursor_positions : static func _step_cursor(text : String, start : int, step : int = 1, ctrl_pressed : bool = false) -> int: + var cur := start + if ctrl_pressed and is_character_whitespace(text, cur + step): + cur += step + while true: - start += step - if !ctrl_pressed or is_character_whitespace(text, start): - if start > text.length(): + cur += step + if !ctrl_pressed or is_character_whitespace(text, cur): + if cur > text.length(): return text.length() - if start < 0: + if cur <= 0: return 0 - return start + if ctrl_pressed and step < 0: + return cur + 1 + + return cur return 0