Class: Tuile::Component::Button

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

Overview

A clickable button. Activated by Enter, Space, or a left mouse click; fires the #on_click callback. Renders as [ caption ] on a single row; the background is highlighted when the button is focused so the user can see which button is active.

Buttons are tab stops — Tab and Shift+Tab will land on them as part of the standard focus cycle. Click-to-focus also works via the inherited #handle_mouse_down?.

Assign a #rect (typically by the surrounding Layout) wide enough to show [ caption ] — that natural width is caption.display_width + 4. A narrower #rect truncates the label with an ellipsis; a wider one leaves a tail that focuses but doesn't activate (see #extent).

Defined Under Namespace

Classes: ClickEvent

Instance Method Summary collapse

Constructor Details

#initialize(caption = nil, &listener) ⇒ Button

@param caption — the button's label, coerced the same way HasCaption#caption= coerces it.

Parameters:

  • caption (?(String | StyledString), nil) (defaults to: nil)


24
25
26
27
28
# File 'lib/tuile/component/button.rb', line 24

def initialize(caption = nil, &listener)
  super()
  self.caption = caption
  on_click << listener if listener
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.

Returns:



5946
# File 'sig/tuile.rbs', line 5946

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

Parameters:



5953
# File 'sig/tuile.rbs', line 5953

def caption=: ((String | StyledString)? new_caption) -> void

#extent ⇒ Size

The cells the button actually paints: one row, caption.display_width + 4 columns, clipped to Tuile::Component#rect. Both the focus highlight and the click hit test use it, so a click on the blank tail of an over-wide rect — or on a lower row, when the rect is taller than one — does not fire #on_click. It still focuses: Mouse::Router's click-to-focus is ungated by geometry. Same rule as Checkbox#extent, which documents the two traps behind it.

Returns:



66
# File 'lib/tuile/component/button.rb', line 66

def extent = Size.new([caption.display_width + 4, rect.width].min, 1)

#focusable? ⇒ Boolean

Returns:

  • (Boolean)


42
# File 'lib/tuile/component/button.rb', line 42

def focusable? = true

#handle_key?(key) ⇒ Boolean

@param key

Parameters:

  • key (String)

Returns:

  • (Boolean)


48
49
50
51
52
53
54
55
56
# File 'lib/tuile/component/button.rb', line 48

def handle_key?(key)
  case key
  when Keys::ENTER, " "
    on_click.fire(ClickEvent.new(source: self))
    true
  else
    false
  end
end

#handle_mouse_down?(event) ⇒ Boolean

Fires #on_click on a left click within #extent; super runs first, so a click anywhere in Tuile::Component#rect still focuses.

@param event

Parameters:

Returns:

  • (Boolean)


72
73
74
75
76
77
# File 'lib/tuile/component/button.rb', line 72

def handle_mouse_down?(event)
  return false unless event.button == :left

  on_click.fire(ClickEvent.new(source: self))
  true
end

#inspect_details ⇒ ::Array[String]

Adds caption="…" to Tuile::Component#inspect, omitted while empty.

Returns:

  • (::Array[String])


5956
# File 'sig/tuile.rbs', line 5956

def inspect_details: () -> ::Array[String]

#on_click ⇒ Listeners

Fired with a ClickEvent when the button is activated — Enter, Space or a left click within #extent.

Returns:



40
# File 'lib/tuile/component/button.rb', line 40

listener :on_click

#repaint(canvas) ⇒ void

This method returns an undefined value.

@param canvas — see Tuile::Component#repaint.

Parameters:



81
82
83
84
85
86
87
88
# File 'lib/tuile/component/button.rb', line 81

def repaint(canvas)
  super
  return if rect.empty?

  label = (StyledString.plain("[ ") + caption + StyledString.plain(" ]")).ellipsize(rect.width)
  label = label.with_bg(screen.theme.active_bg_color) if active?
  canvas.set_text(0, 0, label)
end

#tab_stop? ⇒ Boolean

Returns:

  • (Boolean)


44
# File 'lib/tuile/component/button.rb', line 44

def tab_stop? = true