Class: Tuile::Component::Layout
- Inherits:
-
Component
- Object
- Component
- Tuile::Component::Layout
- Defined in:
- lib/tuile/component/layout.rb,
lib/tuile/component/layout/box.rb,
lib/tuile/component/layout/vertical.rb,
lib/tuile/component/layout/horizontal.rb,
sig/tuile.rbs
Overview
A layout doesn't paint anything by itself: its job is to position child components. Two families, both top-down (see book ch3):
- Absolute — you override #rect= and compute every child's rectangle yourself. Total control, and the base for anything unusual.
- Box / Vertical / Horizontal — you declare each child's extent as
a Fixed, Percent or Expand constraint and the layout does the
arithmetic. Sugar over the same
rect=assignment, for the common case.
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.
Defined Under Namespace
Classes: Absolute, Box, Expand, Fixed, Horizontal, Insets, Percent, Vertical
Instance Method Summary collapse
-
#add(child) ⇒ void
Adds a child component to this layout.
-
#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_mouse(event) ⇒ void
Dispatches the event to the child under the mouse cursor.
- #on_focus ⇒ void
-
#remove(child) ⇒ void
@param
child.
Instance Method Details
#add(child) ⇒ void
This method returns an undefined value.
Adds a child component to this layout.
@param child
175 176 177 178 179 180 181 |
# File 'lib/tuile/component/layout.rb', line 175 def add(child) if child.is_a? Enumerable child.each { add(_1) } else add_child(child) end 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.
170 |
# File 'lib/tuile/component/layout.rb', line 170 def focusable? = true |
#handle_mouse(event) ⇒ void
This method returns an undefined value.
Dispatches the event to the child under the mouse cursor.
@param event
196 197 198 199 200 201 |
# File 'lib/tuile/component/layout.rb', line 196 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.
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
# File 'lib/tuile/component/layout.rb', line 204 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.
@param child
185 186 187 188 189 190 191 |
# File 'lib/tuile/component/layout.rb', line 185 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 remove_child(child) invalidate if @children.empty? # nothing left to paint over the gap end |