reterm 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +131 -0
- data/designer/main.rb +10 -0
- data/designer/src/ComponentParams.rb +120 -0
- data/designer/src/ComponentsList.rb +90 -0
- data/designer/src/CreatedList.rb +156 -0
- data/designer/src/Designer.rb +114 -0
- data/designer/src/ToggleArea.rb +67 -0
- data/designer/src/common.rb +1 -0
- data/designer/src/component_factory.rb +30 -0
- data/designer/src/component_map.rb +116 -0
- data/designer/src/glade/ComponentParams.glade +96 -0
- data/designer/src/glade/Designer.glade +184 -0
- data/designer/src/images/add.png +0 -0
- data/designer/src/images/asciitext.png +0 -0
- data/designer/src/images/blank.png +0 -0
- data/designer/src/images/blank_sm.png +0 -0
- data/designer/src/images/button.png +0 -0
- data/designer/src/images/dial.png +0 -0
- data/designer/src/images/entry.png +0 -0
- data/designer/src/images/hslider.png +0 -0
- data/designer/src/images/label.png +0 -0
- data/designer/src/images/matrix.png +0 -0
- data/designer/src/images/orig/Check.png +0 -0
- data/designer/src/images/orig/ascii_text.png +0 -0
- data/designer/src/images/orig/button.png +0 -0
- data/designer/src/images/orig/dial.png +0 -0
- data/designer/src/images/orig/entry.png +0 -0
- data/designer/src/images/orig/hslider.png +0 -0
- data/designer/src/images/orig/label.png +0 -0
- data/designer/src/images/orig/matrix.png +0 -0
- data/designer/src/images/orig/radio.png +0 -0
- data/designer/src/images/orig/rocker.png +0 -0
- data/designer/src/images/orig/slist.png +0 -0
- data/designer/src/images/orig/vslider.png +0 -0
- data/designer/src/images/radio.png +0 -0
- data/designer/src/images/remove.png +0 -0
- data/designer/src/images/rocker.png +0 -0
- data/designer/src/images/slist.png +0 -0
- data/designer/src/images/vslider.png +0 -0
- data/lib/reterm.rb +22 -0
- data/lib/reterm/color_pair.rb +66 -0
- data/lib/reterm/component.rb +40 -0
- data/lib/reterm/components.rb +32 -0
- data/lib/reterm/components/ascii_text.rb +46 -0
- data/lib/reterm/components/button.rb +31 -0
- data/lib/reterm/components/dial.rb +95 -0
- data/lib/reterm/components/dialog.rb +34 -0
- data/lib/reterm/components/entry.rb +38 -0
- data/lib/reterm/components/hslider.rb +32 -0
- data/lib/reterm/components/image.rb +55 -0
- data/lib/reterm/components/label.rb +20 -0
- data/lib/reterm/components/matrix.rb +43 -0
- data/lib/reterm/components/radio.rb +33 -0
- data/lib/reterm/components/rocker.rb +71 -0
- data/lib/reterm/components/slist.rb +32 -0
- data/lib/reterm/components/template.rb +23 -0
- data/lib/reterm/components/vslider.rb +61 -0
- data/lib/reterm/init.rb +95 -0
- data/lib/reterm/layout.rb +63 -0
- data/lib/reterm/layouts.rb +16 -0
- data/lib/reterm/layouts/horizontal.rb +19 -0
- data/lib/reterm/layouts/vertical.rb +19 -0
- data/lib/reterm/loader.rb +126 -0
- data/lib/reterm/menu.rb +81 -0
- data/lib/reterm/mixins/cdk_component.rb +45 -0
- data/lib/reterm/mixins/component_input.rb +37 -0
- data/lib/reterm/mixins/event_dispatcher.rb +20 -0
- data/lib/reterm/mixins/nav_input.rb +126 -0
- data/lib/reterm/panel.rb +37 -0
- data/lib/reterm/resize.rb +27 -0
- data/lib/reterm/terminal.rb +33 -0
- data/lib/reterm/version.rb +3 -0
- data/lib/reterm/window.rb +260 -0
- metadata +155 -0
@@ -0,0 +1,63 @@
|
|
1
|
+
module RETerm
|
2
|
+
# Provides mechanism to orgranize on screen components according
|
3
|
+
# to rules defined by subclasses.
|
4
|
+
#
|
5
|
+
# Layouts themselves are specialized types of components as they
|
6
|
+
# are intended to be associated with windows in which to be rendered
|
7
|
+
class Layout < Component
|
8
|
+
include NavInput
|
9
|
+
|
10
|
+
# TODO padding / margin support
|
11
|
+
|
12
|
+
attr_accessor :children
|
13
|
+
|
14
|
+
def initialize(args={})
|
15
|
+
@children ||= []
|
16
|
+
end
|
17
|
+
|
18
|
+
# Subclasses should override this method returning
|
19
|
+
# boolean if child window exceeds bounds of layout
|
20
|
+
def exceeds_bounds?(child)
|
21
|
+
raise "NotImplemented"
|
22
|
+
end
|
23
|
+
|
24
|
+
# Return boolean indicating if any child component
|
25
|
+
# is activatable
|
26
|
+
def activatable?
|
27
|
+
children.any? { |c| c.component.activatable? }
|
28
|
+
end
|
29
|
+
|
30
|
+
# Create new child window and add it to layout
|
31
|
+
def add_child(h={})
|
32
|
+
child = window.create_child(h)
|
33
|
+
|
34
|
+
if child.win.nil?
|
35
|
+
raise ArgumentError, "could not create child window"
|
36
|
+
end
|
37
|
+
|
38
|
+
if exceeds_bounds?(child)
|
39
|
+
window.del_child(child) unless child.win.nil?
|
40
|
+
raise ArgumentError, "child exceeds bounds"
|
41
|
+
end
|
42
|
+
|
43
|
+
children << child
|
44
|
+
update_reterm
|
45
|
+
child
|
46
|
+
end
|
47
|
+
|
48
|
+
# Draw all layout children
|
49
|
+
def draw!
|
50
|
+
children.each { |c| c.draw! }
|
51
|
+
end
|
52
|
+
|
53
|
+
# Activates layout by dispatching to navigation
|
54
|
+
# input system.
|
55
|
+
#
|
56
|
+
# @see NavInput
|
57
|
+
def activate!
|
58
|
+
draw!
|
59
|
+
update_reterm
|
60
|
+
handle_input
|
61
|
+
end
|
62
|
+
end # class Layout
|
63
|
+
end # module RETerm
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module RETerm
|
2
|
+
# Encapsulates all Layouts currently implemented
|
3
|
+
module Layouts
|
4
|
+
def self.all
|
5
|
+
@a ||= Layouts.constants
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.names
|
9
|
+
@n ||= all.collect { |l| l.to_s }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'reterm/layout'
|
15
|
+
require 'reterm/layouts/horizontal'
|
16
|
+
require 'reterm/layouts/vertical'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module RETerm
|
2
|
+
module Layouts
|
3
|
+
# Layout which arrainges items horizontally across screen cols
|
4
|
+
class Horizontal < Layout
|
5
|
+
def current_cols
|
6
|
+
children.sum { |c| c.cols } + 1
|
7
|
+
end
|
8
|
+
|
9
|
+
def exceeds_bounds?(child)
|
10
|
+
child.rows > window.rows || (current_cols + child.cols) > window.cols
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_child(h={})
|
14
|
+
# set x/y to next appropriate location
|
15
|
+
super(h.merge(:y => 1, :x => current_cols))
|
16
|
+
end
|
17
|
+
end # class Horizontal
|
18
|
+
end # module Layouts
|
19
|
+
end # module RETerm
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module RETerm
|
2
|
+
module Layouts
|
3
|
+
# Layout which arrainges items vertically down screen rows
|
4
|
+
class Vertical < Layout
|
5
|
+
def current_rows
|
6
|
+
children.sum { |c| c.rows } + 1
|
7
|
+
end
|
8
|
+
|
9
|
+
def exceeds_bounds?(child)
|
10
|
+
child.cols > window.cols || (current_rows + child.rows) > window.rows
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_child(h={})
|
14
|
+
# set x/y to next appropriate location
|
15
|
+
super(h.merge(:y => current_rows, :x => 1))
|
16
|
+
end
|
17
|
+
end # class Horizontal
|
18
|
+
end # module Layouts
|
19
|
+
end # module RETerm
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module RETerm
|
2
|
+
# JSON schema loader, instantiates RETerm subsystem from schema
|
3
|
+
# stored in a JSON string. Currently this schema does not have a
|
4
|
+
# formal definition, but see examples/ for what can currently be
|
5
|
+
# done.
|
6
|
+
class Loader
|
7
|
+
# Initialie new loader from JSON string
|
8
|
+
def initialize(str)
|
9
|
+
require 'json'
|
10
|
+
parse_doc(str)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Return top level window in loader
|
14
|
+
def window
|
15
|
+
@win
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def parse_doc(str)
|
21
|
+
@j = JSON.parse(str)
|
22
|
+
|
23
|
+
raise ArgumentError unless @j.is_a?(Hash)
|
24
|
+
raise ArgumentError unless @j.key?("window")
|
25
|
+
|
26
|
+
parse_colors(@j['colors']) if @j.key?('colors')
|
27
|
+
|
28
|
+
@win = parse_win(@j['window'])
|
29
|
+
update_reterm
|
30
|
+
end
|
31
|
+
|
32
|
+
def parse_colors(colors)
|
33
|
+
raise ArgumentError unless colors.is_a?(Array)
|
34
|
+
colors.each { |c|
|
35
|
+
raise ArgumentError unless c.is_a?(Array) && c.size >= 3
|
36
|
+
ColorPair.register(*c)
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def parse_win(win)
|
41
|
+
raise ArgumentError unless win.is_a?(Hash)
|
42
|
+
|
43
|
+
args = {}
|
44
|
+
|
45
|
+
args[:rows] = win['rows'] if win.key?('rows')
|
46
|
+
args[:cols] = win['cols'] if win.key?('cols')
|
47
|
+
|
48
|
+
w = Window.new args
|
49
|
+
parse_child(w, win)
|
50
|
+
|
51
|
+
w.border! if win.key?('border') && !!win['border']
|
52
|
+
w.colors = ColorPair.for(win['colors']) if win.key?('colors')
|
53
|
+
|
54
|
+
w
|
55
|
+
end
|
56
|
+
|
57
|
+
def parse_child(win, parent)
|
58
|
+
raise ArgumentError if parent.key?('component') && parent.key?('layout')
|
59
|
+
raise ArgumentError if !parent.key?('component') && !parent.key?('layout')
|
60
|
+
|
61
|
+
if parent.key?('component')
|
62
|
+
parse_component win, parent['component']
|
63
|
+
|
64
|
+
else # if parent.key?('layout')
|
65
|
+
parse_layout win, parent['layout']
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def prep_init(init)
|
70
|
+
raise ArgumentError unless init.is_a?(Hash)
|
71
|
+
init = Hash[init] # make copy
|
72
|
+
|
73
|
+
init.keys.each { |k|
|
74
|
+
init[k.intern] = init[k] # XXX unsafe intern
|
75
|
+
init.delete(k)
|
76
|
+
}
|
77
|
+
|
78
|
+
init
|
79
|
+
end
|
80
|
+
|
81
|
+
def parse_component(win, c)
|
82
|
+
raise ArgumentError unless c.is_a?(Hash)
|
83
|
+
raise ArgumentError unless c.key?("type") ||
|
84
|
+
!Components.names.include?(c["type"])
|
85
|
+
|
86
|
+
init = c.key?("init") ? prep_init(c["init"]) : {}
|
87
|
+
c = RETerm::Components.const_get(c["type"]).new(init)
|
88
|
+
win.component = c
|
89
|
+
c
|
90
|
+
end
|
91
|
+
|
92
|
+
def parse_layout(win, l)
|
93
|
+
raise ArgumentError unless l.is_a?(Hash)
|
94
|
+
raise ArgumentError unless l.key?("type") ||
|
95
|
+
!Layouts.names.include?(l["type"])
|
96
|
+
|
97
|
+
init = l.key?("init") ? prep_init(l["init"]) : {}
|
98
|
+
lc = RETerm::Layouts.const_get(l["type"]).new(init)
|
99
|
+
win.component = lc
|
100
|
+
parse_layout_children(lc, l["children"]) if l.key?("children")
|
101
|
+
lc
|
102
|
+
end
|
103
|
+
|
104
|
+
def parse_layout_children(l, children)
|
105
|
+
raise ArgumentError unless children.is_a?(Array)
|
106
|
+
|
107
|
+
children.each { |child|
|
108
|
+
raise ArgumentError unless child.is_a?(Hash)
|
109
|
+
rows = child['rows'] if child.key?('rows')
|
110
|
+
cols = child['cols'] if child.key?('cols')
|
111
|
+
|
112
|
+
cw = l.add_child :rows => rows,
|
113
|
+
:cols => cols
|
114
|
+
|
115
|
+
cw.border! if child.key?('border') && !!child['border']
|
116
|
+
cw.colors = ColorPair.for(child['colors']) if child.key?('colors')
|
117
|
+
|
118
|
+
cw.component = parse_child(cw, child)
|
119
|
+
}
|
120
|
+
end
|
121
|
+
end # class Loader
|
122
|
+
|
123
|
+
def load_reterm(str)
|
124
|
+
Loader.new(str).window
|
125
|
+
end
|
126
|
+
end # module RETerm
|
data/lib/reterm/menu.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
module RETerm
|
2
|
+
# Graphical menu rendered on screen
|
3
|
+
#
|
4
|
+
# TODO: look into working this into std component/activation subsystem
|
5
|
+
class Menu
|
6
|
+
include EventDispatcher
|
7
|
+
|
8
|
+
attr_accessor :window
|
9
|
+
|
10
|
+
# Menus should be instantiated with a hash of label/value
|
11
|
+
# pairs to assign to menu
|
12
|
+
def initialize(items={})
|
13
|
+
@items = items
|
14
|
+
@menu = Ncurses::Menu.new_menu(items.collect { |k,v|
|
15
|
+
Ncurses::Menu.new_item(k.to_s, v.to_s)
|
16
|
+
})
|
17
|
+
|
18
|
+
@menu.menu_opts_off(Ncurses::Menu::O_SHOWDESC)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Return number of rows in menu
|
22
|
+
def rows
|
23
|
+
@items.size
|
24
|
+
end
|
25
|
+
|
26
|
+
# Return number of cols in menu
|
27
|
+
def cols
|
28
|
+
@cols ||= @items.keys.max { |k| k.size }.size + 3
|
29
|
+
end
|
30
|
+
|
31
|
+
# Attach menu to specified window
|
32
|
+
def attach_to(win)
|
33
|
+
@menu.set_menu_win(win.win)
|
34
|
+
win.component = self
|
35
|
+
|
36
|
+
@child_win = win.create_child :rows => rows,
|
37
|
+
:cols => cols,
|
38
|
+
:x => 1, :y => 1
|
39
|
+
@menu.set_menu_sub(@child_win.win)
|
40
|
+
@menu.post_menu
|
41
|
+
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
# Destroy menu subsystem
|
46
|
+
def detach
|
47
|
+
@menu.unpost_menu
|
48
|
+
end
|
49
|
+
|
50
|
+
def finalize!
|
51
|
+
end
|
52
|
+
|
53
|
+
# Static map of menu drivers to internal representations.
|
54
|
+
#
|
55
|
+
# XXX defined here (as opposed to a const) since these
|
56
|
+
# consts won't be available until after init_scr
|
57
|
+
def driver_map
|
58
|
+
@driver_map ||= {
|
59
|
+
:left => Ncurses::Menu::REQ_LEFT_ITEM,
|
60
|
+
:right => Ncurses::Menu::REQ_RIGHT_ITEM,
|
61
|
+
:up => Ncurses::Menu::REQ_UP_ITEM,
|
62
|
+
:down => Ncurses::Menu::REQ_DOWN_ITEM,
|
63
|
+
:scr_up_line => Ncurses::Menu::REQ_SCR_ULINE,
|
64
|
+
:scr_down_line => Ncurses::Menu::REQ_SCR_DLINE,
|
65
|
+
:scr_down_page => Ncurses::Menu::REQ_SCR_DPAGE,
|
66
|
+
:scr_up_page => Ncurses::Menu::REQ_SCR_UPAGE,
|
67
|
+
:first => Ncurses::Menu::REQ_FIRST_ITEM,
|
68
|
+
:last => Ncurses::Menu::REQ_LAST_ITEM,
|
69
|
+
:next => Ncurses::Menu::REQ_NEXT_ITEM,
|
70
|
+
:prev => Ncurses::Menu::REQ_PREV_ITEM,
|
71
|
+
:toggle => Ncurses::Menu::REQ_TOGGLE_ITEM
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
# Drive menu from input
|
76
|
+
def drive(i)
|
77
|
+
i = driver_map[i] if i.is_a?(Symbol)
|
78
|
+
Ncurses::Menu.menu_driver(@menu, i)
|
79
|
+
end
|
80
|
+
end # class Menu
|
81
|
+
end # module RETerm
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module RETerm
|
2
|
+
# Mixin used by CDK based component defining cdk-specific helpers
|
3
|
+
module CDKComponent
|
4
|
+
# Should be implemented in subclass to initialize component
|
5
|
+
def _component
|
6
|
+
raise "NotImplemented"
|
7
|
+
end
|
8
|
+
|
9
|
+
# Return completely initialized CDK component
|
10
|
+
def component
|
11
|
+
enable_cdk!
|
12
|
+
@component ||= begin
|
13
|
+
c = _component
|
14
|
+
c.setBackgroundColor("</#{@colors.id}>") if colored?
|
15
|
+
c
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Assign {ColorPair} to component
|
20
|
+
def colors=(c)
|
21
|
+
super
|
22
|
+
component.setBackgroundColor("</#{@colors.id}>")
|
23
|
+
end
|
24
|
+
|
25
|
+
# Invoke CDK draw routine
|
26
|
+
def draw!
|
27
|
+
component.draw([])
|
28
|
+
end
|
29
|
+
|
30
|
+
# CDK components may be activated
|
31
|
+
def activatable?
|
32
|
+
true
|
33
|
+
end
|
34
|
+
|
35
|
+
# Invoke CDK activation routine
|
36
|
+
def activate!
|
37
|
+
component.activate([])
|
38
|
+
end
|
39
|
+
|
40
|
+
# Return stored value of cdk component
|
41
|
+
def value
|
42
|
+
component.getValue
|
43
|
+
end
|
44
|
+
end # module CDKComponent
|
45
|
+
end # module RETerm
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module RETerm
|
2
|
+
# Helper mixin defining standard input controls for custom
|
3
|
+
# components. 'In House' components included in the project
|
4
|
+
# may used this to standarding their usage.
|
5
|
+
module ComponentInput
|
6
|
+
# Key which if pressed cause the component to
|
7
|
+
# lose focus / become deactivated
|
8
|
+
QUIT_CONTROLS = [10, 27] # 10 = enter, 27 = ESC
|
9
|
+
|
10
|
+
# Keys if pressed invoked the increment operation
|
11
|
+
INC_CONTROLS = ['+'.ord, Ncurses::KEY_UP, Ncurses::KEY_RIGHT]
|
12
|
+
|
13
|
+
# Keys if pressed invoked the decrement operation
|
14
|
+
DEC_CONTROLS = ['-'.ord, Ncurses::KEY_DOWN, Ncurses::KEY_LEFT]
|
15
|
+
|
16
|
+
# May be overrideen in subclass, invoked when the user requests
|
17
|
+
# an 'increment'
|
18
|
+
def on_inc
|
19
|
+
end
|
20
|
+
|
21
|
+
# May be overrideen in subclass, invoked when the user
|
22
|
+
# requests a decrement
|
23
|
+
def on_dec
|
24
|
+
end
|
25
|
+
|
26
|
+
# Helper to be internally invoked by component on activation
|
27
|
+
def handle_input
|
28
|
+
while(!QUIT_CONTROLS.include?((ch = window.getch)))
|
29
|
+
if INC_CONTROLS.include?(ch)
|
30
|
+
on_inc
|
31
|
+
elsif DEC_CONTROLS.include?(ch)
|
32
|
+
on_dec
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end # module ComponentInput
|
37
|
+
end # module RETerm
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module RETerm
|
2
|
+
# Helper mixin defining methods to register event
|
3
|
+
# handlers and dispatch events. Events are simply
|
4
|
+
# composed parameters which are passed to the
|
5
|
+
# callback blocks when dispatched
|
6
|
+
module EventDispatcher
|
7
|
+
def dispatch(e, h={})
|
8
|
+
@event_handlers ||= {}
|
9
|
+
@event_handlers[e].each { |eh|
|
10
|
+
eh.call h
|
11
|
+
} if @event_handlers.key?(e)
|
12
|
+
end
|
13
|
+
|
14
|
+
def handle(e, &bl)
|
15
|
+
@event_handlers ||= {}
|
16
|
+
@event_handlers[e] ||= []
|
17
|
+
@event_handlers[e] << bl
|
18
|
+
end
|
19
|
+
end # module EventDispatcher
|
20
|
+
end # module RETerm
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module RETerm
|
2
|
+
# Helper mixin defining standard navigation controls.
|
3
|
+
# Used by layouts and top level components this tightly
|
4
|
+
# defines keyboard navigation and allows the user to
|
5
|
+
# seemlessly move between and activate/decativate
|
6
|
+
# components.
|
7
|
+
module NavInput
|
8
|
+
# Key which if pressed causes the navigation component
|
9
|
+
# to lose focus / become deactivated
|
10
|
+
QUIT_CONTROLS = [27, 'q'.ord, 'Q'.ord] # 27 = ESC
|
11
|
+
|
12
|
+
# Key if pressed focuses on / activates a component
|
13
|
+
ENTER_CONTROLS = [10] # 10 = enter , space
|
14
|
+
|
15
|
+
# Up navigation keys
|
16
|
+
UP_CONTROLS = ['k'.ord, 'K'.ord, Ncurses::KEY_UP]
|
17
|
+
|
18
|
+
# Down navigation keys
|
19
|
+
DOWN_CONTROLS = ['j'.ord, 'J'.ord, Ncurses::KEY_DOWN]
|
20
|
+
|
21
|
+
# Left navigation keys
|
22
|
+
LEFT_CONTROLS = ['h'.ord, 'H'.ord, Ncurses::KEY_BACKSPACE,
|
23
|
+
Ncurses::KEY_BTAB,
|
24
|
+
Ncurses::KEY_LEFT]
|
25
|
+
|
26
|
+
# Right navigation keys
|
27
|
+
RIGHT_CONTROLS = ['l'.ord, 'L'.ord, "\t".ord, Ncurses::KEY_RIGHT]
|
28
|
+
|
29
|
+
# Return children which are focusabled/activable
|
30
|
+
def focusable
|
31
|
+
children.select { |c| c.component.activatable? }
|
32
|
+
end
|
33
|
+
|
34
|
+
# Return boolean indicating if any children are focusable
|
35
|
+
def focusable?
|
36
|
+
!focusable.empty?
|
37
|
+
end
|
38
|
+
|
39
|
+
# Helper to be internally invoked by navigation component
|
40
|
+
# on activation
|
41
|
+
def handle_input(from_parent=false)
|
42
|
+
@focus ||= 0
|
43
|
+
|
44
|
+
ch = handle_focused
|
45
|
+
|
46
|
+
while(!QUIT_CONTROLS.include?(ch))
|
47
|
+
if ENTER_CONTROLS.include?(ch)
|
48
|
+
focused.activate!
|
49
|
+
|
50
|
+
elsif UP_CONTROLS.include?(ch)
|
51
|
+
focused.no_border!
|
52
|
+
|
53
|
+
return ch if window.component.is_a?(Layouts::Horizontal) &&
|
54
|
+
from_parent &&
|
55
|
+
!window.parent.children.index(window) != 0
|
56
|
+
|
57
|
+
@focus -= 1
|
58
|
+
|
59
|
+
elsif LEFT_CONTROLS.include?(ch)
|
60
|
+
focused.no_border!
|
61
|
+
|
62
|
+
return ch if window.component.is_a?(Layouts::Vertical) &&
|
63
|
+
from_parent &&
|
64
|
+
!window.parent.children.index(window) != 0
|
65
|
+
|
66
|
+
@focus -= 1
|
67
|
+
|
68
|
+
elsif DOWN_CONTROLS.include?(ch)
|
69
|
+
focused.no_border!
|
70
|
+
|
71
|
+
return ch if window.component.is_a?(Layouts::Horizontal) &&
|
72
|
+
from_parent &&
|
73
|
+
!window.parent.children.index(window) != (window.parent.children.size - 1)
|
74
|
+
|
75
|
+
@focus += 1
|
76
|
+
|
77
|
+
elsif RIGHT_CONTROLS.include?(ch)
|
78
|
+
focused.no_border!
|
79
|
+
|
80
|
+
return ch if window.component.is_a?(Layouts::Vertical) &&
|
81
|
+
from_parent &&
|
82
|
+
!window.parent.children.index(window) != (window.parent.children.size - 1)
|
83
|
+
|
84
|
+
@focus += 1
|
85
|
+
end
|
86
|
+
|
87
|
+
if @focus >= focusable.size
|
88
|
+
@focus = focusable.size - 1
|
89
|
+
return ch if from_parent
|
90
|
+
end
|
91
|
+
|
92
|
+
if @focus < 0
|
93
|
+
@focus = 0
|
94
|
+
return ch if from_parent
|
95
|
+
end
|
96
|
+
|
97
|
+
ch = handle_focused
|
98
|
+
end
|
99
|
+
|
100
|
+
ch
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def focused
|
106
|
+
focusable[@focus]
|
107
|
+
end
|
108
|
+
|
109
|
+
def handle_focused
|
110
|
+
ch = nil
|
111
|
+
|
112
|
+
focused.border! unless focused.component.kind_of?(Layout)
|
113
|
+
update_reterm
|
114
|
+
window.draw!
|
115
|
+
|
116
|
+
if focused.component.kind_of?(Layout)
|
117
|
+
ch = focused.component.handle_input(true)
|
118
|
+
|
119
|
+
else
|
120
|
+
ch = window.getch
|
121
|
+
end
|
122
|
+
|
123
|
+
ch
|
124
|
+
end
|
125
|
+
end # module NavInput
|
126
|
+
end # module RETerm
|