class_name GDTable extends GridContainer ## Виджет "Таблица" @export var node_gap: = Vector2(0.0, 0.0) ## Зазор между ячейками, который невозможно вычислить или получить @export var selector_color: = Color(1.0, 1.0, 1.0, 1.0) ## Окрашивающий цвет для селектора выбранного ряда @export var selector_key_down: Key = KEY_DOWN ## Клавиша увеличения индекса выбранного ряда @export var selector_key_up: Key = KEY_UP ## Клавиша уменьшения индекса выбранного ряда @export_file var node_type_file: = 'res://table/node.tscn': get = get_node_scene, set = load_node_scene ## Сцена ячейки устанавливаемая по умолчанию @export_file var selector_type_file: = 'res://table/selector.tscn': set = load_selector_scene ## Сцена селектора для выбранного ряда const HEAD_PREFIX = 'HEAD_' ## Префикс ключа заголовка таблицы const INDEX_FORMAT = '%03d_%03d' ## Формат индекса ключа const MAX_INDEX = 199 ## Максимальное значение индекса для колонки и строки. Связано с форматом индекса ключа const NODE_PREFIX = 'NODE_' ## Префикс ключа ячейки таблицы const SLCT_PREFIX = 'SLCT_' ## Префикс ключа индикатора выбранного ряда таблицы const HEAD_KEY_FORMAT = HEAD_PREFIX + INDEX_FORMAT ## Формат ключа заголовка const NODE_KEY_FORMAT = NODE_PREFIX + INDEX_FORMAT ## Формат ключа ячейки const SELECTOR_KEY_FORMAT = SLCT_PREFIX + INDEX_FORMAT ## Формат ключа индикаторов выбранного ряда const META_USER_DATA = 'user_data' ## Имя свойства для сохранения данных пользователя const META_CALLABLE = 'callable' ## Имя свойства для сохранения вызываемого const META_EVENT_NAME = 'event_name' ## Имя свойства для сохранения названия события var header: GridContainer ## Заголовок таблицы var in_focus: = false ## Фокус пользовательского ввода var nodes: = {} ## Все ячейки таблицы и дополнительные элементы var columns_min_size = [] ## Ширина каждой колонки var ScnNode: PackedScene ## Прототип ячейки таблицы var ScnSelector: PackedScene ## Прототип индикатора выбранного ряда var selector_color_proc: Callable ## Ссылка на процедуру генератора цвета индикатора выбранного ряда var selector_size_proc: Callable ## Ссылка на процедуру генератора размера индикатора выбранного ряда ## Вызывается при нажатии кнопкой мыши на строку. ## Активируемый ползователем элемент ряда должен вызывать ## сигнал [b]focus_entered[/b], что бы сигнал [b]row_pressed[/b] работал.[br] ## [param i_row] - Индекс ряда в котором пользователь активировал элемент.[br] ## [i]Godot 4.2: [/i][b] Label[/b] [i] - не вызывает сигнал[/i] [b]focus_entered[/b]. signal row_pressed(i_row: int) ## Вызывается при изменении выбранного ряда.[br] ## [param i_row] - Текущий выбранный ряд. signal selected_changed(i_row: int) var scroll: Control var scroll_adj: = Vector2(0, 0) func get_node_scene(): return node_type_file func _assert_meta(i_col: int, i_row: int, pname: String, node): _assert(node.has_meta(pname), 'нет мета-свойства \"%s\" у ячейки колонка:%d ряд:%d' % [pname, i_col, i_row]) func get_node_user_data(i_col: int, i_row: int): return get_node_meta(i_col, i_row, META_USER_DATA) func set_node_user_data(i_col: int, i_row: int, val) -> void: set_node_meta(i_col, i_row, META_USER_DATA, val) func defaultselector_color_proc(_i_row: int): return selector_color func _assert(val: bool, msg: String = '<не указано>'): if val: return push_error('%s\n' % msg) get_tree().quit(1) func set_columns_min_size_auto(cc: int): if cc <= 0: return var sizes: = Array() sizes.resize(cc) sizes.fill(size.x / cc) set_columns_min_size(sizes) ## Закрепляет заголовок таблицы.[br] ## [param scrl] - Прокрутка родитель таблицы.[br] ## [param prnt] - Родитель прокрутки.[br] func pin_header(scrl: ScrollContainer, prnt: Control): scrl.remove_child(header) prnt.add_child(header) var v_separation: = get_theme_constant('v_separation') header.position = scrl.position - Vector2(0, header.size.y + v_separation) scroll = scrl scroll_adj = scrl.position ## Закрепляет заголовок таблицы.[br] ## GridView должен лежать на Scrol. Scrol должен иметь родителя func pin_header2(): var scrl = get_parent() var prnt = scrl.get_parent() pin_header(scrl, prnt) header.size.x = scrl.size.x ## Перемещает указатель на одну строку вверх func selector_up(): var i_rows: = [] get_selected_rows(i_rows) for i in i_rows: set_row_selected(i, false) var rows_count: = get_rows_count() if not (i_rows.size() and (rows_count > 1)): return var i_row = i_rows[0] - 1 if i_row < 0: i_row = rows_count - 1 set_row_selected(i_row, true) var node = get_node2(0, i_row) if scroll is ScrollContainer: scroll.ensure_control_visible(node) emit_signal('selected_changed', i_row) ## Перемещает указатель на одну строку вниз func selector_down(): var i_rows: = [] get_selected_rows(i_rows) for i in i_rows: set_row_selected(i, false) var rows_count: = get_rows_count() if not (i_rows.size() and (rows_count > 1)): return var i_row = i_rows[0] + 1 i_row %= rows_count set_row_selected(i_row, true) var node = get_node2(0, i_row) if scroll is ScrollContainer: scroll.ensure_control_visible(node) emit_signal('selected_changed', i_row) ## Делает ряд [param i_row] видимым. func ensure_row_visible(i_row: int): _assert(scroll is ScrollContainer, 'таблица %s должна быть дочерней к ScrollContainer' % self) var key: = get_node_key(NODE_KEY_FORMAT, 0, i_row) var node = nodes[key] scroll.ensure_control_visible(node) ## Устанавливает выравнивание из массива [param alignments] для всех колонок. func set_columns_alignments(alignments: Array): _assert(columns == alignments.size()) var rows_count: = get_rows_count() for i_row in rows_count: for i_col in alignments.size(): set_node_alligment(i_col, i_row, alignments[i_col]) ## func load_selector_scene(file_name: String): _assert(ResourceLoader.exists(file_name), '\"%s\" не найден' % file_name) selector_type_file = file_name ScnSelector = load(file_name) _assert(ScnSelector is PackedScene, '\"%s\" не PackedScene' % ScnSelector) ## func load_node_scene(file_name: String): _assert(ResourceLoader.exists(file_name), '\"%s\" не найден' % file_name) node_type_file = file_name ScnNode = load(file_name) _assert(ScnNode is PackedScene, '\"%s\" не PackedScene' % ScnNode) ## func _ready(): ScnSelector = load(selector_type_file) ScnNode = load(node_type_file) _assert(ScnSelector is PackedScene, '\"%s\" не PackedScene' % ScnSelector) _assert(ScnNode is PackedScene, '\"%s\" не PackedScene' % ScnNode) ## Выравнивает по нижнему левому углу. Используется для выравнивания по заголовку. func align_to_left_bottompos(hdr: Control, gap: = Vector2(0, 0)): position.x = hdr.position.x + gap.x position.y = hdr.position.y + hdr.size.y + gap.y ## Устанавливает пользовательские данные для ячейки.[br] ## [param i_col] - Индекс колонки.[br] ## [param i_row] - Индекс ряда.[br] ## [param pname] - Имя пользовательских данных.[br] ## [param val] - Значение пользовательских данных.[br] 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) ## Возвращает пользовательские данные для ячейки.[br] ## [param i_col] - Индекс колонки.[br] ## [param i_row] - Индекс ряда.[br] ## [param pname] - Имя пользовательских данных.[br] 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) ## Устанавливает свойство ячейки.[br] ## [param i_col] - Индекс колонки.[br] ## [param i_row] - Индекс ряда.[br] ## [param pname] - Имя свойства.[br] ## [param val] - Значение свойства.[br] ## [i]Свойство[/i] [param pname] [i]ячейки должно существовать.[/i] 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) ## Устанавливает свойство ячейки заголовка[br] ## [param i_col] - Индекс колонки.[br] ## [param pname] - Имя свойства.[br] ## [param val] - Значение свойства.[br] ## [i]Свойство[/i] [param pname] [i]ячейки должно существовать.[/i] func set_header_property(i_col: int, pname: String, val) -> void: var key: = get_node_key(HEAD_KEY_FORMAT, i_col, 0) var node = nodes[key] node.set(pname, val) ## Возвращает значение поля ячейки.[br] ## [param pname] - Имя поля.[br] ## [param i_col] - Индекс колонки.[br] ## [param i_row] - Индекс ряда. 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) ## Возвращает ключ для существующей ячейки. Если ячейки с заданными индексами нет, ## вызывает исключение.[br] ## [param i_col] - Индекс колонки.[br] ## [param i_row] - Индекс ряда 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 ## Возвращает массив с индексом ячейки:[br] ## [b][0][/b] - индекс колонки,[br] ## [b][1][/b] - индекс строки. 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: _assert(columns == sizes.size(), 'неверный размер массива с ширинами колонок: передано %d, но требуется %d' % [sizes.size(), columns]) columns = sizes.size() var rows_count: = get_rows_count() columns_min_size.clear() for sz in sizes: columns_min_size.append(sz) for i_row in rows_count: set_nodes_min_size(i_row, sizes, NODE_KEY_FORMAT) if nodes.has(HEAD_KEY_FORMAT % [0, 0]): set_nodes_min_size(0, sizes, HEAD_KEY_FORMAT) func set_headers_min_size(sizes: Array) -> void: for i_col in columns: var key: = get_node_key(HEAD_KEY_FORMAT, i_col, 0) var node = nodes[key] node.custom_minimum_size.x = sizes[i_col] node.set_deferred('size.x', sizes[i_col]) ## func set_columns_aligment(alignments: Array) -> void: var rows_count: = get_rows_count() for i_row in rows_count: for i_col in alignments.size(): set_node_alligment(i_col, i_row, alignments[i_col]) ## func _input(event) -> void: if not is_visible_in_tree(): return if event is InputEventKey: if event.pressed and in_focus: if event.keycode == selector_key_up: selector_up() elif event.keycode == selector_key_down: selector_down() elif event is InputEventMouseButton: if not event.pressed: return if check_event_pos(event.position, scroll): if not in_focus: in_focus = true emit_signal('focus_entered') elif not check_event_pos(event.position, scroll): if in_focus: in_focus = false emit_signal('focus_exited') ## func _init() -> void: selector_color_proc = Callable(self, 'defaultselector_color_proc') _assert(selector_color_proc.is_valid(), 'defaultselector_color_proc(): обработчик \"%s\" не является Callable' % selector_color_proc) selector_size_proc = Callable(self, 'default_selector_size_proc') _assert(selector_size_proc.is_valid(), 'default_selector_size_proc(): обработчик \"%s\" не является Callable' % selector_size_proc) scroll = self ## func check_event_pos(event_pos: Vector2, obj: Control): return \ (event_pos.x > obj.position.x) \ and (event_pos.x < (obj.position.x + obj.size.x)) \ and (event_pos.y > obj.position.y) \ and (event_pos.y < (obj.position.y + obj.size.y)) ## 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()) ## [param i_row] - Индекс ряда func default_selector_size_proc(i_row: int, rect: Array) -> void: get_row_rect(i_row, rect) rect[0] += position.x - node_gap.x / 2.0 rect[1] += position.y - node_gap.y / 2.0 rect[2] += node_gap.x rect[3] += node_gap.y ## func clear_rows_selected(): var rows_count: = get_rows_count() for i_row in rows_count: _set_row_selected(i_row, false) ## [param i_row] - Индекс ряда. func set_row_selected(i_row: int, selected: bool = true) -> void: _set_row_selected(i_row, selected) ## [param i_row] - Индекс ряда. func _set_row_selected(i_row: int, selected: bool = true) -> void: var selector_key: = SELECTOR_KEY_FORMAT % [i_row, 0] var node_key: = NODE_KEY_FORMAT % [i_row, 0] if not nodes.has(node_key): var rows_count = get_rows_count() push_error('нет такого ряда: %d (всего %d рядов)\n' % [i_row, rows_count]) return if nodes.has(selector_key) and (not selected): var selector = nodes[selector_key] nodes.erase(selector_key) if is_instance_valid(selector): var prnt = selector.get_parent_control() if prnt: prnt.remove_child(selector) selector.queue_free() else: push_warning('обращение к удалённому элементу \"%s\"' % selector_key) elif (not nodes.has(selector_key)) and selected: var selector = ScnSelector.instantiate() _set_selector_size(selector, i_row) selector.modulate = selector_color_proc.call(i_row) selector.z_index = z_index + 1 nodes[selector_key] = selector var node = nodes[node_key] node.add_child(selector) func _set_selector_size(selector, i_row): var rect: = [0, 0, 0, 0] selector_size_proc.call(i_row, rect) selector.size.x = rect[2] selector.size.y = rect[3] ## [param i_row] - Индекс ряда. func is_row_selected(i_row: int) -> bool: var key: = SELECTOR_KEY_FORMAT % [i_row, 0] return nodes.has(key) ## [param i_row] - Индексы рядов. func get_selected_rows(i_rows: Array) -> void: for key in nodes.keys(): if SLCT_PREFIX in key: var indexes = get_key_indexes(key) i_rows.append(indexes[1]) ## Возвращает количество рядов func get_rows_count() -> int: var nodes_count: int = 0 for key in nodes: nodes_count += int(NODE_PREFIX in key) return nodes_count / columns ## [param i_col] - Индекс колонки. [param i_row] - Индекс ряда. 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] ## Добавляет ряд. [param row_types] - Массив типов ячеек ряда func add_row(row_types: Array) -> void: _assert(row_types.size() < MAX_INDEX, 'колонок больше, чем %d' % (MAX_INDEX + 1)) if columns == 1: set_columns(row_types.size()) else: _assert(columns == row_types.size(), '%s количество колонок установлено ранее - %d, но требуется %d' % [self, columns, row_types.size()]) var i_row: int = get_rows_count() _assert(i_row < MAX_INDEX, 'рядов больше, чем %d' % (MAX_INDEX + 1)) var allow_set_size = columns_min_size.size() >= row_types.size() for i_col in columns: var node_type = row_types[i_col] var node = node_type.instantiate() _assert(node.get('name') is StringName, 'экземпляр не имеет поля \"name\"') var node_key: String = NODE_KEY_FORMAT % [i_row, i_col] node.name = node_key add_child(node) if allow_set_size: node.custom_minimum_size.x = columns_min_size[i_col] node.set_deferred('size', Vector2(columns_min_size[i_col], node.size.y)) nodes[node_key] = node var conn = Callable(self, '_on_focus_entered').bind(node) node.connect('focus_entered', conn) set_node_meta(i_col, i_row, 'callable_focus_entered', conn) func _set_size_x_defered(node: Control, val: Vector2): node.size = val ## func _on_focus_entered(node): var indexes = get_key_indexes(node.name) emit_signal('row_pressed', indexes[1]) ## func set_header(header_types: Array): _assert(header_types.size() > 0, 'количество колонок в заголовке: %d' % [header_types.size()]) _assert(header_types.size() < MAX_INDEX, 'количество колонок в заголовке: %d больше допустимого: %d' % [header_types.size(), MAX_INDEX + 1]) if columns == 1: set_columns(header_types.size()) else: _assert(columns == header_types.size(), 'количество колонок в заголовке %d, но ранее установлено: %d' % [header_types.size(), columns]) var do_adj: = false if not header: header = GridContainer.new() var parent = scroll.get_parent() parent.add_child(header) header.position = position header.columns = header_types.size() do_adj = true else: var childrens = header.get_children() for node in childrens: childrens.remove_child(node) node.queue_free() for i_col in columns: var node = header_types[i_col].instantiate() var key: String = HEAD_KEY_FORMAT % [0, i_col] node.name = key nodes[key] = node header.add_child(node) if do_adj: var rect = [0, 0, 0, 0] get_node_rect(0, 0, rect, HEAD_KEY_FORMAT) position.y += rect[3] ## func set_header_text(headers: Array): _assert(columns == headers.size(), 'количество элементов для заголовка %d, а должно быть %d' % [headers.size(), columns]) set_row_text(0, headers, 0, HEAD_KEY_FORMAT) ## [param i_row] - индекс ряда. func remove_row(i_row: int): var sel_key: String = SELECTOR_KEY_FORMAT % [i_row, 0] if nodes.has(sel_key): var selector = nodes[sel_key] if is_instance_valid(selector): var prnt = selector.get_parent() if prnt: prnt.remove_child(selector) selector.queue_free() nodes.erase(sel_key) for i_col in columns: var node_key: String = NODE_KEY_FORMAT % [i_row, i_col] var node = nodes[node_key] var conn = get_node_meta(i_col, i_row, 'callable_focus_entered') node.disconnect('focus_entered', conn) if node.has_meta(META_CALLABLE): conn = node.get_meta(META_CALLABLE) var event_name = node.get_meta(META_EVENT_NAME) node.disconnect(event_name, conn) remove_child(node) nodes.erase(node_key) node.queue_free() rehash() ## func next_index(indexes: Array) -> void: indexes[0].x += 1 if indexes[0].x == columns: indexes[0].x = 0 indexes[0].y += 1 ## func swap_nodes(nodes_copy, node_key_format, i0, i1) -> void: var key0 = node_key_format % [i0.y, i0.x] var key1 = node_key_format % [i1.y, i1.x] nodes_copy[key1] = nodes[key0] nodes_copy[key1].name = key1 ## Переназначает ключи в соответствии с размерами таблицы. func rehash(): var i_node: = [Vector2i()] var i_hdr: = [Vector2i()] var i_slct: = [Vector2i()] var nodes_copy = {} for key in nodes: var iv = get_key_indexes(key) var i0 = Vector2(iv[0], iv[1]) if HEAD_PREFIX in key: swap_nodes(nodes_copy, HEAD_KEY_FORMAT, i0, i_hdr[0]) next_index(i_hdr) elif NODE_PREFIX in key: swap_nodes(nodes_copy, NODE_KEY_FORMAT, i0, i_node[0]) next_index(i_node) elif SLCT_PREFIX in key: swap_nodes(nodes_copy, SELECTOR_KEY_FORMAT, i0, i_slct[0]) next_index(i_slct) nodes.clear() nodes.merge(nodes_copy) ## Удаляет все строки таблицы func clear_rows() -> void: var rows_count: int = get_rows_count() for i_row in range(rows_count - 1, -1, -1): remove_row(i_row) ## Настраевает обратные вызовы для колонок.[br] ## [param dest_node] - Получатель события[br] ## [param event_name] - Имя события[br] ## [param callback] - Имя функции получателя события[br] ## [param clmns] - Индексы колонок[br] ## [param user_data] - Аргументы функции получателя события[br] func connect_columns(dest_node: Node, event_name: String, callback: String, clmns: Array, user_data: Array = []) -> void: 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 = get_node2(i_col, i_row) if node.has_meta(META_CALLABLE): var conn_prev = node.get_meta(META_CALLABLE) node.disconnect(event_name, conn_prev) var conn = Callable(dest_node, callback).bind([node] + user_data) var rc = node.connect(event_name, conn) if rc == Error.OK: node.set_meta(META_CALLABLE, conn) node.set_meta(META_EVENT_NAME, event_name) else: var es = error_string(rc) push_error('ошибка: событие \"%s\" не может быть подключено к %s: %s' % [event_name, node, es]) ## 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] ## [param i_row] - Индекс ряда. func set_nodes_min_size(i_row: int, sizes: Array, key_format: String = NODE_KEY_FORMAT) -> void: columns_min_size.clear() for sz in sizes: columns_min_size.append(sz) for i_col in columns: var key: = get_node_key(key_format, i_col, i_row) var node = nodes[key] node.custom_minimum_size.x = sizes[i_col] node.set_deferred('size.x', sizes[i_col]) ## [param hight] - Высота ряда. func set_rows_min_hight(hight: int) -> void: var rows_count = get_rows_count() for i_row in rows_count: set_row_min_hight(i_row, hight) ## [param i_row] - Индекс ряда. ## [param hight] - Высота ряда. func set_row_min_hight(i_row: int, hight: int) -> 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.y = hight node.set_deferred('size.y', hight) func set_header_min_hight(hight: int): for i_col in columns: var key: = get_node_key(HEAD_KEY_FORMAT, i_col, 0) var node = nodes[key] node.custom_minimum_size.y = hight node.set_deferred('size.y', hight) ## [param i_col] - Индекс колонки.[br][param i_row] - Индекс ряда. 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[key].button_pressed = rows[i_row][i_col] ## Устанавливает состояние ячейки. ## [param i_col] - Индекс колонки.[br] ## [param i_row] - Индекс ряда.[br] ## [param val] - Новое состояние.[br] ## [i]Ячейка должна иметь свойство[/i] [param button_pressed] 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 ## [param i_col] - Индекс колонки.[br] ## [param i_row] - Индекс ряда. func set_node_frame(i_col: int, i_row: int, node_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(node_name) sprite.frame = val ## Разрешает редактирование ячейки. Ячейка должна иметь свойство [param editable].[br] ## [param i_col] - Индекс колонки.[br] ## [param i_row] - Индекс ряда. 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 ## Устанавливает контекстное меню для ячейки таблицы. Ячейка должна иметь свойство [param context_menu].[br] ## [param i_col] - Индекс колонки.[br] ## [param i_row] - Индекс ряда.[br] ## [param val] - Включить или отключить контекстное меню. func set_node_control(i_col: int, i_row: int, val: bool) -> void: var key: = get_node_key(NODE_KEY_FORMAT, i_col, i_row) nodes[key].shortcut_keys_enabled = val nodes[key].selecting_enabled = val nodes[key].deselect_on_focus_loss_enabled = val ## Разрешает редактирование ряда. Ячейки ряда должны иметь свойство [param editable].[br] ## [param i_row] - Индекс ряда. func set_row_editable(i_row: int, val: bool) -> void: for i_col in columns: var key: = get_node_key(NODE_KEY_FORMAT, i_col, i_row) nodes[key].editable = val ## [param i_col] - Индекс колонки.[br] ## [param i_row] - Индекс ряда. 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) if 'horizontal_alignment' in nodes[key]: nodes[key].horizontal_alignment = val elif 'alignment' in nodes[key]: nodes[key].alignment = val ## [param i_row] - Индекс ряда. 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] ## [param i_row] - Индекс ряда. 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 ## Устанавливает текст ячеек ряда.[br] ## [param i_row] - Индекс ряда.[br] ## [param row] - Массив новых значений текста (поле элемента [param text]).[br] ## [param i_begin] - Начальный индекс колонки начиная с которого установить новые значения.[br] ## [i]Ячейки ряда должны иметь свойство[/i] [param text] func set_row_text(i_row: int, row: Array, i_begin: int = 0, key_format: String = NODE_KEY_FORMAT) -> void: _assert((i_begin >= 0) and (i_begin < columns)) _assert((columns - i_begin) >= row.size()) for i_col in row.size(): var key: = get_node_key(key_format, i_col + i_begin, 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 ## Возвращает массив ячеек из ряда [param i_row] 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 ## Возвращает прямоугольник [param rect] занимаемый ячейкой в ряду [param i_row] и колонке [param i_col]. ## [param key_format] - Формат ключа ячейки func get_node_rect(i_col: int, i_row: int, rect: Array, key_format: String = NODE_KEY_FORMAT) -> void: _assert(rect.size() >= 4, 'мало элементов для возврата результата') var key: = get_node_key(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 ## Возвращает прямоугольник [param rect] занимаемый рядом [param i_row].[br] ## [param key_format] - Формат ключа ячейки func get_row_rect(i_row: int, rect: Array, key_format: String = NODE_KEY_FORMAT) -> void: _assert(rect.size() >= 4, 'мало элементов для возврата результата') var cell0 = nodes[get_node_key(key_format, 0, i_row)] var cell1 = nodes[get_node_key(key_format, columns - 1, i_row)] rect[0] = cell0.position.x + scroll_adj.x rect[1] = cell0.position.y + scroll_adj.y rect[2] = (cell1.position.x - cell0.position.x) + cell1.size.x rect[3] = cell0.size.y