Class: Tuile::Component::MenuBar::Item
- Inherits:
-
Object
- Object
- Tuile::Component::MenuBar::Item
- Defined in:
- lib/tuile/component/menu_bar.rb,
sig/tuile.rbs
Overview
One menu item: a caption, an optional click listener, and its children.
file = .add_item("File") # minted by the bar
file.add_item("New") { create } # …and nested by the same method
file.items.size # => 1
An item with children is a submenu and its own listener is dead (#submenu? decides). An item with neither children nor a listener is legal and inert: it highlights, Enter closes the menu, nothing happens — an item that looks live but does nothing is the app's error to fix, not the framework's to raise on.
Instance Attribute Summary collapse
-
#caption ⇒ StyledString
readonly
@return — the label painted on the strip or the row.
- #cued_caption ⇒ StyledString readonly
-
#items ⇒ ::Array[Item]
readonly
@return — this item's children, in menu order.
-
#mnemonic ⇒ String?
readonly
@return — the downcased letter that activates this item while its own level is the live one;
nilwhen it has none. -
#on_click ⇒ Proc, ...
@return — no-arg callable fired when the item is activated (Enter, Space or a left click), exactly as Button#on_click.
Instance Method Summary collapse
-
#add_item(caption = nil, mnemonic: nil, &on_click) ⇒ Item
Appends a child and returns its handle.
-
#build_cued_caption(caption, mnemonic) ⇒ StyledString
StyledString#slice counts columns while a caption search yields a character index, so the prefix is measured, never counted.
-
#initialize(caption, mnemonic, on_click) ⇒ Item
constructor
@param
caption— already coerced by the caller. - #inspect ⇒ String
-
#submenu? ⇒ Boolean
@return — whether this item opens a submenu, i.e.
-
#validate_mnemonic(mnemonic) ⇒ void
Rejects a mnemonic that couldn't work, or that would make two siblings ambiguous — all three at registration, since none has a sane answer at keypress time.
Constructor Details
#initialize(caption, mnemonic, on_click) ⇒ Item
@param caption — already coerced by the caller.
@param mnemonic — already validated by the caller, in the case it was given in.
@param on_click
100 101 102 103 104 105 106 |
# File 'lib/tuile/component/menu_bar.rb', line 100 def initialize(caption, mnemonic, on_click) @caption = caption @mnemonic = mnemonic&.downcase @cued_caption = build_cued_caption(caption, mnemonic) @on_click = on_click @items = [] end |
Instance Attribute Details
#caption ⇒ StyledString (readonly)
@return — the label painted on the strip or the row.
111 112 113 |
# File 'lib/tuile/component/menu_bar.rb', line 111 def caption @caption end |
#cued_caption ⇒ StyledString (readonly)
@return — #caption with the #mnemonic underlined —
what both paint sites draw. Equal to #caption when there is no
mnemonic or the caption doesn't contain it. Computed once, at
construction: caption and mnemonic are both fixed there, and
underline is a plain attribute with no theme or bg_color input, so
this is not a cached theme value.
123 124 125 |
# File 'lib/tuile/component/menu_bar.rb', line 123 def cued_caption @cued_caption end |
#items ⇒ ::Array[Item] (readonly)
@return — this item's children, in menu order. Read-only by convention, like Tuile::Component#children — grow it through #add_item.
127 128 129 |
# File 'lib/tuile/component/menu_bar.rb', line 127 def items @items end |
#mnemonic ⇒ String? (readonly)
@return — the downcased letter that activates this item
while its own level is the live one; nil when it has none.
115 116 117 |
# File 'lib/tuile/component/menu_bar.rb', line 115 def mnemonic @mnemonic end |
#on_click ⇒ Proc, ...
@return — no-arg callable fired when the item is activated (Enter, Space or a left click), exactly as Button#on_click. Never fired on an item with children.
132 133 134 |
# File 'lib/tuile/component/menu_bar.rb', line 132 def on_click @on_click end |
Instance Method Details
#add_item(caption = nil, mnemonic: nil, &on_click) ⇒ Item
Appends a child and returns its handle.
@param caption — parsed as StyledString.parse parses it.
@param mnemonic — the letter that activates this child while this item's children are the live level; see Tuile::Component::MenuBar#add_item.
146 147 148 149 |
# File 'lib/tuile/component/menu_bar.rb', line 146 def add_item(caption = nil, mnemonic: nil, &on_click) validate_mnemonic(mnemonic) Item.send(:new, StyledString.parse(caption), mnemonic, on_click).tap { @items << _1 } end |
#build_cued_caption(caption, mnemonic) ⇒ StyledString
StyledString#slice counts columns while a caption search yields a character index, so the prefix is measured, never counted.
@param caption
@param mnemonic — in the case it was given in.
185 186 187 188 189 190 191 192 193 194 195 196 197 |
# File 'lib/tuile/component/menu_bar.rb', line 185 def build_cued_caption(caption, mnemonic) return caption if mnemonic.nil? text = caption.to_s # Exact case first, so "Save As" can underline either "a" via the case # it was given in. index = text.index(mnemonic) || text.downcase.index(mnemonic.downcase) return caption if index.nil? start = StyledString.plain(text[0, index]).display_width caption.slice(0, start) + caption.slice(start, 1).with_underline + caption.slice(start + 1, caption.display_width - start - 1) end |
#inspect ⇒ String
152 153 154 155 |
# File 'lib/tuile/component/menu_bar.rb', line 152 def inspect mn = @mnemonic.nil? ? "" : " [#{@mnemonic}]" "#<#{self.class.name} #{caption.to_s.inspect}#{mn}#{ ? " (#{@items.size} items)" : ""}>" end |
#submenu? ⇒ Boolean
@return — whether this item opens a submenu, i.e. has children.
135 |
# File 'lib/tuile/component/menu_bar.rb', line 135 def = !@items.empty? |
#validate_mnemonic(mnemonic) ⇒ void
This method returns an undefined value.
Rejects a mnemonic that couldn't work, or that would make two siblings ambiguous — all three at registration, since none has a sane answer at keypress time.
@param mnemonic
165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/tuile/component/menu_bar.rb', line 165 def validate_mnemonic(mnemonic) return if mnemonic.nil? # Not implied by printable?, which accepts " ". raise ArgumentError, "mnemonic must not be a space: Space activates the highlighted item" if mnemonic == " " unless Keys.printable?(mnemonic) && StyledString.plain(mnemonic).display_width == 1 raise ArgumentError, "mnemonic must be a single one-column printable character; got #{mnemonic.inspect}" end down = mnemonic.downcase return unless @items.any? { |item| item.mnemonic == down } raise ArgumentError, "duplicate mnemonic #{down.inspect} among these menu items" end |