Class: Tuile::Component::Layout

Inherits:
Component
  • Object
show all
Defined in:
lib/tuile/component/layout.rb,
sig/tuile.rbs

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.

Defined Under Namespace

Classes: Absolute

Instance Method Summary collapse

Constructor Details

#initializeLayout

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.

@param child

Parameters:



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(_1) }
  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

#childrenArray<Component>

Returns:



20
# File 'lib/tuile/component/layout.rb', line 20

def children = @children.to_a

#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.

Returns:

  • (Boolean)


30
# File 'lib/tuile/component/layout.rb', line 30

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

Parameters:



62
63
64
65
66
67
# File 'lib/tuile/component/layout.rb', line 62

def handle_mouse(event)
  super
  @children.each do |child|
    child.handle_mouse(event) if child.rect.contains?(event.point)
  end
end

#on_focusvoid

This method returns an undefined value.



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/tuile/component/layout.rb', line 70

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

Parameters:



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