Class: Teek::UI::Document
- Inherits:
-
Object
- Object
- Teek::UI::Document
- Defined in:
- lib/teek/ui/document.rb
Overview
The build-phase tree: an unattached root Node plus a name index (Symbol -> Node). Plain Ruby, no Tk - building and traversing a Document never touches an interpreter, which is what makes the DSL headless-testable.
Document only constructs and indexes nodes; it has no opinion on tree shape (which node is whose parent) - the build surface decides that by calling Node#add_child itself, so Document stays reusable underneath whatever parent-tracking scheme the builder uses.
Instance Attribute Summary collapse
-
#root ⇒ Node
readonly
The tree's root - starts with no children.
Instance Method Summary collapse
-
#claim_path_segment(parent_path, segment) ⇒ String
fresh instance for every separate realize pass (the initial realize, each Session#add, each lazily-Handle#realize!d screen) - tracking claims here instead keeps them honest across every one of those passes for this Document's whole lifetime.
-
#create(type:, name: nil, opts: {}, scope: Scope::TOP_LEVEL) ⇒ Node
Construct a node and register it under its name (if any), scoped to
scope- the same name used in two different scopes indexes as two distinct entries, so a component's local:savenever collides with another component's (or the top level's) own:save. -
#each_named_node {|name, node| ... } ⇒ Enumerator
Every named node, regardless of whether it's actually attached anywhere in the tree - see Validator's orphan check, which is exactly the reason this differs from #each_node.
-
#each_node {|node| ... } ⇒ Enumerator
Depth-first, pre-order traversal of the whole tree from #root.
- #find(name, scope: Scope::TOP_LEVEL) ⇒ Node? (also: #[])
-
#find_by_path(path) ⇒ Node?
Reverse lookup: given a real Tk path (from an error message, a
winfoquery, or poking around in a REPL), find which node it belongs to - the counterpart to #find's name-based lookup. -
#initialize ⇒ Document
constructor
A new instance of Document.
- #notify(event, *args) ⇒ void
-
#subscribe(event, &block) ⇒ Proc
private
A minimal, always-on, generic build-event hook - Node#add_child notifies
:append; the build stack's own push/pop (WidgetDSL#push_stack/WidgetDSL#pop_stack) notify +:push+/:pop. -
#unregister(node) ⇒ void
Removes
nodefrom the name index, scoped exactly like #register does - a no-op ifnodewas never named (nothing to remove) or already unregistered.
Constructor Details
Instance Attribute Details
Instance Method Details
#claim_path_segment(parent_path, segment) ⇒ String
fresh instance for every separate realize pass (the initial realize, each Session#add, each lazily-Handle#realize!d screen) - tracking claims here instead keeps them honest across every one of those passes for this Document's whole lifetime. Two mounts of the same component requesting the same key under the same real parent (e.g. a reusable row/screen, realized more than once - see WidgetDSL#component) get distinct, disambiguated segments; the common, non-colliding case keeps its plain segment unchanged.
142 143 144 145 146 147 |
# File 'lib/teek/ui/document.rb', line 142 def claim_path_segment(parent_path, segment) seen = (@used_segments[parent_path] ||= Hash.new(0)) count = seen[segment] seen[segment] += 1 count.zero? ? segment : "#{segment}##{count + 1}" end |
#create(type:, name: nil, opts: {}, scope: Scope::TOP_LEVEL) ⇒ Node
Construct a node and register it under its name (if any), scoped
to scope - the same name used in two different scopes indexes
as two distinct entries, so a component's local :save never
collides with another component's (or the top level's) own
:save. Does NOT attach it to any parent - the caller does that
with Node#add_child, so Document never needs to know about a
current-parent stack.
The node's own +name+/+key+ stay bare/unqualified - only this index is scope-aware. A node's real Tk path is already distinct per scope with no help needed here, since it's built from the parent chain (Realizer#allocate_path), and two components' subtrees are never siblings of themselves.
77 78 79 80 81 |
# File 'lib/teek/ui/document.rb', line 77 def create(type:, name: nil, opts: {}, scope: Scope::TOP_LEVEL) node = Node.new(type: type, name: name, key: generate_key(name), opts: opts, scope: scope, document: self) register(scope, node) if name node end |
#each_named_node {|name, node| ... } ⇒ Enumerator
Every named node, regardless of whether it's actually attached anywhere in the tree - see Validator's orphan check, which is exactly the reason this differs from #each_node.
106 107 108 109 110 |
# File 'lib/teek/ui/document.rb', line 106 def each_named_node(&block) return enum_for(:each_named_node) unless block @index.each(&block) end |
#each_node {|node| ... } ⇒ Enumerator
Depth-first, pre-order traversal of the whole tree from #root.
96 97 98 |
# File 'lib/teek/ui/document.rb', line 96 def each_node(&block) root.each(&block) end |
#find(name, scope: Scope::TOP_LEVEL) ⇒ Node? Also known as: []
88 89 90 |
# File 'lib/teek/ui/document.rb', line 88 def find(name, scope: Scope::TOP_LEVEL) @index[[scope, name]] end |
#find_by_path(path) ⇒ Node?
Reverse lookup: given a real Tk path (from an error message, a
winfo query, or poking around in a REPL), find which node it
belongs to - the counterpart to #find's name-based lookup. Only
ever matches a node's own RealizedNode#path, never its
arrange_path (the scrollbar-wrapper case - the wrapper frame
itself has no owning node of its own to return) or a
WidgetType#addressing strategy's synthesized virtual path (a
menu entry has no real Tk path at all - see
MenuEntryAddressing#virtual_path's own +!+-marked format, never
something Tk itself would hand back from winfo or an error).
124 125 126 |
# File 'lib/teek/ui/document.rb', line 124 def find_by_path(path) each_node.find { |node| node.realized&.path == path } end |
#notify(event, *args) ⇒ void
This method returns an undefined value.
54 55 56 |
# File 'lib/teek/ui/document.rb', line 54 def notify(event, *args) @events.emit(event, *args) end |
#subscribe(event, &block) ⇒ Proc
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
A minimal, always-on, generic build-event hook - Node#add_child
notifies :append; the build stack's own push/pop
(WidgetDSL#push_stack/WidgetDSL#pop_stack) notify +:push+/
:pop. Document has no idea what (if anything) is listening, or
why - it's a plain EventBus, same mechanism Session's own
public +ui.on+/+ui.emit+ already uses, just private and scoped to
build-time instrumentation instead of app events. With nothing
subscribed (the overwhelmingly common case), #notify costs one
hash lookup into an empty list - not something a normal build
needs to think about. See TreeInspector, the one built-in
subscriber.
46 47 48 |
# File 'lib/teek/ui/document.rb', line 46 def subscribe(event, &block) @events.on(event, &block) end |
#unregister(node) ⇒ void
This method returns an undefined value.
Removes node from the name index, scoped exactly like
#register does - a no-op if node was never named (nothing to
remove) or already unregistered. Called by Handle#destroy! for
a destroyed node and every named descendant of its own subtree
(Tk destroys descendants recursively, so their names need to
stop resolving too), so a later widget can reuse the same name
in the same scope, and #find correctly reports the name as
gone in the meantime.
159 160 161 162 163 |
# File 'lib/teek/ui/document.rb', line 159 def unregister(node) return unless node.name @index.delete([node.scope, node.name]) end |