Class: Ruby2D::Button
- Inherits:
-
Object
- Object
- Ruby2D::Button
- Includes:
- Interactive
- Defined in:
- lib/ruby2d/button.rb
Overview
A clickable region with optional visual and label. Button is itself the
interactive entity: it registers directly with Window's interactive
registry and holds its own event handlers. The optional @visual is a
rendering concern, never an event-routing one.
Constant Summary
Constants included from Interactive
Interactive::OBJECT_EVENTS, Interactive::OBJECT_EVENT_FILTER_PREDICATES
Instance Attribute Summary collapse
-
#label_text ⇒ Object
readonly
Returns the value of attribute label_text.
Instance Method Summary collapse
-
#add ⇒ Object
Re-add the button after
remove. -
#color ⇒ Object
(also: #colour)
Get the button's fill color.
-
#color=(c) ⇒ Object
Set the button's fill color.
- #colour=(c) ⇒ Object
-
#contains?(x, y) ⇒ Boolean
Hit-test the button.
- #height ⇒ Object
-
#hide ⇒ Object
Mark the button hidden (cascades to visual and label).
-
#initialize(visual = nil, x: 0, y: 0, z: 0, width: 200, height: 50, label: nil, color: nil, colour: nil, hover_color: nil, hover_colour: nil, pressed_color: nil, pressed_colour: nil, label_color: nil, label_colour: nil, hover_label_color: nil, hover_label_colour: nil, pressed_label_color: nil, pressed_label_colour: nil, stroke_color: nil, stroke_colour: nil, stroke_width: 0, label_size: 20, add: true, visible: true, padding: nil, padding_top: nil, padding_right: nil, padding_bottom: nil, padding_left: nil, &on_click) ⇒ Button
constructor
Create a button.
-
#label ⇒ Object
Get the label string.
-
#label=(text) ⇒ Object
Set the label string.
-
#remove ⇒ Object
Remove the button: drop visual + label from the render list and unregister Button from the interactive registry.
-
#show ⇒ Object
Mark the button visible (cascades to visual and label).
-
#visible? ⇒ Boolean
Whether the button is currently visible.
- #width ⇒ Object
- #x ⇒ Object
-
#x=(x) ⇒ Object
Set the x position.
-
#x_align ⇒ Object
Horizontal alignment intent of the underlying visual, or nil.
-
#x_align=(sym) ⇒ Object
Set alignment intent on the underlying visual (owned visuals only), so these mirror the getters above rather than writing a
@x_alignthe getters would never read. - #y ⇒ Object
-
#y=(y) ⇒ Object
Set the y position.
-
#y_align ⇒ Object
Vertical alignment intent of the underlying visual, or nil.
- #y_align=(sym) ⇒ Object
- #z ⇒ Object
Methods included from Interactive
#_fire_event, #interactive?, #off, #on
Constructor Details
#initialize(visual = nil, x: 0, y: 0, z: 0, width: 200, height: 50, label: nil, color: nil, colour: nil, hover_color: nil, hover_colour: nil, pressed_color: nil, pressed_colour: nil, label_color: nil, label_colour: nil, hover_label_color: nil, hover_label_colour: nil, pressed_label_color: nil, pressed_label_colour: nil, stroke_color: nil, stroke_colour: nil, stroke_width: 0, label_size: 20, add: true, visible: true, padding: nil, padding_top: nil, padding_right: nil, padding_bottom: nil, padding_left: nil, &on_click) ⇒ Button
Create a button. Three forms:
Self-rendered:
Button.new(x:, y:, width:, height:, label:, color:, hover_color:) { ... }
Wrap a custom visual (any shape responding to x, y, and contains?):
Button.new(some_shape) { ... }
Visual-less hit area (nothing rendered):
Button.new(x:, y:, width:, height:) { ... }
hover_color: and pressed_color: are opt-in. Pass an explicit color,
or :auto (lightens for hover, darkens for pressed). Omit for no tint.
When both are set and the user is hovering+pressed, pressed wins.
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/ruby2d/button.rb', line 27 def initialize(visual = nil, x: 0, y: 0, z: 0, width: 200, height: 50, label: nil, color: nil, colour: nil, hover_color: nil, hover_colour: nil, pressed_color: nil, pressed_colour: nil, label_color: nil, label_colour: nil, hover_label_color: nil, hover_label_colour: nil, pressed_label_color: nil, pressed_label_colour: nil, stroke_color: nil, stroke_colour: nil, stroke_width: 0, label_size: 20, add: true, visible: true, padding: nil, padding_top: nil, padding_right: nil, padding_bottom: nil, padding_left: nil, &on_click) base_color_input = color || colour hover_input = hover_color || hover_colour pressed_input = pressed_color || pressed_colour hover_label_input = hover_label_color || hover_label_colour pressed_label_input = pressed_label_color || pressed_label_colour stroke_input = stroke_color || stroke_colour # Label tints only make sense with a label to tint. Without one they would # silently install no-op hover/press handlers and force the button # interactive for no effect, so reject the combination up front. if (hover_label_input || pressed_label_input) && !label raise ArgumentError, '`hover_label_color`/`pressed_label_color` require a `label:` to tint' end if visual @visual = visual @owns_visual = false @x = visual.x @y = visual.y @z = visual.respond_to?(:z) ? visual.z : 0 @width = visual.respond_to?(:width) ? visual.width : 0 @height = visual.respond_to?(:height) ? visual.height : 0 else @x = x.is_a?(Symbol) ? 0 : x @y = y.is_a?(Symbol) ? 0 : y @z = z @width = width @height = height if label || base_color_input || stroke_input # Self-rendered: own a Rectangle for drawing. @visual = Rectangle.new(x: x, y: y, width: width, height: height, z: z, color: base_color_input || '#333', stroke_color: stroke_input, stroke_width: stroke_width, add: add, visible: visible, padding: padding, padding_top: padding_top, padding_right: padding_right, padding_bottom: padding_bottom, padding_left: padding_left) @owns_visual = true else # Visual-less: no rendering, no @visual. Just an interactive region. @visual = nil @owns_visual = false end end if label @label = Text.new(label, x: 0, y: 0, z: @z + 1, size: label_size, color: label_color || label_colour || 'white', add: add, visible: visible) center_label end if @visual && (hover_input || pressed_input || hover_label_input || pressed_label_input) install_state_tint(hover_input, pressed_input, hover_label_input, pressed_label_input) end hook_visual_alignment if @owns_visual on(:click) { |e| on_click.call(e) } if on_click # Honor `add: false` for interactivity, not just rendering. Registering the # click/tint handlers above auto-added this Button to the interactive # registry; without this an `add: false` Button would keep swallowing # clicks while its un-added visual draws nothing. Only touches the registry # when handlers exist, so a handler-less Button doesn't force a window into # being. `add`/`remove` manage registration thereafter. Window.unregister_interactive(self) if interactive? && !add end |
Instance Attribute Details
#label_text ⇒ Object (readonly)
Returns the value of attribute label_text.
11 12 13 |
# File 'lib/ruby2d/button.rb', line 11 def label_text @label_text end |
Instance Method Details
#add ⇒ Object
Re-add the button after remove. Restores rendering and re-registers
in the interactive registry if any handlers are attached.
120 121 122 123 124 |
# File 'lib/ruby2d/button.rb', line 120 def add @visual.add if @visual @label.add if @label Window.register_interactive(self) if interactive? end |
#color ⇒ Object Also known as: colour
Get the button's fill color. When a hover/pressed tint is configured this
returns the resting (base) color you set — not the transient tint while
hovered or pressed — so it stays symmetric with color=. Otherwise it
delegates to the visual. Returns nil when there is no colorable visual.
234 235 236 237 238 |
# File 'lib/ruby2d/button.rb', line 234 def color return @base_color if @base_color @visual.respond_to?(:color) ? @visual.color : nil end |
#color=(c) ⇒ Object
Set the button's fill color. When a hover/pressed tint is configured this
updates the resting (base) color and re-applies the current state so the
change survives the next hover/press cycle; otherwise it sets the visual
directly. Accepts a single color or a gradient (Color::Set). A no-op on
a visual-less button.
245 246 247 248 249 250 251 252 253 254 |
# File 'lib/ruby2d/button.rb', line 245 def color=(c) return unless @visual.respond_to?(:color=) if @base_color @base_color = Color.set(c) apply_state else @visual.color = c end end |
#colour=(c) ⇒ Object
258 259 260 |
# File 'lib/ruby2d/button.rb', line 258 def colour=(c) self.color = c end |
#contains?(x, y) ⇒ Boolean
Hit-test the button. Wrapped visuals delegate so non-rectangular shapes (Circle, Polygon, …) test against their actual geometry.
145 146 147 148 149 |
# File 'lib/ruby2d/button.rb', line 145 def contains?(x, y) return @visual.contains?(x, y) if @visual && !@owns_visual x >= @x && x <= (@x + @width) && y >= @y && y <= (@y + @height) end |
#height ⇒ Object
191 192 193 |
# File 'lib/ruby2d/button.rb', line 191 def height @owns_visual ? @height : (@visual && @visual.respond_to?(:height) ? @visual.height : @height) end |
#hide ⇒ Object
Mark the button hidden (cascades to visual and label).
133 134 135 136 |
# File 'lib/ruby2d/button.rb', line 133 def hide @visual.hide if @visual @label.hide if @label end |
#label ⇒ Object
Get the label string.
218 219 220 |
# File 'lib/ruby2d/button.rb', line 218 def label @label ? @label.content : nil end |
#label=(text) ⇒ Object
Set the label string.
223 224 225 226 227 228 |
# File 'lib/ruby2d/button.rb', line 223 def label=(text) return unless @label @label.content = text center_label end |
#remove ⇒ Object
Remove the button: drop visual + label from the render list and unregister Button from the interactive registry.
112 113 114 115 116 |
# File 'lib/ruby2d/button.rb', line 112 def remove @visual.remove if @visual @label.remove if @label Window.unregister_interactive(self) end |
#show ⇒ Object
Mark the button visible (cascades to visual and label).
127 128 129 130 |
# File 'lib/ruby2d/button.rb', line 127 def show @visual.show if @visual @label.show if @label end |
#visible? ⇒ Boolean
Whether the button is currently visible.
139 140 141 |
# File 'lib/ruby2d/button.rb', line 139 def visible? @visual ? @visual.visible? : true end |
#width ⇒ Object
187 188 189 |
# File 'lib/ruby2d/button.rb', line 187 def width @owns_visual ? @width : (@visual && @visual.respond_to?(:width) ? @visual.width : @width) end |
#x ⇒ Object
151 152 153 |
# File 'lib/ruby2d/button.rb', line 151 def x @owns_visual ? @x : (@visual ? @visual.x : @x) end |
#x=(x) ⇒ Object
Set the x position. Pass a symbol (:left, :center, :right) to
set alignment intent on the underlying visual (owned visuals only).
157 158 159 160 161 162 163 164 165 |
# File 'lib/ruby2d/button.rb', line 157 def x=(x) return self.x_align = x if x.is_a?(Symbol) @x = x # Move the visual too — owned or wrapped — so the visual, the (delegated) # hit region, the getter, and the label all move together. @visual.x = x if @visual.respond_to?(:x=) center_label if @label end |
#x_align ⇒ Object
Horizontal alignment intent of the underlying visual, or nil.
196 197 198 |
# File 'lib/ruby2d/button.rb', line 196 def x_align @visual.respond_to?(:x_align) ? @visual.x_align : nil end |
#x_align=(sym) ⇒ Object
Set alignment intent on the underlying visual (owned visuals only), so
these mirror the getters above rather than writing a @x_align the
getters would never read. A wrapped visual is positioned by its owner,
so alignment there is the caller's to set on the visual itself.
209 210 211 |
# File 'lib/ruby2d/button.rb', line 209 def x_align=(sym) @visual.x_align = sym if @owns_visual end |
#y ⇒ Object
167 168 169 |
# File 'lib/ruby2d/button.rb', line 167 def y @owns_visual ? @y : (@visual ? @visual.y : @y) end |
#y=(y) ⇒ Object
Set the y position. Pass a symbol (:top, :center, :bottom) to
set alignment intent on the underlying visual (owned visuals only).
173 174 175 176 177 178 179 180 181 |
# File 'lib/ruby2d/button.rb', line 173 def y=(y) return self.y_align = y if y.is_a?(Symbol) @y = y # Move the visual too — owned or wrapped — so the visual, the (delegated) # hit region, the getter, and the label all move together. @visual.y = y if @visual.respond_to?(:y=) center_label if @label end |
#y_align ⇒ Object
Vertical alignment intent of the underlying visual, or nil.
201 202 203 |
# File 'lib/ruby2d/button.rb', line 201 def y_align @visual.respond_to?(:y_align) ? @visual.y_align : nil end |
#y_align=(sym) ⇒ Object
213 214 215 |
# File 'lib/ruby2d/button.rb', line 213 def y_align=(sym) @visual.y_align = sym if @owns_visual end |
#z ⇒ Object
183 184 185 |
# File 'lib/ruby2d/button.rb', line 183 def z @owns_visual ? @z : (@visual && @visual.respond_to?(:z) ? @visual.z : @z) end |