tool extends Resource # Layout Resource definition, holding the root LayoutNode and hidden tabs. # # LayoutSplit are binary trees with nested LayoutSplit subtrees and LayoutPanel # leaves. Both of them inherit from LayoutNode to help with type annotation and # define common funcionality. # # Hidden tabs are marked in the `hidden_tabs` Dictionary by name. const LayoutNode = preload("layout_node.gd") const LayoutPanel = preload("layout_panel.gd") const LayoutSplit = preload("layout_split.gd") export(Resource) var root = LayoutPanel.new() setget set_root, get_root export(Dictionary) var hidden_tabs = {} setget set_hidden_tabs, get_hidden_tabs var _changed_signal_queued = false var _first_leaf: LayoutPanel var _hidden_tabs: Dictionary var _leaf_by_node_name: Dictionary var _root: LayoutNode = LayoutPanel.new() func _init() -> void: resource_name = "Layout" func set_root(value: LayoutNode, should_emit_changed = true) -> void: if not value: value = LayoutPanel.new() if _root == value: return if _root and _root.is_connected("changed", self, "_on_root_changed"): _root.disconnect("changed", self, "_on_root_changed") _root = value _root.parent = null _root.connect("changed", self, "_on_root_changed") if should_emit_changed: _on_root_changed() func get_root() -> LayoutNode: return _root func set_hidden_tabs(value: Dictionary) -> void: if value != _hidden_tabs: _hidden_tabs = value emit_signal("changed") func get_hidden_tabs() -> Dictionary: return _hidden_tabs func clone(): var new_layout = get_script().new() new_layout.root = _root.clone() new_layout._hidden_tabs = _hidden_tabs.duplicate() return new_layout func get_names() -> PoolStringArray: return _root.get_names() # Add missing nodes on first leaf and remove nodes outside indices from leaves. # # _leaf_by_node_name = { # (string keys) = respective Leaf that holds the node name, # } func update_nodes(names: PoolStringArray) -> void: _leaf_by_node_name.clear() _first_leaf = null var empty_leaves = [] _ensure_names_in_node(_root, names, empty_leaves) for l in empty_leaves: _remove_leaf(l) if not _first_leaf: _first_leaf = LayoutPanel.new() set_root(_first_leaf) for n in names: if not _leaf_by_node_name.has(n): _first_leaf.push_name(n) _leaf_by_node_name[n] = _first_leaf _on_root_changed() func move_node_to_leaf(node: Node, leaf: LayoutPanel, relative_position: int) -> void: var node_name = node.name var previous_leaf = _leaf_by_node_name.get(node_name) if previous_leaf: previous_leaf.remove_node(node) if previous_leaf.empty(): _remove_leaf(previous_leaf) leaf.insert_node(relative_position, node) _leaf_by_node_name[node_name] = leaf _on_root_changed() func get_leaf_for_node(node: Node) -> LayoutPanel: return _leaf_by_node_name.get(node.name) func split_leaf_with_node(leaf, node: Node, margin: int) -> void: var root_branch = leaf.parent var new_leaf = LayoutPanel.new() var new_branch = LayoutSplit.new() if margin == MARGIN_LEFT or margin == MARGIN_RIGHT: new_branch.direction = LayoutSplit.Direction.HORIZONTAL else: new_branch.direction = LayoutSplit.Direction.VERTICAL if margin == MARGIN_LEFT or margin == MARGIN_TOP: new_branch.first = new_leaf new_branch.second = leaf else: new_branch.first = leaf new_branch.second = new_leaf if _root == leaf: set_root(new_branch, false) elif root_branch: if leaf == root_branch.first: root_branch.first = new_branch else: root_branch.second = new_branch move_node_to_leaf(node, new_leaf, 0) func add_node(node: Node) -> void: var node_name = node.name if _leaf_by_node_name.has(node_name): return _first_leaf.push_name(node_name) _leaf_by_node_name[node_name] = _first_leaf _on_root_changed() func remove_node(node: Node) -> void: var node_name = node.name var leaf: LayoutPanel = _leaf_by_node_name.get(node_name) if not leaf: return leaf.remove_node(node) _leaf_by_node_name.erase(node_name) if leaf.empty(): _remove_leaf(leaf) _on_root_changed() func rename_node(previous_name: String, new_name: String) -> void: var leaf = _leaf_by_node_name.get(previous_name) if not leaf: return leaf.rename_node(previous_name, new_name) _leaf_by_node_name.erase(previous_name) _leaf_by_node_name[new_name] = leaf _on_root_changed() func set_tab_hidden(name: String, hidden: bool) -> void: if not _leaf_by_node_name.has(name): return if hidden: _hidden_tabs[name] = true else: _hidden_tabs.erase(name) _on_root_changed() func is_tab_hidden(name: String) -> bool: return _hidden_tabs.get(name, false) func set_node_hidden(node: Node, hidden: bool) -> void: set_tab_hidden(node.name, hidden) func is_node_hidden(node: Node) -> bool: return is_tab_hidden(node.name) func _on_root_changed() -> void: if _changed_signal_queued: return _changed_signal_queued = true set_deferred("_changed_signal_queued", false) call_deferred("emit_signal", "changed") func _ensure_names_in_node(node: LayoutNode, names: PoolStringArray, empty_leaves: Array) -> void: if node is LayoutPanel: node.update_nodes(names, _leaf_by_node_name) if node.empty(): empty_leaves.append(node) if not _first_leaf: _first_leaf = node elif node is LayoutSplit: _ensure_names_in_node(node.first, names, empty_leaves) _ensure_names_in_node(node.second, names, empty_leaves) else: assert(false, "Invalid Resource, should be branch or leaf, found %s" % node) func _remove_leaf(leaf: LayoutPanel) -> void: assert(leaf.empty(), "FIXME: trying to remove a leaf with nodes") if _root == leaf: return var collapsed_branch = leaf.parent assert(collapsed_branch is LayoutSplit, "FIXME: leaf is not a child of branch") var kept_branch = ( collapsed_branch.first if leaf == collapsed_branch.second else collapsed_branch.second ) var root_branch = collapsed_branch.parent if collapsed_branch == _root: set_root(kept_branch, true) elif root_branch: if collapsed_branch == root_branch.first: root_branch.first = kept_branch else: root_branch.second = kept_branch func _print_tree() -> void: print("TREE") _print_tree_step(_root, 0, 0) print("") func _print_tree_step(tree_or_leaf, level, idx) -> void: if tree_or_leaf is LayoutPanel: print(" |".repeat(level), "- (%d) = " % idx, tree_or_leaf.names) else: print( " |".repeat(level), "-+ (%d) = " % idx, tree_or_leaf.direction, " ", tree_or_leaf.percent ) _print_tree_step(tree_or_leaf.first, level + 1, 1) _print_tree_step(tree_or_leaf.second, level + 1, 2)