Class: Tuile::Component::Layout
- Inherits:
-
Tuile::Component
- Object
- Tuile::Component
- Tuile::Component::Layout
- Defined in:
- lib/tuile/component/layout.rb
Overview
A layout doesn’t paint anything by itself: its job is to position child components.
Children that fully tile the layout’s rect repaint themselves and cover everything; children that leave gaps (e.g. a form with widgets of varying widths) trigger #repaint‘s default behavior —the background is cleared and children are re-invalidated so they paint over a clean surface.
Direct Known Subclasses
Defined Under Namespace
Classes: Absolute
Instance Attribute Summary
Attributes inherited from Tuile::Component
Instance Method Summary collapse
-
#add(child) ⇒ void
Adds a child component to this layout.
- #children ⇒ Array<Component>
- #content_size ⇒ Size
-
#focusable? ⇒ Boolean
Layouts are focusable containers — like Window and Popup, they don’t accept input themselves but they need to participate in the HasContent focus cascade so a Popup wrapping a Layout wrapping a TextField ends up focusing the field rather than parking focus on the popup.
-
#handle_key(key) ⇒ Boolean
Called when a character is pressed on the keyboard.
-
#handle_mouse(event) ⇒ void
Dispatches the event to the child under the mouse cursor.
-
#initialize ⇒ Layout
constructor
A new instance of Layout.
- #on_focus ⇒ void
- #remove(child) ⇒ void
Methods inherited from Tuile::Component
#active=, #active?, #attached?, #cursor_position, #depth, #find_shortcut_component, #focus, #keyboard_hint, #on_child_removed, #on_tree, #repaint, #root, #screen, #tab_stop?
Constructor Details
#initialize ⇒ Layout
Returns a new instance of Layout.
14 15 16 17 |
# File 'lib/tuile/component/layout.rb', line 14 def initialize super @children = [] end |
Instance Method Details
#add(child) ⇒ void
This method returns an undefined value.
Adds a child component to this layout.
35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/tuile/component/layout.rb', line 35 def add(child) if child.is_a? Enumerable child.each { add(it) } else raise TypeError, "expected Component, got #{child.inspect}" unless child.is_a? Component raise ArgumentError, "#{child} already has a parent #{child.parent}" unless child.parent.nil? @children << child child.parent = self end end |
#children ⇒ Array<Component>
20 |
# File 'lib/tuile/component/layout.rb', line 20 def children = @children.to_a |
#content_size ⇒ Size
60 61 62 63 64 65 66 |
# File 'lib/tuile/component/layout.rb', line 60 def content_size return Size::ZERO if @children.empty? right = @children.map { |c| c.rect.left + c.rect.width }.max bottom = @children.map { |c| c.rect.top + c.rect.height }.max Size.new(right - rect.left, bottom - rect.top) end |
#focusable? ⇒ Boolean
Layouts are focusable containers — like Window and Popup, they don’t accept input themselves but they need to participate in the HasContent focus cascade so a Popup wrapping a Layout wrapping a TextField ends up focusing the field rather than parking focus on the popup. Layouts don’t paint any visible chrome of their own (the auto-cleared background is just blank space), so this has no mouse-routing consequences — clicks on a gap area land back on the Layout itself and the on_focus cascade forwards to a tab stop.
30 |
# File 'lib/tuile/component/layout.rb', line 30 def focusable? = true |
#handle_key(key) ⇒ Boolean
Called when a character is pressed on the keyboard.
81 82 83 84 85 86 87 88 |
# File 'lib/tuile/component/layout.rb', line 81 def handle_key(key) return true if super sc = @children.find(&:active?) return false if sc.nil? sc.handle_key(key) end |
#handle_mouse(event) ⇒ void
This method returns an undefined value.
Dispatches the event to the child under the mouse cursor.
71 72 73 74 75 76 |
# File 'lib/tuile/component/layout.rb', line 71 def handle_mouse(event) super @children.each do |child| child.handle_mouse(event) if child.rect.contains?(event.point) end end |
#on_focus ⇒ void
This method returns an undefined value.
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/tuile/component/layout.rb', line 91 def on_focus super # Forward focus to the first interactive widget in the subtree so the # user can start typing / cursoring immediately. Prefer a {#tab_stop?} # descendant (TextField, List, Button…) so we skip past intermediate # containers like a {Window} or another {Layout}. Fall back to the # first focusable direct child for the rare case where the layout has # focusable but non-tab-stop children (e.g. an empty {Window}). first_tab_stop = nil on_tree { |c| first_tab_stop ||= c if !c.equal?(self) && c.tab_stop? } if first_tab_stop screen.focused = first_tab_stop else first_focusable = @children.find(&:focusable?) screen.focused = first_focusable unless first_focusable.nil? end end |
#remove(child) ⇒ void
This method returns an undefined value.
49 50 51 52 53 54 55 56 57 |
# File 'lib/tuile/component/layout.rb', line 49 def remove(child) raise TypeError, "expected Component, got #{child.inspect}" unless child.is_a? Component raise ArgumentError, "#{child}'s parent is #{child.parent}, not this layout #{self}" if child.parent != self child.parent = nil @children.delete(child) invalidate if @children.empty? on_child_removed(child) end |