322 lines
9.9 KiB
GDScript
322 lines
9.9 KiB
GDScript
@tool
|
|
extends Control
|
|
|
|
signal cells_selected(cells_positions)
|
|
signal cells_rightclicked(cells_positions)
|
|
|
|
const EditorViewClass := preload("res://addons/resources_spreadsheet_view/editor_view.gd")
|
|
const TextEditingUtilsClass := preload("res://addons/resources_spreadsheet_view/text_editing_utils.gd")
|
|
|
|
@export var cell_editor_classes : Array[Script] = []
|
|
|
|
@onready var node_property_editors : VBoxContainer = $"../HeaderContentSplit/MarginContainer/FooterContentSplit/Footer/PropertyEditors"
|
|
@onready var scrollbar : ScrollContainer = $"../HeaderContentSplit/MarginContainer/FooterContentSplit/Panel/Scroll"
|
|
|
|
@onready var editor_view : EditorViewClass = get_parent()
|
|
|
|
var edited_cells : Array = []
|
|
var edited_cells_text : Array = []
|
|
var edit_cursor_positions : Array[int] = []
|
|
|
|
var all_cell_editors : Array = []
|
|
var column_editors : Array[Object] = []
|
|
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)
|
|
|
|
scrollbar.get_h_scroll_bar().value_changed.connect(queue_redraw.unbind(1), CONNECT_DEFERRED)
|
|
scrollbar.get_v_scroll_bar().value_changed.connect(queue_redraw.unbind(1), CONNECT_DEFERRED)
|
|
|
|
if ProjectSettings.get_setting(editor_view.TablesPluginSettingsClass.PREFIX + "fold_docks", false):
|
|
for x in node_property_editors.get_children():
|
|
x.resize_set_hidden(true)
|
|
|
|
|
|
func _draw():
|
|
if edited_cells.size() == 0 or edit_cursor_positions.size() != edited_cells.size() or !column_editors[edited_cells[0].x].is_text():
|
|
return
|
|
|
|
var font := get_theme_font(&"font", &"Label")
|
|
var font_size := get_theme_font_size(&"font", &"Label")
|
|
var caret_color := get_theme_color(&"caret_color", &"LineEdit")
|
|
var label_padding_left := 2.0
|
|
var newline_char := 10
|
|
for i in edited_cells.size():
|
|
var cell : Control = get_cell_node_from_position(edited_cells[i])
|
|
var caret_rect := Rect2()
|
|
if cell.has_method(&"get_character_bounds"):
|
|
if edited_cells_text[i].length() == edit_cursor_positions[i]:
|
|
caret_rect = cell.get_character_bounds(edit_cursor_positions[i] - 1)
|
|
caret_rect.position.x += caret_rect.size.x
|
|
|
|
else:
|
|
caret_rect = cell.get_character_bounds(edit_cursor_positions[i])
|
|
|
|
caret_rect.size.x = 1.0
|
|
|
|
else:
|
|
caret_rect = TextEditingUtilsClass.get_caret_rect(edited_cells_text[i], edit_cursor_positions[i], font, font_size, label_padding_left, 1.0)
|
|
|
|
caret_rect.position += cell.global_position - global_position
|
|
draw_rect(caret_rect, caret_color)
|
|
|
|
|
|
func initialize_editors(column_values, column_types, column_hints):
|
|
_set_visible_selected(false)
|
|
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():
|
|
_set_visible_selected(false)
|
|
edited_cells.clear()
|
|
edited_cells_text.clear()
|
|
edit_cursor_positions.clear()
|
|
_selection_changed()
|
|
|
|
|
|
func deselect_cell(cell : Vector2i):
|
|
var idx := edited_cells.find(cell)
|
|
if idx == -1: return
|
|
|
|
edited_cells.remove_at(idx)
|
|
if edited_cells_text.size() != 0:
|
|
edited_cells_text.remove_at(idx)
|
|
edit_cursor_positions.remove_at(idx)
|
|
|
|
var cell_node := get_cell_node_from_position(cell)
|
|
if cell_node != null:
|
|
column_editors[get_cell_column(cell)].set_selected(cell_node, false)
|
|
|
|
_selection_changed()
|
|
|
|
|
|
func select_cell(cell : Vector2i):
|
|
var column_index := get_cell_column(cell)
|
|
if edited_cells.size() == 0 or edited_cells[0].x == cell.x:
|
|
_add_cell_to_selection(cell)
|
|
_try_open_docks(cell)
|
|
inspector_resource = editor_view.rows[get_cell_row(cell)]
|
|
editor_view.editor_plugin.get_editor_interface().edit_resource(inspector_resource)
|
|
|
|
_selection_changed()
|
|
|
|
|
|
func select_cells(cells : Array):
|
|
var last_selectible := Vector2i(-1, -1)
|
|
var started_empty := edited_cells.size() == 0
|
|
for x in cells:
|
|
if started_empty or edited_cells[0].x != x.x:
|
|
_add_cell_to_selection(x)
|
|
if get_cell_node_from_position(x) != null:
|
|
last_selectible = x
|
|
|
|
if last_selectible != Vector2i(-1, -1):
|
|
select_cell(last_selectible)
|
|
|
|
|
|
func select_cells_to(cell : Vector2i):
|
|
var column_index := get_cell_column(cell)
|
|
if edited_cells.size() == 0 or column_index != get_cell_column(edited_cells[-1]):
|
|
return
|
|
|
|
var row_start := get_cell_row(edited_cells[-1])
|
|
var row_end := get_cell_row(cell)
|
|
var edge_shift := -1 if row_start > row_end else 1
|
|
row_start += edge_shift
|
|
row_end += edge_shift
|
|
|
|
var column_editor := column_editors[column_index]
|
|
for i in range(row_start, row_end, edge_shift):
|
|
var cur_cell := Vector2i(column_index, i)
|
|
var cur_cell_node := get_cell_node_from_position(cur_cell)
|
|
if cur_cell not in edited_cells:
|
|
edited_cells.append(cur_cell)
|
|
|
|
var cur_cell_value = editor_view.io.get_value(editor_view.rows[cur_cell.y], editor_view.columns[cur_cell.x])
|
|
var cur_cell_text : String = column_editor.to_text(cur_cell_value)
|
|
edited_cells_text.append(cur_cell_text)
|
|
edit_cursor_positions.append(cur_cell_text.length())
|
|
|
|
if cur_cell_node == null or !cur_cell_node.visible or cur_cell_node.mouse_filter == MOUSE_FILTER_IGNORE:
|
|
# When showing several classes, empty cells will be non-selectable.
|
|
continue
|
|
|
|
column_editors[column_index].set_selected(cur_cell_node, true)
|
|
|
|
_selection_changed()
|
|
|
|
|
|
func rightclick_cells():
|
|
cells_rightclicked.emit(edited_cells)
|
|
|
|
|
|
func is_cell_node_selected(cell : Control) -> bool:
|
|
return get_cell_node_position(cell) in edited_cells
|
|
|
|
|
|
func is_cell_selected(cell : Vector2i) -> bool:
|
|
return cell in edited_cells
|
|
|
|
|
|
func can_select_cell(cell : Vector2i) -> bool:
|
|
if edited_cells.size() == 0:
|
|
return true
|
|
|
|
if (
|
|
get_cell_column(cell)
|
|
!= get_cell_column(edited_cells[0])
|
|
):
|
|
return false
|
|
|
|
return !cell in edited_cells
|
|
|
|
|
|
func get_cell_node_from_position(cell_pos : Vector2i) -> Control:
|
|
var cell_index := (cell_pos.y - editor_view.first_row) * editor_view.columns.size() + cell_pos.x
|
|
if cell_index < 0 or cell_index >= editor_view.node_table_root.get_child_count():
|
|
return null
|
|
|
|
return editor_view.node_table_root.get_child(cell_index)
|
|
|
|
|
|
func get_cell_node_position(cell : Control) -> Vector2i:
|
|
var col_count := editor_view.columns.size()
|
|
var cell_index := cell.get_index()
|
|
return Vector2i(cell_index % col_count, cell_index / col_count + editor_view.first_row)
|
|
|
|
|
|
func get_cell_column(cell : Vector2i) -> int:
|
|
return cell.x
|
|
|
|
|
|
func get_cell_row(cell : Vector2i) -> int:
|
|
return cell.y
|
|
|
|
|
|
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 clipboard_paste():
|
|
if column_editors[edited_cells[0].x].is_text():
|
|
editor_view.set_edited_cells_values(
|
|
TextEditingUtilsClass.multi_paste(
|
|
edited_cells_text,
|
|
edit_cursor_positions,
|
|
)
|
|
)
|
|
|
|
elif DisplayServer.clipboard_has():
|
|
var values := []
|
|
values.resize(edited_cells.size())
|
|
var pasted_lines := DisplayServer.clipboard_get().replace("\r", "").split("\n")
|
|
var paste_each_line := pasted_lines.size() == values.size()
|
|
|
|
for i in values.size():
|
|
values[i] = str_to_var(
|
|
pasted_lines[i] if paste_each_line else DisplayServer.clipboard_get()
|
|
)
|
|
|
|
editor_view.set_edited_cells_values(values)
|
|
|
|
|
|
func _selection_changed():
|
|
queue_redraw()
|
|
cells_selected.emit(edited_cells)
|
|
|
|
|
|
func _set_visible_selected(state : bool):
|
|
for x in edited_cells:
|
|
var cell_node := get_cell_node_from_position(x)
|
|
if cell_node != null:
|
|
column_editors[get_cell_column(x)].set_selected(cell_node, state)
|
|
|
|
|
|
func _add_cell_to_selection(cell : Vector2i):
|
|
if !edited_cells.has(cell):
|
|
edited_cells.append(cell)
|
|
|
|
var column_editor := column_editors[get_cell_column(cell)]
|
|
var cell_node := get_cell_node_from_position(cell)
|
|
if cell_node != null:
|
|
column_editor.set_selected(cell_node, true)
|
|
|
|
var cell_value = editor_view.io.get_value(editor_view.rows[cell.y], editor_view.columns[cell.x])
|
|
var text_value : String = column_editor.to_text(cell_value)
|
|
edited_cells_text.append(text_value)
|
|
edit_cursor_positions.append(text_value.length())
|
|
|
|
|
|
func _update_selected_cells_text():
|
|
if edited_cells_text.size() == 0:
|
|
return
|
|
|
|
var column_editor := column_editors[get_cell_column(edited_cells[0])]
|
|
if !column_editor.text_update_on_edit():
|
|
return
|
|
|
|
for i in edited_cells.size():
|
|
edited_cells_text[i] = column_editor.to_text(editor_view.io.get_value(
|
|
editor_view.rows[edited_cells[i].y],
|
|
editor_view.columns[edited_cells[i].x],
|
|
))
|
|
edit_cursor_positions[i] = edited_cells_text[i].length()
|
|
|
|
|
|
func _try_open_docks(cell : Vector2i):
|
|
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 : StringName):
|
|
if !editor_view.is_visible_in_tree(): return
|
|
if inspector_resource != editor_view.editor_plugin.get_editor_interface().get_inspector().get_edited_object():
|
|
return
|
|
|
|
if editor_view.columns[get_cell_column(edited_cells[0])] != property:
|
|
var columns := editor_view.columns
|
|
var previously_edited := edited_cells.duplicate()
|
|
var new_column := columns.find(property)
|
|
deselect_all_cells()
|
|
for i in previously_edited.size():
|
|
_add_cell_to_selection(Vector2i(new_column, previously_edited[i].y))
|
|
|
|
var new_value = inspector_resource[property]
|
|
var values := []
|
|
values.resize(edited_cells.size())
|
|
values.fill(new_value)
|
|
if new_value is Resource and new_value.resource_path == "":
|
|
for i in values.size():
|
|
values[i] = new_value.duplicate()
|
|
|
|
editor_view.set_edited_cells_values.call_deferred(values)
|
|
_try_open_docks(edited_cells[0])
|