Class: RGame::Engine::UI::Menu
- Defined in:
- lib/rgame/engine/ui/menu.rb
Overview
A vertical list of things to choose from, navigated by keyboard or controller.
= layer.add_node(UI::Menu.new(item_width: 220, item_height: 44))
.add_item('Resume').on_activated { close }
.add_item('Quit').on_activated { game.close }
Focus is the whole design
With no pointer there is no hover, so something has to own which
control is focused and how the directions move it. That is this class,
and everything else about a menu follows from it: an item draws
differently because it is focused, and ui_confirm activates the focused
one.
Focus is per player, and that costs nothing
A Menu inside a PlayerLayer inherits that player as its input_owner,
so the actions its on_control receives are already that player's.
Two players with a menu open at once are independent without either menu
knowing the other exists, and without a word of focus-specific
per-player machinery. That falls out of ownership being inherited down
the tree — see docs/api/scene_graph.md, "Who a node answers to".
What this is not
It is a menu, not a widget library. Items are stacked vertically at a fixed size, and that is the whole of its layout. The package this replaces positioned everything absolutely and hit-tested a mouse; none of it is a reference, and how UI should be laid out in general is still an open question — see docs/api/ui.md, "What this is not".
Instance Attribute Summary collapse
-
#focused_index ⇒ Object
readonly
Returns the value of attribute focused_index.
-
#items ⇒ Object
readonly
Returns the value of attribute items.
Attributes inherited from Node2D
#abs_angle, #abs_band, #abs_input_owner, #abs_x, #abs_y, #angle, #band, #children, #components, #context, #height, #input_owner, #parent, #paused, #scene, #sibling_order, #width, #x, #y, #z
Instance Method Summary collapse
-
#add_item(label, enabled: true) ⇒ Object
Adds an item below the last one and returns it, so a caller can connect to its signal in the same line.
- #focus(index) ⇒ Object
-
#focus_by(delta) ⇒ Object
Moves focus by
delta, skipping anything disabled, and wrapping. - #focused ⇒ Object
-
#initialize(item_width:, item_height:, spacing: 8, style: MenuItem::STYLE) ⇒ Menu
constructor
Navigation wraps: a short vertical list is quicker to use when the ends join, and every console menu does it.
- #on_control(actions) ⇒ Object
Methods inherited from Node2D
#add_component, #add_node, #children_unsorted!, #control, #draw, #enter_tree, #exit_tree, #freed?, #get_component, #in_tree?, #on_add, #on_draw, #on_remove, #on_update, #queue_free, #remove_component, #remove_node, #root, #sweep_freed, #system, #update
Methods included from Signal::DSL
Constructor Details
#initialize(item_width:, item_height:, spacing: 8, style: MenuItem::STYLE) ⇒ Menu
Navigation wraps: a short vertical list is quicker to use when the ends join, and every console menu does it.
40 41 42 43 44 45 46 47 48 49 |
# File 'lib/rgame/engine/ui/menu.rb', line 40 def initialize(item_width:, item_height:, spacing: 8, style: MenuItem::STYLE, **) super(**) @item_width = item_width @item_height = item_height @spacing = spacing @style = style @items = [] @focused_index = 0 end |
Instance Attribute Details
#focused_index ⇒ Object (readonly)
Returns the value of attribute focused_index.
51 52 53 |
# File 'lib/rgame/engine/ui/menu.rb', line 51 def focused_index @focused_index end |
#items ⇒ Object (readonly)
Returns the value of attribute items.
51 52 53 |
# File 'lib/rgame/engine/ui/menu.rb', line 51 def items @items end |
Instance Method Details
#add_item(label, enabled: true) ⇒ Object
Adds an item below the last one and returns it, so a caller can connect to its signal in the same line.
55 56 57 58 59 60 61 62 63 |
# File 'lib/rgame/engine/ui/menu.rb', line 55 def add_item(label, enabled: true) item = MenuItem.new(label: label, enabled: enabled, style: @style, x: 0, y: @items.size * (@item_height + @spacing), width: @item_width, height: @item_height) @items << item add_node(item) refocus item end |
#focus(index) ⇒ Object
82 83 84 85 |
# File 'lib/rgame/engine/ui/menu.rb', line 82 def focus(index) @focused_index = index @items.each_with_index { |item, i| item.focused = (i == index) } end |
#focus_by(delta) ⇒ Object
Moves focus by delta, skipping anything disabled, and wrapping. Does
nothing at all if no item can take focus.
69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/rgame/engine/ui/menu.rb', line 69 def focus_by(delta) return if @items.empty? index = @focused_index @items.size.times do index = (index + delta) % @items.size next unless @items[index].enabled? focus(index) return end end |
#focused ⇒ Object
65 |
# File 'lib/rgame/engine/ui/menu.rb', line 65 def focused = @items[@focused_index] |
#on_control(actions) ⇒ Object
87 88 89 90 91 92 93 94 95 96 |
# File 'lib/rgame/engine/ui/menu.rb', line 87 def on_control(actions) focus_by(-1) if actions.pressed?(:ui_up) focus_by(1) if actions.pressed?(:ui_down) current = focused return if current.nil? current.pressed = actions.held?(:ui_confirm) current.activate if actions.pressed?(:ui_confirm) end |