Files
imi_sky/table.gd

417 lines
13 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@tool
extends GridContainer
@export var _selector_color: Color = Color(1.0, 1.0, 1.0, 1.0)
@export var columns_count: int = 1: set = _set_columns_count
@export var rows_count: int = 1: set = _set_rows_count
@export var node_type_file: String = 'res://ячейка-1.tscn'
const Selector = preload('res://Selector.tscn')
const INDEX_FORMAT = '%02d_%02d'
const NODE_PREFIX = 'NODE_'
const DATA_PREFIX = 'DATA_'
const CONN_PREFIX = 'CONN_'
const SLCT_PREFIX = 'SLCT_'
const NODE_KEY_FORMAT = NODE_PREFIX + INDEX_FORMAT
const DATA_KEY_FORMAT = DATA_PREFIX + INDEX_FORMAT
const CONNECTED_KEY_FORMAT = CONN_PREFIX + INDEX_FORMAT
const ROW_SELECTOR_KEY_FORMAT = SLCT_PREFIX + INDEX_FORMAT
var nodes: = {}
var selector_color_proc: Callable
var selector_size_proc: Callable
signal row_pressed(i_row) # Вызывается при нажатии кнопкой мыши на строку
const MAX_INDEX = 99 # Максимальное значение индекса для колонки и строки. Связано с форматом ключа
func align_to_left_bottom(header, gap: = Vector2(0, 0)):
""" Выравнивает по нижнему левому углу. Используется для выравнивания по заголовку """
position.x = header.position.x + gap.x
position.y = header.position.y + header.size.y + gap.y
func _assert_meta(i_col: int, i_row: int, pname: String, node):
assert(node.has_meta(pname)) #,'ошибка: нет мета-свойства \"%s\" у ячейки (%s): колонка:%d ряд:%d' % [pname, typeof(node), i_col, i_row])
func _set_columns_count(cc: int):
columns_count = cc
func _set_rows_count(rc: int):
rows_count = rc
func set_node_meta(i_col: int, i_row: int, pname: String, val) -> void:
var key: = get_node_key(NODE_KEY_FORMAT, i_col, i_row)
var node = nodes[key]
node.set_meta(pname, val)
func get_node_meta(i_col: int, i_row: int, pname: String):
var key: = get_node_key(NODE_KEY_FORMAT, i_col, i_row)
var node = nodes[key]
_assert_meta(i_col, i_row, pname, node)
return node.get_meta(pname)
func set_node_property(i_col: int, i_row: int, pname: String, val) -> void:
var key: = get_node_key(NODE_KEY_FORMAT, i_col, i_row)
var node = nodes[key]
node.set(pname, val)
func get_node_property(i_col: int, i_row: int, pname: String):
var key: = get_node_key(NODE_KEY_FORMAT, i_col, i_row)
var node = nodes[key]
return node.get(pname)
func get_node_key(format: String, i_col: int, i_row: int) -> String:
var key: String = format % [i_row, i_col]
assert(nodes.has(key)) #,'ошибка: нет такой ячейки: колонка:%d ряд:%d' % [i_col, i_row])
return key
func get_key_indexes(key: String) -> Array:
var tokens: = key.split('_')
assert(tokens.size() == 3) #,'ошибка: неверный ключ: \"%s\"' % key)
return [int(tokens[2]), int(tokens[1])]
func set_columns_min_size(sizes: Array) -> void:
for i_row in get_rows_count():
set_nodes_min_size(i_row, sizes)
func set_columns_aligment(alignments: Array) -> void:
for i_row in get_rows_count():
for i_col in alignments.size():
set_node_alligment(i_col, i_row, alignments[i_col])
func _input(event) -> void:
if event is InputEventMouseButton:
if not event.pressed:
return
if event.position.x < position.x:
emit_signal('focus_exited')
return
if event.position.x > (position.x + size.x):
emit_signal('focus_exited')
return
if event.position.y < position.y:
emit_signal('focus_exited')
return
if event.position.y > (position.y + size.y):
emit_signal('focus_exited')
return
var rows_count: = get_rows_count()
var parent = get_parent()
var y0 = parent.position.y
for i_row in rows_count:
var rect = [0, 0, 0, 0]
get_node_rect(0, i_row, rect)
var y = rect[1] + position.y + y0
if event.position.y < y:
continue
if event.position.y > (y + rect[3]):
continue
emit_signal('row_pressed', i_row)
break
func _init() -> void:
selector_color_proc = Callable(self, 'default_selector_color_proc')
assert(selector_color_proc.is_valid())
selector_size_proc = Callable(self, 'default_selector_size_proc')
assert(selector_size_proc.is_valid())
func default_selector_color_proc(i_row: int):
return _selector_color
func set_selector_color_proc(instance: Object, function: String) -> void:
selector_color_proc = Callable(instance, function)
assert(selector_color_proc.is_valid())
func set_selector_size_proc(instance: Object, function: String) -> void:
selector_size_proc = Callable(instance, function)
assert(selector_size_proc.is_valid())
func default_selector_size_proc(i_row: int, rect: Array) -> void:
get_node_rect(0, i_row, rect)
rect[0] = rect[0] + position.x - 1
rect[1] = rect[1] + position.y - 4
rect[2] = size.x + 2
rect[3] = rect[3] + 8
func clear_rows_selected():
var rows_count: = get_rows_count()
for i_row in rows_count:
set_row_selected(i_row, false)
func set_row_selected(i_row: int, selected: bool = true) -> void:
var key: = ROW_SELECTOR_KEY_FORMAT % [i_row, 0]
var parent = get_parent()
if nodes.has(key) and (not selected):
var selector = nodes[key]
parent.remove_child(selector)
selector.queue_free()
nodes.erase(key)
elif (not nodes.has(key)) and selected:
var selector = Selector.instantiate()
var rect: = [0, 0, 0, 0]
selector_size_proc.call(i_row, rect)
selector.position.x = rect[0]
selector.position.y = rect[1]
selector.size.x = rect[2]
selector.size.y = rect[3]
selector.modulate = selector_color_proc.call(i_row)
nodes[key] = selector
parent.add_child(selector)
func is_row_selected(i_row: int) -> bool:
var key: = get_node_key(ROW_SELECTOR_KEY_FORMAT, 0, i_row)
return nodes.has(key)
func get_selected_rows(rows: Array) -> void:
for key in nodes.keys():
if SLCT_PREFIX in key:
var indexes = get_key_indexes(key)
rows.append(indexes[1])
func get_rows_count() -> int:
var rows_count: int = 0
for key in nodes:
rows_count += int(NODE_PREFIX in key)
return rows_count / columns
func get_node2(i_col: int, i_row: int) -> Node:
var key: = get_node_key(NODE_KEY_FORMAT, i_col, i_row)
return nodes[key]
func set_node_user_data(i_col: int, i_row: int, user_data: Object) -> void:
var key: = get_node_key(NODE_KEY_FORMAT, i_col, i_row)
nodes[key] = user_data
func add_row(row_types: Array) -> void:
assert(row_types.size() < MAX_INDEX, 'ошибка: слишком много колонок, больше %d' % (MAX_INDEX + 1))
set_columns(row_types.size())
var rows_count: int = get_rows_count()
assert(rows_count < MAX_INDEX, 'ошибка: слишком много рядов, больше %d' % (MAX_INDEX + 1))
for i_col in columns:
var node = row_types[i_col].instantiate()
var key: String = NODE_KEY_FORMAT % [rows_count, i_col]
node.name = key
add_child(node)
nodes[key] = node
func get_node_user_data(i_col: int, i_row: int) -> Object:
var key: = get_node_key(DATA_KEY_FORMAT, i_col, i_row)
return nodes[key]
func remove_row(i_row: int):
var sel_key: String = ROW_SELECTOR_KEY_FORMAT % [i_row, 0]
if nodes.has(sel_key):
var selector = nodes[sel_key]
get_parent().remove_child(selector)
selector.queue_free()
nodes.erase(sel_key)
for i_col in columns:
var node_key: String = get_node_key(NODE_KEY_FORMAT, i_col, i_row)
var conn_key: String = CONNECTED_KEY_FORMAT % [i_row, i_col]
var data_key: String = DATA_KEY_FORMAT % [i_row, i_col]
var node = nodes[node_key]
nodes.erase(node_key)
remove_child(node)
node.queue_free()
if nodes.has(data_key):
nodes.erase(data_key)
if nodes.has(conn_key):
var conn = nodes[conn_key]
var callback = conn[0]
var dest_node = conn[1]
node.disconnect('pressed', Callable(dest_node, callback))
nodes.erase(conn_key)
func rehash(swaps: Array):
""" Переназначает ключи в соответствии с размерами таблицы.
После вызова swaps содержит массив пар [<прежний ключ>, <текущий ключ>] """
assert(not swaps.size()) #,'ошибка: swaps не пустой')
var nodes_copy = {}
var i_col = 0
var i_row = 0
for key0 in nodes:
if NODE_PREFIX in key0:
var node_key1: String = NODE_KEY_FORMAT % [i_row, i_col]
nodes_copy[node_key1] = nodes[key0]
swaps.append([key0, node_key1])
var indxs = get_key_indexes(key0)
var slct_key0: String = ROW_SELECTOR_KEY_FORMAT % [indxs[1], indxs[0]]
if nodes.has(slct_key0):
var slct_key1: String = ROW_SELECTOR_KEY_FORMAT % [i_row, i_col]
nodes_copy[slct_key1] = nodes[slct_key0]
var conn_key0: String = CONNECTED_KEY_FORMAT % [indxs[1], indxs[0]]
if nodes.has(conn_key0):
var conn_key1: String = CONNECTED_KEY_FORMAT % [i_row, i_col]
nodes_copy[conn_key1] = nodes[conn_key0]
var data_key0: String = DATA_KEY_FORMAT % [indxs[1], indxs[0]]
if nodes.has(data_key0):
var data_key1: String = DATA_KEY_FORMAT % [i_row, i_col]
nodes_copy[data_key1] = nodes[data_key0]
i_col += 1
if i_col == columns:
i_col = 0
i_row += 1
nodes.clear()
nodes.merge(nodes_copy)
func clear_rows() -> void:
""" Удаляет все строки таблицы """
var rows_count: int = get_rows_count()
for i_row in rows_count:
remove_row(i_row)
func set_callbacks(dest_node: Node, event_name: String, callback: String, clmns: Array, user_data: Array) -> void:
""" Настраивает обратные вызовы для колонок.
:dest_node: Получатель события
:event_name: Имя события
:callback: Имя функции получателя события
:clmns: Индексы колонок
:user_data: Аргументы функции получателя события """
var rows_count = get_rows_count()
for i_row in rows_count:
for i_col in clmns:
assert(i_col >= 0 and i_col < columns) #,'ошибка: нет колонки %d' % i_col)
var node_key: String = NODE_KEY_FORMAT % [i_row, i_col]
var conn_key: String = CONNECTED_KEY_FORMAT % [i_row, i_col]
var node = nodes[node_key]
if nodes.has(conn_key):
var conn = nodes[conn_key]
var prev_callback = conn[0]
var prev_dest_node = conn[1]
node.disconnect(event_name, Callable(prev_dest_node, prev_callback))
assert (OK == node.connect(event_name, Callable(dest_node, callback).bind([node] + user_data)))
nodes[conn_key] = [callback, dest_node]
func set_rows_text(rows: Array) -> void:
for i_row in rows.size():
for i_col in columns:
var key: = get_node_key(NODE_KEY_FORMAT, i_col, i_row)
var node = nodes[key]
node.text = rows[i_row][i_col]
func set_nodes_min_size(i_row: int, sizes: Array) -> void:
for i_col in columns:
var key: = get_node_key(NODE_KEY_FORMAT, i_col, i_row)
var node = nodes[key]
node.custom_minimum_size.x = sizes[i_col]
node.size.x = sizes[i_col]
func set_node_text(i_col: int, i_row: int, val: String) -> void:
var key: = get_node_key(NODE_KEY_FORMAT, i_col, i_row)
nodes[key].text = val
func set_rows_state(rows: Array) -> void:
for i_row in rows.size():
for i_col in columns:
var key: = get_node_key(NODE_KEY_FORMAT, i_col, i_row)
nodes[name].button_pressed = rows[i_row][i_col]
func set_node_state(i_col: int, i_row: int, val: bool) -> void:
var key: = get_node_key(NODE_KEY_FORMAT, i_col, i_row)
nodes[key].button_pressed = val
func set_node_frame(i_col: int, i_row: int, name: String, val: int) -> void:
var key: = get_node_key(NODE_KEY_FORMAT, i_col, i_row)
var node = nodes[key]
var sprite = node.get_node(name)
sprite.frame = val
func set_node_editable(i_col: int, i_row: int, val: bool) -> void:
var key: = get_node_key(NODE_KEY_FORMAT, i_col, i_row)
nodes[key].editable = val
func set_node_alligment(i_col: int, i_row: int, val: int) -> void:
var key: = get_node_key(NODE_KEY_FORMAT, i_col, i_row)
nodes[key].align = val
func set_row_alligments(i_row: int, aligns: Array) -> void:
for i_col in columns:
var key: = get_node_key(NODE_KEY_FORMAT, i_col, i_row)
nodes[key].align = aligns[i_col]
func get_row_text(i_row: int) -> Array:
var row: = []
for i_col in columns:
var key: = get_node_key(NODE_KEY_FORMAT, i_col, i_row)
row.append(nodes[key].text)
return row
func set_row_text(i_row: int, row: Array, i_begin: int = 0) -> void:
for i_col in row.size():
var key: = get_node_key(NODE_KEY_FORMAT, i_col, i_row)
nodes[key].text = row[i_col]
func get_rows_text() -> Array:
var rows_count: = get_rows_count()
var rows: = []
for i_row in rows_count:
var row = get_row_text(i_row)
rows.append(row)
return rows
func get_row(i_row: int) -> Array:
var row: = []
for i_col in columns:
var key: = get_node_key(NODE_KEY_FORMAT, i_col, i_row)
row.append(nodes[key])
return row
func get_node_rect(i_col: int, i_row: int, rect: Array) -> void:
assert(rect.size() >= 4) #,'ошибка: мало элементов для возврата результата')
var key: = get_node_key(NODE_KEY_FORMAT, i_col, i_row)
var cell = nodes[key]
rect[0] = cell.position.x
rect[1] = cell.position.y
rect[2] = cell.size.x
rect[3] = cell.size.y