class BBS::Selector
A menu at the cursor for a line-by-line board, on a Commodore 64 and a modern terminal alike: the items one under the other (in one column or several, or side by side on one row), the chosen one lit as a reversed bar that the cursor keys move. RETURN takes the lit item; an item’s own key (a letter or a digit) takes it at once, so a caller who knows the menu types as before. With numbers the items are numbered instead, and the digits typed light the item of that number. More items than a page holds turn into pages.
Like BBS::Field it draws from where the cursor is (at the start of a line) downwards and moves with a BBS::Cursor, so a C64 never gets HOME; after the choice the cursor is on the line under the menu.
Under the items it can show two more rows: the lit item’s about, when an item has one, and a status row with the hint, the number being typed and the page.
Keys:
- cursor up, down
-
the item before, after (down a column, then on to the next one and across the pages)
- cursor left, right
-
on one row the item before, after; in columns the item beside it; otherwise the page before, after
- +, -, PgUp, PgDn
-
the page before, after
- HOME, END
-
the first, the last item
- RETURN
-
takes the lit item
- an item’s key
-
takes that item
- digits
-
with
numbers: lights the item of that number - RUN/STOP, ←, Esc
-
returns
back
items = [BBS::Selector.item('latest posts', key: 'l', value: :latest), BBS::Selector.item('log off', key: 'q', value: :quit)] case BBS::Selector.new(session, items, width: 38).run when :latest then latest when :quit, nil then return end
Constants
- BACK_KEYS
-
The keys that return
back. - COLORS
-
The colours of the roles, from the C64 palette (
BBS::Petscii::Colors):textthe labels,keythe keys and numbers,barthe lit cell (reversed),headingthe headings,aboutthe about row,hintthe status row. - DIGIT
-
One digit typed.
- Item
-
One cell of the menu: its
label(ANSI colours allowed), itskey(a letter or digit that takes it, ornil), itsvalue(whatrunreturns for it), itsabout(a line shown under the menu while it is lit, ornil), itscolor(a colour for its key and its bar in place of the menu’s, ornil) andheading(true for a cell that only labels the ones under it, or keeps a place empty, and is never lit). - MAX_DIGITS
-
The most digits a number typed with
numberscan have. - PAGE_KEYS
-
The keys that turn the page, and which way.
- SIDE_KEYS
-
The keys that go sideways, and which way.
- STEP_KEYS
-
The keys that go up and down, and which way.
Attributes
The columns the menu takes.
Public Class Methods
Source
# File lib/bbs/selector.rb, line 85 def self.heading(label = '') = Item.new(label: label, heading: true)
A heading Item: label (ANSI colours allowed) shown in the heading colour, never lit. An empty label keeps a place empty.
# File lib/bbs/selector.rb, line 79 def self.item(label, key: nil, value: nil, about: nil, color: nil) Item.new(label: label, key: key, value: value, about: about, color: color) end
# File lib/bbs/selector.rb, line 91 def self.list(session, objects, back: nil, **, &label) items = objects.map { |o| item(label.call(o).to_s, value: o) } new(session, items, numbers: true, back: back, **).run end
A numbered menu of objects, each labelled by the block; the object taken, or back. The other options are those of ::new.
post = BBS::Selector.list(session, posts, width: 38, height: 18, &:title)
# File lib/bbs/selector.rb, line 127 def initialize(session, items, width:, height: nil, columns: 1, index: nil, numbers: false, across: false, hint: nil, exits: {}, back: nil, colors: {}, fill: false) raise ArgumentError, 'a selector needs an item to select' unless items.any?(&:selectable?) @session = session @items = items @width = [width, 4].max @layout = across ? Row.new(items.size) : Columns.new(items.size, height: height, columns: columns, fill: fill) @index = first_index(index) @numbers = numbers @hint = hint @exits = exits.transform_keys { |k| fold(k) } @back = back @colors = self.class::COLORS.merge(colors) @typed = +'' end
Creates a menu for session.
- items
-
the cells, an Array of
Item(see::itemand::heading), down the first column, then down the next; at least one must be selectable - width
-
the columns the menu takes; on a C64 keep it out of the last column
- height
-
the rows a page shows; default all the items, shared out among the
columns - fill
-
true to keep
heightrows even when the items take fewer: the rows under the last item stay blank, so the about and status rows stand where a full page puts them - columns
-
the columns of cells side by side, each as wide as the
widthshared out - across
-
true to put the items side by side on one row (a
Rowlayout instead ofColumns) - index
-
the item lit first; default the first selectable one
- numbers
-
true to number the items (1, 2, …) and let the digits typed light them; the items’ own keys still work
- hint
-
a line for the status row, or
nil - exits
-
more keys and what
runreturns for them, e.g.{ 'q' => :quit } - back
-
what
runreturns for RUN/STOP, ← or Esc - colors
-
colours of the roles over
COLORS
Raises ArgumentError when no item can be selected.
Public Instance Methods
Source
# File lib/bbs/selector.rb, line 170 def jump(index) @typed.clear light(index) end
Lights item index, forgetting the digits typed.
Source
# File lib/bbs/selector.rb, line 167 def move(dir) = jump(step(@index, dir))
Lights the next selectable item in direction dir (1 or -1).
Source
# File lib/bbs/selector.rb, line 161 def page = @layout.page_of(@index)
The page shown, from 0.
Source
# File lib/bbs/selector.rb, line 149 def run draw loop do key = read_key result = take(key) return finish(result.first) if result react(key) end end
Draws the menu, then reads keys until an item is taken or the caller goes back; the cursor ends on the line under the menu.
Returns the taken item’s value, an exits value, or back. Raises IOError when the caller hangs up.
Source
# File lib/bbs/selector.rb, line 164 def selectable?(index) = @items[index].selectable?
Whether item index can be lit.
Source
# File lib/bbs/selector.rb, line 177 def turn(dir) return if pages == 1 first = ((page + dir) % pages) * per_page jump(@items[first].selectable? ? first : step(first, 1)) end
Turns to the page beside, dir (1 or -1), lighting its first selectable item.