natty-ui 0.10.0 → 0.11.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/README.md +4 -11
- data/examples/24bit-colors.rb +8 -16
- data/examples/3bit-colors.rb +1 -1
- data/examples/8bit-colors.rb +15 -11
- data/examples/animate.rb +31 -11
- data/examples/attributes.rb +16 -14
- data/examples/attributes_list.rb +14 -0
- data/examples/demo.rb +4 -4
- data/examples/illustration.rb +52 -24
- data/examples/ls.rb +4 -4
- data/examples/message.rb +11 -9
- data/examples/progress.rb +2 -2
- data/examples/query.rb +1 -1
- data/examples/table.rb +23 -23
- data/lib/natty-ui/animation/binary.rb +37 -0
- data/lib/natty-ui/animation/default.rb +42 -0
- data/lib/natty-ui/animation/matrix.rb +55 -0
- data/lib/natty-ui/animation/rainbow.rb +28 -0
- data/lib/natty-ui/animation/type_writer.rb +44 -0
- data/lib/natty-ui/animation.rb +69 -0
- data/lib/natty-ui/ansi/constants.rb +1 -1
- data/lib/natty-ui/ansi.rb +46 -26
- data/lib/natty-ui/ansi_wrapper.rb +44 -51
- data/lib/natty-ui/key_map.rb +72 -49
- data/lib/natty-ui/preload.rb +1 -1
- data/lib/natty-ui/text.rb +76 -103
- data/lib/natty-ui/version.rb +1 -1
- data/lib/natty-ui/wrapper/animate.rb +1 -1
- data/lib/natty-ui/wrapper/element.rb +18 -10
- data/lib/natty-ui/wrapper/features.rb +0 -8
- data/lib/natty-ui/wrapper/framed.rb +4 -4
- data/lib/natty-ui/wrapper/section.rb +18 -18
- data/lib/natty-ui/wrapper/table.rb +432 -171
- data/lib/natty-ui/wrapper.rb +37 -20
- data/lib/natty-ui.rb +14 -10
- metadata +10 -8
- data/lib/natty-ui/line_animation/default.rb +0 -36
- data/lib/natty-ui/line_animation/matrix.rb +0 -29
- data/lib/natty-ui/line_animation/rainbow.rb +0 -31
- data/lib/natty-ui/line_animation/type_writer.rb +0 -45
- data/lib/natty-ui/line_animation.rb +0 -49
data/lib/natty-ui/text.rb
CHANGED
@@ -5,50 +5,41 @@ require_relative 'ansi'
|
|
5
5
|
module NattyUI
|
6
6
|
module Text
|
7
7
|
class << self
|
8
|
-
def plain(str) = Ansi.blemish(plain_but_ansi(str))
|
9
|
-
|
10
8
|
def plain_but_ansi(str)
|
11
9
|
(str = str.to_s).empty? and return str
|
12
|
-
str.gsub(
|
13
|
-
match = Regexp.last_match[
|
14
|
-
|
15
|
-
|
10
|
+
str.gsub(BBCODE) do
|
11
|
+
match = Regexp.last_match[1]
|
12
|
+
if match[0] == '/'
|
13
|
+
next if match.size == 1
|
14
|
+
next "[#{match[1..]}]" if match[1] == '/'
|
15
|
+
end
|
16
|
+
Ansi.try_convert(match) ? nil : "[#{match}]"
|
16
17
|
end
|
17
18
|
end
|
18
19
|
|
20
|
+
def plain(str) = Ansi.blemish(plain_but_ansi(str))
|
21
|
+
|
19
22
|
def embellish(str)
|
20
23
|
(str = str.to_s).empty? and return str
|
21
24
|
reset = false
|
22
25
|
str =
|
23
|
-
str.gsub(
|
24
|
-
match = Regexp.last_match[
|
26
|
+
str.gsub(BBCODE) do
|
27
|
+
match = Regexp.last_match[1]
|
25
28
|
if match[0] == '/'
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
+
if match.size == 1
|
30
|
+
reset = false
|
31
|
+
next Ansi::RESET
|
32
|
+
end
|
33
|
+
next "[#{match[1..]}]" if match[1] == '/'
|
29
34
|
end
|
35
|
+
|
30
36
|
ansi = Ansi.try_convert(match)
|
31
|
-
ansi ? reset = ansi : "[
|
37
|
+
ansi ? reset = ansi : "[#{match}]"
|
32
38
|
end
|
33
39
|
reset ? "#{str}#{Ansi::RESET}" : str
|
34
40
|
end
|
35
41
|
|
36
|
-
|
37
|
-
return 0 if (str = plain(str)).empty?
|
38
|
-
str = str.encode(UTF_8) if str.encoding != UTF_8
|
39
|
-
width = 0
|
40
|
-
in_zero_width = false
|
41
|
-
str.scan(WIDTH_SCANNER) do |np_start, np_end, _csi, _osc, gc|
|
42
|
-
if in_zero_width
|
43
|
-
in_zero_width = false if np_end
|
44
|
-
next
|
45
|
-
end
|
46
|
-
next in_zero_width = true if np_start
|
47
|
-
width += char_width(gc) if gc
|
48
|
-
end
|
49
|
-
width
|
50
|
-
end
|
51
|
-
|
42
|
+
# works for UTF-8 chars only!
|
52
43
|
def char_width(char)
|
53
44
|
ord = char.ord
|
54
45
|
return SPECIAL_CHARS[ord] || 2 if ord <= 0x1f
|
@@ -63,13 +54,24 @@ module NattyUI
|
|
63
54
|
size
|
64
55
|
end
|
65
56
|
|
66
|
-
def
|
67
|
-
|
68
|
-
|
57
|
+
def width(str)
|
58
|
+
return 0 if (str = plain_but_ansi(str)).empty?
|
59
|
+
str = str.encode(UTF_8) if str.encoding != UTF_8
|
60
|
+
width = 0
|
61
|
+
in_zero_width = false
|
62
|
+
str.scan(WIDTH_SCANNER) do |np_start, np_end, _csi, _osc, gc|
|
63
|
+
if in_zero_width
|
64
|
+
in_zero_width = false if np_end
|
65
|
+
next
|
66
|
+
end
|
67
|
+
next in_zero_width = true if np_start
|
68
|
+
width += char_width(gc) if gc
|
69
|
+
end
|
70
|
+
width
|
69
71
|
end
|
70
72
|
|
71
|
-
def
|
72
|
-
return if (max_width = max_width.to_i)
|
73
|
+
def each_line_plain(strs, max_width)
|
74
|
+
return if (max_width = max_width.to_i) < 1
|
73
75
|
strs.each do |str|
|
74
76
|
plain_but_ansi(str).each_line(chomp: true) do |line|
|
75
77
|
next yield(line, 0) if line.empty?
|
@@ -97,94 +99,65 @@ module NattyUI
|
|
97
99
|
nil
|
98
100
|
end
|
99
101
|
|
100
|
-
def
|
101
|
-
return if (max_width = max_width.to_i) <= 0
|
102
|
-
plain_but_ansi(str).each_line(chomp: true) do |line|
|
103
|
-
return line, 0 if line.empty?
|
104
|
-
current = String.new(encoding: line.encoding)
|
105
|
-
width = 0
|
106
|
-
in_zero_width = false
|
107
|
-
line = line.encode(UTF_8) if line.encoding != UTF_8
|
108
|
-
line.scan(WIDTH_SCANNER) do |np_start, np_end, csi, osc, gc|
|
109
|
-
next in_zero_width = (current << "\1") if np_start
|
110
|
-
next in_zero_width = !(current << "\2") if np_end
|
111
|
-
next if osc || csi
|
112
|
-
next current << gc if in_zero_width
|
113
|
-
cw = char_width(gc)
|
114
|
-
return current, width - cw if (width += cw) > max_width
|
115
|
-
current << gc
|
116
|
-
end
|
117
|
-
return current, width
|
118
|
-
end
|
119
|
-
[+'', 0]
|
120
|
-
end
|
121
|
-
|
122
|
-
def as_plain_lines(strs, width, height)
|
102
|
+
def as_lines_plain(strs, width, height = nil)
|
123
103
|
ret = []
|
124
|
-
|
104
|
+
each_line_plain(strs, width) do |*info|
|
125
105
|
ret << info
|
126
|
-
break if ret.size
|
106
|
+
break if height == ret.size
|
127
107
|
end
|
128
108
|
ret
|
129
109
|
end
|
130
110
|
|
131
|
-
def
|
132
|
-
return if (max_width = max_width.to_i)
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
111
|
+
def each_line(strs, max_width)
|
112
|
+
return if (max_width = max_width.to_i) < 1
|
113
|
+
strs.each do |str|
|
114
|
+
str
|
115
|
+
.to_s
|
116
|
+
.each_line(chomp: true) do |line|
|
117
|
+
line = embellish(line)
|
118
|
+
next yield(line, 0) if line.empty?
|
119
|
+
current = String.new(encoding: line.encoding)
|
120
|
+
seq = current.dup
|
121
|
+
width = 0
|
122
|
+
in_zero_width = false
|
123
|
+
line = line.encode(UTF_8) if line.encoding != UTF_8
|
124
|
+
line.scan(WIDTH_SCANNER) do |np_start, np_end, csi, osc, gc|
|
125
|
+
next in_zero_width = (current << "\1") if np_start
|
126
|
+
next in_zero_width = !(current << "\2") if np_end
|
127
|
+
next (current << osc) && (seq << osc) if osc
|
128
|
+
if csi
|
129
|
+
current << csi
|
130
|
+
next seq.clear if csi == "\e[m" || csi == "\e[0m"
|
131
|
+
next if in_zero_width
|
132
|
+
next seq << csi
|
133
|
+
end
|
134
|
+
next current << gc if in_zero_width
|
135
|
+
cw = char_width(gc)
|
136
|
+
if (width += cw) > max_width
|
137
|
+
yield(current, width - cw)
|
138
|
+
width = cw
|
139
|
+
current = seq.dup
|
140
|
+
end
|
141
|
+
current << gc
|
142
|
+
end
|
143
|
+
yield(current, width)
|
157
144
|
end
|
158
|
-
current << gc
|
159
|
-
end
|
160
|
-
yield(current, width)
|
161
145
|
end
|
162
146
|
end
|
163
147
|
|
164
|
-
def
|
148
|
+
def as_lines(strs, width, height = nil)
|
165
149
|
ret = []
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
break if ret.size == height
|
170
|
-
end
|
171
|
-
else
|
172
|
-
each_embellished_line(strs, width) { |*info| ret << info }
|
150
|
+
each_line(strs, width) do |*info|
|
151
|
+
ret << info
|
152
|
+
break if height == ret.size
|
173
153
|
end
|
174
154
|
ret
|
175
155
|
end
|
176
|
-
|
177
|
-
def as_embellished_lines_min(strs, width)
|
178
|
-
ret = []
|
179
|
-
each_embellished_line(strs, width) { ret << _1 }
|
180
|
-
ret
|
181
|
-
end
|
182
156
|
end
|
183
157
|
|
184
158
|
UTF_8 = Encoding::UTF_8
|
185
|
-
|
159
|
+
BBCODE = /(?:\[((?~[\[\]]))\])/
|
186
160
|
WIDTH_SCANNER = /\G(?:(\1)|(\2)|(#{Ansi::CSI})|(#{Ansi::OSC})|(\X))/
|
187
|
-
|
188
161
|
SPECIAL_CHARS = {
|
189
162
|
0x00 => 0,
|
190
163
|
0x01 => 1,
|
data/lib/natty-ui/version.rb
CHANGED
@@ -6,7 +6,7 @@ module NattyUI
|
|
6
6
|
#
|
7
7
|
# @overload animate(..., animation: :default)
|
8
8
|
# @param [#to_s] ... objects to print
|
9
|
-
# @param [:default, :matrix, :rainbow, :type_writer]
|
9
|
+
# @param [:binary, :default, :matrix, :rainbow, :type_writer]
|
10
10
|
# animation type of animation
|
11
11
|
# @return [Wrapper::Section, Wrapper] it's parent object
|
12
12
|
def animate(*args, **kwargs)
|
@@ -5,25 +5,33 @@ require_relative 'features'
|
|
5
5
|
module NattyUI
|
6
6
|
class Wrapper
|
7
7
|
#
|
8
|
-
# Basic visual element
|
8
|
+
# Basic visual element.
|
9
9
|
#
|
10
10
|
class Element
|
11
11
|
include Features
|
12
12
|
|
13
|
-
# @
|
14
|
-
# @return [
|
15
|
-
|
16
|
-
|
17
|
-
# @return [Symbol] close status when closed
|
18
|
-
# @return [nil] when not closed
|
19
|
-
attr_reader :status
|
13
|
+
# @attribute [r] available_width
|
14
|
+
# @return [Integer] available columns count within the element
|
15
|
+
def available_width = @parent.available_width
|
20
16
|
|
21
17
|
# @attribute [r] closed?
|
22
18
|
# @return [Boolean] whether its closed or not
|
23
19
|
def closed? = (@status != nil)
|
24
20
|
|
25
|
-
# @return [
|
26
|
-
|
21
|
+
# @return [Section, Wrapper] parent element
|
22
|
+
attr_reader :parent
|
23
|
+
|
24
|
+
# @return [Symbol, nil] status when closed
|
25
|
+
attr_reader :status
|
26
|
+
|
27
|
+
# @attribute [r] wrapper
|
28
|
+
# @return [Wrapper] assigned output stream wrapper
|
29
|
+
def wrapper
|
30
|
+
return @wrapper if @wrapper
|
31
|
+
@wrapper = @parent
|
32
|
+
@wrapper = @wrapper.parent until @wrapper.is_a?(Wrapper)
|
33
|
+
@wrapper
|
34
|
+
end
|
27
35
|
|
28
36
|
# Close the element.
|
29
37
|
#
|
@@ -5,14 +5,6 @@ module NattyUI
|
|
5
5
|
# Features of {NattyUI} - methods to display natty elements.
|
6
6
|
#
|
7
7
|
module Features
|
8
|
-
# @return [Wrapper] assigned output stream wrapper
|
9
|
-
def wrapper
|
10
|
-
return @wrapper if @wrapper
|
11
|
-
@wrapper = @parent
|
12
|
-
@wrapper = @wrapper.parent until @wrapper.is_a?(Wrapper)
|
13
|
-
@wrapper
|
14
|
-
end
|
15
|
-
|
16
8
|
protected
|
17
9
|
|
18
10
|
def _element(type, ...)
|
@@ -11,7 +11,7 @@ module NattyUI
|
|
11
11
|
# {Wrapper::Element#close}.
|
12
12
|
#
|
13
13
|
# @param [Array<#to_s>] args more objects to print
|
14
|
-
# @param [:
|
14
|
+
# @param [:double, :heavy, :rounded, :semi, :simple] type frame type
|
15
15
|
# @yieldparam [Wrapper::Framed] framed the created section
|
16
16
|
# @return [Object] the result of the code block
|
17
17
|
# @return [Wrapper::Framed] itself, when no code block is given
|
@@ -29,14 +29,14 @@ module NattyUI
|
|
29
29
|
protected
|
30
30
|
|
31
31
|
def initialize(parent, type:)
|
32
|
-
super(parent, prefix: "#{type[
|
32
|
+
super(parent, prefix: "#{type[4]} ", prefix_width: 2, suffix_width: 2)
|
33
33
|
init(type)
|
34
34
|
end
|
35
35
|
|
36
36
|
def init(type)
|
37
37
|
aw = @parent.available_width - 1
|
38
|
-
parent.puts("#{type[
|
39
|
-
@finish = "#{type[
|
38
|
+
parent.puts("#{type[0]}#{type[5] * aw}")
|
39
|
+
@finish = "#{type[2]}#{type[5] * aw}"
|
40
40
|
end
|
41
41
|
|
42
42
|
def finish = @parent.puts(@finish)
|
@@ -35,18 +35,18 @@ module NattyUI
|
|
35
35
|
|
36
36
|
# Print given arguments line-wise into the section.
|
37
37
|
#
|
38
|
-
# @
|
39
|
-
#
|
40
|
-
#
|
41
|
-
def puts(*args, **
|
38
|
+
# @param [#to_s] args objects to print
|
39
|
+
# @option options [:left, :right, :center] :align text alignment
|
40
|
+
# @return [Section] itself
|
41
|
+
def puts(*args, **options)
|
42
42
|
return self if @status
|
43
43
|
@parent.puts(
|
44
44
|
*args,
|
45
|
-
**
|
46
|
-
prefix: "#{@prefix}#{
|
47
|
-
prefix_width: @prefix_width +
|
48
|
-
suffix: "#{
|
49
|
-
suffix_width: @suffix_width +
|
45
|
+
**options.merge!(
|
46
|
+
prefix: "#{@prefix}#{options[:prefix]}",
|
47
|
+
prefix_width: @prefix_width + options[:prefix_width].to_i,
|
48
|
+
suffix: "#{options[:suffix]}#{@suffix}",
|
49
|
+
suffix_width: @suffix_width + options[:suffix_width].to_i
|
50
50
|
)
|
51
51
|
)
|
52
52
|
self
|
@@ -54,18 +54,18 @@ module NattyUI
|
|
54
54
|
|
55
55
|
# Print given arguments into the section.
|
56
56
|
#
|
57
|
-
# @
|
58
|
-
#
|
59
|
-
#
|
60
|
-
def print(*args, **
|
57
|
+
# @param [#to_s] args objects to print
|
58
|
+
# @option options [:left, :right, :center] :align text alignment
|
59
|
+
# @return [Section] itself
|
60
|
+
def print(*args, **options)
|
61
61
|
return self if @status
|
62
62
|
@parent.print(
|
63
63
|
*args,
|
64
|
-
**
|
65
|
-
prefix: "#{@prefix}#{
|
66
|
-
prefix_width: @prefix_width +
|
67
|
-
suffix: "#{
|
68
|
-
suffix_width: @suffix_width +
|
64
|
+
**options.merge!(
|
65
|
+
prefix: "#{@prefix}#{options[:prefix]}",
|
66
|
+
prefix_width: @prefix_width + options[:prefix_width].to_i,
|
67
|
+
suffix: "#{options[:suffix]}#{@suffix}",
|
68
|
+
suffix_width: @suffix_width + options[:suffix_width].to_i
|
69
69
|
)
|
70
70
|
)
|
71
71
|
self
|