Class: Tuile::Component::Checkbox
- Inherits:
-
Component
- Object
- Component
- Tuile::Component::Checkbox
- Includes:
- HasCaption, HasValue
- Defined in:
- lib/tuile/component/checkbox.rb,
sig/tuile.rbs
Overview
A boolean input on one row. Space or a left click toggles it:
[x] Enable syslog forwarding
[ ] Enable syslog forwarding
cb = Component::Checkbox.new("Enable syslog forwarding", value: true)
cb.on_value_change = ->(on) { config.syslog = on }
cb.toggle # unchecks it, firing the listener with false
cb.checked? # => false
#value is the canonical seam (HasValue), always true/false and
never nil; #checked? / #checked= / #toggle are the domain-word face
over it — one piece of state, four names. Unchecked is the
#empty_value, so a fresh checkbox is empty and
HasValue#clear unchecks.
Space toggles. Enter is unhandled — unlike Button — simply because a checkbox has no default action to confirm, so it bubbles to an ancestor; treat that as this widget declining a key, not as a guarantee the framework makes (a TextArea claims Enter for newline, and a checkable row in a List toggles on it).
A tab stop, so Tab lands on it, and the widget highlights while on the focus
chain. Assign a #rect (typically from the surrounding Layout) at least
caption.display_width + 4 wide; a narrower one ellipsizes the caption, a
wider one leaves a dead tail — see #extent.
Implementation details
The glyphs are a house convention rather than constants: three columns plus
a trailing space ([x] , [ ] ), ASCII because ☑/☐ are absent from
most monospace fonts and the fallback glyph bleeds over its cell. A widget
painting checkbox-like rows without instantiating a Checkbox — checkable
rows in a List — repeats those literals to match.
Instance Attribute Summary
Attributes included from HasValue
Instance Method Summary collapse
-
#caption ⇒ StyledString
Read through this method, never
@caption— the ivar stays nil until the first non-empty set (#caption= short-circuits when unchanged). -
#caption= ⇒ void
Sets the caption and invalidates the component.
-
#checked=(new_value) ⇒ void
#value= under its domain word.
-
#checked? ⇒ Boolean
@return — #value under its domain word —
license.checked?reads better thanlicense.value. -
#clear ⇒ void
Resets #value to #empty_value.
-
#empty? ⇒ Boolean
@return — true iff #value equals #empty_value.
-
#empty_value ⇒ Boolean
@return —
false— HasValue#empty? means unchecked. -
#extent ⇒ Rect
The cells the widget actually paints: one row,
caption.display_width + 4columns, clipped to #rect. -
#focusable? ⇒ Boolean
Input fields are focusable by default (overrides #focusable?); a read-only display field could override back to
false. -
#handle_key(key) ⇒ Boolean
Toggles on Space.
- #handle_mouse(event) ⇒ void
-
#initialize(caption = nil, value: false) ⇒ Checkbox
constructor
@param
caption— the label, coerced as HasCaption#caption= coerces it. - #repaint ⇒ void
- #tab_stop? ⇒ Boolean
-
#toggle ⇒ void
Flips #value.
-
#value ⇒ Object
@return — the current value;
niluntil first set. -
#value=(new_value) ⇒ void
Coerces to
true/falsebefore storing, so the two-state invariant holds whatever a caller assigns — andcb.value = nilon a fresh checkbox is the no-op it looks like rather than a spurious change event.
Constructor Details
#initialize(caption = nil, value: false) ⇒ Checkbox
@param caption — the label, coerced as HasCaption#caption= coerces it.
@param value — initial state. Assigned through #value=, which also seeds the backing ivar — an unseeded checkbox would read nil and so report itself non-empty while fresh.
47 48 49 50 51 |
# File 'lib/tuile/component/checkbox.rb', line 47 def initialize(caption = nil, value: false) super() self.caption = caption self.value = value end |
Instance Method Details
#caption ⇒ StyledString
Read through this method, never @caption — the ivar stays nil until
the first non-empty set (#caption= short-circuits when unchanged).
@return — the caption; empty when never set.
2470 |
# File 'sig/tuile.rbs', line 2470
def caption: () -> StyledString
|
#caption= ⇒ void
This method returns an undefined value.
Sets the caption and invalidates the component. No-op when unchanged. A
String is parsed via StyledString.parse (embedded ANSI is honored);
a StyledString is used as-is; nil clears it.
@param new_caption
2477 |
# File 'sig/tuile.rbs', line 2477
def caption=: ((String | StyledString)? new_caption) -> void
|
#checked=(new_value) ⇒ void
76 77 78 |
# File 'lib/tuile/component/checkbox.rb', line 76 def checked=(new_value) self.value = new_value end |
#checked? ⇒ Boolean
@return — #value under its domain word — license.checked?
reads better than license.value. Not a second piece of state.
69 |
# File 'lib/tuile/component/checkbox.rb', line 69 def checked? = value |
#clear ⇒ void
This method returns an undefined value.
Resets #value to #empty_value.
2486 |
# File 'sig/tuile.rbs', line 2486
def clear: () -> void
|
#empty? ⇒ Boolean
@return — true iff #value equals #empty_value.
2483 |
# File 'sig/tuile.rbs', line 2483
def empty?: () -> bool
|
#empty_value ⇒ Boolean
@return — false — HasValue#empty? means unchecked.
56 |
# File 'lib/tuile/component/checkbox.rb', line 56 def empty_value = false |
#extent ⇒ Rect
The cells the widget actually paints: one row, caption.display_width + 4
columns, clipped to Tuile::Component#rect. A form column routinely hands a checkbox a
40-column rect for a 22-column [ ] Enable syslog forwarding — the extent
is those 22 columns.
Both the focus highlight and the click hit test use it, so a click on the blank tail — or on a lower row, when the rect is taller than one — does not toggle. It still focuses: Tuile::Component#handle_mouse's click-to-focus is ungated by geometry, and the tail is the field's own row.
The extent ignores Tuile::Component#bg_color: an inherited tint paints the dead tail, but a hit test that silently widened with a background would be a mode switch invisible in the code and untestable by inspection.
98 |
# File 'lib/tuile/component/checkbox.rb', line 98 def extent = Rect.new(rect.left, rect.top, [caption.display_width + 4, rect.width].min, 1) |
#focusable? ⇒ Boolean
Input fields are focusable by default (overrides Tuile::Component#focusable?);
a read-only display field could override back to false. Only
focusable? lives here — tab_stop? diverges between leaf fields and
composing wrappers, so it stays per-class (DECISIONS.md
D-integer-field).
2493 |
# File 'sig/tuile.rbs', line 2493
def focusable?: () -> bool
|
#handle_key(key) ⇒ Boolean
Toggles on Space. Every other key — Enter included — is left unhandled so it bubbles to an ancestor.
@param key
104 105 106 107 108 109 |
# File 'lib/tuile/component/checkbox.rb', line 104 def handle_key(key) return false unless key == " " toggle true end |
#handle_mouse(event) ⇒ void
This method returns an undefined value.
Toggles on a left click within #extent; super runs first, so a click
anywhere in Tuile::Component#rect still focuses.
@param event
115 116 117 118 119 120 |
# File 'lib/tuile/component/checkbox.rb', line 115 def handle_mouse(event) super return unless event. == :left && extent.contains?(event.point) toggle end |
#repaint ⇒ void
This method returns an undefined value.
123 124 125 126 127 128 129 130 |
# File 'lib/tuile/component/checkbox.rb', line 123 def repaint super return if rect.empty? label = (StyledString.plain(value ? "[x] " : "[ ] ") + caption).ellipsize(rect.width) label = label.with_bg(screen.theme.active_bg_color) if active? draw_line(rect.left, rect.top, label) end |
#tab_stop? ⇒ Boolean
53 |
# File 'lib/tuile/component/checkbox.rb', line 53 def tab_stop? = true |
#toggle ⇒ void
This method returns an undefined value.
Flips #value.
82 |
# File 'lib/tuile/component/checkbox.rb', line 82 def toggle = (self.value = !value) |
#value ⇒ Object
@return — the current value; nil until first set.
2480 |
# File 'sig/tuile.rbs', line 2480
def value: () -> Object
|
#value=(new_value) ⇒ void
This method returns an undefined value.
Coerces to true/false before storing, so the two-state invariant holds
whatever a caller assigns — and cb.value = nil on a fresh checkbox is
the no-op it looks like rather than a spurious change event.
@param new_value — anything; truthiness decides.
63 64 65 |
# File 'lib/tuile/component/checkbox.rb', line 63 def value=(new_value) super(new_value ? true : false) end |