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
@@ -1,116 +0,0 @@
|
|
1
|
-
require_relative '../cursor'
|
2
|
-
require_relative '../log'
|
3
|
-
require_relative '../key'
|
4
|
-
|
5
|
-
module TermGui
|
6
|
-
module EditorBaseHandlers
|
7
|
-
def handle_enter
|
8
|
-
if current_x >= current_line.length
|
9
|
-
@lines.insert(current_y + 1, '')
|
10
|
-
else
|
11
|
-
line1 = current_x == 0 ? '' : current_line[0..[current_x - 1, 0].max]
|
12
|
-
line2 = current_line[[current_x, current_line.length].min..current_line.length]
|
13
|
-
@lines.delete_at(current_y)
|
14
|
-
@lines.insert(current_y, line1, line2)
|
15
|
-
end
|
16
|
-
self.current_y += 1
|
17
|
-
self.current_x = 0
|
18
|
-
end
|
19
|
-
|
20
|
-
def insert_chars(s)
|
21
|
-
prefix = current_x < 1 ? '' : current_line[0..[current_x - 1, 0].max] || ''
|
22
|
-
postfix = current_line[[current_x, 0].max..current_line.length] || ''
|
23
|
-
self.current_line = prefix + s + postfix
|
24
|
-
self.current_x += s.length
|
25
|
-
end
|
26
|
-
|
27
|
-
def handle_delete
|
28
|
-
if current_x >= current_line.length && current_y >= @lines.length - 1
|
29
|
-
@screen.alert
|
30
|
-
else
|
31
|
-
if current_line.length <= 1
|
32
|
-
self.current_x = 0
|
33
|
-
self.current_line = ''
|
34
|
-
else
|
35
|
-
prefix = current_line[0..[current_x - 1, 0].max]
|
36
|
-
postfix = current_line[[current_x + 1, current_line.length].min..current_line.length] || ''
|
37
|
-
self.current_line = prefix + postfix
|
38
|
-
# TODO: join lines if at the end of line and there's a following
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def cursor_down
|
44
|
-
if self.current_y == @lines.length - 1
|
45
|
-
if current_x > current_line.length
|
46
|
-
@screen.alert
|
47
|
-
else
|
48
|
-
self.current_x = current_line.length
|
49
|
-
end
|
50
|
-
else
|
51
|
-
self.current_y = self.current_y == @lines.length - 1 ? current_y : current_y + 1
|
52
|
-
self.current_x = [current_x, current_line.length].min
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def cursor_right
|
57
|
-
if current_x >= current_line.length
|
58
|
-
if current_y < @lines.length - 1
|
59
|
-
self.current_y += 1
|
60
|
-
self.current_x = 0
|
61
|
-
else
|
62
|
-
@screen.alert
|
63
|
-
end
|
64
|
-
else
|
65
|
-
self.current_x += 1
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def cursor_left
|
70
|
-
if current_x < 1
|
71
|
-
if current_y > 0
|
72
|
-
self.current_y -= 1
|
73
|
-
self.current_x = current_line.length
|
74
|
-
else
|
75
|
-
@screen.alert
|
76
|
-
end
|
77
|
-
else
|
78
|
-
self.current_x -= 1
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
def handle_backspace
|
83
|
-
if current_x < 1 && current_y <= 0
|
84
|
-
@screen.alert
|
85
|
-
elsif current_x < 1
|
86
|
-
old_line = (@lines.delete_at(current_y) unless @lines.empty?) || ''
|
87
|
-
self.current_y = [current_y - 1, 0].max
|
88
|
-
self.current_x = current_line.length
|
89
|
-
self.current_line = current_line + old_line
|
90
|
-
elsif current_line.length <= 1
|
91
|
-
self.current_x = 0
|
92
|
-
self.current_line = ''
|
93
|
-
else
|
94
|
-
prefix = current_line[0..[current_x - 2, 0].max]
|
95
|
-
postfix = current_line[[current_x, current_line.length].min..current_line.length] || ''
|
96
|
-
self.current_line = prefix + postfix
|
97
|
-
self.current_x = [current_x - 1, 0].max
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def cursor_up
|
102
|
-
if self.current_y == 0
|
103
|
-
if self.current_x == 0
|
104
|
-
@screen.alert
|
105
|
-
else
|
106
|
-
self.current_x = 0
|
107
|
-
end
|
108
|
-
else
|
109
|
-
self.current_y = [current_y - 1, 0].max
|
110
|
-
self.current_x = [current_x, current_line.length - 1].min
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
EditorBaseHandlers = TermGui::EditorBaseHandlers
|
data/src/element.rb
DELETED
@@ -1,61 +0,0 @@
|
|
1
|
-
require_relative 'node'
|
2
|
-
require_relative 'style'
|
3
|
-
require_relative 'renderer'
|
4
|
-
require_relative 'element_bounds'
|
5
|
-
require_relative 'element_box'
|
6
|
-
require_relative 'element_render'
|
7
|
-
require_relative 'element_style'
|
8
|
-
|
9
|
-
module TermGui
|
10
|
-
# Node responsible of
|
11
|
-
# * x, y, width, height, abs_x, abs_y
|
12
|
-
# * rendering text, wrap (TODO)
|
13
|
-
# * border, margin & padding & abs_* update (TODO)
|
14
|
-
# * scroll
|
15
|
-
# TODO: separate each responsibility on its module or subclass
|
16
|
-
class Element < Node
|
17
|
-
include ElementBounds
|
18
|
-
include ElementBox
|
19
|
-
include ElementRender
|
20
|
-
include ElementStyle
|
21
|
-
|
22
|
-
def initialize(**args)
|
23
|
-
super
|
24
|
-
install(%i[focus blur action enter escape])
|
25
|
-
args[:attributes] = { x: args[:x] || 0, y: args[:y] || 0, width: args[:width] || 0, height: args[:height] || 0 } if args[:attributes] == nil
|
26
|
-
a = {}.merge(args, args[:attributes] || {})
|
27
|
-
a[:style] = default_style.assign(Style.from_hash(a[:attributes][:style])).assign(Style.from_hash(a[:style]))
|
28
|
-
self.attributes = a
|
29
|
-
self.style = a[:style]
|
30
|
-
on(%i[focus blur action enter escape]) do |e|
|
31
|
-
s = root_screen
|
32
|
-
if s
|
33
|
-
if e.name == 'action'
|
34
|
-
set_attribute('actioned', true)
|
35
|
-
render s
|
36
|
-
s.set_timeout(get_attribute('actioned-interval') || 0.2) do
|
37
|
-
set_attribute('actioned', false)
|
38
|
-
render s
|
39
|
-
end
|
40
|
-
else
|
41
|
-
render s
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def focus
|
48
|
-
if get_attribute('focusable')
|
49
|
-
root_screen&.focus&.focused = self
|
50
|
-
trigger :bounds_change
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def text=(v)
|
55
|
-
@text = v
|
56
|
-
trigger :bounds_change
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
Element = TermGui::Element
|
data/src/element_bounds.rb
DELETED
@@ -1,111 +0,0 @@
|
|
1
|
-
require_relative 'util'
|
2
|
-
require_relative 'geometry'
|
3
|
-
|
4
|
-
# Adds support for Element's x, y, abs_x, abs_y, width, height, abs_width, abs_height, offset (scroll viewport)
|
5
|
-
module ElementBounds
|
6
|
-
def initialize(**args)
|
7
|
-
super
|
8
|
-
install(:bounds_change)
|
9
|
-
end
|
10
|
-
|
11
|
-
def x=(x)
|
12
|
-
set_attribute('x', x)
|
13
|
-
trigger :bounds_change
|
14
|
-
end
|
15
|
-
|
16
|
-
def x
|
17
|
-
get_attribute('x') || 0
|
18
|
-
end
|
19
|
-
|
20
|
-
def abs_x
|
21
|
-
val = if is_percent x
|
22
|
-
((@parent ? @parent.abs_x : 0) + x * (@parent ? @parent.abs_width : abs_width)).truncate
|
23
|
-
else
|
24
|
-
((@parent ? @parent.abs_x : 0) + x).truncate
|
25
|
-
end
|
26
|
-
o = @parent && parent.offset
|
27
|
-
val -= o.left if o
|
28
|
-
# val -= 1 if border
|
29
|
-
val
|
30
|
-
end
|
31
|
-
|
32
|
-
def offset
|
33
|
-
v = get_attribute('offset')
|
34
|
-
set_attribute('offset', v = Offset.new) unless v
|
35
|
-
v
|
36
|
-
end
|
37
|
-
|
38
|
-
def offset=(value)
|
39
|
-
set_attribute('offset', value)
|
40
|
-
trigger :bounds_change
|
41
|
-
end
|
42
|
-
|
43
|
-
def abs_x=(value)
|
44
|
-
# TODO: parent offset
|
45
|
-
self.x = value - (@parent ? @parent.abs_x : 0).truncate
|
46
|
-
end
|
47
|
-
|
48
|
-
def y=(y)
|
49
|
-
set_attribute('y', y)
|
50
|
-
trigger :bounds_change
|
51
|
-
end
|
52
|
-
|
53
|
-
def y
|
54
|
-
get_attribute('y') || 0
|
55
|
-
end
|
56
|
-
|
57
|
-
def abs_y
|
58
|
-
val = if is_percent y
|
59
|
-
((@parent ? @parent.abs_y : 0) + y * (@parent ? @parent.abs_height : abs_height)).truncate
|
60
|
-
else
|
61
|
-
((@parent ? @parent.abs_y : 0) + y).truncate
|
62
|
-
end
|
63
|
-
o = @parent && parent.offset
|
64
|
-
val -= o.top if o
|
65
|
-
# val += 1 if border
|
66
|
-
val
|
67
|
-
end
|
68
|
-
|
69
|
-
def abs_y=(value)
|
70
|
-
# TODO: parent offset
|
71
|
-
self.y = value - (@parent ? @parent.abs_y : 0).truncate
|
72
|
-
end
|
73
|
-
|
74
|
-
def width=(width)
|
75
|
-
set_attribute('width', width)
|
76
|
-
trigger :bounds_change
|
77
|
-
end
|
78
|
-
|
79
|
-
def width
|
80
|
-
get_attribute('width') || 0
|
81
|
-
end
|
82
|
-
|
83
|
-
def abs_width
|
84
|
-
val = if (is_percent width) && @parent
|
85
|
-
(@parent.abs_width * width).truncate
|
86
|
-
else
|
87
|
-
width.truncate
|
88
|
-
end
|
89
|
-
val += 2 if border
|
90
|
-
val
|
91
|
-
end
|
92
|
-
|
93
|
-
def height=(height)
|
94
|
-
set_attribute('height', height)
|
95
|
-
trigger :bounds_change
|
96
|
-
end
|
97
|
-
|
98
|
-
def height
|
99
|
-
get_attribute('height') || 0
|
100
|
-
end
|
101
|
-
|
102
|
-
def abs_height
|
103
|
-
val = if is_percent height
|
104
|
-
(@parent ? @parent.abs_height * height : 0).truncate
|
105
|
-
else
|
106
|
-
height.truncate
|
107
|
-
end
|
108
|
-
val += 2 if border&.style
|
109
|
-
val
|
110
|
-
end
|
111
|
-
end
|
data/src/element_box.rb
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
require_relative 'geometry'
|
2
|
-
require_relative 'element_bounds'
|
3
|
-
require_relative 'util/hash_object'
|
4
|
-
|
5
|
-
# Adds html-like box-model support for Element: margin, padding, border
|
6
|
-
module ElementBox
|
7
|
-
include ElementBounds
|
8
|
-
|
9
|
-
def abs_content_x
|
10
|
-
abs_x + abs_padding.left + (border ? 1 : 0)
|
11
|
-
end
|
12
|
-
|
13
|
-
def abs_content_y
|
14
|
-
abs_y + abs_padding.top + (border ? 1 : 0)
|
15
|
-
end
|
16
|
-
|
17
|
-
def abs_content_width
|
18
|
-
m = abs_padding
|
19
|
-
abs_width - m.left - m.right - (border ? 2 : 0)
|
20
|
-
end
|
21
|
-
|
22
|
-
def abs_content_height
|
23
|
-
m = abs_padding
|
24
|
-
abs_height - m.top - m.bottom - (border ? 2 : 0)
|
25
|
-
end
|
26
|
-
|
27
|
-
def abs_content_bounds
|
28
|
-
y = abs_content_y
|
29
|
-
x = abs_content_x
|
30
|
-
Bounds.new(left: x, right: x + abs_content_width, top: y, bottom: y + abs_content_height)
|
31
|
-
end
|
32
|
-
|
33
|
-
def abs_content_box
|
34
|
-
Rectangle.new(x: abs_content_x, y: abs_content_y, width: abs_content_width, height: abs_content - abs_content_height)
|
35
|
-
end
|
36
|
-
|
37
|
-
# returns padding as Offset instance
|
38
|
-
def padding
|
39
|
-
padding = get_style('padding')
|
40
|
-
if !padding
|
41
|
-
Bounds.new
|
42
|
-
elsif padding.instance_of? Hash
|
43
|
-
Offset.from_hash(padding)
|
44
|
-
else
|
45
|
-
padding
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def padding=(padding)
|
50
|
-
set_style('padding', padding)
|
51
|
-
trigger :bounds_change
|
52
|
-
end
|
53
|
-
|
54
|
-
# computes absolute padding transforming padding percents to absolute pixel amounts.
|
55
|
-
def abs_padding
|
56
|
-
p = padding
|
57
|
-
Bounds.new(
|
58
|
-
top: is_percent(p.top) ? (p.top * abs_height).truncate : p.top,
|
59
|
-
bottom: is_percent(p.bottom) ? (p.bottom * abs_height).truncate : p.bottom,
|
60
|
-
left: is_percent(p.left) ? (p.left * abs_width).truncate : p.left,
|
61
|
-
right: is_percent(p.right) ? (p.right * abs_width).truncate : p.right
|
62
|
-
)
|
63
|
-
end
|
64
|
-
end
|
data/src/element_render.rb
DELETED
@@ -1,102 +0,0 @@
|
|
1
|
-
require_relative 'element_box'
|
2
|
-
require_relative 'element_style'
|
3
|
-
require_relative 'util'
|
4
|
-
require_relative 'util/wrap'
|
5
|
-
require_relative 'log'
|
6
|
-
require_relative 'box'
|
7
|
-
|
8
|
-
# implements element rendering (self, border, child, text) for which it depends on ElementBox
|
9
|
-
module ElementRender
|
10
|
-
include ElementBox
|
11
|
-
include ElementStyle
|
12
|
-
|
13
|
-
attr_accessor :dirty
|
14
|
-
|
15
|
-
def initialize(**args)
|
16
|
-
super
|
17
|
-
@render_cache = args[:render_cache] || false
|
18
|
-
@dirty = true
|
19
|
-
@render_cache_data = nil
|
20
|
-
on(:bounds_change) do
|
21
|
-
@dirty = true
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def border
|
26
|
-
style&.border
|
27
|
-
end
|
28
|
-
|
29
|
-
def render(screen = root_screen, force = false)
|
30
|
-
self.dirty = true if force
|
31
|
-
return '' unless screen
|
32
|
-
|
33
|
-
trigger(:before_render)
|
34
|
-
|
35
|
-
layout
|
36
|
-
if @render_cache && !dirty && @render_cache_data
|
37
|
-
screen.write @render_cache_data
|
38
|
-
else
|
39
|
-
@render_cache_data = [
|
40
|
-
render_border(screen),
|
41
|
-
render_self(screen),
|
42
|
-
render_text(screen)
|
43
|
-
].join('')
|
44
|
-
end
|
45
|
-
trigger(:after_render)
|
46
|
-
self.dirty = false if @render_cache
|
47
|
-
[@render_cache_data, render_children(screen)].join('')
|
48
|
-
end
|
49
|
-
|
50
|
-
def root_screen
|
51
|
-
@parent&.root_screen
|
52
|
-
end
|
53
|
-
|
54
|
-
def clear
|
55
|
-
root_screen.rect(x: abs_x, y: abs_y, width: abs_width, height: abs_height, style: parent.final_style) if parent && root_screen
|
56
|
-
end
|
57
|
-
|
58
|
-
protected
|
59
|
-
|
60
|
-
def render_self(screen)
|
61
|
-
screen.rect(
|
62
|
-
x: abs_x + (border ? 1 : 0),
|
63
|
-
y: abs_y + (border ? 1 : 0),
|
64
|
-
width: abs_width - (border ? 2 : 0),
|
65
|
-
height: abs_height - (border ? 2 : 0),
|
66
|
-
ch: get_attribute('ch'),
|
67
|
-
style: final_style
|
68
|
-
)
|
69
|
-
end
|
70
|
-
|
71
|
-
# IMPORTANT: border is rendered in a +2 bigger rectangle that sourounds actual element bounds (abs_* methods)
|
72
|
-
def render_border(screen)
|
73
|
-
border ? screen.box(abs_x, abs_y, abs_width, abs_height, border.style, border_style) : ''
|
74
|
-
end
|
75
|
-
|
76
|
-
def render_text(screen)
|
77
|
-
(render_text_lines.map.with_index do |line, i|
|
78
|
-
screen.text(x: abs_content_x, y: abs_content_y + i, text: line, style: final_style)
|
79
|
-
end).join('')
|
80
|
-
end
|
81
|
-
|
82
|
-
def render_children(screen)
|
83
|
-
(@children.map do |c|
|
84
|
-
c.render(screen)
|
85
|
-
end).join('')
|
86
|
-
end
|
87
|
-
|
88
|
-
def render_text_lines(text = @text || '')
|
89
|
-
text&.length ? (style.wrap ? wrap_text(text, abs_content_width) : text.split('\n')) : []
|
90
|
-
end
|
91
|
-
|
92
|
-
# can be used by text widgets like labels or buttons to automatically set preffered size according to its text
|
93
|
-
def render_text_size(text = @text)
|
94
|
-
lines = render_text_lines(text)
|
95
|
-
width = lines.map(&:length).max
|
96
|
-
height = lines.length
|
97
|
-
{ width: width, height: height }
|
98
|
-
end
|
99
|
-
|
100
|
-
def layout
|
101
|
-
end
|
102
|
-
end
|