termkit 0.0.0 → 0.1.0
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 +4 -4
- data/.ackrc +7 -0
- data/.editorconfig +14 -0
- data/.gitignore +5 -1
- data/.gitlab-ci.yml +70 -0
- data/.rdoc_options +21 -0
- data/.travis.yml +5 -1
- data/Gemfile +8 -0
- data/Makefile +16 -1
- data/Makefile.common +5 -3
- data/README.md +2 -1
- data/bin/build_info.sh +22 -0
- data/bin/dev +10 -0
- data/lib/termkit.rb +32 -0
- data/lib/termkit/app/app.rb +61 -0
- data/lib/termkit/app/app_curses.rb +159 -0
- data/lib/termkit/app/app_ui.rb +153 -0
- data/lib/termkit/controller/controller.rb +58 -0
- data/lib/termkit/controller/controller_app.rb +22 -0
- data/lib/termkit/controller/controller_view.rb +28 -0
- data/lib/termkit/event/event.rb +14 -0
- data/lib/termkit/event/event_key.rb +33 -0
- data/lib/termkit/event/event_later.rb +16 -0
- data/lib/termkit/exception/exception_event_key_unhandled.rb +11 -0
- data/lib/termkit/exception/exception_event_unhandled.rb +18 -0
- data/lib/termkit/exception/exception_initialized_not_class_parent.rb +17 -0
- data/lib/termkit/exception/exception_std.rb +11 -0
- data/lib/termkit/misc/curses_color.rb +23 -0
- data/lib/termkit/misc/point.rb +114 -0
- data/lib/termkit/misc/rect.rb +116 -0
- data/lib/termkit/misc/size.rb +29 -0
- data/lib/termkit/model/model.rb +16 -0
- data/lib/termkit/version.rb +5 -4
- data/lib/termkit/view/content_view.rb +59 -0
- data/lib/termkit/view/content_view_clear.rb +20 -0
- data/lib/termkit/view/row_grid_view.rb +12 -0
- data/lib/termkit/view/view.rb +825 -0
- data/lib/termkit/view/view_grid.rb +12 -0
- data/lib/termkit/view/view_table.rb +296 -0
- data/lib/termkit/view/view_table_cell.rb +38 -0
- data/lib/termkit/view/view_text.rb +63 -0
- data/termkit.gemspec +8 -0
- data/termkit.sublime-project +2 -2
- metadata +120 -5
- data/tests/ts_all.rb +0 -1
@@ -0,0 +1,153 @@
|
|
1
|
+
|
2
|
+
module TheFox
|
3
|
+
module TermKit
|
4
|
+
|
5
|
+
class UIApp < App
|
6
|
+
|
7
|
+
attr_accessor :app_controller
|
8
|
+
attr_accessor :active_controller
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
super()
|
12
|
+
|
13
|
+
#puts 'UIApp initialize'
|
14
|
+
|
15
|
+
@render_count = 0
|
16
|
+
@app_controller = nil
|
17
|
+
@active_controller = nil
|
18
|
+
|
19
|
+
ui_init
|
20
|
+
end
|
21
|
+
|
22
|
+
##
|
23
|
+
# See App `run_cycle()` method.
|
24
|
+
def run_cycle
|
25
|
+
super()
|
26
|
+
|
27
|
+
#puts 'UIApp->run_cycle'
|
28
|
+
|
29
|
+
render
|
30
|
+
end
|
31
|
+
|
32
|
+
def set_app_controller(app_controller)
|
33
|
+
if !app_controller.is_a?(AppController)
|
34
|
+
raise ArgumentError, "Argument is not a AppController -- #{app_controller.class} given"
|
35
|
+
end
|
36
|
+
|
37
|
+
@app_controller = app_controller
|
38
|
+
end
|
39
|
+
|
40
|
+
def set_active_controller(active_controller)
|
41
|
+
if !active_controller.is_a?(ViewController)
|
42
|
+
raise ArgumentError, "Argument is not a ViewController -- #{active_controller.class} given"
|
43
|
+
end
|
44
|
+
|
45
|
+
if !@active_controller.nil?
|
46
|
+
@active_controller.inactive
|
47
|
+
end
|
48
|
+
|
49
|
+
@active_controller = active_controller
|
50
|
+
@active_controller.active
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# Handles the actual rendering and drawing of the UI layer. Calls `draw_point()` for all points of `@active_controller`.
|
55
|
+
def render
|
56
|
+
#sleep 1 # @TODO: remove this line
|
57
|
+
|
58
|
+
area = nil # @TODO: use current terminal size as area
|
59
|
+
|
60
|
+
@render_count += 1
|
61
|
+
# @logger.debug("--- RENDER: #{@render_count} ---")
|
62
|
+
if !@active_controller.nil?
|
63
|
+
# @logger.debug("RENDER active_controller OK: #{@active_controller.inspect}")
|
64
|
+
# @logger.debug("RENDER active_controller view grid_cache: #{@active_controller.view.grid_cache.inspect}")
|
65
|
+
|
66
|
+
@active_controller.render(area).each do |y_pos, row|
|
67
|
+
row.each do |x_pos, content|
|
68
|
+
#sleep 0.1 # @TODO: remove this line
|
69
|
+
|
70
|
+
# @logger.debug("RENDER #{x_pos}:#{y_pos} '#{content}'")
|
71
|
+
|
72
|
+
draw_point(Point.new(x_pos, y_pos), content)
|
73
|
+
|
74
|
+
#ui_refresh # @TODO: remove this line
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
ui_refresh
|
79
|
+
end
|
80
|
+
|
81
|
+
def draw_line(point, row)
|
82
|
+
x_pos = point.x
|
83
|
+
y_pos = point.y
|
84
|
+
|
85
|
+
row.length.times do |n|
|
86
|
+
draw_point(Point.new(x_pos, y_pos), ViewContent.new(row[n]))
|
87
|
+
x_pos += 1
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
##
|
92
|
+
# Needs to be implemented by the sub-class.
|
93
|
+
#
|
94
|
+
# For example, CursesApp is a sub-class of UIApp. CursesApp uses `Curses.setpos` and `Curses.addstr` in `draw_point()` to draw the points.
|
95
|
+
def draw_point(point, content)
|
96
|
+
raise NotImplementedError
|
97
|
+
end
|
98
|
+
|
99
|
+
def ui_refresh
|
100
|
+
raise NotImplementedError
|
101
|
+
end
|
102
|
+
|
103
|
+
def ui_max_x
|
104
|
+
-1
|
105
|
+
end
|
106
|
+
|
107
|
+
def ui_max_y
|
108
|
+
-1
|
109
|
+
end
|
110
|
+
|
111
|
+
protected
|
112
|
+
|
113
|
+
def app_will_terminate
|
114
|
+
#puts 'UIApp app_will_terminate'
|
115
|
+
|
116
|
+
ui_close
|
117
|
+
end
|
118
|
+
|
119
|
+
def ui_init
|
120
|
+
# raise NotImplementedError
|
121
|
+
end
|
122
|
+
|
123
|
+
def ui_close
|
124
|
+
# raise NotImplementedError
|
125
|
+
end
|
126
|
+
|
127
|
+
def key_down(key)
|
128
|
+
if !key.nil? && !@active_controller.nil?
|
129
|
+
event = KeyEvent.new
|
130
|
+
event.key = key
|
131
|
+
|
132
|
+
begin
|
133
|
+
@active_controller.handle_event(event)
|
134
|
+
rescue Exception::UnhandledKeyEventException => e
|
135
|
+
@logger.warn("#{self.class} UnhandledKeyEventException: #{e}")
|
136
|
+
|
137
|
+
if @app_controller.nil?
|
138
|
+
@logger.warn("#{self.class} UnhandledKeyEventException: no app controller set, raise")
|
139
|
+
|
140
|
+
raise e
|
141
|
+
end
|
142
|
+
|
143
|
+
@app_controller.handle_event(e.event)
|
144
|
+
rescue Exception::UnhandledEventException => e
|
145
|
+
@logger.warn("#{self.class} UnhandledEventException: #{e}")
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
|
2
|
+
module TheFox
|
3
|
+
module TermKit
|
4
|
+
|
5
|
+
class Controller
|
6
|
+
|
7
|
+
attr_reader :subcontrollers
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
#puts 'Controller initialize'
|
11
|
+
|
12
|
+
@is_active = false
|
13
|
+
@subcontrollers = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def active
|
17
|
+
@is_active = true
|
18
|
+
|
19
|
+
@subcontrollers.each do |subcontroller|
|
20
|
+
subcontroller.active
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def inactive
|
25
|
+
@is_active = false
|
26
|
+
|
27
|
+
@subcontrollers.each do |subcontroller|
|
28
|
+
subcontroller.inactive
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def is_active?
|
33
|
+
@is_active
|
34
|
+
end
|
35
|
+
|
36
|
+
def handle_event(event)
|
37
|
+
#puts "Controller handle_event: #{event.class}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def add_subcontroller(subcontroller)
|
41
|
+
if !subcontroller.is_a?(Controller)
|
42
|
+
raise ArgumentError, "Argument is not a Controller -- #{subcontroller.class} given"
|
43
|
+
end
|
44
|
+
if !@subcontrollers.is_a?(Array)
|
45
|
+
raise Exception::ParentClassNotInitializedException, "@subcontrollers is not an Array -- #{@subcontrollers.class} given"
|
46
|
+
end
|
47
|
+
|
48
|
+
@subcontrollers.push(subcontroller)
|
49
|
+
end
|
50
|
+
|
51
|
+
def remove_subcontroller(subcontroller)
|
52
|
+
@subcontrollers.delete(subcontroller)
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
module TheFox
|
3
|
+
module TermKit
|
4
|
+
|
5
|
+
##
|
6
|
+
# Controller sub-class.
|
7
|
+
class AppController < Controller
|
8
|
+
|
9
|
+
def initialize(app)
|
10
|
+
if !app.is_a?(App)
|
11
|
+
raise ArgumentError, "Argument is not a App -- #{app.class} given"
|
12
|
+
end
|
13
|
+
|
14
|
+
super()
|
15
|
+
|
16
|
+
@app = app
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
|
2
|
+
module TheFox
|
3
|
+
module TermKit
|
4
|
+
|
5
|
+
class ViewController < Controller
|
6
|
+
|
7
|
+
attr_accessor :app
|
8
|
+
attr_accessor :view
|
9
|
+
|
10
|
+
def initialize(view = nil)
|
11
|
+
if !view.nil? && !view.is_a?(View)
|
12
|
+
raise ArgumentError, "Argument is not a View -- #{view.class} given"
|
13
|
+
end
|
14
|
+
|
15
|
+
super()
|
16
|
+
|
17
|
+
@app = nil
|
18
|
+
@view = view
|
19
|
+
end
|
20
|
+
|
21
|
+
def render(area = nil)
|
22
|
+
@view.render(area)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
|
2
|
+
module TheFox
|
3
|
+
module TermKit
|
4
|
+
|
5
|
+
##
|
6
|
+
# Typically used to handle user keystrokes.
|
7
|
+
class KeyEvent
|
8
|
+
|
9
|
+
attr_accessor :key
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
super()
|
13
|
+
|
14
|
+
@key = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
@key.to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
def inspect
|
22
|
+
s = "#<#{self.class}"
|
23
|
+
unless @key.nil?
|
24
|
+
s << "->#{@key.ord}[#{@key}]"
|
25
|
+
end
|
26
|
+
s << '>'
|
27
|
+
s
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
module TheFox
|
3
|
+
module TermKit
|
4
|
+
module Exception
|
5
|
+
|
6
|
+
class ParentClassNotInitializedException < StdException
|
7
|
+
|
8
|
+
def initialize(msg = nil)
|
9
|
+
msg << ' -- Forgot to call super() for parent initialization?'
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
require 'curses'
|
3
|
+
|
4
|
+
module TheFox
|
5
|
+
module TermKit
|
6
|
+
|
7
|
+
class CursesColor
|
8
|
+
|
9
|
+
COLORS = {
|
10
|
+
:color_black => Curses::COLOR_BLACK,
|
11
|
+
:color_blue => Curses::COLOR_BLUE,
|
12
|
+
:color_cyan => Curses::COLOR_CYAN,
|
13
|
+
:color_green => Curses::COLOR_GREEN,
|
14
|
+
:color_magenta => Curses::COLOR_MAGENTA,
|
15
|
+
:color_red => Curses::COLOR_RED,
|
16
|
+
:color_white => Curses::COLOR_WHITE,
|
17
|
+
:color_yellow => Curses::COLOR_YELLOW,
|
18
|
+
}
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
|
2
|
+
module TheFox
|
3
|
+
module TermKit
|
4
|
+
|
5
|
+
##
|
6
|
+
# A single point on a x-y-grid.
|
7
|
+
class Point
|
8
|
+
|
9
|
+
attr_accessor :x
|
10
|
+
attr_accessor :y
|
11
|
+
|
12
|
+
def initialize(x = nil, y = nil)
|
13
|
+
case x
|
14
|
+
when Array
|
15
|
+
y = x[1]
|
16
|
+
x = x[0]
|
17
|
+
when Hash
|
18
|
+
y = if x['y']
|
19
|
+
x['y']
|
20
|
+
elsif x[:y]
|
21
|
+
x[:y]
|
22
|
+
end
|
23
|
+
|
24
|
+
x = if x['x']
|
25
|
+
x['x']
|
26
|
+
elsif x[:x]
|
27
|
+
x[:x]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
@x = x
|
32
|
+
@y = y
|
33
|
+
end
|
34
|
+
|
35
|
+
def ==(point)
|
36
|
+
# puts "Point == compare"
|
37
|
+
@x == point.x && @y == point.y
|
38
|
+
end
|
39
|
+
|
40
|
+
# def ===(point)
|
41
|
+
# puts "Point === compare"
|
42
|
+
# #@x == point.x && @y == point.y
|
43
|
+
# false
|
44
|
+
# end
|
45
|
+
|
46
|
+
# def eql?(point)
|
47
|
+
# puts "Point eql? compare"
|
48
|
+
# false
|
49
|
+
# end
|
50
|
+
|
51
|
+
def +(point)
|
52
|
+
x = nil
|
53
|
+
y = nil
|
54
|
+
|
55
|
+
if !@x.nil? || !point.x.nil?
|
56
|
+
x = @x.to_i + point.x.to_i
|
57
|
+
end
|
58
|
+
|
59
|
+
if !@y.nil? || !point.y.nil?
|
60
|
+
y = @y.to_i + point.y.to_i
|
61
|
+
end
|
62
|
+
|
63
|
+
self.class.new(x, y)
|
64
|
+
end
|
65
|
+
|
66
|
+
def -(point)
|
67
|
+
x = nil
|
68
|
+
y = nil
|
69
|
+
|
70
|
+
if !@x.nil? || !point.x.nil?
|
71
|
+
x = @x.to_i - point.x.to_i
|
72
|
+
end
|
73
|
+
|
74
|
+
if !@y.nil? || !point.y.nil?
|
75
|
+
y = @y.to_i - point.y.to_i
|
76
|
+
end
|
77
|
+
|
78
|
+
self.class.new(x, y)
|
79
|
+
end
|
80
|
+
|
81
|
+
def to_s
|
82
|
+
"#{@x}:#{@y}"
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_a
|
86
|
+
[@x, @y]
|
87
|
+
end
|
88
|
+
|
89
|
+
def inspect
|
90
|
+
x_s = x.nil? ? 'NIL' : x
|
91
|
+
y_s = y.nil? ? 'NIL' : y
|
92
|
+
|
93
|
+
"#<Point x=#{x_s} y=#{y_s}>"
|
94
|
+
end
|
95
|
+
|
96
|
+
def missing_function
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.from_s(s)
|
101
|
+
x, y =
|
102
|
+
s
|
103
|
+
.split(/[:,]/, 2)
|
104
|
+
.map{ |pos|
|
105
|
+
pos.nil? || pos == '' ? nil : pos.to_i
|
106
|
+
}
|
107
|
+
|
108
|
+
new(x, y)
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|