termgui 0.0.4 → 0.0.5
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/lib/termgui.rb +1 -1
- metadata +40 -107
- data/Gemfile +0 -14
- data/README.md +0 -321
- data/src/action.rb +0 -58
- data/src/box.rb +0 -90
- data/src/color.rb +0 -174
- data/src/cursor.rb +0 -69
- data/src/editor/editor_base.rb +0 -152
- data/src/editor/editor_base_handlers.rb +0 -116
- data/src/element.rb +0 -61
- data/src/element_bounds.rb +0 -111
- data/src/element_box.rb +0 -64
- data/src/element_render.rb +0 -102
- data/src/element_style.rb +0 -51
- data/src/emitter.rb +0 -102
- data/src/emitter_state.rb +0 -19
- data/src/enterable.rb +0 -93
- data/src/event.rb +0 -92
- data/src/focus.rb +0 -102
- data/src/geometry.rb +0 -53
- data/src/image.rb +0 -60
- data/src/input.rb +0 -85
- data/src/input_grab.rb +0 -17
- data/src/input_time.rb +0 -97
- data/src/key.rb +0 -114
- data/src/log.rb +0 -24
- data/src/node.rb +0 -117
- data/src/node_attributes.rb +0 -27
- data/src/node_visit.rb +0 -52
- data/src/renderer.rb +0 -119
- data/src/renderer_cursor.rb +0 -18
- data/src/renderer_draw.rb +0 -28
- data/src/renderer_image.rb +0 -31
- data/src/renderer_print.rb +0 -40
- data/src/screen.rb +0 -96
- data/src/screen_element.rb +0 -59
- data/src/screen_input.rb +0 -43
- data/src/screen_renderer.rb +0 -53
- data/src/style.rb +0 -149
- data/src/tco/colouring.rb +0 -248
- data/src/tco/config.rb +0 -57
- data/src/tco/palette.rb +0 -603
- data/src/tco/tco_termgui.rb +0 -30
- data/src/termgui.rb +0 -29
- data/src/util.rb +0 -110
- data/src/util/css.rb +0 -98
- data/src/util/css_query.rb +0 -23
- data/src/util/easing.rb +0 -364
- data/src/util/hash_object.rb +0 -131
- data/src/util/imagemagick.rb +0 -27
- data/src/util/justify.rb +0 -20
- data/src/util/unicode-categories.rb +0 -572
- data/src/util/wrap.rb +0 -102
- data/src/widget/button.rb +0 -33
- data/src/widget/checkbox.rb +0 -47
- data/src/widget/col.rb +0 -30
- data/src/widget/image.rb +0 -106
- data/src/widget/inline.rb +0 -40
- data/src/widget/input_number.rb +0 -73
- data/src/widget/inputbox.rb +0 -85
- data/src/widget/label.rb +0 -33
- data/src/widget/modal.rb +0 -69
- data/src/widget/row.rb +0 -26
- data/src/widget/selectbox.rb +0 -100
- data/src/widget/textarea.rb +0 -54
- data/src/xml/xml.rb +0 -80
- data/test/action_test.rb +0 -34
- data/test/box_test.rb +0 -15
- data/test/css_test.rb +0 -39
- data/test/editor/editor_base_test.rb +0 -201
- data/test/element_bounds_test.rb +0 -77
- data/test/element_box_test.rb +0 -8
- data/test/element_render_test.rb +0 -124
- data/test/element_style_test.rb +0 -85
- data/test/element_test.rb +0 -10
- data/test/emitter_test.rb +0 -108
- data/test/event_test.rb +0 -19
- data/test/focus_test.rb +0 -37
- data/test/geometry_test.rb +0 -12
- data/test/input_test.rb +0 -47
- data/test/key_test.rb +0 -14
- data/test/log_test.rb +0 -21
- data/test/node_test.rb +0 -105
- data/test/performance/performance1.rb +0 -48
- data/test/renderer_test.rb +0 -74
- data/test/renderer_test_rect.rb +0 -4
- data/test/screen_test.rb +0 -58
- data/test/style_test.rb +0 -18
- data/test/termgui_test.rb +0 -10
- data/test/test_all.rb +0 -30
- data/test/util_hash_object_test.rb +0 -93
- data/test/util_test.rb +0 -26
- data/test/widget/checkbox_test.rb +0 -99
- data/test/widget/col_test.rb +0 -87
- data/test/widget/inline_test.rb +0 -40
- data/test/widget/label_test.rb +0 -94
- data/test/widget/row_test.rb +0 -40
- data/test/wrap_test.rb +0 -11
- data/test/xml_test.rb +0 -77
data/src/node.rb
DELETED
@@ -1,117 +0,0 @@
|
|
1
|
-
require_relative 'util'
|
2
|
-
require_relative 'node_attributes'
|
3
|
-
require_relative 'node_visit'
|
4
|
-
require_relative 'emitter'
|
5
|
-
|
6
|
-
module TermGui
|
7
|
-
# analog to HTML DOM Node class
|
8
|
-
# Ways of declaring node hierarchies by using parent and children props, or append_child or append_to methods. For declarative complex structures probably you want to use children
|
9
|
-
# `main = Row.new(parent: screen, height: 0.5, children: [Button.new(text: 'clickme', Col.new(width: 0.8, x: 0.2, children: [Label.new(text: 'hello')]))])`
|
10
|
-
# or
|
11
|
-
# `main = screen.append_child(Row.new(height: 0.5, children: [Button.new(text: 'clickme', Col.new(width: 0.8, x: 0.2, children: [Label.new(text: 'hello')]))]))`
|
12
|
-
# or
|
13
|
-
# `main = Row.new(height: 0.5, children: [Button.new(text: 'clickme', Col.new(width: 0.8, x: 0.2, children: [Label.new(text: 'hello')]))]).append_to(screen)`
|
14
|
-
class Node < Emitter
|
15
|
-
include NodeVisit
|
16
|
-
|
17
|
-
attr_reader :children, :text, :parent, :name
|
18
|
-
attr_writer :parent, :text
|
19
|
-
|
20
|
-
def initialize(**args)
|
21
|
-
@name = args[:name] || 'node'
|
22
|
-
@attributes = Attributes.new args[:attributes] || {}
|
23
|
-
@children = args[:children] || []
|
24
|
-
children.each { |child| child.parent = self }
|
25
|
-
@text = args[:text] || ''
|
26
|
-
args[:parent]&.append_children(self)
|
27
|
-
install(%i[after_render before_render])
|
28
|
-
end
|
29
|
-
|
30
|
-
# returns child so we can write: `button = screen.append_child Row.new(text: 'click me')`
|
31
|
-
def append_children(*children)
|
32
|
-
children.each do |child|
|
33
|
-
@children.push(child)
|
34
|
-
child.parent = self
|
35
|
-
end
|
36
|
-
children
|
37
|
-
end
|
38
|
-
|
39
|
-
def append_child(child)
|
40
|
-
(append_children child)[0]
|
41
|
-
end
|
42
|
-
|
43
|
-
def insert_children(index = 0, *children)
|
44
|
-
@children.insert(index, *children)
|
45
|
-
children
|
46
|
-
end
|
47
|
-
|
48
|
-
def prepend_child(child)
|
49
|
-
insert_child(0, child)
|
50
|
-
end
|
51
|
-
|
52
|
-
def insert_child(index, child)
|
53
|
-
(insert_children index, child)[0]
|
54
|
-
end
|
55
|
-
|
56
|
-
def remove_children(*children)
|
57
|
-
children.each do |child|
|
58
|
-
@children.delete(child)
|
59
|
-
child.parent = self
|
60
|
-
end
|
61
|
-
children
|
62
|
-
end
|
63
|
-
|
64
|
-
def remove_child(child)
|
65
|
-
(remove_children child)[0]
|
66
|
-
end
|
67
|
-
|
68
|
-
def append_to(parent)
|
69
|
-
parent.append_child(self)
|
70
|
-
self
|
71
|
-
end
|
72
|
-
|
73
|
-
def remove
|
74
|
-
parent&.remove_child(self)
|
75
|
-
self.parent = nil
|
76
|
-
end
|
77
|
-
|
78
|
-
def empty
|
79
|
-
children.each do |child|
|
80
|
-
child.parent = nil
|
81
|
-
end
|
82
|
-
@children = []
|
83
|
-
self
|
84
|
-
end
|
85
|
-
|
86
|
-
def attributes(attrs = nil)
|
87
|
-
attrs&.each_key { |key| set_attribute(key.to_s, attrs[key]) }
|
88
|
-
@attributes
|
89
|
-
end
|
90
|
-
|
91
|
-
def attributes=(attrs = nil)
|
92
|
-
attributes(attrs)
|
93
|
-
end
|
94
|
-
|
95
|
-
def set_attribute(name, value)
|
96
|
-
@attributes.set_attribute(name, value)
|
97
|
-
self
|
98
|
-
end
|
99
|
-
|
100
|
-
def get_attribute(name)
|
101
|
-
@attributes.get_attribute(name)
|
102
|
-
end
|
103
|
-
|
104
|
-
def to_s
|
105
|
-
"Node(name: #{name}, children: [#{children.map(&:to_s).join(', ')}])"
|
106
|
-
end
|
107
|
-
|
108
|
-
def pretty_print(d = 0)
|
109
|
-
"#{(' ' * d)}<#{name} #{(attributes.pairs.map { |p| "#{p[:name]}=#{pretty_print_attribute p[:value]}" }).join(' ')}>\n#{' ' * (d + 1)}#{text ? " #{text}\n#{' ' * d}" : ''}#{children.map { |c| c.pretty_print d + 1 }.join("\n" + (' ' * d))}\n#{(' ' * (d + 1))}</#{name}>"
|
110
|
-
end
|
111
|
-
|
112
|
-
def pretty_print_attribute(a)
|
113
|
-
a.respond_to?(:pretty_print) ? a.pretty_print : a.to_s
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
Node = TermGui::Node
|
data/src/node_attributes.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
# Manages Node's attributes
|
2
|
-
class Attributes
|
3
|
-
def initialize(attrs = {})
|
4
|
-
@attrs = attrs
|
5
|
-
end
|
6
|
-
|
7
|
-
def names
|
8
|
-
@attrs.keys
|
9
|
-
end
|
10
|
-
|
11
|
-
def pairs
|
12
|
-
@attrs.keys.map { |n| { name: n, value: @attrs[n] } }
|
13
|
-
end
|
14
|
-
|
15
|
-
def set_attribute(name, value)
|
16
|
-
@attrs[name.to_sym] = value
|
17
|
-
self
|
18
|
-
end
|
19
|
-
|
20
|
-
def get_attribute(name)
|
21
|
-
@attrs[name.to_sym]
|
22
|
-
end
|
23
|
-
|
24
|
-
def to_s
|
25
|
-
@attrs.to_s
|
26
|
-
end
|
27
|
-
end
|
data/src/node_visit.rb
DELETED
@@ -1,52 +0,0 @@
|
|
1
|
-
# adds node recirsive visit support and node query operations
|
2
|
-
module NodeVisit
|
3
|
-
def query_by_attribute(attr, value)
|
4
|
-
result = []
|
5
|
-
visit_node(self, proc { |n|
|
6
|
-
result.push n if n.attributes.get_attribute(attr) == value
|
7
|
-
false
|
8
|
-
})
|
9
|
-
result
|
10
|
-
end
|
11
|
-
|
12
|
-
def query_by_name(name)
|
13
|
-
result = []
|
14
|
-
visit_node(self, proc { |n|
|
15
|
-
result.push n if n.name == name
|
16
|
-
false
|
17
|
-
})
|
18
|
-
result
|
19
|
-
end
|
20
|
-
|
21
|
-
def query_one_by_attribute(attr, value)
|
22
|
-
result = nil
|
23
|
-
p = proc do |n|
|
24
|
-
if n.attributes.get_attribute(attr) == value
|
25
|
-
result = n
|
26
|
-
true
|
27
|
-
else
|
28
|
-
false
|
29
|
-
end
|
30
|
-
end
|
31
|
-
visit_node(self, p)
|
32
|
-
result
|
33
|
-
end
|
34
|
-
|
35
|
-
def visit(visitor, children_first = true)
|
36
|
-
visit_node(self, visitor, children_first)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
# visit given node children bottom-up. If visitor returns truthy then visiting finishes
|
41
|
-
def visit_node(node, visitor, children_first = true)
|
42
|
-
result = nil
|
43
|
-
unless children_first
|
44
|
-
result = visitor.call node
|
45
|
-
return result if result
|
46
|
-
end
|
47
|
-
result = some(node.children, proc { |child| visit_node child, visitor, children_first })
|
48
|
-
return result if result
|
49
|
-
|
50
|
-
result = visitor.call node if children_first
|
51
|
-
result
|
52
|
-
end
|
data/src/renderer.rb
DELETED
@@ -1,119 +0,0 @@
|
|
1
|
-
require_relative 'style'
|
2
|
-
require_relative 'key'
|
3
|
-
require_relative 'renderer_print'
|
4
|
-
require_relative 'renderer_cursor'
|
5
|
-
require_relative 'renderer_image'
|
6
|
-
require_relative 'renderer_draw'
|
7
|
-
|
8
|
-
module TermGui
|
9
|
-
# Responsible of (TODO: we should split Renderer into several delegate classes
|
10
|
-
# * build charsequences to render text on a position. these are directly write to $stdout by screen
|
11
|
-
# * maintain bitmap-like buffer of current screen state
|
12
|
-
# * manages current applied style
|
13
|
-
# TODO: add line, empty-rect and more drawing primitives
|
14
|
-
class Renderer
|
15
|
-
include RendererPrint
|
16
|
-
include RendererCursor
|
17
|
-
include RendererImage
|
18
|
-
include RendererDraw
|
19
|
-
|
20
|
-
attr_reader :width, :height, :buffer, :style
|
21
|
-
attr_writer :style, :no_buffer
|
22
|
-
|
23
|
-
def initialize(width = 80, height = 20, no_buffer = false)
|
24
|
-
@width = width
|
25
|
-
@height = height
|
26
|
-
@style = Style.new
|
27
|
-
@no_buffer = no_buffer
|
28
|
-
self.fast_colouring = true
|
29
|
-
unless no_buffer
|
30
|
-
@buffer = (0...@height).to_a.map do
|
31
|
-
(0...@width).to_a.map do
|
32
|
-
Pixel.new
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
# all writing must be done using me
|
39
|
-
def write(x, y, s, style = nil)
|
40
|
-
if y < @height && y >= 0
|
41
|
-
# TODO: x could be negative now, trunc ch to respect screen bounds - if not negative values are printed at the right
|
42
|
-
if x < 0
|
43
|
-
s = s[[x * -1, s.length].min..s.length]
|
44
|
-
x = 0
|
45
|
-
end
|
46
|
-
unless @no_buffer
|
47
|
-
(x...[x + s.length, @width].min).to_a.each do |i|
|
48
|
-
@buffer[y][i].ch = s[i - x]
|
49
|
-
@buffer[y][i].style = (style || @style).clone
|
50
|
-
end
|
51
|
-
end
|
52
|
-
# apply the style after writing to the buffer so it don't contains escape secuences just the chars
|
53
|
-
s = style == nil ? s : style.print(s)
|
54
|
-
"#{move x, y + 1}#{s}" # TODO: investigate why y + 1
|
55
|
-
else
|
56
|
-
''
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def move(x, y)
|
61
|
-
Renderer.move x, y
|
62
|
-
end
|
63
|
-
|
64
|
-
def self.move(x, y)
|
65
|
-
"#{CSI}#{y};#{x}H"
|
66
|
-
end
|
67
|
-
|
68
|
-
def text(x: 0, y: 0, text: ' ', style: nil)
|
69
|
-
write(x, y, text, style)
|
70
|
-
end
|
71
|
-
|
72
|
-
def rect(x: 0, y: 0, width: 5, height: 3, ch: Pixel.EMPTY_CH, style: nil)
|
73
|
-
s = []
|
74
|
-
ch = Pixel.EMPTY_CH if ch == nil
|
75
|
-
height.times do |y_|
|
76
|
-
s .push write(x, y + y_, ch * width, style).to_s
|
77
|
-
end
|
78
|
-
s.join('')
|
79
|
-
end
|
80
|
-
|
81
|
-
def clear
|
82
|
-
@style = Style.new
|
83
|
-
unless @no_buffer
|
84
|
-
@buffer.each_index do |y|
|
85
|
-
@buffer[y].each do |p|
|
86
|
-
p.ch = Pixel.EMPTY_CH
|
87
|
-
p.style.reset
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
"#{CSI}0m#{CSI}2J"
|
92
|
-
end
|
93
|
-
|
94
|
-
def style_assign(style)
|
95
|
-
@style.assign(style)
|
96
|
-
end
|
97
|
-
|
98
|
-
def fast_colouring=(value)
|
99
|
-
Style.fast_colouring(value)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
# Represents a pixel in renderer's buffer
|
104
|
-
class Pixel
|
105
|
-
attr_accessor :ch, :style
|
106
|
-
|
107
|
-
def self.EMPTY_CH
|
108
|
-
' '
|
109
|
-
end
|
110
|
-
|
111
|
-
def initialize(ch = Pixel.EMPTY_CH, style = Style.new)
|
112
|
-
@ch = ch
|
113
|
-
@style = style
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
Renderer = TermGui::Renderer
|
119
|
-
Pixel = TermGui::Pixel
|
data/src/renderer_cursor.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
# takes care of cursor related ansi escape sequences
|
2
|
-
module RendererCursor
|
3
|
-
def cursor_save
|
4
|
-
"#{CSI}s"
|
5
|
-
end
|
6
|
-
|
7
|
-
def cursor_restore
|
8
|
-
"#{CSI}u"
|
9
|
-
end
|
10
|
-
|
11
|
-
def cursor_show
|
12
|
-
"#{CSI}?25h"
|
13
|
-
end
|
14
|
-
|
15
|
-
def cursor_hide
|
16
|
-
"#{CSI}?25l"
|
17
|
-
end
|
18
|
-
end
|
data/src/renderer_draw.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
require 'chunky_png'
|
2
|
-
|
3
|
-
# takes care of drawing shapes. Based on Image (uses chunky_png Canvas)
|
4
|
-
# TODO: we should use TermGui::Image and not ChunkyPNG's so we add value there too
|
5
|
-
module RendererDraw
|
6
|
-
def circle(x: nil, y: nil, radius: nil, stroke_ch: ' ', stroke: nil, fill: nil, fill_ch: stroke_ch)
|
7
|
-
canvas = draw_canvas
|
8
|
-
canvas.circle(x, y, radius, ChunkyPNG::Color::BLACK, ChunkyPNG::Color::WHITE)
|
9
|
-
radius = []
|
10
|
-
canvas.height.times do |y2|
|
11
|
-
canvas.width.times do |x2|
|
12
|
-
if canvas[x2, y2] == ChunkyPNG::Color::BLACK && stroke
|
13
|
-
radius.push text(x: x2, y: y2, text: stroke_ch, style: stroke)
|
14
|
-
elsif canvas[x2, y2] == ChunkyPNG::Color::WHITE && fill
|
15
|
-
radius.push text(x: x2, y: y2, text: fill_ch, style: fill)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
radius.join
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
def draw_canvas
|
25
|
-
# TODO: reuse the canvas
|
26
|
-
ChunkyPNG::Canvas.new(width, height, ChunkyPNG::Color::TRANSPARENT)
|
27
|
-
end
|
28
|
-
end
|
data/src/renderer_image.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
require_relative 'image'
|
2
|
-
|
3
|
-
# takes care rendering given Images
|
4
|
-
module RendererImage
|
5
|
-
# renders given Image or file path at given x, y coords. Currently only PNG format supported.
|
6
|
-
# by default bg attribute is used and space is printed for each pixel but this could be configured using fg, bg, and ch
|
7
|
-
# ch can be an array of chars in which case a random one is taken for each pixel
|
8
|
-
def image(x: 0, y: 0, image: nil, ch: ' ', style: Style.new, fg: false, bg: true, h: height - y, w: width - x,
|
9
|
-
transparent_color: nil) # if a [r,g,b] color is given, then alpha channel will be considered to mix colors accordingly
|
10
|
-
output = []
|
11
|
-
image = image.is_a?(String) ? TermGui::Image.new(image) : image
|
12
|
-
(y..[y + image.height - 1, height - 1].min).to_a.each do |y2|
|
13
|
-
(x..[x + image.width - 1, width - 1].min).to_a.each do |x2|
|
14
|
-
pixel = image.rgb(x2 - x, y2 - y, transparent_color)
|
15
|
-
style.bg = pixel if bg
|
16
|
-
style.fg = pixel if fg
|
17
|
-
output .push text(x: x2, y: y2, text: ch.is_a?(Array) ? ch.sample : ch, style: style) if x2 < w && y2 < h
|
18
|
-
end
|
19
|
-
end
|
20
|
-
output.join('')
|
21
|
-
end
|
22
|
-
|
23
|
-
private
|
24
|
-
|
25
|
-
def mix_colors(fg, bg, alpha = 0.5)
|
26
|
-
r = (fg[0] * alpha + bg[0] * (1 - alpha)).to_i
|
27
|
-
g = (fg[1] * alpha + bg[1] * (1 - alpha)).to_i
|
28
|
-
b = (fg[2] * alpha + bg[2] * (1 - alpha)).to_i
|
29
|
-
[r, g, b]
|
30
|
-
end
|
31
|
-
end
|
data/src/renderer_print.rb
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
require_relative 'style'
|
2
|
-
require_relative 'key'
|
3
|
-
|
4
|
-
# takes care of printing renderer buffer in different ways
|
5
|
-
module RendererPrint
|
6
|
-
# prints current buffer as string
|
7
|
-
def print
|
8
|
-
s = []
|
9
|
-
@buffer.each_index do |y|
|
10
|
-
@buffer[y].each do |p|
|
11
|
-
s .push p.ch
|
12
|
-
end
|
13
|
-
s .push '\n'
|
14
|
-
end
|
15
|
-
s.join('')
|
16
|
-
end
|
17
|
-
|
18
|
-
def print_rows
|
19
|
-
rows = []
|
20
|
-
@buffer.each_index do |y|
|
21
|
-
line = []
|
22
|
-
@buffer[y].each do |p|
|
23
|
-
line .push p.ch
|
24
|
-
end
|
25
|
-
rows.push(line.join(''))
|
26
|
-
end
|
27
|
-
rows
|
28
|
-
end
|
29
|
-
|
30
|
-
# prints to stdout a representation in ruby string concatenated syntax so its easy for devs copy&paste for test asserts
|
31
|
-
def print_dev_stdout
|
32
|
-
print.split('\\n').each { |line| puts "'#{line}\\n' + " }
|
33
|
-
end
|
34
|
-
|
35
|
-
def print_dev
|
36
|
-
s = "'' + \n"
|
37
|
-
print.split('\\n').each { |line| s = "#{s}#{line}\\n' + \n" }
|
38
|
-
s + "''"
|
39
|
-
end
|
40
|
-
end
|
data/src/screen.rb
DELETED
@@ -1,96 +0,0 @@
|
|
1
|
-
require_relative 'element'
|
2
|
-
require_relative 'renderer'
|
3
|
-
require_relative 'input'
|
4
|
-
require_relative 'event'
|
5
|
-
require_relative 'focus'
|
6
|
-
require_relative 'action'
|
7
|
-
require_relative 'util'
|
8
|
-
require_relative 'screen_element'
|
9
|
-
require_relative 'screen_input'
|
10
|
-
require_relative 'screen_renderer'
|
11
|
-
|
12
|
-
module TermGui
|
13
|
-
# Main user API entry point
|
14
|
-
# Manages instances of Input, Event, Renderer (by default disabling its buffer)
|
15
|
-
# Is a Node so new elements can be append_child
|
16
|
-
# Once `start`is called it will block execution and start an event loop
|
17
|
-
# on each interval user input is read and event listeners are called
|
18
|
-
class Screen < Node
|
19
|
-
include ScreenElement
|
20
|
-
include ScreenInput
|
21
|
-
include ScreenRenderer
|
22
|
-
attr_reader :width, :height, :input_stream, :output_stream, :renderer, :input, :event, :focus, :action
|
23
|
-
attr_accessor :silent
|
24
|
-
|
25
|
-
def initialize(
|
26
|
-
children: [], text: '', attributes: {}, exit_keys: %w[q C-c], no_exit_keys: false,
|
27
|
-
width: nil, height: nil, silent: false
|
28
|
-
)
|
29
|
-
super(name: 'screen', children: children, text: text, attributes: attributes, parent: nil)
|
30
|
-
install(%i[destroy after_destroy start after_start])
|
31
|
-
@width = width == nil ? terminal_width : width
|
32
|
-
@height = height == nil ? terminal_height : height
|
33
|
-
@input_stream = $stdin
|
34
|
-
@silent = silent
|
35
|
-
@exit_keys = exit_keys
|
36
|
-
@output_stream = $stdout
|
37
|
-
@renderer = Renderer.new(@width, @height)
|
38
|
-
@input = Input.new
|
39
|
-
@event = EventManager.new @input
|
40
|
-
@focus = FocusManager.new(root: self, event: @event)
|
41
|
-
@action = ActionManager.new(focus: @focus, event: @event)
|
42
|
-
@renderer.no_buffer = true
|
43
|
-
install_exit_keys unless no_exit_keys
|
44
|
-
end
|
45
|
-
|
46
|
-
def self.new_for_testing(**args)
|
47
|
-
instance = new(args.merge(no_exit_keys: true, silent: true))
|
48
|
-
instance.renderer.no_buffer = false
|
49
|
-
instance
|
50
|
-
end
|
51
|
-
|
52
|
-
def terminal_width
|
53
|
-
$stdout.winsize[1]
|
54
|
-
rescue StandardError
|
55
|
-
80
|
56
|
-
end
|
57
|
-
|
58
|
-
def terminal_height
|
59
|
-
$stdout.winsize[0]
|
60
|
-
rescue StandardError
|
61
|
-
24
|
62
|
-
end
|
63
|
-
|
64
|
-
# start listening for user input. This starts an user input event loop
|
65
|
-
# that ends when screen.destroy is called
|
66
|
-
def start(clean: false)
|
67
|
-
emit :start
|
68
|
-
unless clean
|
69
|
-
clear
|
70
|
-
cursor_hide # TODO: move this to a CursorManager :start listener
|
71
|
-
render
|
72
|
-
end
|
73
|
-
emit :after_start
|
74
|
-
yield if block_given?
|
75
|
-
@input.start
|
76
|
-
end
|
77
|
-
|
78
|
-
def destroy
|
79
|
-
emit :destroy
|
80
|
-
@input.stop
|
81
|
-
cursor_show # TODO: move this to a CursorManager :destroy listener
|
82
|
-
emit :after_destroy
|
83
|
-
end
|
84
|
-
|
85
|
-
# writes directly to @output_stream. Shouldn't be used directly since these changes won't be tracked by the buffer.
|
86
|
-
def write(s)
|
87
|
-
@output_stream.write s unless @silent
|
88
|
-
s
|
89
|
-
end
|
90
|
-
|
91
|
-
def alert
|
92
|
-
puts "\a"
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
Screen = TermGui::Screen
|