reterm 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|