From 91e006571d0b6a255f189e92b26df6c1d2af0203 Mon Sep 17 00:00:00 2001 From: don-tnowe <67479453+don-tnowe@users.noreply.github.com> Date: Mon, 23 Jan 2023 14:01:03 +0200 Subject: [PATCH] Port code from 3.0, but not the features --- README.md | 10 +- .../resources_spreadsheet_view/editor_view.gd | 213 ++++++++----- .../editor_view.tscn | 26 +- .../import_export/formats_edit/edit_base.gd | 36 +++ .../import_export/formats_edit/edit_csv.gd | 91 ++++++ .../import_export/formats_edit/edit_tres.gd | 79 +++++ .../formats_export/export_csv.gd | 30 ++ .../formats_import/import_csv.gd | 49 +++ .../import_export/import_export_dialog.gd | 212 +++++++++++++ .../import_export/import_export_dialog.tscn | 172 ++++++++++ .../import_export_enum_format.gd | 20 ++ .../import_export_enum_format.tscn | 76 +++++ .../import_export/property_list_item.gd | 12 + .../import_export/property_list_item.tscn | 26 ++ .../import_export/spreadsheet_import.gd | 293 ++++++++++++++++++ addons/resources_spreadsheet_view/plugin.cfg | 2 +- .../saved_state.json | 1 - .../selection_actions.gd | 177 +++++++++++ .../selection_actions.tscn | 217 +++++++++++++ .../settings_grid.gd | 2 +- .../table_header.gd | 2 +- .../table_header.tscn | 27 +- .../resources_spreadsheet_view/table_pages.gd | 18 +- .../text_editing_utils.gd | 24 +- .../typed_editors/dock_texture.gd | 2 +- project.godot | 55 +--- 26 files changed, 1648 insertions(+), 224 deletions(-) create mode 100644 addons/resources_spreadsheet_view/import_export/formats_edit/edit_base.gd create mode 100644 addons/resources_spreadsheet_view/import_export/formats_edit/edit_csv.gd create mode 100644 addons/resources_spreadsheet_view/import_export/formats_edit/edit_tres.gd create mode 100644 addons/resources_spreadsheet_view/import_export/formats_export/export_csv.gd create mode 100644 addons/resources_spreadsheet_view/import_export/formats_import/import_csv.gd create mode 100644 addons/resources_spreadsheet_view/import_export/import_export_dialog.gd create mode 100644 addons/resources_spreadsheet_view/import_export/import_export_dialog.tscn create mode 100644 addons/resources_spreadsheet_view/import_export/import_export_enum_format.gd create mode 100644 addons/resources_spreadsheet_view/import_export/import_export_enum_format.tscn create mode 100644 addons/resources_spreadsheet_view/import_export/property_list_item.gd create mode 100644 addons/resources_spreadsheet_view/import_export/property_list_item.tscn create mode 100644 addons/resources_spreadsheet_view/import_export/spreadsheet_import.gd delete mode 100644 addons/resources_spreadsheet_view/saved_state.json create mode 100644 addons/resources_spreadsheet_view/selection_actions.gd create mode 100644 addons/resources_spreadsheet_view/selection_actions.tscn diff --git a/README.md b/README.md index 8ad45fb..89c6c70 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,8 @@ Possible inputs: - `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 + R` - Rename resource +- `Ctrl/Cmd + D` - Duplicate selected rows - `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. @@ -41,8 +43,10 @@ To add support of more datatypes, check out the `typed_cells` and `typed_editors # Made by Don Tnowe in 2022. -[https://redbladegames.netlify.app]() +[My Website](https://redbladegames.netlify.app) -[https://twitter.com/don_tnowe]() +[Itch](https://don-tnowe.itch.io) -Copying and Modiication is allowed in accordance to the MIT license, full text is included. +[Twitter](https://twitter.com/don_tnowe) + +Copying and Modification is allowed in accordance to the MIT license, full text is included. diff --git a/addons/resources_spreadsheet_view/editor_view.gd b/addons/resources_spreadsheet_view/editor_view.gd index 5180657..8e87415 100644 --- a/addons/resources_spreadsheet_view/editor_view.gd +++ b/addons/resources_spreadsheet_view/editor_view.gd @@ -2,6 +2,8 @@ extends Control signal grid_updated() +signal cells_selected(cells) +signal cells_context(cells) @export var table_header_scene : PackedScene @export var cell_editor_classes : Array[Script] = [] @@ -22,7 +24,6 @@ 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 undo_redo_version := 0 var all_cell_editors := [] @@ -37,9 +38,10 @@ var remembered_paths := {} var edited_cells := [] var edited_cells_text := [] var edit_cursor_positions := [] + var inspector_resource : Resource var search_cond : RefCounted -var my_undo_redo : UndoRedo +var io : RefCounted var hidden_columns := {} var first_row := 0 @@ -48,7 +50,6 @@ var last_row := 0 func _ready(): node_recent_paths.clear() - my_undo_redo = editor_plugin.get_undo_redo().get_history_undo_redo(editor_plugin.get_undo_redo().get_object_history_id(self)) 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) @@ -69,6 +70,9 @@ func _ready(): 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) func _on_filesystem_changed(): @@ -90,18 +94,23 @@ func _on_filesystem_changed(): break -func display_folder(folderpath : String, sort_by : String = "", sort_reverse : bool = false, force_rebuild : bool = false): +func display_folder(folderpath : String, sort_by : String = "", sort_reverse : bool = false, force_rebuild : bool = false, is_echo : bool = false): if folderpath == "": return # Root folder resources tend to have MANY properties. $"HeaderContentSplit/MarginContainer/FooterContentSplit/Panel/Label".visible = false - if !folderpath.ends_with("/"): - folderpath += "/" + if folderpath.get_extension() == "": + folderpath = folderpath.trim_suffix("/") + "/" + if folderpath.ends_with(".tres") and !folderpath.ends_with(SpreadsheetImport.SUFFIX): + folderpath = folderpath.get_base_dir() + "/" + if search_cond == null: _on_search_cond_text_submitted("true") + 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) + if columns.size() == 0: return node_folder_path.text = folderpath @@ -114,7 +123,7 @@ func display_folder(folderpath : String, sort_by : String = "", sort_reverse : b _update_hidden_columns() _update_column_sizes() - await get_tree().process_frame + await get_tree().create_timer(0.25).timeout if node_table_root.get_child_count() == 0: display_folder(folderpath, sort_by, sort_reverse, force_rebuild) @@ -126,65 +135,50 @@ func refresh(force_rebuild : bool = true): display_folder(current_path, sorting_by, sorting_reverse, force_rebuild) -func _load_resources_from_folder(folderpath : String, sort_by : String, sort_reverse : bool): - var dir = DirAccess.open(folderpath) - dir.list_dir_begin() +func _load_resources_from_folder(path : String, sort_by : String, sort_reverse : bool): + if path.ends_with("/"): + io = SpreadsheetEditFormatTres.new() - rows.clear() - remembered_paths.clear() - var cur_dir_script : Script = null - - var filepath = dir.get_next() - var res : Resource - - while filepath != "": - remembered_paths[folderpath + filepath] = null - if filepath.ends_with(".tres"): - filepath = folderpath + filepath - res = load(filepath) - if !is_instance_valid(cur_dir_script): - columns.clear() - column_types.clear() - column_hints.clear() - column_hint_strings.clear() - column_editors.clear() - var column_index = -1 - for x in res.get_property_list(): - if x["usage"] & PROPERTY_USAGE_EDITOR != 0 and x["name"] != "script": - column_index += 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(res.get(x["name"]), x["type"], x["hint"], column_index): - column_editors.append(y) - break - - cur_dir_script = res.get_script() - if !(sort_by in res): - sort_by = "resource_path" - - if res.get_script() == cur_dir_script: - _insert_row_sorted(res, rows, sort_by, sort_reverse) - remembered_paths[res.resource_path] = res - - filepath = dir.get_next() + else: + io = load(path).view_script.new() + + io.editor_view = self + rows = io.import_from_path(path, insert_row_sorted, sort_by, sort_reverse) -func _insert_row_sorted(res : Resource, rows : Array, sort_by : String, sort_reverse : bool): +func fill_property_data(res): + columns.clear() + column_types.clear() + column_hints.clear() + column_hint_strings.clear() + column_editors.clear() + var column_index = -1 + for x in res.get_property_list(): + if x["usage"] & PROPERTY_USAGE_EDITOR != 0 and x["name"] != "script": + column_index += 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 + + +func insert_row_sorted(res : Resource, rows : Array, sort_by : String, sort_reverse : bool): if !search_cond.can_show(res, rows.size()): return for i in rows.size(): - if sort_reverse == _compare_values(res.get(sort_by), rows[i].get(sort_by)): + if sort_reverse == compare_values(io.get_value(res, sort_by), io.get_value(rows[i], sort_by)): rows.insert(i, res) return rows.append(res) -func _compare_values(a, b) -> bool: +func compare_values(a, b) -> bool: if a == null or b == null: return b == null if a is Color: return a.h > b.h if a.h != b.h else a.v > b.v @@ -267,7 +261,6 @@ func _update_column_sizes(): var min_width := 0 var cell : Control - node_columns.get_parent().custom_minimum_size.y = column_headers[0].size.y for i in column_headers.size(): var header = column_headers[i] cell = node_table_root.get_child(i) @@ -289,6 +282,8 @@ func _update_column_sizes(): 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 @@ -306,17 +301,17 @@ func _update_row(row_index : int, color_rows : bool = true): else: current_node = node_table_root.get_child((row_index - first_row) * columns.size() + i) current_node.tooltip_text = ( - TextEditingUtils.string_snake_to_naming_case(columns[i]) + columns[i].capitalize() + "\n---\n" + "Of " + rows[row_index].resource_path.get_file().get_basename() ) - column_editors[i].set_value(current_node, rows[row_index].get(columns[i])) + column_editors[i].set_value(current_node, io.get_value(rows[row_index], columns[i])) if columns[i] == "resource_path": column_editors[i].set_value(current_node, current_node.text.get_file().get_basename()) if color_rows and column_types[i] == TYPE_COLOR: - next_color = rows[row_index].get(columns[i]) + next_color = io.get_value(rows[row_index], columns[i]) column_editors[i].set_color(current_node, next_color) @@ -383,7 +378,6 @@ func save_data(): func _on_path_text_submitted(new_text : String = ""): if new_text != "": current_path = new_text - add_path_to_recent(new_text) display_folder(new_text, "", false, true) else: @@ -393,12 +387,11 @@ func _on_path_text_submitted(new_text : String = ""): func _on_RecentPaths_item_selected(index : int): current_path = recent_paths[index] node_folder_path.text = recent_paths[index] - display_folder(current_path) + display_folder(current_path, sorting_by, sorting_reverse, true) func _on_FileDialog_dir_selected(dir : String): node_folder_path.text = dir - add_path_to_recent(dir) display_folder(dir) @@ -409,6 +402,7 @@ func deselect_all_cells(): edited_cells.clear() edited_cells_text.clear() edit_cursor_positions.clear() + cells_selected.emit([]) func deselect_cell(cell : Control): @@ -420,6 +414,8 @@ func deselect_cell(cell : Control): 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): @@ -431,6 +427,8 @@ func select_cell(cell : Control): 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) @@ -456,6 +454,8 @@ 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) + func select_column(column_index : int): deselect_all_cells() @@ -486,7 +486,7 @@ 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( - rows[_get_cell_row(cell)].get(columns[column_index]), + io.get_value(rows[_get_cell_row(cell)], columns[column_index]), column_types[column_index], column_hints[column_index] ) @@ -524,10 +524,34 @@ func set_edited_cells_values(new_cell_values : Array): ) editor_plugin.undo_redo.commit_action() editor_interface.get_resource_filesystem().scan() - undo_redo_version = my_undo_redo.get_version() _update_column_sizes() +func rename_row(row, new_name): + if !has_row_names(): return + + io.rename_row(row, new_name) + refresh() + + +func duplicate_selected_rows(new_name : String): + io.duplicate_rows(_get_edited_cells_resources(), new_name) + refresh() + + +func delete_selected_rows(): + io.delete_rows(_get_edited_cells_resources()) + refresh() + + +func has_row_names(): + return io.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 @@ -542,13 +566,13 @@ func get_edited_cells_values() -> Array: var column_index := _get_cell_column(edited_cells[0]) var cell_editor = column_editors[column_index] for i in arr.size(): - arr[i] = rows[_get_cell_row(arr[i])].get(columns[column_index]) + arr[i] = io.get_value(rows[_get_cell_row(arr[i])], columns[column_index]) return arr func get_cell_value(cell : Control): - return rows[_get_cell_row(cell)].get(columns[_get_cell_column(cell)]) + return io.get_value(rows[_get_cell_row(cell)], columns[_get_cell_column(cell)]) func _can_select_cell(cell : Control) -> bool: @@ -579,6 +603,13 @@ 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: @@ -600,6 +631,9 @@ 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(): + cells_context.emit(edited_cells) + return grab_focus() @@ -619,24 +653,24 @@ func _input(event : InputEvent): return # Ctrl + Z (before, and instead of, committing the action!) - if Input.is_key_pressed(KEY_CTRL) and event.keycode == KEY_Z: - if Input.is_key_pressed(KEY_SHIFT): - my_undo_redo.redo() - # Ctrl + z - else: - my_undo_redo.undo() - - return + 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 - # This shortcut is used by Godot as well. - if Input.is_key_pressed(KEY_CTRL) and event.keycode == KEY_Y: - my_undo_redo.redo() - return - _key_specific_action(event) grab_focus() editor_interface.get_resource_filesystem().scan() - undo_redo_version = my_undo_redo.get_version() func _key_specific_action(event : InputEvent): @@ -685,6 +719,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(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": @@ -695,6 +730,7 @@ func _key_specific_action(event : InputEvent): 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: @@ -734,17 +770,24 @@ func _move_selection_on_grid(move_h : int, move_v : int): func _update_resources(update_rows : Array, update_cells : Array, update_column : int, values : Array): + var saved_indices = [] + saved_indices.resize(update_rows.size()) 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]) if values[i] is String: values[i] = _try_convert(values[i], column_types[update_column]) if values[i] == null: continue - - update_rows[i].set(columns[update_column], values[i]) - ResourceSaver.save(update_rows[i]) + io.set_value( + update_rows[i], + columns[update_column], + values[i], + row + ) if column_types[update_column] == TYPE_COLOR: for j in columns.size() - update_column: if j != 0 and column_types[j + update_column] == TYPE_COLOR: @@ -757,6 +800,7 @@ func _update_resources(update_rows : Array, update_cells : Array, update_column values[i] ) + io.save_entries(rows, saved_indices) _update_column_sizes() @@ -771,7 +815,8 @@ func _try_convert(value, type): func _get_edited_cells_resources() -> Array: - var arr := edited_cells.duplicate() + var arr := [] + arr.resize(edited_cells.size()) for i in arr.size(): arr[i] = rows[_get_cell_row(edited_cells[i])] @@ -811,8 +856,6 @@ func _on_inspector_property_edited(property : String): if !visible: return if inspector_resource == null: return - undo_redo_version = my_undo_redo.get_version() - var value = inspector_resource.get(property) var values = [] values.resize(edited_cells.size()) @@ -847,7 +890,7 @@ func _on_visible_cols_about_to_popup(): popup.hide_on_checkable_item_selection = false for i in columns.size(): - popup.add_check_item(TextEditingUtils.string_snake_to_naming_case(columns[i]), i) + popup.add_check_item(columns[i].capitalize(), i) popup.set_item_checked(i, hidden_columns[current_path].has(columns[i])) diff --git a/addons/resources_spreadsheet_view/editor_view.tscn b/addons/resources_spreadsheet_view/editor_view.tscn index 7307452..94d3344 100644 --- a/addons/resources_spreadsheet_view/editor_view.tscn +++ b/addons/resources_spreadsheet_view/editor_view.tscn @@ -1,11 +1,11 @@ -[gd_scene load_steps=23 format=3 uid="uid://dpdgrocww8a51"] +[gd_scene load_steps=21 format=3 uid="uid://blf43y5mcinf7"] [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://bya5ugi40ptxl" path="res://addons/resources_spreadsheet_view/table_header.tscn" id="6"] +[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"] @@ -26,18 +26,6 @@ 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") -[sub_resource type="Image" id="Image_wmhif"] -data = { -"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 93, 93, 55, 255, 97, 97, 58, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 98, 98, 47, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 94, 94, 46, 255, 93, 93, 236, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), -"format": "RGBA8", -"height": 16, -"mipmaps": false, -"width": 16 -} - -[sub_resource type="ImageTexture" id="ImageTexture_we5a3"] -image = SubResource("Image_wmhif") - [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")] anchors_preset = 15 anchor_right = 1.0 @@ -143,7 +131,7 @@ offset_right = 48.0 grow_horizontal = 0 grow_vertical = 2 texture = SubResource("GradientTexture2D_18il8") -ignore_texture_size = true +expand_mode = 1 [node name="ColorRect3" type="Control" parent="HeaderContentSplit/VBoxContainer/HBoxContainer/HBoxContainer"] custom_minimum_size = Vector2(2, 0) @@ -162,14 +150,12 @@ caret_blink = true [node name="SelectDir" type="Button" parent="HeaderContentSplit/VBoxContainer/HBoxContainer/HBoxContainer"] layout_mode = 2 tooltip_text = "Open Folder" -icon = SubResource("ImageTexture_we5a3") script = ExtResource("4") icon_name = "Folder" [node name="DeletePath" type="Button" parent="HeaderContentSplit/VBoxContainer/HBoxContainer/HBoxContainer"] layout_mode = 2 tooltip_text = "Remove Path from Recent" -icon = SubResource("ImageTexture_we5a3") script = ExtResource("4") icon_name = "Remove" @@ -212,7 +198,7 @@ offset_right = 48.0 grow_horizontal = 0 grow_vertical = 2 texture = SubResource("GradientTexture2D_18il8") -ignore_texture_size = true +expand_mode = 1 [node name="Label" type="Label" parent="HeaderContentSplit/VBoxContainer/Search/HBoxContainer"] layout_mode = 2 @@ -242,7 +228,7 @@ offset_right = 48.0 grow_horizontal = 0 grow_vertical = 2 texture = SubResource("GradientTexture2D_18il8") -ignore_texture_size = true +expand_mode = 1 [node name="Label" type="Label" parent="HeaderContentSplit/VBoxContainer/Search/Search"] layout_mode = 2 @@ -306,7 +292,6 @@ text = "Grid" [node name="Refresh" type="Button" parent="HeaderContentSplit/VBoxContainer/HBoxContainer3"] layout_mode = 2 tooltip_text = "Refresh" -icon = SubResource("ImageTexture_we5a3") script = ExtResource("4") icon_name = "Loop" @@ -348,6 +333,7 @@ layout_mode = 0 theme_override_constants/separation = 0 [node name="Sep2" type="Control" parent="HeaderContentSplit/VBoxContainer"] +visible = false layout_mode = 2 [node name="MarginContainer" type="MarginContainer" parent="HeaderContentSplit"] diff --git a/addons/resources_spreadsheet_view/import_export/formats_edit/edit_base.gd b/addons/resources_spreadsheet_view/import_export/formats_edit/edit_base.gd new file mode 100644 index 0000000..68dd1c5 --- /dev/null +++ b/addons/resources_spreadsheet_view/import_export/formats_edit/edit_base.gd @@ -0,0 +1,36 @@ +class_name SpreadsheetEditFormat +extends RefCounted + +var editor_view : Control + +## Override to define reading behaviour. +func get_value(entry, key : String): + pass + +## Override to define writing behaviour. This is NOT supposed to save - use `save_entries`. +func set_value(entry, key : String, value, index : int): + pass + +## Override to define how the data gets saved. +func save_entries(all_entries : Array, indices : Array): + pass + +## Override to allow editing rows from the Inspector. +func create_resource(entry) -> Resource: + return Resource.new() + +## Override to define duplication behaviour. `name_input` should be a suffix if multiple entries, and full name if one. +func duplicate_rows(rows : Array, name_input : String): + pass + +## Override to define removal behaviour. +func delete_rows(rows : Array): + pass + +## Override with `return true` if `resource_path` is defined and the Rename butoon should show. +func has_row_names(): + return false + +## Override to define import behaviour. Must return the `rows` value for the editor view. +func import_from_path(folderpath : String, insert_func : Callable, sort_by : String, sort_reverse : bool = false) -> Array: + return [] diff --git a/addons/resources_spreadsheet_view/import_export/formats_edit/edit_csv.gd b/addons/resources_spreadsheet_view/import_export/formats_edit/edit_csv.gd new file mode 100644 index 0000000..d14f8fd --- /dev/null +++ b/addons/resources_spreadsheet_view/import_export/formats_edit/edit_csv.gd @@ -0,0 +1,91 @@ +class_name SpreadsheetEditFormatCsv +extends SpreadsheetEditFormatTres + +var import_data +var csv_rows = [] +var resource_original_positions = {} +var timer : SceneTreeTimer + + +func get_value(entry, key : String): + return entry.get(key) + + +func set_value(entry, key : String, value, index : int): + entry.set(key, value) + csv_rows[resource_original_positions[entry]] = import_data.resource_to_strings(entry) + + +func save_entries(all_entries : Array, indices : Array, repeat : bool = true): + if timer == null || timer.time_left <= 0.0: + var space_after_delimeter = import_data.delimeter.ends_with(" ") + var file = FileAccess.open(import_data.edited_path, FileAccess.WRITE) + for x in csv_rows: + if space_after_delimeter: + for i in x.size(): + if i == 0: continue + x[i] = " " + x[i] + + file.store_csv_line(x, import_data.delimeter[0]) + + file.close() + if repeat: + timer = editor_view.get_tree().create_timer(3.0) + timer.timeout.connect(save_entries.bind(all_entries, indices, false)) + + +func create_resource(entry) -> Resource: + return entry + + +func duplicate_rows(rows : Array, name_input : String): + for x in rows: + var new_res = x.duplicate() + var index = resource_original_positions[x] + csv_rows.insert(index, import_data.resource_to_strings(new_res)) + _bump_row_indices(index + 1, 1) + resource_original_positions[new_res] = index + 1 + + save_entries([], []) + + +func delete_rows(rows): + for x in rows: + var index = resource_original_positions[x] + csv_rows.remove(index) + _bump_row_indices(index, -1) + resource_original_positions.erase(x) + + save_entries([], []) + + +func has_row_names(): + return false + + +func _bump_row_indices(from : int, increment : int = 1): + for k in resource_original_positions: + if resource_original_positions[k] >= from: + resource_original_positions[k] += increment + + +func import_from_path(path : String, insert_func : Callable, sort_by : String, sort_reverse : bool = false) -> Array: + import_data = load(path) + var file = FileAccess.open(import_data.edited_path, FileAccess.READ) + csv_rows = SpreadsheetImportFormatCsv.import_as_arrays(import_data) + + var rows := [] + var res : Resource + resource_original_positions.clear() + for i in csv_rows.size(): + if import_data.remove_first_row and i == 0: + continue + + res = import_data.strings_to_resource(csv_rows[i]) + res.resource_path = "" + insert_func.call(res, rows, sort_by, sort_reverse) + resource_original_positions[res] = i + + editor_view.fill_property_data(rows[0]) + file.close() + return rows diff --git a/addons/resources_spreadsheet_view/import_export/formats_edit/edit_tres.gd b/addons/resources_spreadsheet_view/import_export/formats_edit/edit_tres.gd new file mode 100644 index 0000000..ad0dea8 --- /dev/null +++ b/addons/resources_spreadsheet_view/import_export/formats_edit/edit_tres.gd @@ -0,0 +1,79 @@ +class_name SpreadsheetEditFormatTres +extends SpreadsheetEditFormat + + +func get_value(entry, key : String): + return entry.get(key) + + +func set_value(entry, key : String, value, index : int): + entry.set(key, value) + + +func save_entries(all_entries : Array, indices : Array): + for x in indices: + ResourceSaver.save(all_entries[x]) + + +func create_resource(entry) -> Resource: + return entry + + +func duplicate_rows(rows : Array, name_input : String): + if rows.size() == 1: + var new_row = rows[0].duplicate() + new_row.resource_path = rows[0].resource_path.get_base_dir() + "/" + name_input + ".tres" + ResourceSaver.save(new_row) + return + + var new_row + for x in rows: + new_row = x.duplicate() + new_row.resource_path = x.resource_path.get_basename() + name_input + ".tres" + ResourceSaver.save(new_row) + + +func rename_row(row, new_name : String): + var new_row = row + DirAccess.open("res://").remove(row.resource_path) + new_row.resource_path = row.resource_path.get_base_dir() + "/" + new_name + ".tres" + ResourceSaver.save(new_row) + + +func delete_rows(rows): + for x in rows: + DirAccess.open("res://").remove(x.resource_path) + + +func has_row_names(): + return true + + +func import_from_path(folderpath : String, insert_func : Callable, sort_by : String, sort_reverse : bool = false) -> Array: + var rows := [] + var dir := DirAccess.open(folderpath) + dir.list_dir_begin() + + editor_view.remembered_paths.clear() + var cur_dir_script : Script = null + + var filepath = dir.get_next() + var res : Resource + + while filepath != "": + if filepath.ends_with(".tres"): + filepath = folderpath + filepath + res = load(filepath) + if !is_instance_valid(cur_dir_script): + editor_view.fill_property_data(res) + cur_dir_script = res.get_script() + if !(sort_by in res): + sort_by = "resource_path" + + if res.get_script() == cur_dir_script: + insert_func.call(res, rows, sort_by, sort_reverse) + editor_view.remembered_paths[res.resource_path] = res + + filepath = dir.get_next() + + return rows diff --git a/addons/resources_spreadsheet_view/import_export/formats_export/export_csv.gd b/addons/resources_spreadsheet_view/import_export/formats_export/export_csv.gd new file mode 100644 index 0000000..d17c0f4 --- /dev/null +++ b/addons/resources_spreadsheet_view/import_export/formats_export/export_csv.gd @@ -0,0 +1,30 @@ +class_name SpreadsheetExportFormatCsv +extends RefCounted + + +static func export_to_file(entries_array : Array, column_names : Array, into_path : String, import_data): + var file = FileAccess.open(into_path, FileAccess.WRITE) + + var line = PackedStringArray() + var space_after_delimeter = import_data.delimeter.ends_with(" ") + import_data.prop_names = column_names + import_data.prop_types = import_data.get_resource_property_types(entries_array[0], column_names) + import_data.resource_path = "" + line.resize(column_names.size()) + if import_data.remove_first_row: + for j in column_names.size(): + line[j] = column_names[j] + if space_after_delimeter and j != 0: + line[j] = " " + line[j] + + file.store_csv_line(line, import_data.delimeter[0]) + + for i in entries_array.size(): + for j in column_names.size(): + line[j] = import_data.property_to_string((entries_array[i].get(column_names[j])), j) + if space_after_delimeter and j != 0: + line[j] = " " + line[j] + + file.store_csv_line(line, import_data.delimeter[0]) + + file.close() diff --git a/addons/resources_spreadsheet_view/import_export/formats_import/import_csv.gd b/addons/resources_spreadsheet_view/import_export/formats_import/import_csv.gd new file mode 100644 index 0000000..0412067 --- /dev/null +++ b/addons/resources_spreadsheet_view/import_export/formats_import/import_csv.gd @@ -0,0 +1,49 @@ +class_name SpreadsheetImportFormatCsv +extends RefCounted + + +static func can_edit_path(path : String): + return path.ends_with(".csv") + + +static func import_as_arrays(import_data) -> Array: + var file = FileAccess.open(import_data.edited_path, FileAccess.READ) + + import_data.delimeter = ";" + var text_lines := [file.get_line().split(import_data.delimeter)] + var space_after_delimeter = false + var line = text_lines[0] + if line.size() == 1: + import_data.delimeter = "," + line = line[0].split(import_data.delimeter) + text_lines[0] = line + + if line[1].begins_with(" "): + for i in line.size(): + line[i] = line[i].trim_prefix(" ") + + text_lines[0] = line + space_after_delimeter = true + import_data.delimeter += " " + + while !file.eof_reached(): + line = file.get_csv_line(import_data.delimeter[0]) + if space_after_delimeter: + for i in line.size(): + line[i] = line[i].trim_prefix(" ") + + if line.size() == text_lines[0].size(): + text_lines.append(line) + + elif line.size() != 1: + line.resize(text_lines[0].size()) + text_lines.append(line) + + file.close() + var entries = [] + entries.resize(text_lines.size()) + + for i in entries.size(): + entries[i] = text_lines[i] + + return entries diff --git a/addons/resources_spreadsheet_view/import_export/import_export_dialog.gd b/addons/resources_spreadsheet_view/import_export/import_export_dialog.gd new file mode 100644 index 0000000..f89f154 --- /dev/null +++ b/addons/resources_spreadsheet_view/import_export/import_export_dialog.gd @@ -0,0 +1,212 @@ +@tool +extends Window + +@export var prop_list_item_scene : PackedScene +@export var formats_export : Array[Script] +@export var formats_import : Array[Script] + +@onready var editor_view := $"../.." +@onready var node_filename_options := $"TabContainer/Import/MarginContainer/ScrollContainer/VBoxContainer/GridContainer/OptionButton" +@onready var node_classname_field := $"TabContainer/Import/MarginContainer/ScrollContainer/VBoxContainer/GridContainer/LineEdit" +@onready var node_filename_props := $"TabContainer/Import/MarginContainer/ScrollContainer/VBoxContainer/GridContainer/OptionButton" +@onready var prop_list := $"TabContainer/Import/MarginContainer/ScrollContainer/VBoxContainer" + +var entries := [] + +var property_used_as_filename := 0 +var import_data : SpreadsheetImport + + +func _ready(): + var create_file_button = Button.new() + $"../FileDialogText".get_child(3).get_child(3).add_child(create_file_button) + create_file_button.get_parent().move_child(create_file_button, 2) + create_file_button.text = "Create File" + create_file_button.visible = true + create_file_button.icon = get_theme_icon("New", "EditorIcons") + create_file_button.pressed.connect(_on_create_file_pressed) + + +func _on_create_file_pressed(): + var new_name = ( + $"../FileDialogText".get_child(3).get_child(3).get_child(1).text + ) + if new_name == "": + new_name += editor_view.current_path.get_base_dir().get_file() + + var file = FileAccess.open( + $"../FileDialogText".get_child(3).get_child(0).get_child(4).text + + "/" + + new_name.get_basename() + ".csv", FileAccess.WRITE + ) + file.close() + $"../FileDialogText".invalidate() + + +func _on_FileDialogText_file_selected(path : String): + import_data = SpreadsheetImport.new() + import_data.initialize(path) + _reset_controls() + _open_dialog(path) + popup_centered() + + +func _open_dialog(path : String): + node_classname_field.text = import_data.edited_path.get_file().get_basename()\ + .capitalize().replace(" ", "") + import_data.script_classname = node_classname_field.text + + for x in formats_import: + if x.new().can_edit_path(path): + entries = x.new().import_as_arrays(import_data) + + _load_property_names() + _create_prop_editors() + $"TabContainer/Import/MarginContainer/ScrollContainer/VBoxContainer/StyleSettingsI"._send_signal() + + +func _load_property_names(): + import_data.prop_names = Array(entries[0]) + import_data.prop_types.resize(import_data.prop_names.size()) + import_data.prop_types.fill(4) + for i in import_data.prop_names.size(): + import_data.prop_names[i] = entries[0][i]\ + .replace("\"", "")\ + .replace(" ", "_")\ + .replace("-", "_")\ + .replace(".", "_")\ + .replace(",", "_")\ + .replace("\t", "_")\ + .replace("/", "_")\ + .replace("\\", "_")\ + .to_lower() + + # Don't guess Ints automatically - further rows might have floats + if entries[1][i].is_valid_float(): + import_data.prop_types[i] = SpreadsheetImport.PropType.FLOAT + + elif entries[1][i].begins_with("res://"): + import_data.prop_types[i] = SpreadsheetImport.PropType.OBJECT + + else: + import_data.prop_types[i] = SpreadsheetImport.PropType.STRING + + node_filename_options.clear() + for i in import_data.prop_names.size(): + node_filename_options.add_item(import_data.prop_names[i], i) + + +func _create_prop_editors(): + for x in prop_list.get_children(): + if !x is GridContainer: x.free() + + for i in import_data.prop_names.size(): + var new_node = prop_list_item_scene.instance() + prop_list.add_child(new_node) + new_node.display(import_data.prop_names[i], import_data.prop_types[i]) + new_node.connect_all_signals(self, i) + + +func _generate_class(save_script = true): + save_script = true # Built-ins didn't work in 3.x, won't change because dont wanna test rn + import_data.new_script = import_data.generate_script(entries, save_script) + if save_script: + import_data.new_script.resource_path = import_data.edited_path.get_basename() + ".gd" + ResourceSaver.save(import_data.new_script) + # Because when instanced, objects have a copy of the script + import_data.new_script = load(import_data.edited_path.get_basename() + ".gd") + + +func _export_tres_folder(): + DirAccess.open("res://").make_dir_recursive(import_data.edited_path.get_basename()) + + import_data.prop_used_as_filename = import_data.prop_names[property_used_as_filename] + var new_res : Resource + for i in entries.size(): + if import_data.remove_first_row && i == 0: + continue + + new_res = import_data.strings_to_resource(entries[i]) + ResourceSaver.save(new_res) + + +func _on_import_to_tres_pressed(): + hide() + _generate_class() + _export_tres_folder() + await get_tree().process_frame + editor_view.display_folder(import_data.edited_path.get_basename() + "/") + await get_tree().process_frame + editor_view.refresh() + + +func _on_import_edit_pressed(): + hide() + _generate_class(false) + import_data.prop_used_as_filename = "" + import_data.save() + await get_tree().process_frame + editor_view.display_folder(import_data.resource_path) + editor_view.hidden_columns[editor_view.current_path] = { + "resource_path" : true, + "resource_local_to_scene" : true, + } + editor_view.save_data() + await get_tree().process_frame + editor_view.refresh() + + +func _on_export_csv_pressed(): + hide() + var exported_cols = editor_view.columns.duplicate() + exported_cols.erase("resource_local_to_scene") + for x in editor_view.hidden_columns[editor_view.current_path].keys(): + exported_cols.erase(x) + + SpreadsheetExportFormatCsv.export_to_file(editor_view.rows, exported_cols, import_data.edited_path, import_data) + await get_tree().process_frame + editor_view.refresh() + + +# Input controls +func _on_classname_field_text_changed(new_text : String): + import_data.script_classname = new_text.replace(" ", "") + + +func _on_remove_first_row_toggled(button_pressed : bool): + import_data.remove_first_row = button_pressed + $"TabContainer/Export/HBoxContainer2/Button".pressed = true + $"TabContainer/Export/HBoxContainer3/CheckBox".pressed = true + + +func _on_filename_options_item_selected(index): + property_used_as_filename = index + + +func _on_list_item_type_selected(type : int, index : int): + import_data.prop_types[index] = type + + +func _on_list_item_name_changed(name : String, index : int): + import_data.prop_names[index] = name.replace(" ", "") + + +func _on_export_delimeter_pressed(del : String): + import_data.delimeter = del + import_data.delimeter.substr(1) + + +func _on_export_space_toggled(button_pressed : bool): + import_data.delimeter = ( + import_data.delimeter[0] + if !button_pressed else + import_data.delimeter + " " + ) + + +func _reset_controls(): + $"TabContainer/Export/HBoxContainer2/CheckBox".pressed = false + _on_remove_first_row_toggled(true) + + +func _on_enum_format_changed(case, delimiter, bool_yes, bool_no): + import_data.enum_format = [case, delimiter, bool_yes, bool_no] diff --git a/addons/resources_spreadsheet_view/import_export/import_export_dialog.tscn b/addons/resources_spreadsheet_view/import_export/import_export_dialog.tscn new file mode 100644 index 0000000..fa129a2 --- /dev/null +++ b/addons/resources_spreadsheet_view/import_export/import_export_dialog.tscn @@ -0,0 +1,172 @@ +[gd_scene load_steps=5 format=3 uid="uid://b413igx28kkvb"] + +[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/import_export/import_export_dialog.gd" id="1"] +[ext_resource type="PackedScene" path="res://addons/resources_spreadsheet_view/import_export/property_list_item.tscn" id="2"] +[ext_resource type="Script" path="res://addons/resources_spreadsheet_view/import_export/formats_import/import_csv.gd" id="3"] +[ext_resource type="PackedScene" path="res://addons/resources_spreadsheet_view/import_export/import_export_enum_format.tscn" id="4"] + +[node name="Control" type="Window"] +size = Vector2i(600, 373) +wrap_controls = true +min_size = Vector2i(600, 0) +script = ExtResource("1") +prop_list_item_scene = ExtResource("2") +formats_import = [ExtResource("3")] + +[node name="TabContainer" type="TabContainer" parent="."] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_right = -552.0 +offset_bottom = -275.0 +grow_horizontal = 2 +grow_vertical = 2 +use_hidden_tabs_for_min_size = true + +[node name="Import" type="VBoxContainer" parent="TabContainer"] +layout_mode = 2 +mouse_filter = 2 + +[node name="MarginContainer" type="MarginContainer" parent="TabContainer/Import"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="ScrollContainer" type="ScrollContainer" parent="TabContainer/Import/MarginContainer"] +layout_mode = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="TabContainer/Import/MarginContainer/ScrollContainer"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="GridContainer" type="GridContainer" parent="TabContainer/Import/MarginContainer/ScrollContainer/VBoxContainer"] +layout_mode = 2 +columns = 2 + +[node name="Label" type="Label" parent="TabContainer/Import/MarginContainer/ScrollContainer/VBoxContainer/GridContainer"] +layout_mode = 2 +text = "Use as filename:" + +[node name="OptionButton" type="OptionButton" parent="TabContainer/Import/MarginContainer/ScrollContainer/VBoxContainer/GridContainer"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="Label2" type="Label" parent="TabContainer/Import/MarginContainer/ScrollContainer/VBoxContainer/GridContainer"] +layout_mode = 2 +text = "Class Name" + +[node name="LineEdit" type="LineEdit" parent="TabContainer/Import/MarginContainer/ScrollContainer/VBoxContainer/GridContainer"] +layout_mode = 2 +caret_blink = true +caret_blink_interval = 0.5 + +[node name="CheckBox" type="CheckBox" parent="TabContainer/Import/MarginContainer/ScrollContainer/VBoxContainer/GridContainer"] +layout_mode = 2 +text = "First row contains property names" + +[node name="Control" type="Control" parent="TabContainer/Import/MarginContainer/ScrollContainer/VBoxContainer/GridContainer"] +layout_mode = 2 + +[node name="Control2" type="Control" parent="TabContainer/Import/MarginContainer/ScrollContainer/VBoxContainer/GridContainer"] +visible = false +layout_mode = 2 + +[node name="StyleSettingsI" parent="TabContainer/Import/MarginContainer/ScrollContainer/VBoxContainer" instance=ExtResource("4")] +layout_mode = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="TabContainer/Import"] +layout_mode = 2 +mouse_filter = 2 +alignment = 1 + +[node name="Ok2" type="Button" parent="TabContainer/Import/HBoxContainer"] +layout_mode = 2 +text = "Confirm and edit" + +[node name="Ok" type="Button" parent="TabContainer/Import/HBoxContainer"] +layout_mode = 2 +text = "Convert to Resources and edit" + +[node name="Cancel" type="Button" parent="TabContainer/Import/HBoxContainer"] +layout_mode = 2 +text = "Cancel" + +[node name="Control" type="Control" parent="TabContainer/Import"] +layout_mode = 2 +mouse_filter = 2 + +[node name="Export" type="VBoxContainer" parent="TabContainer"] +visible = false +layout_mode = 2 + +[node name="Info" type="Label" parent="TabContainer/Export"] +layout_mode = 2 +size_flags_vertical = 0 +text = "The currently edited folder will be exported into the selected file. + +Rows hidden by the filter will NOT be exported, and order follows the current sorting key. Rows on non-selected pages will not be removed. + +Hidden columns will NOT be exported." +autowrap_mode = 2 + +[node name="HSeparator" type="HSeparator" parent="TabContainer/Export"] +layout_mode = 2 + +[node name="HBoxContainer2" type="HBoxContainer" parent="TabContainer/Export"] +layout_mode = 2 +alignment = 1 + +[node name="Label2" type="Label" parent="TabContainer/Export/HBoxContainer2"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Delimeter:" + +[node name="Button" type="Button" parent="TabContainer/Export/HBoxContainer2"] +layout_mode = 2 +toggle_mode = true +text = "Comma (,)" + +[node name="Button2" type="Button" parent="TabContainer/Export/HBoxContainer2"] +layout_mode = 2 +toggle_mode = true +text = "Semicolon (;)" + +[node name="Button3" type="Button" parent="TabContainer/Export/HBoxContainer2"] +layout_mode = 2 +toggle_mode = true +text = "Tab" + +[node name="CheckBox" type="CheckBox" parent="TabContainer/Export/HBoxContainer2"] +layout_mode = 2 +text = "With space after" + +[node name="HBoxContainer3" type="HBoxContainer" parent="TabContainer/Export"] +layout_mode = 2 + +[node name="CheckBox" type="CheckBox" parent="TabContainer/Export/HBoxContainer3"] +layout_mode = 2 +text = "First row contains property names (CSV)" + +[node name="StyleSettingsE" parent="TabContainer/Export" instance=ExtResource("4")] +layout_mode = 2 + +[node name="Control" type="Control" parent="TabContainer/Export"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="HBoxContainer" type="HBoxContainer" parent="TabContainer/Export"] +layout_mode = 2 +alignment = 1 + +[node name="Button" type="Button" parent="TabContainer/Export/HBoxContainer"] +layout_mode = 2 +text = "Export to CSV" + +[node name="Cancel" type="Button" parent="TabContainer/Export/HBoxContainer"] +layout_mode = 2 +text = "Cancel" + +[node name="Control2" type="Control" parent="TabContainer/Export"] +layout_mode = 2 + +[connection signal="pressed" from="TabContainer/Import/HBoxContainer/Cancel" to="." method="hide"] +[connection signal="pressed" from="TabContainer/Export/HBoxContainer/Cancel" to="." method="hide"] diff --git a/addons/resources_spreadsheet_view/import_export/import_export_enum_format.gd b/addons/resources_spreadsheet_view/import_export/import_export_enum_format.gd new file mode 100644 index 0000000..4dc1f41 --- /dev/null +++ b/addons/resources_spreadsheet_view/import_export/import_export_enum_format.gd @@ -0,0 +1,20 @@ +@tool +extends GridContainer + +signal format_changed(case, delimiter, bool_yes, bool_no) + + +func _send_signal(arg1 = null): + format_changed.emit( + $"HBoxContainer/Case".selected, + [" ", "_", "-"][$"HBoxContainer/Separator".selected], + $"HBoxContainer2/True".text, + $"HBoxContainer2/False".text + ) + + +func _on_format_changed(case, delimiter, bool_yes, bool_no): + $"HBoxContainer/Case".selected = case + $"HBoxContainer/Separator".selected = [" ", "_", "-"].find(delimiter) + $"HBoxContainer2/True".text = bool_yes + $"HBoxContainer2/False".text = bool_no diff --git a/addons/resources_spreadsheet_view/import_export/import_export_enum_format.tscn b/addons/resources_spreadsheet_view/import_export/import_export_enum_format.tscn new file mode 100644 index 0000000..9c0a8df --- /dev/null +++ b/addons/resources_spreadsheet_view/import_export/import_export_enum_format.tscn @@ -0,0 +1,76 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://addons/resources_speadsheet_view/import_export/import_export_enum_format.gd" type="Script" id=1] + +[node name="EnumFormat" type="GridContainer"] +margin_right = 400.0 +margin_bottom = 48.0 +rect_pivot_offset = Vector2( 40, 71 ) +columns = 2 +script = ExtResource( 1 ) + +[node name="Label3" type="Label" parent="."] +margin_top = 3.0 +margin_right = 198.0 +margin_bottom = 17.0 +size_flags_horizontal = 3 +text = "Enum word case/separator" + +[node name="HBoxContainer" type="HBoxContainer" parent="."] +margin_left = 202.0 +margin_right = 400.0 +margin_bottom = 20.0 +size_flags_horizontal = 3 + +[node name="Case" type="OptionButton" parent="HBoxContainer"] +margin_right = 129.0 +margin_bottom = 20.0 +size_flags_horizontal = 3 +text = "Caps Every Word" +clip_text = true +items = [ "all lower", null, false, 0, null, "caps Except First", null, false, 1, null, "Caps Every Word", null, false, 2, null, "ALL CAPS", null, false, 3, null ] +selected = 2 + +[node name="Separator" type="OptionButton" parent="HBoxContainer"] +margin_left = 133.0 +margin_right = 198.0 +margin_bottom = 20.0 +size_flags_horizontal = 3 +size_flags_stretch_ratio = 0.5 +text = "Space \" \"" +clip_text = true +items = [ "Space \" \"", null, false, 0, null, "Underscore \"_\"", null, false, 1, null, "Kebab \"-\"", null, false, 2, null ] +selected = 0 + +[node name="Label4" type="Label" parent="."] +margin_top = 29.0 +margin_right = 198.0 +margin_bottom = 43.0 +size_flags_horizontal = 3 +text = "Boolean True/False" + +[node name="HBoxContainer2" type="HBoxContainer" parent="."] +margin_left = 202.0 +margin_top = 24.0 +margin_right = 400.0 +margin_bottom = 48.0 +size_flags_horizontal = 3 + +[node name="True" type="LineEdit" parent="HBoxContainer2"] +margin_right = 97.0 +margin_bottom = 24.0 +size_flags_horizontal = 3 +text = "Yes" + +[node name="False" type="LineEdit" parent="HBoxContainer2"] +margin_left = 101.0 +margin_right = 198.0 +margin_bottom = 24.0 +size_flags_horizontal = 3 +text = "No" + +[connection signal="mouse_entered" from="Label3" to="." method="_on_Label3_mouse_entered"] +[connection signal="item_selected" from="HBoxContainer/Case" to="." method="_send_signal"] +[connection signal="item_selected" from="HBoxContainer/Separator" to="." method="_send_signal"] +[connection signal="text_changed" from="HBoxContainer2/True" to="." method="_send_signal"] +[connection signal="text_changed" from="HBoxContainer2/False" to="." method="_send_signal"] diff --git a/addons/resources_spreadsheet_view/import_export/property_list_item.gd b/addons/resources_spreadsheet_view/import_export/property_list_item.gd new file mode 100644 index 0000000..ecae660 --- /dev/null +++ b/addons/resources_spreadsheet_view/import_export/property_list_item.gd @@ -0,0 +1,12 @@ +@tool +extends HBoxContainer + + +func display(name : String, type : int): + $"LineEdit".text = name + $"OptionButton".selected = type + + +func connect_all_signals(to : Object, index : int, prefix : String = "_on_list_item_"): + $"LineEdit".text_changed.connect(Callable(to, prefix + "name_changed").bind(index)) + $"OptionButton".item_selected.connect(Callable(to, prefix + "type_selected").bind(index)) diff --git a/addons/resources_spreadsheet_view/import_export/property_list_item.tscn b/addons/resources_spreadsheet_view/import_export/property_list_item.tscn new file mode 100644 index 0000000..34646e0 --- /dev/null +++ b/addons/resources_spreadsheet_view/import_export/property_list_item.tscn @@ -0,0 +1,26 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://addons/resources_speadsheet_view/import_export/property_list_item.gd" type="Script" id=1] + +[node name="Entry" type="HBoxContainer"] +margin_right = 468.0 +margin_bottom = 24.0 +script = ExtResource( 1 ) + +[node name="LineEdit" type="LineEdit" parent="."] +margin_right = 309.0 +margin_bottom = 24.0 +size_flags_horizontal = 3 +size_flags_stretch_ratio = 0.5 +text = "1" + +[node name="OptionButton" type="OptionButton" parent="."] +margin_left = 313.0 +margin_right = 468.0 +margin_bottom = 24.0 +size_flags_horizontal = 3 +size_flags_vertical = 5 +size_flags_stretch_ratio = 0.25 +text = "Bool" +items = [ "Bool", null, false, 1, null, "Integer Number", null, false, 2, null, "Floating Point Number", null, false, 3, null, "String/Other", null, false, 4, null, "Vector2", null, true, 5, null, "Rect2", null, true, 6, null, "Vector3", null, true, 7, null, "Color", null, false, 14, null, "Array", null, true, 19, null, "Resource Path", null, false, 17, null, "Enumeration", null, false, 101, null ] +selected = 0 diff --git a/addons/resources_spreadsheet_view/import_export/spreadsheet_import.gd b/addons/resources_spreadsheet_view/import_export/spreadsheet_import.gd new file mode 100644 index 0000000..64f74de --- /dev/null +++ b/addons/resources_spreadsheet_view/import_export/spreadsheet_import.gd @@ -0,0 +1,293 @@ +@tool +class_name SpreadsheetImport +extends Resource + +enum PropType { + BOOL, + INT, + FLOAT, + STRING, + VECTOR2, + RECT2, + VECTOR3, + COLOR, + ARRAY, + OBJECT, + ENUM, + MAX, +} + +enum NameCasing { + ALL_LOWER, + CAPS_WORD_EXCEPT_FIRST, + CAPS_WORD, + ALL_CAPS, +} + +const SUFFIX := "_spreadsheet_import.tres" +const TYPE_MAP := { + TYPE_STRING : PropType.STRING, + TYPE_FLOAT : PropType.FLOAT, + TYPE_BOOL : PropType.BOOL, + TYPE_INT : PropType.INT, + TYPE_OBJECT : PropType.OBJECT, + TYPE_COLOR : PropType.COLOR, +} + +@export var prop_types : Array +@export var prop_names : Array + +@export var edited_path := "res://" +@export var prop_used_as_filename := "" +@export var script_classname := "" +@export var remove_first_row := true + +@export var new_script : GDScript +@export var view_script : Script = SpreadsheetEditFormatCsv +@export var delimeter := ";" +@export var enum_format : Array = [NameCasing.CAPS_WORD, " ", "Yes", "No"] + +@export var uniques : Dictionary + + +func initialize(path): + edited_path = path + prop_types = [] + prop_names = [] + + +func save(): + ResourceSaver.call_deferred("save", edited_path.get_basename() + SUFFIX, self) + + +func string_to_property(string : String, col_index : int): + match prop_types[col_index]: + PropType.STRING: + return string + + PropType.BOOL: + string = string.to_lower() + if string == enum_format[2].to_lower(): return true + if string == enum_format[3].to_lower(): return false + return !string in ["no", "disabled", "-", "false", "absent", "wrong", "off", ""] + + PropType.FLOAT: + return string.to_float() + + PropType.INT: + return string.to_int() + + PropType.COLOR: + return Color(string) + + PropType.OBJECT: + return load(string) + + PropType.ENUM: + if string == "": + return int(uniques[col_index]["N_A"]) + + else: + return int(uniques[col_index][string.capitalize().replace(" ", "_").to_upper()]) + + +func property_to_string(value, col_index : int) -> String: + if value == null: return "" + if col_index == 0: + if prop_names[col_index] == "resource_path": + return value.get_file().get_basename() + + if prop_types[col_index] is PackedStringArray: + return prop_types[col_index][value].capitalize() + + match prop_types[col_index]: + PropType.STRING: + return str(value) + + PropType.BOOL: + return enum_format[2] if value else enum_format[3] + + PropType.FLOAT, PropType.INT: + return str(value) + + PropType.COLOR: + return value.to_html() + + PropType.OBJECT: + return value.resource_path + + PropType.ENUM: + var dict = uniques[col_index] + for k in dict: + if dict[k] == value: + return change_name_to_format(k, enum_format[0], enum_format[1]) + + return str(value) + + +func create_property_line_for_prop(col_index : int) -> String: + var result = "@export var " + prop_names[col_index] + " :" + match prop_types[col_index]: + PropType.STRING: + return result + "= \"\"\r\n" + + PropType.BOOL: + return result + "= false\r\n" + + PropType.FLOAT: + return result + "= 0.0\r\n" + + PropType.INT: + return result + "= 0\r\n" + + PropType.COLOR: + return result + "= Color.white\r\n" + + PropType.OBJECT: + return result + " Resource\r\n" + + PropType.ENUM: + return result + ": %s\r\n" % _escape_forbidden_enum_names(prop_names[col_index].capitalize().replace(" ", "")) + # return result.replace( + # "@export var", + # "@export_enum(" + _escape_forbidden_enum_names( + # prop_names[col_index].capitalize()\ + # .replace(" ", "") + # ) + ") var" + # ) + "= 0\r\n" + + return "" + + +func _escape_forbidden_enum_names(string : String) -> String: + if ClassDB.class_exists(string): + return string + "_" + + # Not in ClassDB, but are engine types and can be property names + if string in [ + "Color", "String", "Plane", + "Basis", "Transform", "Variant", + ]: + return string + "_" + + return string + + +func create_enum_for_prop(col_index) -> String: + var result := ( + "enum " + + _escape_forbidden_enum_names( + prop_names[col_index].capitalize().replace(" ", "") + ) + " {\r\n" + ) + for k in uniques[col_index]: + result += ( + "\t" + + k # Enum Entry + + " = " + + str(uniques[col_index][k]) # Value + + ",\r\n" + ) + result += "\tMAX,\r\n}\r\n\r\n" + return result + + +func generate_script(entries, has_classname = true) -> GDScript: + var source = "" + if has_classname and script_classname != "": + source = "class_name " + script_classname + " \r\nextends Resource\r\n\r\n" + + else: + source = "extends Resource\r\n\r\n" + + # Enums + uniques = get_uniques(entries) + for i in prop_types.size(): + if prop_types[i] == PropType.ENUM: + source += create_enum_for_prop(i) + + # Properties + for i in prop_names.size(): + if (prop_names[i] != "resource_path") and (prop_names[i] != "resource_name"): + source += create_property_line_for_prop(i) + + var created_script = GDScript.new() + created_script.source_code = source + created_script.reload() + return created_script + + +func strings_to_resource(strings : Array): + var new_res = new_script.new() + for j in min(prop_names.size(), strings.size()): + new_res.set(prop_names[j], string_to_property(strings[j], j)) + + if prop_used_as_filename != "": + new_res.resource_path = edited_path.get_basename() + "/" + new_res.get(prop_used_as_filename) + ".tres" + + return new_res + + +func resource_to_strings(res : Resource): + var strings := [] + strings.resize(prop_names.size()) + for i in prop_names.size(): + strings[i] = property_to_string(res.get(prop_names[i]), i) + + return PackedStringArray(strings) + + +func get_uniques(entries : Array) -> Dictionary: + var result = {} + for i in prop_types.size(): + if prop_types[i] == PropType.ENUM: + var cur_value := "" + result[i] = {} + for j in entries.size(): + if j == 0 and remove_first_row: continue + + cur_value = entries[j][i].capitalize().to_upper().replace(" ", "_") + if cur_value == "": + cur_value = "N_A" + + if !result[i].has(cur_value): + result[i][cur_value] = result[i].size() + + return result + + +static func change_name_to_format(name : String, case : int, delim : String): + var string = name.capitalize().replace(" ", delim) + if case == NameCasing.ALL_LOWER: + return string.to_lower() + + if case == NameCasing.CAPS_WORD_EXCEPT_FIRST: + return string[0].to_lower() + string.substr(1) + + if case == NameCasing.CAPS_WORD: + return string + + if case == NameCasing.ALL_CAPS: + return string.to_upper() + + +static func get_resource_property_types(res : Resource, properties : Array) -> Array: + var result = [] + result.resize(properties.size()) + result.fill(PropType.STRING) + var cur_type := 0 + for x in res.get_property_list(): + var found = properties.find(x["name"]) + if found == -1: continue + if x["usage"] & PROPERTY_USAGE_EDITOR != 0: + if x["hint"] == PROPERTY_HINT_ENUM: + var enum_values = x["hint_string"].split(",") + for i in enum_values.size(): + enum_values[i] = enum_values[i].left(enum_values[i].find(":")) + + result[found] = enum_values + + else: + result[found] = TYPE_MAP.get(x["type"], PropType.STRING) + + return result diff --git a/addons/resources_spreadsheet_view/plugin.cfg b/addons/resources_spreadsheet_view/plugin.cfg index 18faf9f..8acc0fe 100644 --- a/addons/resources_spreadsheet_view/plugin.cfg +++ b/addons/resources_spreadsheet_view/plugin.cfg @@ -5,5 +5,5 @@ description="Edit Many Resources from one Folder as a table. Heavily inspired by Multi-Cursor-Editing in text editors, so after selecting multiple cells (in the same column!) using Ctrl+Click or Shift+Click, most Basic-to-Intermediate movements should be available." author="Don Tnowe" -version="2.1" +version="2.3" script="plugin.gd" diff --git a/addons/resources_spreadsheet_view/saved_state.json b/addons/resources_spreadsheet_view/saved_state.json deleted file mode 100644 index 539c539..0000000 --- a/addons/resources_spreadsheet_view/saved_state.json +++ /dev/null @@ -1 +0,0 @@ -{ "recent_paths": ["res://example/Random Upgrades/upgrades", "res://example/Random Upgrades/upgrades/"], "hidden_columns": { "res://example/Random Upgrades/upgrades/": { "resource_name": true, "resource_local_to_scene": true, "list_item_delimeter": true, "list_row_delimeter": true } } } \ No newline at end of file diff --git a/addons/resources_spreadsheet_view/selection_actions.gd b/addons/resources_spreadsheet_view/selection_actions.gd new file mode 100644 index 0000000..7ea2cd8 --- /dev/null +++ b/addons/resources_spreadsheet_view/selection_actions.gd @@ -0,0 +1,177 @@ +@tool +extends MarginContainer + +enum { + EDITBOX_DUPLICATE = 1, + EDITBOX_RENAME, + EDITBOX_DELETE, +} + +@export var editor_view := NodePath("../..") + +@onready var editbox_node := $"Control/ColorRect/Popup" +@onready var editbox_label := editbox_node.get_node("Panel/VBoxContainer/Label") +@onready var editbox_input := editbox_node.get_node("Panel/VBoxContainer/LineEdit") + +var cell : Control +var editbox_action : int + + +func _ready(): + editbox_input.get_node("../..").add_theme_stylebox_override( + "panel", + get_theme_stylebox("Content", "EditorStyles") + ) + + +func _on_grid_cells_context(cells): + open(cells) + + +func _on_grid_cells_selected(cells): + if ProjectSettings.get_setting(SettingsGrid.SETTING_PREFIX + "context_menu_on_leftclick"): + open(cells, true) + + else: hide() + + +func open(cells : Array, pin_to_cell : bool = false): + if cells.size() == 0: + hide() + cell = null + return + + if pin_to_cell: + cell = cells[-1] + global_position = Vector2( + cell.global_position.x + cell.size.x, + cell.global_position.y + ) + + else: + cell = null + global_position = get_global_mouse_position() + Vector2.ONE + + size = Vector2.ZERO + top_level = true + show() + $"Control2/Label".text = str(cells.size()) + (" Cells" if cells.size() % 10 != 1 else " Cell") + $"GridContainer/Rename".visible = get_node(editor_view).has_row_names() + + +func _unhandled_input(event): + if !get_node(editor_view).is_visible_in_tree(): + hide() + return + + if event is InputEventKey: + if Input.is_key_pressed(KEY_CTRL): + # Dupe + if event.keycode == KEY_D: + _on_Duplicate_pressed() + return + + # Rename + if event.keycode == KEY_R: + _on_Rename_pressed() + return + + if event is InputEventMouseButton && event.is_pressed(): + hide() + + +func _input(event): + if cell == null: return + if !get_node(editor_view).is_visible_in_tree(): + hide() + return + + global_position = Vector2( + cell.global_position.x + cell.size.x, + cell.global_position.y + ) + + +func _on_Duplicate_pressed(): + _show_editbox(EDITBOX_DUPLICATE) + + +func _on_CbCopy_pressed(): + TextEditingUtils.multi_copy(get_node(editor_view).edited_cells_text) + + +func _on_CbPaste_pressed(): + get_node(editor_view).set_edited_cells_values( + TextEditingUtils.multi_paste( + get_node(editor_view).edited_cells_text, + get_node(editor_view).edit_cursor_positions + ) + ) + + +func _on_Rename_pressed(): + _show_editbox(EDITBOX_RENAME) + + +func _on_Delete_pressed(): + _show_editbox(EDITBOX_DELETE) + + +func _show_editbox(action): + var node_editor_view = get_node(editor_view) + editbox_action = action + match action: + EDITBOX_DUPLICATE: + if !node_editor_view.has_row_names(): + _on_editbox_accepted() + return + + if node_editor_view.edited_cells.size() == 1: + editbox_label.text = "Input new row's name..." + editbox_input.text = node_editor_view.get_last_selected_row()\ + .resource_path.get_file().get_basename() + + else: + editbox_label.text = "Input suffix to append to names..." + editbox_input.text = "" + + EDITBOX_RENAME: + editbox_label.text = "Input new name for row..." + editbox_input.text = node_editor_view.get_last_selected_row()\ + .resource_path.get_file().get_basename() + + EDITBOX_DELETE: + editbox_label.text = "Really delete selected rows? (Irreversible!!!)" + editbox_input.text = node_editor_view.get_last_selected_row()\ + .resource_path.get_file().get_basename() + + editbox_input.grab_focus() + editbox_input.caret_position = 999999999 + editbox_node.show() + $"Control/ColorRect".show() + $"Control/ColorRect".top_level = true + $"Control/ColorRect".size = get_viewport_rect().size * 4.0 + editbox_node.global_position = ( + global_position + + size * 0.5 + - editbox_node.get_child(0).size * 0.5 + ) + + +func _on_editbox_closed(): + editbox_node.hide() + $"Control/ColorRect".hide() + + +func _on_editbox_accepted(): + match(editbox_action): + EDITBOX_DUPLICATE: + get_node(editor_view).duplicate_selected_rows(editbox_input.text) + + EDITBOX_RENAME: + get_node(editor_view).rename_row(get_node(editor_view).get_last_selected_row(), editbox_input.text) + + EDITBOX_DELETE: + get_node(editor_view).delete_selected_rows() + + _on_editbox_closed() diff --git a/addons/resources_spreadsheet_view/selection_actions.tscn b/addons/resources_spreadsheet_view/selection_actions.tscn new file mode 100644 index 0000000..7b8eaad --- /dev/null +++ b/addons/resources_spreadsheet_view/selection_actions.tscn @@ -0,0 +1,217 @@ +[gd_scene load_steps=8 format=2] + +[ext_resource path="res://addons/resources_speadsheet_view/editor_icon_button.gd" type="Script" id=1] +[ext_resource path="res://addons/resources_speadsheet_view/selection_actions.gd" type="Script" id=2] + +[sub_resource type="Image" id=6] +data = { +"data": PoolByteArray( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), +"format": "LumAlpha8", +"height": 16, +"mipmaps": false, +"width": 16 +} + +[sub_resource type="ImageTexture" id=2] +flags = 4 +flags = 4 +image = SubResource( 6 ) +size = Vector2( 16, 16 ) + +[sub_resource type="Image" id=7] +data = { +"data": PoolByteArray( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ), +"format": "LumAlpha8", +"height": 16, +"mipmaps": false, +"width": 16 +} + +[sub_resource type="ImageTexture" id=4] +flags = 4 +flags = 4 +image = SubResource( 7 ) +size = Vector2( 16, 16 ) + +[sub_resource type="StyleBoxTexture" id=5] +texture = SubResource( 4 ) +region_rect = Rect2( 0, 0, 16, 16 ) +margin_left = 2.0 +margin_right = 2.0 +margin_top = 2.0 +margin_bottom = 2.0 + +[node name="SelectionActions" type="MarginContainer"] +margin_right = 84.0 +margin_bottom = 44.0 +mouse_filter = 2 +size_flags_horizontal = 9 +script = ExtResource( 2 ) +__meta__ = { +"_edit_group_": true, +"_edit_lock_": true +} + +[node name="Control2" type="Control" parent="."] +margin_right = 84.0 +margin_bottom = 44.0 +mouse_filter = 2 + +[node name="ColorRect" type="ColorRect" parent="Control2"] +show_behind_parent = true +margin_left = -2.0 +margin_top = -18.0 +margin_bottom = 2.0 +mouse_filter = 2 + +[node name="ColorRect2" type="ColorRect" parent="Control2"] +show_behind_parent = true +anchor_bottom = 1.0 +margin_left = -2.0 +mouse_filter = 2 + +[node name="Label" type="Label" parent="Control2"] +margin_left = 2.0 +margin_top = -16.0 +margin_right = 50.0 +margin_bottom = -2.0 +text = "Actions" + +[node name="ColorRect3" type="Panel" parent="Control2/Label"] +show_behind_parent = true +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_left = -2.0 +margin_top = -2.0 +margin_bottom = 2.0 +mouse_filter = 2 + +[node name="GridContainer" type="GridContainer" parent="."] +margin_right = 84.0 +margin_bottom = 44.0 +mouse_filter = 2 +size_flags_horizontal = 9 +size_flags_vertical = 9 +custom_constants/vseparation = 0 +custom_constants/hseparation = 0 +columns = 3 + +[node name="Duplicate" type="Button" parent="GridContainer"] +margin_right = 28.0 +margin_bottom = 22.0 +tooltip_text = "Duplicate Selected Rows (Ctrl+D)" +mouse_filter = 1 +icon = SubResource( 2 ) +script = ExtResource( 1 ) +icon_name = "Duplicate" + +[node name="CbCopy" type="Button" parent="GridContainer"] +margin_left = 28.0 +margin_right = 56.0 +margin_bottom = 22.0 +tooltip_text = "Copy to Clipboard (Ctrl+C)" +mouse_filter = 1 +icon = SubResource( 2 ) +script = ExtResource( 1 ) +icon_name = "ActionCopy" + +[node name="CbPaste" type="Button" parent="GridContainer"] +margin_left = 56.0 +margin_right = 84.0 +margin_bottom = 22.0 +tooltip_text = "Paste Clipboard (Ctrl+V)" +mouse_filter = 1 +icon = SubResource( 2 ) +script = ExtResource( 1 ) +icon_name = "ActionPaste" + +[node name="Rename" type="Button" parent="GridContainer"] +margin_top = 22.0 +margin_right = 28.0 +margin_bottom = 44.0 +tooltip_text = "Rename Selected Rows (Ctrl+R)" +mouse_filter = 1 +icon = SubResource( 2 ) +script = ExtResource( 1 ) +icon_name = "Edit" + +[node name="Delete" type="Button" parent="GridContainer"] +margin_left = 28.0 +margin_top = 22.0 +margin_right = 56.0 +margin_bottom = 44.0 +tooltip_text = "DELETE Selected Rows" +mouse_filter = 1 +icon = SubResource( 2 ) +script = ExtResource( 1 ) +icon_name = "Remove" + +[node name="Control" type="Control" parent="."] +margin_right = 84.0 +margin_bottom = 44.0 +mouse_filter = 2 + +[node name="ColorRect" type="ColorRect" parent="Control"] +visible = false +anchor_right = 1.0 +anchor_bottom = 1.0 +color = Color( 0, 0, 0, 0.498039 ) + +[node name="Popup" type="Popup" parent="Control/ColorRect"] +visible = true +margin_left = 42.0 +margin_top = 22.0 +margin_right = 42.0 +margin_bottom = 22.0 +mouse_filter = 2 +popup_exclusive = true + +[node name="Panel" type="PanelContainer" parent="Control/ColorRect/Popup"] +margin_right = 14.0 +margin_bottom = 14.0 +rect_min_size = Vector2( 192, 0 ) +mouse_filter = 2 +custom_styles/panel = SubResource( 5 ) + +[node name="VBoxContainer" type="VBoxContainer" parent="Control/ColorRect/Popup/Panel"] +margin_left = 7.0 +margin_top = 7.0 +margin_right = 185.0 +margin_bottom = 73.0 +mouse_filter = 2 + +[node name="Label" type="Label" parent="Control/ColorRect/Popup/Panel/VBoxContainer"] +margin_right = 178.0 +margin_bottom = 14.0 +text = "Input new name..." + +[node name="LineEdit" type="LineEdit" parent="Control/ColorRect/Popup/Panel/VBoxContainer"] +margin_top = 18.0 +margin_right = 178.0 +margin_bottom = 42.0 + +[node name="HBoxContainer" type="HBoxContainer" parent="Control/ColorRect/Popup/Panel/VBoxContainer"] +margin_top = 46.0 +margin_right = 178.0 +margin_bottom = 66.0 +alignment = 1 + +[node name="Button" type="Button" parent="Control/ColorRect/Popup/Panel/VBoxContainer/HBoxContainer"] +margin_left = 44.0 +margin_right = 75.0 +margin_bottom = 20.0 +text = "OK" + +[node name="Button2" type="Button" parent="Control/ColorRect/Popup/Panel/VBoxContainer/HBoxContainer"] +margin_left = 79.0 +margin_right = 133.0 +margin_bottom = 20.0 +text = "Cancel" + +[connection signal="pressed" from="GridContainer/Duplicate" to="." method="_on_Duplicate_pressed"] +[connection signal="pressed" from="GridContainer/CbCopy" to="." method="_on_CbCopy_pressed"] +[connection signal="pressed" from="GridContainer/CbPaste" to="." method="_on_CbPaste_pressed"] +[connection signal="pressed" from="GridContainer/Rename" to="." method="_on_Rename_pressed"] +[connection signal="pressed" from="GridContainer/Delete" to="." method="_on_Delete_pressed"] +[connection signal="pressed" from="Control/ColorRect/Popup/Panel/VBoxContainer/HBoxContainer/Button" to="." method="_on_editbox_accepted"] +[connection signal="pressed" from="Control/ColorRect/Popup/Panel/VBoxContainer/HBoxContainer/Button2" to="." method="_on_editbox_closed"] diff --git a/addons/resources_spreadsheet_view/settings_grid.gd b/addons/resources_spreadsheet_view/settings_grid.gd index bc4c629..a1f34ec 100644 --- a/addons/resources_spreadsheet_view/settings_grid.gd +++ b/addons/resources_spreadsheet_view/settings_grid.gd @@ -7,7 +7,7 @@ const SETTING_PREFIX = "addons/resources_spreadsheet_view/" func _ready(): for x in get_children(): - var setting = SETTING_PREFIX + TextEditingUtils.pascal_case_to_snake_case(x.name) + var setting = SETTING_PREFIX + x.name.to_snake_case() if x is BaseButton: x.toggled.connect(_set_setting.bind(setting)) if !ProjectSettings.has_setting(setting): diff --git a/addons/resources_spreadsheet_view/table_header.gd b/addons/resources_spreadsheet_view/table_header.gd index 52a8284..f27699b 100644 --- a/addons/resources_spreadsheet_view/table_header.gd +++ b/addons/resources_spreadsheet_view/table_header.gd @@ -5,7 +5,7 @@ var editor_view : Control func set_label(label : String): - $"Button".text = TextEditingUtils.string_snake_to_naming_case(label) + $"Button".text = label.capitalize() $"Button".tooltip_text = label + "\nClick to sort." diff --git a/addons/resources_spreadsheet_view/table_header.tscn b/addons/resources_spreadsheet_view/table_header.tscn index 58e4000..0c317b9 100644 --- a/addons/resources_spreadsheet_view/table_header.tscn +++ b/addons/resources_spreadsheet_view/table_header.tscn @@ -1,33 +1,16 @@ -[gd_scene load_steps=5 format=3 uid="uid://bya5ugi40ptxl"] +[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"] -[sub_resource type="Image" id="Image_1jisk"] -data = { -"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 93, 93, 55, 255, 97, 97, 58, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 98, 98, 47, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 94, 94, 46, 255, 93, 93, 236, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), -"format": "RGBA8", -"height": 16, -"mipmaps": false, -"width": 16 -} - -[sub_resource type="ImageTexture" id="ImageTexture_wgkau"] -image = SubResource("Image_1jisk") - [node name="Header" type="HBoxContainer"] -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 +offset_right = 179.0 +offset_bottom = 31.0 size_flags_horizontal = 3 script = ExtResource("1_xhap0") [node name="Button" type="Button" parent="."] layout_mode = 2 -offset_right = 1124.0 -offset_bottom = 31.0 size_flags_horizontal = 3 size_flags_vertical = 0 text = "resource_name" @@ -35,11 +18,7 @@ clip_text = true [node name="Button2" type="MenuButton" parent="."] layout_mode = 2 -offset_left = 1128.0 -offset_right = 1152.0 -offset_bottom = 648.0 size_flags_horizontal = 9 -icon = SubResource("ImageTexture_wgkau") item_count = 2 popup/item_0/text = "Select All" popup/item_0/id = 0 diff --git a/addons/resources_spreadsheet_view/table_pages.gd b/addons/resources_spreadsheet_view/table_pages.gd index b27c009..553eacb 100644 --- a/addons/resources_spreadsheet_view/table_pages.gd +++ b/addons/resources_spreadsheet_view/table_pages.gd @@ -3,18 +3,10 @@ extends HBoxContainer @export @onready var node_editor_view_root : Control = $"../../../.." -# These can not be set externally. -var rows_per_page := 50: - set(v): pass - -var current_page := 0: - set(v): pass - -var first_row := 0: - set(v): pass - -var last_row := 50: - set(v): pass +var rows_per_page := 50 +var current_page := 0 +var first_row := 0 +var last_row := 50 func _on_grid_updated(): @@ -85,7 +77,7 @@ func _fill_buttons_with_prefixes(btns, strings, page_count): continue for j in strings[i].length(): - if strings[i].ord_at(j) != strings[i - 1].ord_at(j): + if strings[i].unicode_at(j) != strings[i - 1].unicode_at(j): btns[i].text = strings[i].left(j + 1) btns[i - 1].text = strings[i - 1].left(max(j + 1, btns[i - 1].text.length())) break diff --git a/addons/resources_spreadsheet_view/text_editing_utils.gd b/addons/resources_spreadsheet_view/text_editing_utils.gd index a3d8b2c..e811e8f 100644 --- a/addons/resources_spreadsheet_view/text_editing_utils.gd +++ b/addons/resources_spreadsheet_view/text_editing_utils.gd @@ -89,7 +89,7 @@ static func multi_move_right(values : Array, cursor_positions : Array, ctrl_pres static func multi_paste(values : Array, cursor_positions : Array): - var pasted_lines := DisplayServer.clipboard_get().split("\n") + var pasted_lines := DisplayServer.clipboard_get().replace("\r", "").split("\n") var paste_each_line := pasted_lines.size() == values.size() for i in values.size(): @@ -140,25 +140,3 @@ static func _step_cursor(text : String, start : int, step : int = 1, ctrl_presse return start return 0 - - -static func string_snake_to_naming_case(string : String, add_spaces : bool = true) -> String: - if string == "": return "" - - var split = string.split("_") - for i in split.size(): - split[i] = split[i][0].to_upper() + split[i].substr(1).to_lower() - - return (" " if add_spaces else "").join(split) - - -static func pascal_case_to_snake_case(string : String) -> String: - var i = 0 - while i < string.length(): - if string.unicode_at(i) < 97: - string = string.left(i) + ("_" if i > 0 else "") + string[i].to_lower() + string.substr(i + 1) - i += 1 - - i += 1 - - return string diff --git a/addons/resources_spreadsheet_view/typed_editors/dock_texture.gd b/addons/resources_spreadsheet_view/typed_editors/dock_texture.gd index f523ade..1016814 100644 --- a/addons/resources_spreadsheet_view/typed_editors/dock_texture.gd +++ b/addons/resources_spreadsheet_view/typed_editors/dock_texture.gd @@ -31,7 +31,7 @@ func _on_Button_pressed(): tile.region = Rect2(tile_size * Vector2(i, j), tile_size) tile.atlas = _stored_value tile_array.append(tile) - tile.take_over_path(folder_name + "/" + folder_name.get_file() + "_" + str(j * v_count + i + 1) + ".tres") + tile.take_over_path(folder_name + "/" + folder_name.get_file() + "_" + str(j * h_count + i + 1) + ".tres") ResourceSaver.save(tile) tile_array.resize(sheet.edited_cells.size()) diff --git a/project.godot b/project.godot index 4c71a2e..a2430ee 100644 --- a/project.godot +++ b/project.godot @@ -8,57 +8,6 @@ config_version=5 -_global_script_classes=[{ -"base": "RefCounted", -"class": &"CellEditor", -"language": &"GDScript", -"path": "res://addons/resources_spreadsheet_view/typed_cells/cell_editor.gd" -}, { -"base": "CellEditor", -"class": &"CellEditorArray", -"language": &"GDScript", -"path": "res://addons/resources_spreadsheet_view/typed_cells/cell_editor_array.gd" -}, { -"base": "GridContainer", -"class": &"SettingsGrid", -"language": &"GDScript", -"path": "res://addons/resources_spreadsheet_view/settings_grid.gd" -}, { -"base": "Control", -"class": &"SheetsDockEditor", -"language": &"GDScript", -"path": "res://addons/resources_spreadsheet_view/typed_editors/dock_base.gd" -}, { -"base": "RefCounted", -"class": &"TextEditingUtils", -"language": &"GDScript", -"path": "res://addons/resources_spreadsheet_view/text_editing_utils.gd" -}, { -"base": "Control", -"class": &"ThemeColorSetter", -"language": &"GDScript", -"path": "res://addons/resources_spreadsheet_view/editor_color_setter.gd" -}, { -"base": "Button", -"class": &"ThemeIconButton", -"language": &"GDScript", -"path": "res://addons/resources_spreadsheet_view/editor_icon_button.gd" -}, { -"base": "Control", -"class": &"ThemeStylebox", -"language": &"GDScript", -"path": "res://addons/resources_spreadsheet_view/editor_stylebox_overrider.gd" -}] -_global_script_class_icons={ -"CellEditor": "", -"CellEditorArray": "", -"SettingsGrid": "", -"SheetsDockEditor": "", -"TextEditingUtils": "", -"ThemeColorSetter": "", -"ThemeIconButton": "", -"ThemeStylebox": "" -} color_rows=false color_arrays=true @@ -76,6 +25,10 @@ resources_spreadsheet_view/resource_preview_size=32.0 config/name="Addon: Resources as Sheets" config/features=PackedStringArray("4.0") +[editor] + +export/convert_text_resources_to_binary=true + [editor_plugins] enabled=PackedStringArray("res://addons/resources_spreadsheet_view/plugin.cfg")