Module: Thaum::Concerns::Layout
Instance Attribute Summary collapse
-
#child_layouts ⇒ Object
readonly
Returns the value of attribute child_layouts.
-
#leaf_sigils ⇒ Object
readonly
Returns the value of attribute leaf_sigils.
-
#rect ⇒ Object
readonly
Returns the value of attribute rect.
-
#subtree_children ⇒ Object
readonly
Returns the value of attribute subtree_children.
-
#subtree_leaves ⇒ Object
readonly
Returns the value of attribute subtree_leaves.
Instance Method Summary collapse
-
#collect_octagrams ⇒ Object
Walk this subtree and collect every Octagram node (excluding self).
-
#effective_focus_order ⇒ Object
Build the Tab traversal order for this subtree, recursively expanding any nested Layout that itself has focus_order.
-
#first_focusable_leaf ⇒ Object
Recursively descend into this Octagram (or plain Layout) to find its first focusable leaf — used when Tab enters an Octagram unit.
-
#focus_order ⇒ Object
Override on any Layout node (including App) to specify a Tab traversal order for that subtree.
-
#focus_scope_units ⇒ Object
Scope units for Tab cycling within THIS focus scope.
- #focusable_descendant? ⇒ Boolean
-
#inset_for_partition(rect) ⇒ Object
Override on Octagram (or any Layout) to inset the rect that this node’s children partition into.
-
#last_focusable_leaf ⇒ Object
Mirror of first_focusable_leaf for Shift-Tab entry.
-
#layout_subtree_in_order ⇒ Object
The direct children of this Layout in declaration order — both Sigils and nested Layouts.
-
#partition ⇒ Object
Override to specify the layout.
-
#repartition ⇒ Object
Re-run partition for this node’s subtree using its current rect.
-
#run_partition(rect:, collector: nil) ⇒ Object
Called by the run loop (or repartition) to assign geometry and walk the partition tree.
-
#validate_focus_order_tree ⇒ Object
Walk this Layout node and every descendant Layout, raising FocusOrderError if any defines a focus_order that does not exactly cover the focusable leaves in its subtree.
-
#wire_handler_parents(handler_parent:, app:) ⇒ Object
Walk the subtree top-down, wiring each leaf Sigil and each Octagram.
Instance Attribute Details
#child_layouts ⇒ Object (readonly)
Returns the value of attribute child_layouts.
6 7 8 |
# File 'lib/thaum/concerns/layout.rb', line 6 def child_layouts @child_layouts end |
#leaf_sigils ⇒ Object (readonly)
Returns the value of attribute leaf_sigils.
6 7 8 |
# File 'lib/thaum/concerns/layout.rb', line 6 def leaf_sigils @leaf_sigils end |
#rect ⇒ Object (readonly)
Returns the value of attribute rect.
6 7 8 |
# File 'lib/thaum/concerns/layout.rb', line 6 def rect @rect end |
#subtree_children ⇒ Object (readonly)
Returns the value of attribute subtree_children.
6 7 8 |
# File 'lib/thaum/concerns/layout.rb', line 6 def subtree_children @subtree_children end |
#subtree_leaves ⇒ Object (readonly)
Returns the value of attribute subtree_leaves.
6 7 8 |
# File 'lib/thaum/concerns/layout.rb', line 6 def subtree_leaves @subtree_leaves end |
Instance Method Details
#collect_octagrams ⇒ Object
Walk this subtree and collect every Octagram node (excluding self).
85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/thaum/concerns/layout.rb', line 85 def collect_octagrams result = [] (@subtree_children || []).each do |child| if child.is_a?(Octagram) result << child result.concat(child.collect_octagrams) elsif child.respond_to?(:collect_octagrams) result.concat(child.collect_octagrams) end end result end |
#effective_focus_order ⇒ Object
Build the Tab traversal order for this subtree, recursively expanding any nested Layout that itself has focus_order. When no focus_order is defined at any level, this falls through to left-to-right leaf order.
109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/thaum/concerns/layout.rb', line 109 def effective_focus_order order = focus_order return order if order result = [] layout_subtree_in_order.each do |node| if node.is_a?(Sigil) result << node if node.focusable? else result.concat(node.effective_focus_order) end end result end |
#first_focusable_leaf ⇒ Object
Recursively descend into this Octagram (or plain Layout) to find its first focusable leaf — used when Tab enters an Octagram unit.
150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/thaum/concerns/layout.rb', line 150 def first_focusable_leaf (@subtree_children || []).each do |child| case child when Sigil return child if child.focusable? else leaf = child.first_focusable_leaf if child.respond_to?(:first_focusable_leaf) return leaf if leaf end end nil end |
#focus_order ⇒ Object
Override on any Layout node (including App) to specify a Tab traversal order for that subtree. Return an array of leaf Sigils. nil (default) means “use left-to-right leaf order.”
11 |
# File 'lib/thaum/concerns/layout.rb', line 11 def focus_order = nil |
#focus_scope_units ⇒ Object
Scope units for Tab cycling within THIS focus scope. A “scope” is the App or an Octagram. A unit is either a focusable Sigil (reached through plain Layouts only) or a nested Octagram (which appears as one unit and is itself a scope). Plain Layouts are transparent — their units are flattened into the parent scope.
133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/thaum/concerns/layout.rb', line 133 def focus_scope_units result = [] (@subtree_children || []).each do |child| case child when Sigil result << child if child.focusable? when Octagram result << child if child.focusable_descendant? else result.concat(child.focus_scope_units) if child.respond_to?(:focus_scope_units) end end result end |
#focusable_descendant? ⇒ Boolean
177 178 179 |
# File 'lib/thaum/concerns/layout.rb', line 177 def focusable_descendant? !first_focusable_leaf.nil? end |
#inset_for_partition(rect) ⇒ Object
Override on Octagram (or any Layout) to inset the rect that this node’s children partition into. Defaults to identity.
33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/thaum/concerns/layout.rb', line 33 def inset_for_partition(rect) return rect unless respond_to?(:partition_inset) inset = partition_inset || {} Rect.new( x: rect.x + (inset[:left] || 0), y: rect.y + (inset[:top] || 0), width: [rect.width - (inset[:left] || 0) - (inset[:right] || 0), 0].max, height: [rect.height - (inset[:top] || 0) - (inset[:bottom] || 0), 0].max ) end |
#last_focusable_leaf ⇒ Object
Mirror of first_focusable_leaf for Shift-Tab entry.
164 165 166 167 168 169 170 171 172 173 174 175 |
# File 'lib/thaum/concerns/layout.rb', line 164 def last_focusable_leaf (@subtree_children || []).reverse_each do |child| case child when Sigil return child if child.focusable? else leaf = child.last_focusable_leaf if child.respond_to?(:last_focusable_leaf) return leaf if leaf end end nil end |
#layout_subtree_in_order ⇒ Object
The direct children of this Layout in declaration order — both Sigils and nested Layouts. Built during partition (see place_child).
126 |
# File 'lib/thaum/concerns/layout.rb', line 126 def layout_subtree_in_order = @subtree_children || [] |
#partition ⇒ Object
Override to specify the layout. Must call horizontal/vertical.
202 |
# File 'lib/thaum/concerns/layout.rb', line 202 def partition; end |
#repartition ⇒ Object
Re-run partition for this node’s subtree using its current rect.
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/thaum/concerns/layout.rb', line 46 def repartition return unless @rect old_leaves = @leaf_sigils || [] old_octagrams = collect_octagrams new_leaves = [] run_partition(rect: @rect, collector: new_leaves) new_octagrams = collect_octagrams # Fire on_unmount for removed Sigils and Octagrams. (old_leaves - new_leaves).each(&:on_unmount) (old_octagrams - new_octagrams).each(&:on_unmount) # Rewire handler parents across the (possibly restructured) subtree so # newly-added Octagrams and Sigils see the right chain BEFORE on_mount. # Only rewires when we can identify this node's role in the dispatch # chain — App (root) or Octagram (its own handler scope). app = thaum_app_ref || (is_a?(Octagram) ? @thaum_app : nil) wire_handler_parents(handler_parent: self, app: app) if app # Fire on_mount for newly-added Sigils and Octagrams. (new_octagrams - old_octagrams).each do |o| o.thaum_app = app if app o.on_mount end (new_leaves - old_leaves).each do |s| s.thaum_app = app if app s.on_mount end @leaf_sigils = new_leaves # Re-validate focus_order in this subtree after structural change. validate_focus_order_tree end |
#run_partition(rect:, collector: nil) ⇒ Object
Called by the run loop (or repartition) to assign geometry and walk the partition tree. Returns the flat list of leaf Sigils in render order.
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# File 'lib/thaum/concerns/layout.rb', line 15 def run_partition(rect:, collector: nil) @rect = rect collector ||= [] start = collector.size @leaf_sigils = collector @child_layouts = [] @subtree_children = [] # An Octagram may inset the rect its children partition into so its # render hook (border, padding) survives. Plain Layout passes through. @rect = inset_for_partition(rect) partition @rect = rect @subtree_leaves = collector[start..] || [] collector end |
#validate_focus_order_tree ⇒ Object
Walk this Layout node and every descendant Layout, raising FocusOrderError if any defines a focus_order that does not exactly cover the focusable leaves in its subtree.
101 102 103 104 |
# File 'lib/thaum/concerns/layout.rb', line 101 def validate_focus_order_tree validate_focus_order_node (@child_layouts || []).each(&:validate_focus_order_tree) end |
#wire_handler_parents(handler_parent:, app:) ⇒ Object
Walk the subtree top-down, wiring each leaf Sigil and each Octagram. Leaves and Octagrams get _handler_parent set to the innermost enclosing Octagram (or the App when there is none). Plain Layout nodes are transparent — their children inherit the outer parent.
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/thaum/concerns/layout.rb', line 185 def wire_handler_parents(handler_parent:, app:) (@subtree_children || []).each do |child| case child when Sigil child.handler_parent = handler_parent child.thaum_app = app when Octagram child.handler_parent = handler_parent child.thaum_app = app child.wire_handler_parents(handler_parent: child, app: app) else child.wire_handler_parents(handler_parent:, app:) if child.respond_to?(:wire_handler_parents) end end end |