Class: Typr::Stack

Inherits:
Space
  • Object
show all
Defined in:
lib/stack.rb

Overview

A vertically scrollable list of items displayed one per row or column.

Provides paging, keyboard navigation, key-hint overlays, and alternating row colors. Subclasses Grid, Text, and Browser add domain-specific rendering on top of this foundation.

Key bindings (overridable via keymap):

KEY_TAB    -> cycle       (advance hint numbering to the next chunk)
KEY_RETURN -> confirm     (returns currently selected items, or nil)
KEY_ESCAPE -> exit        (abort selection and return nil)
KEY_UP / KEY_DOWN            (step by one row in the given direction)
KEY_PAGEUP / KEY_PAGEDOWN  (jump by one viewport height)
KEY_HOME / KEY_END           (jump to first or last pageable row)

Direct Known Subclasses

Grid, Text

Constant Summary

Constants included from Typr

COLORS, INPUT, KEY_DELETE, KEY_ENTER, KEY_ESCAPE, KEY_INSERT, KEY_PAGEDOWN, KEY_PAGEUP, KEY_RETURN, KEY_TAB, MIMETYPES, MODES

Instance Attribute Summary collapse

Attributes inherited from Space

#borders, #bottom, #colors, #interval, #left, #margin, #right, #top

Instance Method Summary collapse

Methods inherited from Space

#border=, #draw_border, #stop, #width

Methods included from Typr

#background, clear, #color, #color_code, column, exit, #fade, #foreground, #get_background, #get_foreground, height, init, #mode, #mode_code, #move, #move_code, position, #prepare, #printables, read, #real_size, row, #show, size, width

Constructor Details

#initialize(args = {}) ⇒ Stack

Constructs a Stack widget.

All positional and visual parameters from Typr::Space#initialize are available. Additionally:

Option          | Default | Description
----------------|---------|-------------------------------------------
+keysyms+       | {}      | Custom key-to-method mappings
+hints+         | +"1234..."+ | Characters used in the hint overlay
+alternate+     | true    | Striped row backgrounds
+header+        | nil     | Label shown in a header bar
+separator+     | +" "+   | String drawn between columns (used by Grid)


263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/stack.rb', line 263

def initialize args={}
  @keymap, @separator, @selected = {}, " ", {}
  @start, @hints_start, @highlight = 0, 0, 0#A_STANDOUT
  @alternate = true
  super #args
  @colors = { hints: [255, :black ], header: [232,:grey60 ],
    selected: :grey25, alternate: :grey10, rows: {} }.merge @colors
  @selected = { fields:[], columns:[], rows:[] }.merge @selected
  @keymap = { cycle: KEY_TAB, confirm: KEY_RETURN, exit: KEY_ESCAPE,
    page_down: KEY_PAGEDOWN, page_up: KEY_PAGEUP, up: KEY_UP,
    down: KEY_DOWN, to_top: KEY_HOME, to_bottom: KEY_END }.merge @keymap
  @hints ||= "1234567890qwertyuiopasdfghjklzxcvbnm-=[];'\,./"
end

Instance Attribute Details

#headerObject

Widget configuration. left, top, right, bottom set the viewport; see Typr::Space. alternate toggles striped rows (+:grey10+ background alternating with the default bg).



27
28
29
# File 'lib/stack.rb', line 27

def header
  @header
end

#hintsObject

Returns the value of attribute hints.



28
29
30
# File 'lib/stack.rb', line 28

def hints
  @hints
end

#hints_startObject

Returns the value of attribute hints_start.



28
29
30
# File 'lib/stack.rb', line 28

def hints_start
  @hints_start
end

#keymapObject

Widget configuration. left, top, right, bottom set the viewport; see Typr::Space. alternate toggles striped rows (+:grey10+ background alternating with the default bg).



27
28
29
# File 'lib/stack.rb', line 27

def keymap
  @keymap
end

#selectedObject

Widget configuration. left, top, right, bottom set the viewport; see Typr::Space. alternate toggles striped rows (+:grey10+ background alternating with the default bg).



27
28
29
# File 'lib/stack.rb', line 27

def selected
  @selected
end

#separatorObject

Returns the value of attribute separator.



28
29
30
# File 'lib/stack.rb', line 28

def separator
  @separator
end

#startObject

Widget configuration. left, top, right, bottom set the viewport; see Typr::Space. alternate toggles striped rows (+:grey10+ background alternating with the default bg).



27
28
29
# File 'lib/stack.rb', line 27

def start
  @start
end

Instance Method Details

#cycleObject

Advance the numeric hints by one full set of hint characters so that newly-fitted items beyond the first viewport also get labeled numbers.

stack.cycle   # hints now start at index 10 (or wrap back to 0)


244
245
246
247
248
# File 'lib/stack.rb', line 244

def cycle
  @hints_start += @hints.size
  @hints_start = 0 if @hints_start > [height-1, rows-1+headspace].min
  return
end

#downObject

Step down by one row.

stack.down


157
# File 'lib/stack.rb', line 157

def down; @start += 1; return end

#drawObject

Renders the full widget: header bar (if present), all visible items with background coloring and alternating stripes, a selected-item highlight, and bottom borders.

This method is called by your application's main loop each frame; it does not handle events itself (see #send).



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/stack.rb', line 103

def draw
  if @header
    color @colors[ :header ]
    move left,top
    show @margin
    print :header
  end
  list = page.to_a
  list[ height-1 ] = nil if list.length < height
  for id, relative_id in list.each_with_index
  	move left,top + relative_id + headspace
    foreground ( @colors[:rows][id] || @colors[:default][0] )
    select = @selected[ :rows ].include?( id )
    dark=!dark if @alternate
    background ( select ? @colors[:selected] :
      (@alternate and dark) ? @colors[:alternate] : @colors[:default][1] )
    show @margin
    print id
    background if select
  end
  background
  super
end

#draw_hints(type = nil, row_id = 0) ⇒ Object

Renders the numeric key-hint overlay for items in the current page.

stack.draw_hints            # vertical hints along left margin
stack.draw_hints :column, 2 # horizontal hints across columns of row 2


82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/stack.rb', line 82

def draw_hints type=nil, row_id=0
  color @colors[ :hints ]
  if type.to_s[ /column/ ]
    positions( row_id )[ @hints_start..-1 ].each_with_index{ |pos, idx|
      move( left + pos, top + row_id )
      show @hints[idx] }
  else @hints.chars.each_with_index{ |char, idx|
    pos = idx + @hints_start + headspace
    break if pos > [height, rows-1+headspace].min
    move( left, top + pos )
    show char }
  end
end

#headspaceObject

The number of rows reserved for a header bar within the widget's footprint.

stack.headspace   #=> 1  (if header is set)
stack.headspace   #=> 0  (if no header)


43
# File 'lib/stack.rb', line 43

def headspace; @header ? 1 : 0 end

#heightObject

The usable height after subtracting any header space.

stack.height   #=> 14


50
# File 'lib/stack.rb', line 50

def height; super - headspace end

#pageObject

Returns a Range of internal data indices visible on the current page.

stack.page   #=> 0..14


35
# File 'lib/stack.rb', line 35

def page; @start..(@start + height - 1) end

#page_downObject

Jump down by one viewport of rows.

stack.page_down


169
# File 'lib/stack.rb', line 169

def page_down; @start += height; return end

#page_upObject

Jump up by one viewport of rows.

stack.page_up


175
# File 'lib/stack.rb', line 175

def page_up; @start -= height; return end

#pick(type = :row, row = 0) ⇒ Object

Blocks the calling thread and lets the user interactively pick items from the current page using hint characters or arrow keys. After selection, the user must press Return to confirm (or Escape to abort).

The type argument controls what kind of picker is shown:

Symbol/String type           | Purpose
-----------------------------|-------------------------------------------
+:row, +"file"+              | Pick a single row id
+"rows"+                     | Multi-select rows
+:column, +"field"+          | Nested: pick a row then a column
+:fields+                    | Multi-select fields
+:none+                      | Navigation-only mode
anything with +relative_|    | Returns the internal integer index

stack.pick                # single row picker
stack.pick :column, 3     # pick a column in row 3
stack.pick "rows"         # multi-select rows


197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/stack.rb', line 197

def pick type = :row, row=0 #, key=nil
  @hints_start = 0
  type = type.to_s
  multiple = type[-1] == ?s
  loop do
    if type['field']
      row = pick( :row ) or return
      column = pick( :column, page.to_a.index( row  + headspace)) or return
      value = [ row , column ]
    else
      draw
      draw_hints type, row unless type['none']
      limit = type['row'] ? height : positions(row).count
      key = Typr.read :key
      if key.is_a?(String) and value =
        @hints[0..limit-@hints_start-1].index(key)
        value += @hints_start
        relative = value + @start if type['relative_']
        if type['row'] then value = page.to_a[ value ]
        elsif type['column'] and @sequence; value = @sequence[value] end
      end unless type['none']
    end

    if value
      type = type[9..-1] if type['relative_']
      type += ?s unless multiple
      type = type.to_sym
      if @selected[type].include? value
        @selected[type].delete value
      else @selected[type] << value end if multiple
      return relative || value unless multiple
    else
      case key
        when @keymap[:exit]; return
        when @keymap[:confirm]; return @selected[type]
        else send key #if type['id']
      end
    end
  end
end

Placeholder renderer used when no subclass overrides print for an item id.

stack.print 42   # renders a blank row


57
# File 'lib/stack.rb', line 57

def print id; show " " * (width-@margin.size) end

#reset(type = :all) ⇒ Object

Resets widget state by one or more categories.

stack.reset              # resets position and selection
stack.reset :position    # only scrolls back to top
stack.reset :selection   # clears all selected rows/columns/fields
stack.reset [:display, :format]


67
68
69
70
71
72
73
74
# File 'lib/stack.rb', line 67

def reset type=:all
  case type
    when Array; type.each{ |type| reset type }
    when :position; @start = @hints_start = 0
    when :selection; @selected = @selected.keys.map{ |type| [ type, [] ] }.to_h
    when :all; reset [ :position, :selection ]
  end
end

#send(key) ⇒ Object

Dispatches a key from the terminal to its semantic action and clamps the current page offset.

stack.send Typr::KEY_UP      # scrolls up one row
stack.send Typr::KEY_RETURN   # confirms selection


134
135
136
137
138
139
# File 'lib/stack.rb', line 134

def send key#, map=nil
  response = eval @keymap.invert[key].to_s if @keymap.values.include? key
  @start = rows - height if @start > rows - height
  @start = 0 if @start < 0
  return response
end

#to_bottomObject

Scroll to the bottom pageable position (last row minus viewport height).

stack.to_bottom


151
# File 'lib/stack.rb', line 151

def to_bottom; @start = rows - height; return end

#to_topObject

Scroll to the top of the dataset.

stack.to_top


145
# File 'lib/stack.rb', line 145

def to_top; @start = 0; return end

#upObject

Step up by one row.

stack.up


163
# File 'lib/stack.rb', line 163

def up; @start -= 1; return end