From 2f9d92e8ca351058ef16bccebba469d5da2edae3 Mon Sep 17 00:00:00 2001 From: don-tnowe <67479453+don-tnowe@users.noreply.github.com> Date: Mon, 23 Jan 2023 18:06:09 +0200 Subject: [PATCH] Refactor to split main megaclass --- .../editor_stylebox_overrider.gd | 21 - .../resources_spreadsheet_view/editor_view.gd | 616 ++---------------- .../editor_view.tscn | 98 +-- .../main_screen/column_header_manager.gd | 144 ++++ .../main_screen/input_handler.gd | 177 +++++ .../main_screen/recent_paths.gd | 54 ++ .../{ => main_screen}/selection_actions.gd | 0 .../{ => main_screen}/selection_actions.tscn | 0 .../main_screen/selection_manager.gd | 197 ++++++ .../{ => main_screen}/table_header.gd | 6 +- .../{ => main_screen}/table_header.tscn | 8 +- .../{ => main_screen}/table_pages.gd | 0 .../typed_cells/cell_editor.gd | 2 +- .../typed_editors/dock_base.gd | 4 +- 14 files changed, 699 insertions(+), 628 deletions(-) delete mode 100644 addons/resources_spreadsheet_view/editor_stylebox_overrider.gd create mode 100644 addons/resources_spreadsheet_view/main_screen/column_header_manager.gd create mode 100644 addons/resources_spreadsheet_view/main_screen/input_handler.gd create mode 100644 addons/resources_spreadsheet_view/main_screen/recent_paths.gd rename addons/resources_spreadsheet_view/{ => main_screen}/selection_actions.gd (100%) rename addons/resources_spreadsheet_view/{ => main_screen}/selection_actions.tscn (100%) create mode 100644 addons/resources_spreadsheet_view/main_screen/selection_manager.gd rename addons/resources_spreadsheet_view/{ => main_screen}/table_header.gd (85%) rename addons/resources_spreadsheet_view/{ => main_screen}/table_header.tscn (80%) rename addons/resources_spreadsheet_view/{ => main_screen}/table_pages.gd (100%) diff --git a/addons/resources_spreadsheet_view/editor_stylebox_overrider.gd b/addons/resources_spreadsheet_view/editor_stylebox_overrider.gd deleted file mode 100644 index 246988b..0000000 --- a/addons/resources_spreadsheet_view/editor_stylebox_overrider.gd +++ /dev/null @@ -1,21 +0,0 @@ -@tool -class_name ThemeStylebox -extends Control - -@export var box_class := "EditorStyles" -@export var box_name := "Background" - - -func _ready(): - _set_box_name(box_name) - _set_box_class(box_class) - - -func _set_box_name(v): - box_name = v - add_theme_stylebox_override(box_name, get_theme_stylebox(box_name, box_class)) - - -func _set_box_class(v): - box_class = v - add_theme_stylebox_override(box_name, get_theme_stylebox(box_name, box_class)) diff --git a/addons/resources_spreadsheet_view/editor_view.gd b/addons/resources_spreadsheet_view/editor_view.gd index 8e87415..bfb9035 100644 --- a/addons/resources_spreadsheet_view/editor_view.gd +++ b/addons/resources_spreadsheet_view/editor_view.gd @@ -5,74 +5,49 @@ signal grid_updated() signal cells_selected(cells) signal cells_context(cells) -@export var table_header_scene : PackedScene -@export var cell_editor_classes : Array[Script] = [] - @export @onready var node_folder_path : LineEdit = $"HeaderContentSplit/VBoxContainer/HBoxContainer/HBoxContainer/Path" @export @onready var node_recent_paths : OptionButton = $"HeaderContentSplit/VBoxContainer/HBoxContainer/HBoxContainer2/RecentPaths" @export @onready var node_table_root : GridContainer = $"HeaderContentSplit/MarginContainer/FooterContentSplit/Panel/Scroll/MarginContainer/TableGrid" -@export @onready var node_property_editors : VBoxContainer = $"HeaderContentSplit/MarginContainer/FooterContentSplit/Footer/PropertyEditors" @export @onready var node_columns : HBoxContainer = $"HeaderContentSplit/VBoxContainer/Columns/Columns" -@export @onready var node_hide_columns_button : BaseButton = $"HeaderContentSplit/VBoxContainer/MenuStrip/VisibleCols" @export @onready var node_page_manager : Control = $"HeaderContentSplit/VBoxContainer/HBoxContainer3/Pages" +@onready var _on_cell_gui_input = $"InputHandler"._on_cell_gui_input +@onready var _selection = $"SelectionManager" + var editor_interface : EditorInterface var editor_plugin : EditorPlugin var current_path := "" -var recent_paths := [] var save_data_path : String = get_script().resource_path.get_base_dir() + "/saved_state.json" var sorting_by := "" var sorting_reverse := false -var all_cell_editors := [] - var columns := [] var column_types := [] var column_hints := [] var column_hint_strings := [] -var column_editors := [] var rows := [] var remembered_paths := {} -var edited_cells := [] -var edited_cells_text := [] -var edit_cursor_positions := [] - -var inspector_resource : Resource var search_cond : RefCounted var io : RefCounted -var hidden_columns := {} var first_row := 0 var last_row := 0 func _ready(): - node_recent_paths.clear() editor_interface.get_resource_filesystem().filesystem_changed.connect(_on_filesystem_changed) - editor_interface.get_inspector().property_edited.connect(_on_inspector_property_edited) - node_hide_columns_button.get_popup().id_pressed.connect(_on_visible_cols_id_pressed) - node_table_root.get_node("../..").get_h_scroll_bar().value_changed.connect(func(x): node_columns.position.x = -x) - - # Load saved recent paths if FileAccess.file_exists(save_data_path): var file = FileAccess.open(save_data_path, FileAccess.READ) - var as_text = file.get_as_text() var as_var = JSON.parse_string(as_text) - for x in as_var["recent_paths"]: - add_path_to_recent(x, true) - hidden_columns = as_var.get("hidden_columns", {}) + node_recent_paths.load_paths(as_var.get("recent_paths", [])) + node_columns.hidden_columns = as_var.get("hidden_columns", {}) - # Load cell editors and instantiate them - for x in cell_editor_classes: - all_cell_editors.append(x.new()) - all_cell_editors[all_cell_editors.size() - 1].hint_strings_array = column_hint_strings - - node_recent_paths.selected = 0 - display_folder(recent_paths[0], "resource_name", false, true) + if node_recent_paths.recent_paths.size() >= 1: + display_folder(node_recent_paths.recent_paths[0], "resource_name", false, true) func _on_filesystem_changed(): @@ -106,7 +81,7 @@ func display_folder(folderpath : String, sort_by : String = "", sort_reverse : b if search_cond == null: _on_search_cond_text_submitted("true") - add_path_to_recent(folderpath) + node_recent_paths.add_path_to_recent(folderpath) first_row = node_page_manager.first_row last_row = min(node_page_manager.last_row, rows.size()) _load_resources_from_folder(folderpath, sort_by, sort_reverse) @@ -114,14 +89,13 @@ func display_folder(folderpath : String, sort_by : String = "", sort_reverse : b if columns.size() == 0: return node_folder_path.text = folderpath - _create_table( + _update_table( force_rebuild or current_path != folderpath or columns.size() != node_columns.get_child_count() ) current_path = folderpath - _update_hidden_columns() - _update_column_sizes() + node_columns.update() await get_tree().create_timer(0.25).timeout if node_table_root.get_child_count() == 0: @@ -151,19 +125,18 @@ func fill_property_data(res): column_types.clear() column_hints.clear() column_hint_strings.clear() - column_editors.clear() - var column_index = -1 + var column_values := [] + var i := -1 for x in res.get_property_list(): if x["usage"] & PROPERTY_USAGE_EDITOR != 0 and x["name"] != "script": - column_index += 1 + i += 1 columns.append(x["name"]) column_types.append(x["type"]) column_hints.append(x["hint"]) column_hint_strings.append(x["hint_string"].split(",")) - for y in all_cell_editors: - if y.can_edit_value(io.get_value(res, x["name"]), x["type"], x["hint"], column_index): - column_editors.append(y) - break + column_values.append(io.get_value(res, columns[i])) + + _selection.initialize_editors(column_values, column_types, column_hints) func insert_row_sorted(res : Resource, rows : Array, sort_by : String, sort_reverse : bool): @@ -199,33 +172,14 @@ func _set_sorting(sort_by): sorting_by = sort_by -func _select_column(column_name): - deselect_all_cells() - select_cell(node_table_root.get_child(columns.find(column_name))) - select_cells_to(node_table_root.get_child(columns.find(column_name) + columns.size() * (rows.size() - 1))) - - -func _create_table(columns_changed : bool): - deselect_all_cells() - edited_cells = [] - edited_cells_text = [] - edit_cursor_positions = [] - var new_node : Control +func _update_table(columns_changed : bool): if columns_changed: node_table_root.columns = columns.size() for x in node_table_root.get_children(): x.free() - - for x in node_columns.get_children(): - x.queue_free() - for x in columns: - new_node = table_header_scene.instantiate() - node_columns.add_child(new_node) - new_node.editor_view = self - new_node.set_label(x) - new_node.get_node("Button").pressed.connect(_set_sorting.bind(x)) - + node_columns.columns = columns + var to_free = node_table_root.get_child_count() - (last_row - first_row) * columns.size() while to_free > 0: node_table_root.get_child(0).free() @@ -245,53 +199,10 @@ func _update_row_range(first : int, last : int, color_rows : bool): _update_row(first + i, color_rows) -func _update_column_sizes(): - if node_table_root.get_child_count() == 0: - return - - await get_tree().process_frame - var column_headers := node_columns.get_children() - - if node_table_root.get_child_count() < column_headers.size(): return - if column_headers.size() != columns.size(): - refresh() - return - - var clip_text : bool = ProjectSettings.get_setting(SettingsGrid.SETTING_PREFIX + "clip_headers") - var min_width := 0 - var cell : Control - - for i in column_headers.size(): - var header = column_headers[i] - cell = node_table_root.get_child(i) - - header.get_child(0).clip_text = clip_text - header.custom_minimum_size.x = 0 - cell.custom_minimum_size.x = 0 - header.size.x = 0 - node_columns.queue_sort() - - min_width = max(header.size.x, cell.size.x) - header.custom_minimum_size.x = min_width - cell.custom_minimum_size.x = header.get_minimum_size().x - header.size.x = min_width - - node_table_root.hide() - node_columns.hide() - node_table_root.show() - node_columns.show() - - await get_tree().process_frame - - node_columns.get_parent().custom_minimum_size.y = column_headers[0].size.y - for i in column_headers.size(): - column_headers[i].position.x = node_table_root.get_child(i).position.x - column_headers[i].size.x = node_table_root.get_child(i).size.x - - func _update_row(row_index : int, color_rows : bool = true): var current_node : Control var next_color := Color.WHITE + var column_editors = _selection.column_editors for i in columns.size(): if node_table_root.get_child_count() <= (row_index - first_row) * columns.size() + i: current_node = column_editors[i].create_cell(self) @@ -316,61 +227,12 @@ func _update_row(row_index : int, color_rows : bool = true): column_editors[i].set_color(current_node, next_color) -func _update_hidden_columns(): - if !hidden_columns.has(current_path): - hidden_columns[current_path] = {} - return - - var visible_column_count = 0 - for i in columns.size(): - var column_visible = !hidden_columns[current_path].has(columns[i]) - - node_columns.get_child(i).visible = column_visible - for j in last_row - first_row: - node_table_root.get_child(j * columns.size() + i).visible = column_visible - - if column_visible: - visible_column_count += 1 - - node_table_root.columns = visible_column_count - - -func add_path_to_recent(path : String, is_loading : bool = false): - if path in recent_paths: return - - var idx_in_array := recent_paths.find(path) - if idx_in_array != -1: - node_recent_paths.remove_item(idx_in_array) - recent_paths.remove_at(idx_in_array) - - recent_paths.append(path) - node_recent_paths.add_item(path) - node_recent_paths.select(node_recent_paths.get_item_count() - 1) - - if !is_loading: - save_data() - - -func remove_selected_path_from_recent(): - if node_recent_paths.get_item_count() == 0: - return - - var idx_in_array = node_recent_paths.selected - recent_paths.remove_at(idx_in_array) - node_recent_paths.remove_item(idx_in_array) - - if node_recent_paths.get_item_count() != 0: - node_recent_paths.select(0) - display_folder(recent_paths[0]) - save_data() - - func save_data(): var file = FileAccess.open(save_data_path, FileAccess.WRITE) file.store_string(str( { - "recent_paths" : recent_paths, - "hidden_columns" : hidden_columns, + "recent_paths" : node_recent_paths.recent_paths, + "hidden_columns" : node_columns.hidden_columns, } )) @@ -384,118 +246,28 @@ func _on_path_text_submitted(new_text : String = ""): refresh() -func _on_RecentPaths_item_selected(index : int): - current_path = recent_paths[index] - node_folder_path.text = recent_paths[index] - display_folder(current_path, sorting_by, sorting_reverse, true) - - func _on_FileDialog_dir_selected(dir : String): node_folder_path.text = dir display_folder(dir) -func deselect_all_cells(): - for x in edited_cells: - column_editors[_get_cell_column(x)].set_selected(x, false) - - edited_cells.clear() - edited_cells_text.clear() - edit_cursor_positions.clear() - cells_selected.emit([]) - - -func deselect_cell(cell : Control): - var idx := edited_cells.find(cell) - if idx == -1: return - - column_editors[_get_cell_column(cell)].set_selected(cell, false) - edited_cells.remove_at(idx) - if edited_cells_text.size() != 0: - edited_cells_text.remove_at(idx) - edit_cursor_positions.remove_at(idx) - - cells_selected.emit(edited_cells) - - -func select_cell(cell : Control): - var column_index := _get_cell_column(cell) - if _can_select_cell(cell): - _add_cell_to_selection(cell) - _try_open_docks(cell) -# inspector_resource = rows[_get_cell_row(cell)].duplicate() # - inspector_resource = rows[_get_cell_row(cell)] - editor_plugin.get_editor_interface().edit_resource(inspector_resource) - - cells_selected.emit(edited_cells) - - -func select_cells_to(cell : Control): - var column_index := _get_cell_column(cell) - if column_index != _get_cell_column(edited_cells[edited_cells.size() - 1]): - return - - var row_start = _get_cell_row(edited_cells[edited_cells.size() - 1]) - first_row - var row_end := _get_cell_row(cell) - first_row - var edge_shift = -1 if row_start > row_end else 1 - row_start += edge_shift - row_end += edge_shift - - for i in range(row_start, row_end, edge_shift): - var cur_cell := node_table_root.get_child(i * columns.size() + column_index) - if !cur_cell.visible: - # When search is active, some cells will be hidden. - continue - - column_editors[column_index].set_selected(cur_cell, true) - if !cur_cell in edited_cells: - edited_cells.append(cur_cell) - if column_editors[column_index].is_text(): - edited_cells_text.append(str(cur_cell.text)) - edit_cursor_positions.append(cur_cell.text.length()) - - cells_selected.emit(edited_cells) +func get_selected_column() -> int: + return _selection.get_cell_column(_selection.edited_cells[0]) func select_column(column_index : int): - deselect_all_cells() - select_cell(node_table_root.get_child(column_index)) - select_cells_to(node_table_root.get_child(column_index + columns.size() * (last_row - first_row - 1))) - - -func hide_column(column_index : int): - hidden_columns[current_path][columns[column_index]] = true - save_data() - _update_hidden_columns() - _update_column_sizes() - - -func get_selected_column() -> int: - return _get_cell_column(edited_cells[0]) - - -func _add_cell_to_selection(cell : Control): - column_editors[_get_cell_column(cell)].set_selected(cell, true) - edited_cells.append(cell) - if column_editors[_get_cell_column(cell)].is_text(): - edited_cells_text.append(str(cell.text)) - edit_cursor_positions.append(cell.text.length()) - - -func _try_open_docks(cell : Control): - var column_index = _get_cell_column(cell) - for x in node_property_editors.get_children(): - x.visible = x.try_edit_value( - io.get_value(rows[_get_cell_row(cell)], columns[column_index]), - column_types[column_index], - column_hints[column_index] - ) - x.get_node(x.path_property_name).text = columns[column_index] + _selection.deselect_all_cells() + _selection.select_cell(node_table_root.get_child(column_index)) + _selection.select_cells_to(node_table_root.get_child( + column_index + columns.size() + * (last_row - first_row - 1)) + ) func set_edited_cells_values(new_cell_values : Array): - var column = _get_cell_column(edited_cells[0]) - var edited_cells_resources = _get_edited_cells_resources() + var edited_rows = _selection.get_edited_rows() + var column = _selection.get_cell_column(_selection.edited_cells[0]) + var edited_cells_resources = _get_row_resources(edited_rows) # Duplicated here since if using text editing, edited_cells_text needs to modified # but here, it would be converted from a String breaking editing @@ -504,27 +276,26 @@ func set_edited_cells_values(new_cell_values : Array): editor_plugin.undo_redo.create_action("Set Cell Values") editor_plugin.undo_redo.add_undo_method( self, - "_update_resources", + &"_update_resources", edited_cells_resources.duplicate(), - edited_cells.duplicate(), + edited_rows.duplicate(), column, get_edited_cells_values() ) editor_plugin.undo_redo.add_undo_method( - self, - "_update_selected_cells_text" + _selection, + &"_update_selected_cells_text" ) editor_plugin.undo_redo.add_do_method( self, - "_update_resources", + &"_update_resources", edited_cells_resources.duplicate(), - edited_cells.duplicate(), + edited_rows.duplicate(), column, new_cell_values.duplicate() ) editor_plugin.undo_redo.commit_action() editor_interface.get_resource_filesystem().scan() - _update_column_sizes() func rename_row(row, new_name): @@ -535,12 +306,12 @@ func rename_row(row, new_name): func duplicate_selected_rows(new_name : String): - io.duplicate_rows(_get_edited_cells_resources(), new_name) + io.duplicate_rows(_get_row_resources(_selection.get_edited_rows()), new_name) refresh() func delete_selected_rows(): - io.delete_rows(_get_edited_cells_resources()) + io.delete_rows(_get_row_resources(_selection.get_edited_rows())) refresh() @@ -549,233 +320,26 @@ func has_row_names(): func get_last_selected_row(): - return rows[_get_cell_row(edited_cells[-1])] - - -func _update_selected_cells_text(): - if edited_cells_text.size() == 0: - return - - for i in edited_cells.size(): - edited_cells_text[i] = str(edited_cells[i].text) - edit_cursor_positions[i] = edited_cells_text[i].length() + return rows[_selection.get_cell_row(_selection.edited_cells[-1])] func get_edited_cells_values() -> Array: - var arr := edited_cells.duplicate() - var column_index := _get_cell_column(edited_cells[0]) - var cell_editor = column_editors[column_index] + var arr = _selection.edited_cells.duplicate() + var column_index = _selection.get_cell_column(_selection.edited_cells[0]) + var cell_editor = _selection.column_editors[column_index] for i in arr.size(): - arr[i] = io.get_value(rows[_get_cell_row(arr[i])], columns[column_index]) - + arr[i] = io.get_value(rows[_selection.get_cell_row(arr[i])], columns[column_index]) + return arr -func get_cell_value(cell : Control): - return io.get_value(rows[_get_cell_row(cell)], columns[_get_cell_column(cell)]) - - -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 - - -func _get_cell_column(cell) -> int: - return cell.get_index() % columns.size() - - -func _get_cell_row(cell) -> int: - return cell.get_index() / columns.size() + first_row - - -func _on_cell_gui_input(event : InputEvent, cell : Control): - if event is InputEventMouseButton: - grab_focus() - if event.button_index != MOUSE_BUTTON_LEFT: - if event.button_index == MOUSE_BUTTON_RIGHT && event.is_pressed(): - if !cell in edited_cells: - deselect_all_cells() - select_cell(cell) - - cells_context.emit(edited_cells) - - return - - if event.pressed: - if Input.is_key_pressed(KEY_CTRL): - if cell in edited_cells: - deselect_cell(cell) - - else: - select_cell(cell) - - elif Input.is_key_pressed(KEY_SHIFT): - select_cells_to(cell) - - else: - deselect_all_cells() - select_cell(cell) - - -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(): - cells_context.emit(edited_cells) - - return - - grab_focus() - if !event.pressed: - deselect_all_cells() - - -func _input(event : InputEvent): - if !event is InputEventKey or !event.pressed: - return - - if !has_focus() or edited_cells.size() == 0: - return - - if event.keycode == KEY_CTRL or event.keycode == KEY_SHIFT: - # Modifier keys do not get processed. - return - - # Ctrl + Z (before, and instead of, committing the action!) - if Input.is_key_pressed(KEY_CTRL): - if event.keycode == KEY_Z: - if Input.is_key_pressed(KEY_SHIFT): - editor_plugin.undo_redo.redo() - # Ctrl + z (smol) - else: - editor_plugin.undo_redo.undo() - - return - - # This shortcut is used by Godot as well. - if event.keycode == KEY_Y: - editor_plugin.undo_redo.redo() - return - - _key_specific_action(event) - grab_focus() - editor_interface.get_resource_filesystem().scan() - - -func _key_specific_action(event : InputEvent): - var column = _get_cell_column(edited_cells[0]) - var ctrl_pressed := Input.is_key_pressed(KEY_CTRL) - - # BETWEEN-CELL NAVIGATION - if event.keycode == KEY_UP: - _move_selection_on_grid(0, (-1 if !ctrl_pressed else -10)) - - elif event.keycode == KEY_DOWN: - _move_selection_on_grid(0, (1 if !ctrl_pressed else 10)) - - elif Input.is_key_pressed(KEY_SHIFT) and event.keycode == KEY_TAB: - _move_selection_on_grid((-1 if !ctrl_pressed else -10), 0) - - elif event.keycode == KEY_TAB: - _move_selection_on_grid((1 if !ctrl_pressed else 10), 0) - - # Non-text and paths can't be edited. - if columns[column] == "resource_path": - return - - if !column_editors[column].is_text(): - return - - # CURSOR MOVEMENT - if event.keycode == KEY_LEFT: - TextEditingUtils.multi_move_left( - edited_cells_text, edit_cursor_positions, ctrl_pressed - ) - - elif event.keycode == KEY_RIGHT: - TextEditingUtils.multi_move_right( - edited_cells_text, edit_cursor_positions, ctrl_pressed - ) - - elif event.keycode == KEY_HOME: - for i in edit_cursor_positions.size(): - edit_cursor_positions[i] = 0 - - elif event.keycode == KEY_END: - for i in edit_cursor_positions.size(): - edit_cursor_positions[i] = edited_cells_text[i].length() - - # 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(edited_cells_text) - get_tree().set_input_as_handled() - - # The following actions do not work on non-editable cells. - if !column_editors[column].is_text() or columns[column] == "resource_path": - return - - # Ctrl + V - elif ctrl_pressed and event.keycode == KEY_V: - set_edited_cells_values(TextEditingUtils.multi_paste( - edited_cells_text, edit_cursor_positions - )) - get_tree().set_input_as_handled() - - # ERASING - elif event.keycode == KEY_BACKSPACE: - set_edited_cells_values(TextEditingUtils.multi_erase_left( - edited_cells_text, edit_cursor_positions, ctrl_pressed - )) - - elif event.keycode == KEY_DELETE: - set_edited_cells_values(TextEditingUtils.multi_erase_right( - edited_cells_text, edit_cursor_positions, ctrl_pressed - )) - get_viewport().set_input_as_handled() # Because this is one dangerous action - - - # And finally, text typing. - elif event.keycode == KEY_ENTER: - set_edited_cells_values(TextEditingUtils.multi_input( - "\n", edited_cells_text, edit_cursor_positions - )) - - elif event.unicode != 0 and event.unicode != 127: - set_edited_cells_values(TextEditingUtils.multi_input( - char(event.unicode), edited_cells_text, edit_cursor_positions - )) - - -func _move_selection_on_grid(move_h : int, move_v : int): - var cell = edited_cells[0] - grab_focus() - deselect_all_cells() - select_cell( - node_table_root.get_child( - cell.get_index() - + move_h + move_v * columns.size() - ) - ) - - -func _update_resources(update_rows : Array, update_cells : Array, update_column : int, values : Array): - var saved_indices = [] - saved_indices.resize(update_rows.size()) +func _update_resources(update_rows : Array, update_row_indices : Array[int], update_column : int, values : Array): + var column_editor = _selection.column_editors[update_column] for i in update_rows.size(): - var row = _get_cell_row(update_cells[i]) - saved_indices[i] = row - column_editors[update_column].set_value(update_cells[i], values[i]) + var row = update_row_indices[i] + var update_cell = node_table_root.get_child((row - first_row) * columns.size() + update_column) + + column_editor.set_value(update_cell, values[i]) if values[i] is String: values[i] = _try_convert(values[i], column_types[update_column]) @@ -792,21 +356,20 @@ func _update_resources(update_rows : Array, update_cells : Array, update_column for j in columns.size() - update_column: if j != 0 and column_types[j + update_column] == TYPE_COLOR: break - - column_editors[j + update_column].set_color( - update_cells[i].get_parent().get_child( - _get_cell_row(update_cells[i]) * columns.size() + update_column + j - first_row + + _selection.column_editors[j + update_column].set_color( + update_cell.get_parent().get_child( + row * columns.size() + update_column + j - first_row ), values[i] ) - io.save_entries(rows, saved_indices) - _update_column_sizes() + io.save_entries(rows, update_row_indices) + node_columns._update_column_sizes() func _try_convert(value, type): if type == TYPE_BOOL: - _update_selected_cells_text() # "off" displayed in lowercase, "ON" in uppercase. return value[0] == "o" @@ -814,11 +377,11 @@ func _try_convert(value, type): return convert(value, type) -func _get_edited_cells_resources() -> Array: +func _get_row_resources(row_indices) -> Array: var arr := [] - arr.resize(edited_cells.size()) + arr.resize(row_indices.size()) for i in arr.size(): - arr[i] = rows[_get_cell_row(edited_cells[i])] + arr[i] = rows[row_indices[i]] return arr @@ -844,66 +407,17 @@ func _on_process_expr_text_submitted(new_text : String): var new_script_instance = new_script.new() var values = get_edited_cells_values() var cur_row := 0 - + + var edited_rows = _selection.get_edited_rows() for i in values.size(): - cur_row = _get_cell_row(edited_cells[i]) - values[i] = new_script_instance.get_result(values[i], rows[cur_row], cur_row, i) + values[i] = new_script_instance.get_result(values[i], rows[edited_rows[i]], edited_rows[i], i) set_edited_cells_values(values) -func _on_inspector_property_edited(property : String): - if !visible: return - if inspector_resource == null: return - - var value = inspector_resource.get(property) - var values = [] - values.resize(edited_cells.size()) - for i in edited_cells.size(): - values[i] = value - - var previously_edited = edited_cells - if columns[_get_cell_column(edited_cells[0])] != property: - previously_edited = previously_edited.duplicate() - var new_column := columns.find(property) - deselect_all_cells() - var index := 0 - for i in previously_edited.size(): - index = _get_cell_row(previously_edited[i]) * columns.size() + new_column - _add_cell_to_selection(node_table_root.get_child(index - first_row)) - - set_edited_cells_values(values) - _try_open_docks(edited_cells[0]) - - func _on_File_pressed(): node_folder_path.get_parent().get_parent().visible = !node_folder_path.get_parent().get_parent().visible func _on_SearchProcess_pressed(): $"HeaderContentSplit/VBoxContainer/Search".visible = !$"HeaderContentSplit/VBoxContainer/Search".visible - - -func _on_visible_cols_about_to_popup(): - var popup = node_hide_columns_button.get_popup() - popup.clear() - popup.hide_on_checkable_item_selection = false - - for i in columns.size(): - popup.add_check_item(columns[i].capitalize(), i) - popup.set_item_checked(i, hidden_columns[current_path].has(columns[i])) - - -func _on_visible_cols_id_pressed(id : int): - var popup = node_hide_columns_button.get_popup() - if popup.is_item_checked(id): - popup.set_item_checked(id, false) - hidden_columns[current_path].erase(columns[id]) - - else: - popup.set_item_checked(id, true) - hidden_columns[current_path][columns[id]] = true - - save_data() - _update_hidden_columns() - _update_column_sizes() diff --git a/addons/resources_spreadsheet_view/editor_view.tscn b/addons/resources_spreadsheet_view/editor_view.tscn index 94d3344..3924611 100644 --- a/addons/resources_spreadsheet_view/editor_view.tscn +++ b/addons/resources_spreadsheet_view/editor_view.tscn @@ -1,23 +1,27 @@ -[gd_scene load_steps=21 format=3 uid="uid://blf43y5mcinf7"] +[gd_scene load_steps=25 format=3 uid="uid://tmleonv20aqk"] -[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/editor_view.gd" id="1"] -[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/typed_cells/cell_editor_string.gd" id="2"] -[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/typed_cells/cell_editor_color.gd" id="3"] -[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/editor_icon_button.gd" id="4"] -[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/typed_cells/cell_editor_bool.gd" id="5"] -[ext_resource type="PackedScene" uid="uid://d1s6oihqedvo5" path="res://addons/resources_spreadsheet_view/table_header.tscn" id="6"] -[ext_resource type="PackedScene" uid="uid://b3a3bo6cfyh5t" path="res://addons/resources_spreadsheet_view/typed_editors/dock_color.tscn" id="7"] -[ext_resource type="PackedScene" path="res://addons/resources_spreadsheet_view/typed_editors/dock_number.tscn" id="8"] -[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/typed_cells/cell_editor_enum_array.gd" id="8_2kaah"] -[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/typed_cells/cell_editor_resource.gd" id="9"] -[ext_resource type="PackedScene" uid="uid://rww3gpl052bn" path="res://addons/resources_spreadsheet_view/typed_editors/dock_texture.tscn" id="10"] -[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/editor_color_setter.gd" id="10_b87c7"] -[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/typed_cells/cell_editor_array.gd" id="11"] -[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/table_pages.gd" id="11_5gh4r"] -[ext_resource type="PackedScene" uid="uid://c3a2cip8ffccv" path="res://addons/resources_spreadsheet_view/typed_editors/dock_array.tscn" id="12"] -[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/typed_cells/cell_editor_enum.gd" id="13"] -[ext_resource type="PackedScene" uid="uid://ddqak780cwwfj" path="res://addons/resources_spreadsheet_view/typed_editors/dock_enum_array.tscn" id="13_7nia1"] -[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/settings_grid.gd" id="14"] +[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/editor_view.gd" id="1_wfx75"] +[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/editor_color_setter.gd" id="2_t2s7k"] +[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/editor_icon_button.gd" id="3_7ja2l"] +[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/main_screen/recent_paths.gd" id="4_umoob"] +[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/main_screen/table_pages.gd" id="5_ka2yn"] +[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/main_screen/column_header_manager.gd" id="6_emnmd"] +[ext_resource type="PackedScene" uid="uid://d1s6oihqedvo5" path="res://addons/resources_spreadsheet_view/main_screen/table_header.tscn" id="7_3dx0v"] +[ext_resource type="PackedScene" uid="uid://ddqak780cwwfj" path="res://addons/resources_spreadsheet_view/typed_editors/dock_enum_array.tscn" id="8_234wn"] +[ext_resource type="PackedScene" uid="uid://c3a2cip8ffccv" path="res://addons/resources_spreadsheet_view/typed_editors/dock_array.tscn" id="9_nts08"] +[ext_resource type="PackedScene" uid="uid://b3a3bo6cfyh5t" path="res://addons/resources_spreadsheet_view/typed_editors/dock_color.tscn" id="10_nsma2"] +[ext_resource type="PackedScene" path="res://addons/resources_spreadsheet_view/typed_editors/dock_number.tscn" id="11_q1ao4"] +[ext_resource type="PackedScene" uid="uid://rww3gpl052bn" path="res://addons/resources_spreadsheet_view/typed_editors/dock_texture.tscn" id="12_4kr6q"] +[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/settings_grid.gd" id="13_v0vm4"] +[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/main_screen/input_handler.gd" id="14_2t57a"] +[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/main_screen/selection_manager.gd" id="15_mx6qn"] +[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/typed_cells/cell_editor_enum_array.gd" id="16_p7n52"] +[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/typed_cells/cell_editor_array.gd" id="17_sofdw"] +[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/typed_cells/cell_editor_color.gd" id="18_oeewr"] +[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/typed_cells/cell_editor_bool.gd" id="19_7x44x"] +[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/typed_cells/cell_editor_enum.gd" id="20_swsbn"] +[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/typed_cells/cell_editor_resource.gd" id="21_58wf8"] +[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/typed_cells/cell_editor_string.gd" id="22_bni8r"] [sub_resource type="Gradient" id="Gradient_8kp6w"] offsets = PackedFloat32Array(0, 0.995413, 1) @@ -26,7 +30,7 @@ colors = PackedColorArray(1, 1, 1, 0.490196, 1, 1, 1, 0.0458716, 1, 1, 1, 0) [sub_resource type="GradientTexture2D" id="GradientTexture2D_18il8"] gradient = SubResource("Gradient_8kp6w") -[node name="Control" type="MarginContainer" node_paths=PackedStringArray("node_folder_path", "node_recent_paths", "node_table_root", "node_property_editors", "node_columns", "node_hide_columns_button", "node_page_manager")] +[node name="Control" type="MarginContainer"] anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 @@ -44,16 +48,7 @@ focus_mode = 2 theme_override_constants/margin_left = 3 theme_override_constants/margin_right = 3 theme_override_constants/margin_bottom = 5 -script = ExtResource("1") -table_header_scene = ExtResource("6") -cell_editor_classes = [ExtResource("8_2kaah"), ExtResource("11"), ExtResource("5"), ExtResource("3"), ExtResource("13"), ExtResource("9"), ExtResource("2")] -node_folder_path = NodePath("HeaderContentSplit/VBoxContainer/HBoxContainer/HBoxContainer/Path") -node_recent_paths = NodePath("HeaderContentSplit/VBoxContainer/HBoxContainer/HBoxContainer2/RecentPaths") -node_table_root = NodePath("HeaderContentSplit/MarginContainer/FooterContentSplit/Panel/Scroll/MarginContainer/TableGrid") -node_property_editors = NodePath("HeaderContentSplit/MarginContainer/FooterContentSplit/Footer/PropertyEditors") -node_columns = NodePath("HeaderContentSplit/VBoxContainer/Columns/Columns") -node_hide_columns_button = NodePath("HeaderContentSplit/VBoxContainer/MenuStrip/VisibleCols") -node_page_manager = NodePath("HeaderContentSplit/VBoxContainer/HBoxContainer3/Pages") +script = ExtResource("1_wfx75") metadata/_edit_lock_ = true [node name="HeaderContentSplit" type="VBoxContainer" parent="."] @@ -119,7 +114,7 @@ theme_override_constants/separation = 0 modulate = Color(0, 0, 0, 1) custom_minimum_size = Vector2(6, 18) layout_mode = 2 -script = ExtResource("10_b87c7") +script = ExtResource("2_t2s7k") [node name="TextureRect" type="TextureRect" parent="HeaderContentSplit/VBoxContainer/HBoxContainer/HBoxContainer/ColorRect4"] layout_mode = 1 @@ -150,13 +145,13 @@ caret_blink = true [node name="SelectDir" type="Button" parent="HeaderContentSplit/VBoxContainer/HBoxContainer/HBoxContainer"] layout_mode = 2 tooltip_text = "Open Folder" -script = ExtResource("4") +script = ExtResource("3_7ja2l") icon_name = "Folder" [node name="DeletePath" type="Button" parent="HeaderContentSplit/VBoxContainer/HBoxContainer/HBoxContainer"] layout_mode = 2 tooltip_text = "Remove Path from Recent" -script = ExtResource("4") +script = ExtResource("3_7ja2l") icon_name = "Remove" [node name="HBoxContainer2" type="HBoxContainer" parent="HeaderContentSplit/VBoxContainer/HBoxContainer"] @@ -172,6 +167,7 @@ layout_mode = 2 size_flags_horizontal = 3 clip_text = true fit_to_longest_item = false +script = ExtResource("4_umoob") [node name="Search" type="VBoxContainer" parent="HeaderContentSplit/VBoxContainer"] visible = false @@ -186,7 +182,7 @@ modulate = Color(0, 0, 0, 1) custom_minimum_size = Vector2(6, 18) layout_mode = 2 size_flags_vertical = 5 -script = ExtResource("10_b87c7") +script = ExtResource("2_t2s7k") [node name="TextureRect" type="TextureRect" parent="HeaderContentSplit/VBoxContainer/Search/HBoxContainer/ColorRect4"] layout_mode = 1 @@ -216,7 +212,7 @@ modulate = Color(0, 0, 0, 1) custom_minimum_size = Vector2(6, 18) layout_mode = 2 size_flags_vertical = 5 -script = ExtResource("10_b87c7") +script = ExtResource("2_t2s7k") [node name="TextureRect" type="TextureRect" parent="HeaderContentSplit/VBoxContainer/Search/Search/ColorRect2"] layout_mode = 1 @@ -292,12 +288,12 @@ text = "Grid" [node name="Refresh" type="Button" parent="HeaderContentSplit/VBoxContainer/HBoxContainer3"] layout_mode = 2 tooltip_text = "Refresh" -script = ExtResource("4") +script = ExtResource("3_7ja2l") icon_name = "Loop" [node name="Pages" type="HBoxContainer" parent="HeaderContentSplit/VBoxContainer/HBoxContainer3" node_paths=PackedStringArray("node_editor_view_root")] layout_mode = 2 -script = ExtResource("11_5gh4r") +script = ExtResource("5_ka2yn") node_editor_view_root = NodePath("../../../..") [node name="Label" type="Label" parent="HeaderContentSplit/VBoxContainer/HBoxContainer3/Pages"] @@ -331,6 +327,8 @@ layout_mode = 2 [node name="Columns" type="HBoxContainer" parent="HeaderContentSplit/VBoxContainer/Columns"] layout_mode = 0 theme_override_constants/separation = 0 +script = ExtResource("6_emnmd") +table_header_scene = ExtResource("7_3dx0v") [node name="Sep2" type="Control" parent="HeaderContentSplit/VBoxContainer"] visible = false @@ -390,23 +388,23 @@ layout_mode = 2 unique_name_in_owner = true layout_mode = 2 -[node name="EditEnumArray" parent="HeaderContentSplit/MarginContainer/FooterContentSplit/Footer/PropertyEditors" instance=ExtResource("13_7nia1")] +[node name="EditEnumArray" parent="HeaderContentSplit/MarginContainer/FooterContentSplit/Footer/PropertyEditors" instance=ExtResource("8_234wn")] visible = false layout_mode = 2 -[node name="EditArray" parent="HeaderContentSplit/MarginContainer/FooterContentSplit/Footer/PropertyEditors" instance=ExtResource("12")] +[node name="EditArray" parent="HeaderContentSplit/MarginContainer/FooterContentSplit/Footer/PropertyEditors" instance=ExtResource("9_nts08")] visible = false layout_mode = 2 -[node name="EditColor" parent="HeaderContentSplit/MarginContainer/FooterContentSplit/Footer/PropertyEditors" instance=ExtResource("7")] +[node name="EditColor" parent="HeaderContentSplit/MarginContainer/FooterContentSplit/Footer/PropertyEditors" instance=ExtResource("10_nsma2")] visible = false layout_mode = 2 -[node name="EditNumber" parent="HeaderContentSplit/MarginContainer/FooterContentSplit/Footer/PropertyEditors" instance=ExtResource("8")] +[node name="EditNumber" parent="HeaderContentSplit/MarginContainer/FooterContentSplit/Footer/PropertyEditors" instance=ExtResource("11_q1ao4")] visible = false layout_mode = 2 -[node name="EditTexture" parent="HeaderContentSplit/MarginContainer/FooterContentSplit/Footer/PropertyEditors" instance=ExtResource("10")] +[node name="EditTexture" parent="HeaderContentSplit/MarginContainer/FooterContentSplit/Footer/PropertyEditors" instance=ExtResource("12_4kr6q")] visible = false layout_mode = 2 @@ -504,7 +502,7 @@ size_flags_vertical = 3 [node name="GridContainer" type="GridContainer" parent="Control/Settings/MarginContainer/RichTextLabel"] layout_mode = 2 columns = 2 -script = ExtResource("14") +script = ExtResource("13_v0vm4") [node name="Label" type="Label" parent="Control/Settings/MarginContainer/RichTextLabel/GridContainer"] layout_mode = 2 @@ -573,16 +571,24 @@ layout_mode = 2 button_pressed = true text = "Enable" +[node name="InputHandler" type="Node" parent="."] +script = ExtResource("14_2t57a") + +[node name="SelectionManager" type="Node" parent="." node_paths=PackedStringArray("node_property_editors")] +script = ExtResource("15_mx6qn") +cell_editor_classes = [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") + [connection signal="grid_updated" from="." to="HeaderContentSplit/VBoxContainer/HBoxContainer3/Pages" method="_on_grid_updated"] +[connection signal="gui_input" from="." to="InputHandler" method="_gui_input"] [connection signal="pressed" from="HeaderContentSplit/VBoxContainer/MenuStrip/File" to="." method="_on_File_pressed"] [connection signal="pressed" from="HeaderContentSplit/VBoxContainer/MenuStrip/SearchProcess" to="." method="_on_SearchProcess_pressed"] -[connection signal="about_to_popup" from="HeaderContentSplit/VBoxContainer/MenuStrip/VisibleCols" to="." method="_on_visible_cols_about_to_popup"] +[connection signal="about_to_popup" from="HeaderContentSplit/VBoxContainer/MenuStrip/VisibleCols" to="HeaderContentSplit/VBoxContainer/Columns/Columns" method="_on_visible_cols_about_to_popup"] [connection signal="pressed" from="HeaderContentSplit/VBoxContainer/MenuStrip/Settings" to="Control/Settings" method="popup_centered"] [connection signal="pressed" from="HeaderContentSplit/VBoxContainer/MenuStrip/Info" to="Control/Info" method="popup_centered"] [connection signal="text_submitted" from="HeaderContentSplit/VBoxContainer/HBoxContainer/HBoxContainer/Path" to="." method="_on_path_text_submitted"] [connection signal="pressed" from="HeaderContentSplit/VBoxContainer/HBoxContainer/HBoxContainer/SelectDir" to="Control/FileDialog" method="popup_centered"] -[connection signal="pressed" from="HeaderContentSplit/VBoxContainer/HBoxContainer/HBoxContainer/DeletePath" to="." method="remove_selected_path_from_recent"] -[connection signal="item_selected" from="HeaderContentSplit/VBoxContainer/HBoxContainer/HBoxContainer2/RecentPaths" to="." method="_on_RecentPaths_item_selected"] +[connection signal="pressed" from="HeaderContentSplit/VBoxContainer/HBoxContainer/HBoxContainer/DeletePath" to="HeaderContentSplit/VBoxContainer/HBoxContainer/HBoxContainer2/RecentPaths" method="remove_selected_path_from_recent"] [connection signal="text_submitted" from="HeaderContentSplit/VBoxContainer/Search/Search/SearchCond" to="." method="_on_search_cond_text_submitted"] [connection signal="text_submitted" from="HeaderContentSplit/VBoxContainer/Search/Search/ProcessExpr" to="." method="_on_process_expr_text_submitted"] [connection signal="pressed" from="HeaderContentSplit/VBoxContainer/HBoxContainer3/Refresh" to="." method="_on_path_text_submitted"] diff --git a/addons/resources_spreadsheet_view/main_screen/column_header_manager.gd b/addons/resources_spreadsheet_view/main_screen/column_header_manager.gd new file mode 100644 index 0000000..894c76e --- /dev/null +++ b/addons/resources_spreadsheet_view/main_screen/column_header_manager.gd @@ -0,0 +1,144 @@ +@tool +extends Control + +@export var table_header_scene : PackedScene + +@export @onready var editor_view : Control = $"../../../.." +@export @onready var hide_columns_button : BaseButton = $"../../MenuStrip/VisibleCols" +@export @onready var grid : GridContainer = $"../../../MarginContainer/FooterContentSplit/Panel/Scroll/MarginContainer/TableGrid" + + +var hidden_columns := {} +var columns := []: + set(v): + columns = v + for x in get_children(): + x.queue_free() + + var new_node : Control + for x in v: + new_node = table_header_scene.instantiate() + add_child(new_node) + new_node.manager = self + new_node.set_label(x) + new_node.get_node("Button").pressed.connect(editor_view._set_sorting.bind(x)) + + +func _ready(): + hide_columns_button\ + .get_popup()\ + .id_pressed\ + .connect(_on_visible_cols_id_pressed) + $"../../../MarginContainer/FooterContentSplit/Panel/Scroll"\ + .get_h_scroll_bar()\ + .value_changed\ + .connect(_on_h_scroll_changed) + + +func update(): + _update_hidden_columns() + _update_column_sizes() + + +func hide_column(column_index : int): + hidden_columns[editor_view.current_path][editor_view.columns[column_index]] = true + editor_view.save_data() + update() + + +func select_column(column_index : int): + editor_view.select_column(column_index) + + +func _update_column_sizes(): + if grid.get_child_count() == 0: + return + + await get_tree().process_frame + var column_headers := get_children() + + if grid.get_child_count() < column_headers.size(): return + if column_headers.size() != columns.size(): + editor_view.refresh() + return + + var clip_text : bool = ProjectSettings.get_setting(SettingsGrid.SETTING_PREFIX + "clip_headers") + var min_width := 0 + var cell : Control + + for i in column_headers.size(): + var header = column_headers[i] + cell = grid.get_child(i) + + header.get_child(0).clip_text = clip_text + header.custom_minimum_size.x = 0 + cell.custom_minimum_size.x = 0 + header.size.x = 0 + + min_width = max(header.size.x, cell.size.x) + header.custom_minimum_size.x = min_width + cell.custom_minimum_size.x = header.get_minimum_size().x + header.size.x = min_width + + grid.hide() + grid.show() + hide() + show() + await get_tree().process_frame + + get_parent().custom_minimum_size.y = column_headers[0].size.y + for i in column_headers.size(): + column_headers[i].position.x = grid.get_child(i).position.x + column_headers[i].size.x = grid.get_child(i).size.x + + +func _update_hidden_columns(): + var current_path = editor_view.current_path + var rows_shown = editor_view.last_row - editor_view.first_row + + if !hidden_columns.has(current_path): + hidden_columns[current_path] = {} + return + + var visible_column_count = 0 + for i in columns.size(): + var column_visible = !hidden_columns[current_path].has(columns[i]) + + get_child(i).visible = column_visible + for j in rows_shown: + grid.get_child(j * columns.size() + i).visible = column_visible + + if column_visible: + visible_column_count += 1 + + grid.columns = visible_column_count + + +func _on_h_scroll_changed(value): + position.x = -value + + +func _on_visible_cols_about_to_popup(): + var current_path = editor_view.current_path + var popup = hide_columns_button.get_popup() + popup.clear() + popup.hide_on_checkable_item_selection = false + + for i in columns.size(): + popup.add_check_item(columns[i].capitalize(), i) + popup.set_item_checked(i, hidden_columns[current_path].has(columns[i])) + + +func _on_visible_cols_id_pressed(id : int): + var current_path = editor_view.current_path + var popup = hide_columns_button.get_popup() + if popup.is_item_checked(id): + popup.set_item_checked(id, false) + hidden_columns[current_path].erase(columns[id]) + + else: + popup.set_item_checked(id, true) + hidden_columns[current_path][columns[id]] = true + + editor_view.save_data() + update() diff --git a/addons/resources_spreadsheet_view/main_screen/input_handler.gd b/addons/resources_spreadsheet_view/main_screen/input_handler.gd new file mode 100644 index 0000000..1d4822b --- /dev/null +++ b/addons/resources_spreadsheet_view/main_screen/input_handler.gd @@ -0,0 +1,177 @@ +@tool +extends Node + +const EditorView = preload("res://addons/resources_spreadsheet_view/editor_view.gd") +const SelectionManager = preload("res://addons/resources_spreadsheet_view/main_screen/selection_manager.gd") + +@onready var editor_view : EditorView = get_parent() +@onready var selection : SelectionManager = get_node("../SelectionManager") + + +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) + + editor_view.cells_context.emit(selection.edited_cells) + + return + + if event.pressed: + if Input.is_key_pressed(KEY_CTRL): + if cell in selection.edited_cells: + selection.deselect_cell(cell) + + else: + selection.select_cell(cell) + + elif Input.is_key_pressed(KEY_SHIFT): + selection.select_cells_to(cell) + + else: + selection.deselect_all_cells() + selection.select_cell(cell) + + +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(): + editor_view.cells_context.emit(selection.edited_cells) + + return + + editor_view.grab_focus() + if !event.pressed: + selection.deselect_all_cells() + + +func _input(event : InputEvent): + if !event is InputEventKey or !event.pressed: + return + + if !editor_view.has_focus() or selection.edited_cells.size() == 0: + return + + if event.keycode == KEY_CTRL or event.keycode == KEY_SHIFT: + # Modifier keys do not get processed. + return + + # Ctrl + Z (before, and instead of, committing the action!) + if Input.is_key_pressed(KEY_CTRL): + if event.keycode == KEY_Z: + if Input.is_key_pressed(KEY_SHIFT): + editor_view.editor_plugin.undo_redo.redo() + # Ctrl + z (smol) + else: + editor_view.editor_plugin.undo_redo.undo() + + return + + # This shortcut is used by Godot as well. + if event.keycode == KEY_Y: + editor_view.editor_plugin.undo_redo.redo() + return + + _key_specific_action(event) + editor_view.grab_focus() + editor_view.editor_interface.get_resource_filesystem().scan() + + +func _key_specific_action(event : InputEvent): + var column = selection.get_cell_column(selection.edited_cells[0]) + var ctrl_pressed := Input.is_key_pressed(KEY_CTRL) + + # BETWEEN-CELL NAVIGATION + if event.keycode == KEY_UP: + _move_selection_on_grid(0, (-1 if !ctrl_pressed else -10)) + + elif event.keycode == KEY_DOWN: + _move_selection_on_grid(0, (1 if !ctrl_pressed else 10)) + + elif Input.is_key_pressed(KEY_SHIFT) and event.keycode == KEY_TAB: + _move_selection_on_grid((-1 if !ctrl_pressed else -10), 0) + + elif event.keycode == KEY_TAB: + _move_selection_on_grid((1 if !ctrl_pressed else 10), 0) + + # Non-text and paths can't be edited. + if editor_view.columns[column] == "resource_path": + return + + if !selection.column_editors[column].is_text(): + return + + # CURSOR MOVEMENT + if event.keycode == KEY_LEFT: + TextEditingUtils.multi_move_left( + selection.edited_cells_text, selection.edit_cursor_positions, ctrl_pressed + ) + + elif event.keycode == KEY_RIGHT: + TextEditingUtils.multi_move_right( + selection.edited_cells_text, selection.edit_cursor_positions, ctrl_pressed + ) + + elif event.keycode == KEY_HOME: + for i in selection.edit_cursor_positions.size(): + selection.edit_cursor_positions[i] = 0 + + elif event.keycode == KEY_END: + for i in selection.edit_cursor_positions.size(): + selection.edit_cursor_positions[i] = selection.edited_cells_text[i].length() + + # 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() + + # The following actions do not work on non-editable cells. + if !selection.column_editors[column].is_text() or editor_view.columns[column] == "resource_path": + return + + # Ctrl + V + elif ctrl_pressed and event.keycode == KEY_V: + editor_view.set_edited_cells_values(TextEditingUtils.multi_paste( + selection.edited_cells_text, selection.edit_cursor_positions + )) + get_tree().set_input_as_handled() + + # ERASING + elif event.keycode == KEY_BACKSPACE: + editor_view.set_edited_cells_values(TextEditingUtils.multi_erase_left( + selection.edited_cells_text, selection.edit_cursor_positions, ctrl_pressed + )) + + elif event.keycode == KEY_DELETE: + editor_view.set_edited_cells_values(TextEditingUtils.multi_erase_right( + selection.edited_cells_text, selection.edit_cursor_positions, ctrl_pressed + )) + get_viewport().set_input_as_handled() + + # And finally, text typing. + elif event.keycode == KEY_ENTER: + editor_view.set_edited_cells_values(TextEditingUtils.multi_input( + "\n", selection.edited_cells_text, selection.edit_cursor_positions + )) + + elif event.unicode != 0 and event.unicode != 127: + editor_view.set_edited_cells_values(TextEditingUtils.multi_input( + char(event.unicode), selection.edited_cells_text, selection.edit_cursor_positions + )) + + +func _move_selection_on_grid(move_h : int, move_v : int): + var cell = selection.edited_cells[0] + 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() + ) + ) diff --git a/addons/resources_spreadsheet_view/main_screen/recent_paths.gd b/addons/resources_spreadsheet_view/main_screen/recent_paths.gd new file mode 100644 index 0000000..c51dc2e --- /dev/null +++ b/addons/resources_spreadsheet_view/main_screen/recent_paths.gd @@ -0,0 +1,54 @@ +@tool +extends OptionButton + +@export @onready var editor_view := $"../../../../.." + +var recent_paths := [] + + +func _ready(): + item_selected.connect(_on_item_selected) + + +func load_paths(paths): + clear() + for x in paths: + add_path_to_recent(x, true) + + selected = 0 + + +func add_path_to_recent(path : String, is_loading : bool = false): + if path in recent_paths: return + + var idx_in_array := recent_paths.find(path) + if idx_in_array != -1: + remove_item(idx_in_array) + recent_paths.remove_at(idx_in_array) + + recent_paths.append(path) + add_item(path) + select(get_item_count() - 1) + + if !is_loading: + editor_view.save_data() + + +func remove_selected_path_from_recent(): + if get_item_count() == 0: + return + + var idx_in_array = selected + recent_paths.remove_at(idx_in_array) + remove_item(idx_in_array) + + if get_item_count() != 0: + select(0) + editor_view.display_folder(recent_paths[0]) + editor_view.save_data() + + +func _on_item_selected(index : int): + editor_view.current_path = recent_paths[index] + editor_view.node_folder_path.text = recent_paths[index] + editor_view.refresh() diff --git a/addons/resources_spreadsheet_view/selection_actions.gd b/addons/resources_spreadsheet_view/main_screen/selection_actions.gd similarity index 100% rename from addons/resources_spreadsheet_view/selection_actions.gd rename to addons/resources_spreadsheet_view/main_screen/selection_actions.gd diff --git a/addons/resources_spreadsheet_view/selection_actions.tscn b/addons/resources_spreadsheet_view/main_screen/selection_actions.tscn similarity index 100% rename from addons/resources_spreadsheet_view/selection_actions.tscn rename to addons/resources_spreadsheet_view/main_screen/selection_actions.tscn diff --git a/addons/resources_spreadsheet_view/main_screen/selection_manager.gd b/addons/resources_spreadsheet_view/main_screen/selection_manager.gd new file mode 100644 index 0000000..37e7275 --- /dev/null +++ b/addons/resources_spreadsheet_view/main_screen/selection_manager.gd @@ -0,0 +1,197 @@ +@tool +extends Node + +signal cells_selected(cells) + +const EditorView = preload("res://addons/resources_spreadsheet_view/editor_view.gd") + +@export var cell_editor_classes : Array[Script] = [] + +@export @onready var node_property_editors : VBoxContainer = $"../HeaderContentSplit/MarginContainer/FooterContentSplit/Footer/PropertyEditors" + +@onready var editor_view : EditorView = get_parent() + +var edited_cells := [] +var edited_cells_text := [] +var edit_cursor_positions := [] + +var all_cell_editors : Array[Object] +var column_editors := [] +var inspector_resource : Resource + + +func _ready(): + # Load cell editors and instantiate them + for x in cell_editor_classes: + all_cell_editors.append(x.new()) + all_cell_editors[all_cell_editors.size() - 1].hint_strings_array = editor_view.column_hint_strings + + get_parent()\ + .editor_interface\ + .get_inspector()\ + .property_edited\ + .connect(_on_inspector_property_edited) + + +func initialize_editors(column_values, column_types, column_hints): + deselect_all_cells() + edited_cells.clear() + edited_cells_text.clear() + edit_cursor_positions.clear() + + column_editors.clear() + for i in column_values.size(): + for x in all_cell_editors: + if x.can_edit_value(column_values[i], column_types[i], column_hints[i], i): + column_editors.append(x) + break + + +func deselect_all_cells(): + for x in edited_cells: + column_editors[get_cell_column(x)].set_selected(x, false) + + edited_cells.clear() + edited_cells_text.clear() + edit_cursor_positions.clear() + cells_selected.emit([]) + + +func deselect_cell(cell : Control): + var idx := edited_cells.find(cell) + if idx == -1: return + + column_editors[get_cell_column(cell)].set_selected(cell, false) + edited_cells.remove_at(idx) + if edited_cells_text.size() != 0: + edited_cells_text.remove_at(idx) + edit_cursor_positions.remove_at(idx) + + cells_selected.emit(edited_cells) + + +func select_cell(cell : Control): + var column_index := get_cell_column(cell) + if can_select_cell(cell): + _add_cell_to_selection(cell) + _try_open_docks(cell) +# inspector_resource = rows[get_cell_row(cell)].duplicate() # + inspector_resource = editor_view.rows[get_cell_row(cell)] + editor_view.editor_plugin.get_editor_interface().edit_resource(inspector_resource) + + cells_selected.emit(edited_cells) + + +func select_cells_to(cell : Control): + var column_index := get_cell_column(cell) + if column_index != get_cell_column(edited_cells[edited_cells.size() - 1]): + return + + var row_start = get_cell_row(edited_cells[edited_cells.size() - 1]) - editor_view.first_row + var row_end := get_cell_row(cell) - editor_view.first_row + var edge_shift = -1 if row_start > row_end else 1 + row_start += edge_shift + row_end += edge_shift + + for i in range(row_start, row_end, edge_shift): + var cur_cell := editor_view.node_table_root.get_child(i * editor_view.columns.size() + column_index) + if !cur_cell.visible: + # When search is active, some cells will be hidden. + continue + + column_editors[column_index].set_selected(cur_cell, true) + if !cur_cell in edited_cells: + edited_cells.append(cur_cell) + if column_editors[column_index].is_text(): + edited_cells_text.append(str(cur_cell.text)) + edit_cursor_positions.append(cur_cell.text.length()) + + cells_selected.emit(edited_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 + + +func get_cell_column(cell) -> int: + return cell.get_index() % editor_view.columns.size() + + +func get_cell_row(cell) -> int: + return cell.get_index() / editor_view.columns.size() + editor_view.first_row + + +func get_edited_rows() -> Array[int]: + var rows : Array[int] = [] + rows.resize(edited_cells.size()) + for i in rows.size(): + rows[i] = get_cell_row(edited_cells[i]) + + return rows + + +func _add_cell_to_selection(cell : Control): + var column_editor = column_editors[get_cell_column(cell)] + column_editor.set_selected(cell, true) + edited_cells.append(cell) + if column_editor.is_text(): + edited_cells_text.append(str(cell.text)) + edit_cursor_positions.append(cell.text.length()) + + +func _update_selected_cells_text(): + if edited_cells_text.size() == 0: + return + + for i in edited_cells.size(): + edited_cells_text[i] = str(edited_cells[i].text) + edit_cursor_positions[i] = edited_cells_text[i].length() + + +func _try_open_docks(cell : Control): + var column_index = get_cell_column(cell) + var row = editor_view.rows[get_cell_row(cell)] + var column = editor_view.columns[column_index] + var type = editor_view.column_types[column_index] + var hints = editor_view.column_hints[column_index] + + for x in node_property_editors.get_children(): + x.visible = x.try_edit_value(editor_view.io.get_value(row, column), type, hints) + x.get_node(x.path_property_name).text = column + + +func _on_inspector_property_edited(property : String): + if !editor_view.is_visible_in_tree(): return + if inspector_resource == null: return + + var value = inspector_resource[property] + var values = [] + values.resize(edited_cells.size()) + for i in edited_cells.size(): + values[i] = value + + var columns = editor_view.columns + var previously_edited = edited_cells + if columns[get_cell_column(edited_cells[0])] != property: + previously_edited = previously_edited.duplicate() + var new_column := columns.find(property) + deselect_all_cells() + var index := 0 + for i in previously_edited.size(): + index = get_cell_row(previously_edited[i]) * columns.size() + new_column + _add_cell_to_selection(editor_view.node_table_root.get_child(index - editor_view.first_row)) + + editor_view.set_edited_cells_values(values) + _try_open_docks(edited_cells[0]) diff --git a/addons/resources_spreadsheet_view/table_header.gd b/addons/resources_spreadsheet_view/main_screen/table_header.gd similarity index 85% rename from addons/resources_spreadsheet_view/table_header.gd rename to addons/resources_spreadsheet_view/main_screen/table_header.gd index f27699b..684b8a1 100644 --- a/addons/resources_spreadsheet_view/table_header.gd +++ b/addons/resources_spreadsheet_view/main_screen/table_header.gd @@ -1,7 +1,7 @@ @tool extends HBoxContainer -var editor_view : Control +var manager : Control func set_label(label : String): @@ -27,7 +27,7 @@ func _on_main_gui_input(event): func _on_list_id_pressed(id : int): if id == 0: - editor_view.select_column(get_index()) + manager.select_column(get_index()) else: - editor_view.hide_column(get_index()) + manager.hide_column(get_index()) diff --git a/addons/resources_spreadsheet_view/table_header.tscn b/addons/resources_spreadsheet_view/main_screen/table_header.tscn similarity index 80% rename from addons/resources_spreadsheet_view/table_header.tscn rename to addons/resources_spreadsheet_view/main_screen/table_header.tscn index 0c317b9..bd7fbb7 100644 --- a/addons/resources_spreadsheet_view/table_header.tscn +++ b/addons/resources_spreadsheet_view/main_screen/table_header.tscn @@ -1,13 +1,13 @@ [gd_scene load_steps=3 format=3 uid="uid://d1s6oihqedvo5"] -[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/table_header.gd" id="1_xhap0"] -[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/editor_icon_button.gd" id="2_t08bs"] +[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/main_screen/table_header.gd" id="1_5fd1m"] +[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/editor_icon_button.gd" id="2_0ymob"] [node name="Header" type="HBoxContainer"] offset_right = 179.0 offset_bottom = 31.0 size_flags_horizontal = 3 -script = ExtResource("1_xhap0") +script = ExtResource("1_5fd1m") [node name="Button" type="Button" parent="."] layout_mode = 2 @@ -24,5 +24,5 @@ popup/item_0/text = "Select All" popup/item_0/id = 0 popup/item_1/text = "Hide" popup/item_1/id = 1 -script = ExtResource("2_t08bs") +script = ExtResource("2_0ymob") icon_name = "ArrowDown" diff --git a/addons/resources_spreadsheet_view/table_pages.gd b/addons/resources_spreadsheet_view/main_screen/table_pages.gd similarity index 100% rename from addons/resources_spreadsheet_view/table_pages.gd rename to addons/resources_spreadsheet_view/main_screen/table_pages.gd diff --git a/addons/resources_spreadsheet_view/typed_cells/cell_editor.gd b/addons/resources_spreadsheet_view/typed_cells/cell_editor.gd index e13b97d..363e066 100644 --- a/addons/resources_spreadsheet_view/typed_cells/cell_editor.gd +++ b/addons/resources_spreadsheet_view/typed_cells/cell_editor.gd @@ -11,7 +11,7 @@ func can_edit_value(value, type, property_hint, column_index) -> bool: return value != null ## Override to change how the cell is created; preload a scene or create nodes from code. -## Caller is an instance of `editor_view.tscn`. +## Caller is an instance of [code]editor_view.tscn[/code]. func create_cell(caller : Control) -> Control: return load(CELL_SCENE_DIR + "basic.tscn").instantiate() diff --git a/addons/resources_spreadsheet_view/typed_editors/dock_base.gd b/addons/resources_spreadsheet_view/typed_editors/dock_base.gd index aa7a053..420665d 100644 --- a/addons/resources_spreadsheet_view/typed_editors/dock_base.gd +++ b/addons/resources_spreadsheet_view/typed_editors/dock_base.gd @@ -10,11 +10,11 @@ var selection : Array func _ready(): var parent := get_parent() - while parent != null and !parent.has_method("select_cell"): + while parent != null and !parent.has_method(&"display_folder"): parent = parent.get_parent() sheet = parent - get_node(path_property_name).add_theme_font_override("normal", get_theme_font("bold", "EditorFonts")) + get_node(path_property_name).add_theme_font_override(&"normal", get_theme_font(&"bold", &"EditorFonts")) ## Override to define when to show the dock and, if it can edit the value, how to handle it. func try_edit_value(value, type, property_hint) -> bool: