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.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/lib/termgui.rb +1 -1
  3. metadata +40 -107
  4. data/Gemfile +0 -14
  5. data/README.md +0 -321
  6. data/src/action.rb +0 -58
  7. data/src/box.rb +0 -90
  8. data/src/color.rb +0 -174
  9. data/src/cursor.rb +0 -69
  10. data/src/editor/editor_base.rb +0 -152
  11. data/src/editor/editor_base_handlers.rb +0 -116
  12. data/src/element.rb +0 -61
  13. data/src/element_bounds.rb +0 -111
  14. data/src/element_box.rb +0 -64
  15. data/src/element_render.rb +0 -102
  16. data/src/element_style.rb +0 -51
  17. data/src/emitter.rb +0 -102
  18. data/src/emitter_state.rb +0 -19
  19. data/src/enterable.rb +0 -93
  20. data/src/event.rb +0 -92
  21. data/src/focus.rb +0 -102
  22. data/src/geometry.rb +0 -53
  23. data/src/image.rb +0 -60
  24. data/src/input.rb +0 -85
  25. data/src/input_grab.rb +0 -17
  26. data/src/input_time.rb +0 -97
  27. data/src/key.rb +0 -114
  28. data/src/log.rb +0 -24
  29. data/src/node.rb +0 -117
  30. data/src/node_attributes.rb +0 -27
  31. data/src/node_visit.rb +0 -52
  32. data/src/renderer.rb +0 -119
  33. data/src/renderer_cursor.rb +0 -18
  34. data/src/renderer_draw.rb +0 -28
  35. data/src/renderer_image.rb +0 -31
  36. data/src/renderer_print.rb +0 -40
  37. data/src/screen.rb +0 -96
  38. data/src/screen_element.rb +0 -59
  39. data/src/screen_input.rb +0 -43
  40. data/src/screen_renderer.rb +0 -53
  41. data/src/style.rb +0 -149
  42. data/src/tco/colouring.rb +0 -248
  43. data/src/tco/config.rb +0 -57
  44. data/src/tco/palette.rb +0 -603
  45. data/src/tco/tco_termgui.rb +0 -30
  46. data/src/termgui.rb +0 -29
  47. data/src/util.rb +0 -110
  48. data/src/util/css.rb +0 -98
  49. data/src/util/css_query.rb +0 -23
  50. data/src/util/easing.rb +0 -364
  51. data/src/util/hash_object.rb +0 -131
  52. data/src/util/imagemagick.rb +0 -27
  53. data/src/util/justify.rb +0 -20
  54. data/src/util/unicode-categories.rb +0 -572
  55. data/src/util/wrap.rb +0 -102
  56. data/src/widget/button.rb +0 -33
  57. data/src/widget/checkbox.rb +0 -47
  58. data/src/widget/col.rb +0 -30
  59. data/src/widget/image.rb +0 -106
  60. data/src/widget/inline.rb +0 -40
  61. data/src/widget/input_number.rb +0 -73
  62. data/src/widget/inputbox.rb +0 -85
  63. data/src/widget/label.rb +0 -33
  64. data/src/widget/modal.rb +0 -69
  65. data/src/widget/row.rb +0 -26
  66. data/src/widget/selectbox.rb +0 -100
  67. data/src/widget/textarea.rb +0 -54
  68. data/src/xml/xml.rb +0 -80
  69. data/test/action_test.rb +0 -34
  70. data/test/box_test.rb +0 -15
  71. data/test/css_test.rb +0 -39
  72. data/test/editor/editor_base_test.rb +0 -201
  73. data/test/element_bounds_test.rb +0 -77
  74. data/test/element_box_test.rb +0 -8
  75. data/test/element_render_test.rb +0 -124
  76. data/test/element_style_test.rb +0 -85
  77. data/test/element_test.rb +0 -10
  78. data/test/emitter_test.rb +0 -108
  79. data/test/event_test.rb +0 -19
  80. data/test/focus_test.rb +0 -37
  81. data/test/geometry_test.rb +0 -12
  82. data/test/input_test.rb +0 -47
  83. data/test/key_test.rb +0 -14
  84. data/test/log_test.rb +0 -21
  85. data/test/node_test.rb +0 -105
  86. data/test/performance/performance1.rb +0 -48
  87. data/test/renderer_test.rb +0 -74
  88. data/test/renderer_test_rect.rb +0 -4
  89. data/test/screen_test.rb +0 -58
  90. data/test/style_test.rb +0 -18
  91. data/test/termgui_test.rb +0 -10
  92. data/test/test_all.rb +0 -30
  93. data/test/util_hash_object_test.rb +0 -93
  94. data/test/util_test.rb +0 -26
  95. data/test/widget/checkbox_test.rb +0 -99
  96. data/test/widget/col_test.rb +0 -87
  97. data/test/widget/inline_test.rb +0 -40
  98. data/test/widget/label_test.rb +0 -94
  99. data/test/widget/row_test.rb +0 -40
  100. data/test/wrap_test.rb +0 -11
  101. data/test/xml_test.rb +0 -77
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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