Class: Tuile::Component
- Inherits:
-
Object
- Object
- Tuile::Component
- Defined in:
- lib/tuile/component.rb,
lib/tuile/component/list.rb,
lib/tuile/component/label.rb,
lib/tuile/component/popup.rb,
lib/tuile/component/button.rb,
lib/tuile/component/layout.rb,
lib/tuile/component/window.rb,
lib/tuile/component/text_area.rb,
lib/tuile/component/log_window.rb,
lib/tuile/component/text_field.rb,
lib/tuile/component/has_content.rb,
lib/tuile/component/info_window.rb,
lib/tuile/component/picker_window.rb
Overview
Defined Under Namespace
Modules: HasContent Classes: Button, InfoWindow, Label, Layout, List, LogWindow, PickerWindow, Popup, TextArea, TextField, Window
Instance Attribute Summary collapse
-
#key_shortcut ⇒ String?
A global keyboard shortcut.
-
#parent ⇒ Component?
readonly
The parent component or nil if the component has no parent.
-
#rect ⇒ Rect
The rectangle the component occupies on screen.
Instance Method Summary collapse
- #active=(active) ⇒ void
-
#active? ⇒ Boolean
True if the component is on the active chain — i.e.
-
#attached? ⇒ Boolean
True if this component’s tree is currently mounted on the Screen, i.e.
-
#children ⇒ Array<Component>
List of child components, defaults to an empty array.
-
#content_size ⇒ Size
The Size big enough to show the entire component contents without scrolling.
-
#cursor_position ⇒ Point?
Where the hardware terminal cursor should sit when this component is the cursor owner.
-
#depth ⇒ Integer
The distance from the root component; 0 if #parent is nil.
-
#find_shortcut_component(key) ⇒ Component?
The component whose #key_shortcut matches ‘key`, or nil.
-
#focus ⇒ void
Focuses this component.
-
#focusable? ⇒ Boolean
Whether this component is a valid focus target.
-
#handle_key(key) ⇒ Boolean
Called when a character is pressed on the keyboard.
-
#handle_mouse(event) ⇒ void
Handles mouse event.
-
#initialize ⇒ Component
constructor
A new instance of Component.
-
#keyboard_hint ⇒ String
Formatted keyboard hint surfaced in the status bar by Screen when this component is the active tiled window or the topmost popup.
-
#on_child_removed(child) ⇒ void
Called by container components after ‘child` has been detached from `self.children` (its `parent` is already nil and it is no longer in the children list).
-
#on_focus ⇒ void
Called when the component receives focus.
-
#on_tree {|component| ... } ⇒ void
Calls block for this component and for every descendant component.
-
#repaint ⇒ void
Repaints the component.
-
#root ⇒ Component
The root component of this component hierarchy.
-
#screen ⇒ Screen
The screen which owns this component.
-
#tab_stop? ⇒ Boolean
Whether this component participates in Tab / Shift+Tab focus cycling.
Constructor Details
Instance Attribute Details
#key_shortcut ⇒ String?
A global keyboard shortcut. When pressed, will focus this component.
103 104 105 |
# File 'lib/tuile/component.rb', line 103 def key_shortcut @key_shortcut end |
#parent ⇒ Component?
Returns the parent component or nil if the component has no parent.
166 167 168 |
# File 'lib/tuile/component.rb', line 166 def parent @parent end |
#rect ⇒ Rect
Returns the rectangle the component occupies on screen.
16 17 18 |
# File 'lib/tuile/component.rb', line 16 def rect @rect end |
Instance Method Details
#active=(active) ⇒ void
This method returns an undefined value.
133 134 135 136 137 138 139 |
# File 'lib/tuile/component.rb', line 133 def active=(active) active = active ? true : false return unless @active != active @active = active invalidate end |
#active? ⇒ Boolean
Returns true if the component is on the active chain — i.e. it is the focused component or an ancestor of it. Set by Screen#focused=.
128 |
# File 'lib/tuile/component.rb', line 128 def active? = @active |
#attached? ⇒ Boolean
Returns true if this component’s tree is currently mounted on the Screen, i.e. its root is the ScreenPane.
196 |
# File 'lib/tuile/component.rb', line 196 def attached? = root == screen.pane |
#children ⇒ Array<Component>
List of child components, defaults to an empty array.
178 |
# File 'lib/tuile/component.rb', line 178 def children = [] |
#content_size ⇒ Size
The Size big enough to show the entire component contents without scrolling. Plain components have no intrinsic content and report Size::ZERO; container/decorative components (e.g. Label, List, Layout, Window) override this to fold in their content’s natural extent. Used by callers like Popup to auto-size to whatever content was assigned, regardless of its concrete type.
229 |
# File 'lib/tuile/component.rb', line 229 def content_size = Size::ZERO |
#cursor_position ⇒ Point?
Where the hardware terminal cursor should sit when this component is the cursor owner. Returns ‘nil` to indicate the cursor should be hidden. The Screen positions the hardware cursor after each repaint cycle by consulting the Screen#focused component only.
236 |
# File 'lib/tuile/component.rb', line 236 def cursor_position = nil |
#depth ⇒ Integer
Returns the distance from the root component; 0 if #parent is nil.
170 |
# File 'lib/tuile/component.rb', line 170 def depth = parent.nil? ? 0 : parent.depth + 1 |
#find_shortcut_component(key) ⇒ Component?
Returns the component whose #key_shortcut matches ‘key`, or nil.
108 109 110 111 112 113 114 115 116 |
# File 'lib/tuile/component.rb', line 108 def find_shortcut_component(key) return self if key_shortcut == key children.each do |child| sc = child.find_shortcut_component(key) return sc unless sc.nil? end nil end |
#focus ⇒ void
This method returns an undefined value.
Focuses this component. Equivalent to ‘screen.focused = self`.
43 44 45 |
# File 'lib/tuile/component.rb', line 43 def focus screen.focused = self end |
#focusable? ⇒ Boolean
Whether this component is a valid focus target. ‘false` by default —passive components like Label are decoration and don’t accept focus. The flag gates click-to-focus (#handle_mouse) and the focus-cascade in container components (Tuile::Component::HasContent#on_focus, Tuile::Component::Layout#on_focus). Independent from #active?: every component carries the active flag, but only focusable ones can become a focus target that puts themselves and their ancestors on the active chain.
See also #tab_stop?: focusable controls can receive focus (via click or programmatic assignment), but only tab stops participate in Tab / Shift+Tab cycling. Containers like Window and Popup are focusable (so a click on chrome lands focus) but are not tab stops.
154 |
# File 'lib/tuile/component.rb', line 154 def focusable? = false |
#handle_key(key) ⇒ Boolean
Called when a character is pressed on the keyboard.
Also called for inactive components. Inactive component should just return false.
Default implementation searches for a component with #key_shortcut and focuses it. The shortcut search is suppressed while the focused component owns the hardware cursor (e.g. a TextField the user is typing into) so that hotkeys don’t steal printable keys from the editor.
89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/tuile/component.rb', line 89 def handle_key(key) return false unless screen.cursor_position.nil? c = find_shortcut_component(key) if !c.nil? screen.focused = c true else false end end |
#handle_mouse(event) ⇒ void
This method returns an undefined value.
Handles mouse event. Default implementation focuses this component when clicked (if #focusable?).
122 123 124 |
# File 'lib/tuile/component.rb', line 122 def handle_mouse(event) screen.focused = self unless event. != :left || active? || !focusable? end |
#keyboard_hint ⇒ String
Returns formatted keyboard hint surfaced in the status bar by Screen when this component is the active tiled window or the topmost popup. Empty by default; override to advertise shortcuts.
241 |
# File 'lib/tuile/component.rb', line 241 def keyboard_hint = "" |
#on_child_removed(child) ⇒ void
This method returns an undefined value.
Called by container components after ‘child` has been detached from `self.children` (its `parent` is already nil and it is no longer in the children list). Default behavior repairs dangling focus: if the focused component lived inside the removed subtree, focus shifts to `self` so the cursor doesn’t dangle on a detached component. No-op if ‘self` is not attached to the screen — focus state in a detached subtree is moot.
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
# File 'lib/tuile/component.rb', line 206 def on_child_removed(child) return unless attached? f = screen.focused return if f.nil? cursor = f until cursor.nil? if cursor == child screen.focused = self return end cursor = cursor.parent end end |
#on_focus ⇒ void
This method returns an undefined value.
Called when the component receives focus.
192 |
# File 'lib/tuile/component.rb', line 192 def on_focus; end |
#on_tree {|component| ... } ⇒ void
This method returns an undefined value.
Calls block for this component and for every descendant component.
185 186 187 188 |
# File 'lib/tuile/component.rb', line 185 def on_tree(&block) block.call(self) children.each { it.on_tree(&block) } end |
#repaint ⇒ void
This method returns an undefined value.
Repaints the component.
The default does the bookkeeping that almost every component would otherwise have to remember: it clears the background and re-invalidates any direct children whose rects leave gaps in #rect. Concretely:
-
Leaf (no children): always clears, so subclasses can paint their content directly without an explicit ‘clear_background` call.
-
Container with children that fully tile #rect: skipped — the children themselves will repaint and cover everything.
-
Container with gappy children (e.g. a form layout where widgets don’t tile): clears, then invalidates the children so they re-paint on top of the cleared background. This is what makes mixed field/button forms safe without each container learning a custom damage-tracking pass.
Subclasses that paint their entire rect themselves (e.g. Window‘s border draws over the area the default would clear; List explicitly paints every row) may skip super and take full responsibility for #rect. Everything else should call super.
A component must not draw outside of #rect.
70 71 72 73 74 75 76 |
# File 'lib/tuile/component.rb', line 70 def repaint return if rect.empty? || rect.left.negative? || rect.top.negative? return if children.any? && children_tile_rect? clear_background children.each { |c| screen.invalidate(c) } end |
#root ⇒ Component
Returns the root component of this component hierarchy.
173 |
# File 'lib/tuile/component.rb', line 173 def root = parent.nil? ? self : parent.root |
#screen ⇒ Screen
Returns the screen which owns this component.
39 |
# File 'lib/tuile/component.rb', line 39 def screen = Screen.instance |
#tab_stop? ⇒ Boolean
Whether this component participates in Tab / Shift+Tab focus cycling. ‘false` by default. Only true on components that accept direct user input (e.g. TextField, List, Button). Implies #focusable? — Screen will skip non-focusable tab stops, but in practice every override should keep the two consistent.
162 |
# File 'lib/tuile/component.rb', line 162 def tab_stop? = false |