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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -11
  3. data/examples/24bit-colors.rb +8 -16
  4. data/examples/3bit-colors.rb +1 -1
  5. data/examples/8bit-colors.rb +15 -11
  6. data/examples/animate.rb +31 -11
  7. data/examples/attributes.rb +16 -14
  8. data/examples/attributes_list.rb +14 -0
  9. data/examples/demo.rb +4 -4
  10. data/examples/illustration.rb +52 -24
  11. data/examples/ls.rb +4 -4
  12. data/examples/message.rb +11 -9
  13. data/examples/progress.rb +2 -2
  14. data/examples/query.rb +1 -1
  15. data/examples/table.rb +23 -23
  16. data/lib/natty-ui/animation/binary.rb +37 -0
  17. data/lib/natty-ui/animation/default.rb +42 -0
  18. data/lib/natty-ui/animation/matrix.rb +55 -0
  19. data/lib/natty-ui/animation/rainbow.rb +28 -0
  20. data/lib/natty-ui/animation/type_writer.rb +44 -0
  21. data/lib/natty-ui/animation.rb +69 -0
  22. data/lib/natty-ui/ansi/constants.rb +1 -1
  23. data/lib/natty-ui/ansi.rb +46 -26
  24. data/lib/natty-ui/ansi_wrapper.rb +44 -51
  25. data/lib/natty-ui/key_map.rb +72 -49
  26. data/lib/natty-ui/preload.rb +1 -1
  27. data/lib/natty-ui/text.rb +76 -103
  28. data/lib/natty-ui/version.rb +1 -1
  29. data/lib/natty-ui/wrapper/animate.rb +1 -1
  30. data/lib/natty-ui/wrapper/element.rb +18 -10
  31. data/lib/natty-ui/wrapper/features.rb +0 -8
  32. data/lib/natty-ui/wrapper/framed.rb +4 -4
  33. data/lib/natty-ui/wrapper/section.rb +18 -18
  34. data/lib/natty-ui/wrapper/table.rb +432 -171
  35. data/lib/natty-ui/wrapper.rb +37 -20
  36. data/lib/natty-ui.rb +14 -10
  37. metadata +10 -8
  38. data/lib/natty-ui/line_animation/default.rb +0 -36
  39. data/lib/natty-ui/line_animation/matrix.rb +0 -29
  40. data/lib/natty-ui/line_animation/rainbow.rb +0 -31
  41. data/lib/natty-ui/line_animation/type_writer.rb +0 -45
  42. 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(/(\[\[((?~\]\]))\]\])/) do
13
- match = Regexp.last_match[2]
14
- next match.size == 1 ? nil : "[[#{match[1..]}]]" if match[0] == '/'
15
- Ansi.try_convert(match) ? nil : "[[#{match}]]"
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(/(\[\[((?~\]\]))\]\])/) do
24
- match = Regexp.last_match[2]
26
+ str.gsub(BBCODE) do
27
+ match = Regexp.last_match[1]
25
28
  if match[0] == '/'
26
- next "[[#{match[1..]}]]" if match.size > 1
27
- reset = false
28
- next Ansi::RESET
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 : "[[#{match}]]"
37
+ ansi ? reset = ansi : "[#{match}]"
32
38
  end
33
39
  reset ? "#{str}#{Ansi::RESET}" : str
34
40
  end
35
41
 
36
- def width(str)
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 simple_each_line(strs, &block)
67
- strs.each { _1.to_s.each_line(chomp: true, &block) }
68
- nil
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 each_plain_line(strs, max_width)
72
- return if (max_width = max_width.to_i) <= 0
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 first_plain_line(str, max_width)
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
- each_plain_line(strs, width) do |*info|
104
+ each_line_plain(strs, width) do |*info|
125
105
  ret << info
126
- break if ret.size == height
106
+ break if height == ret.size
127
107
  end
128
108
  ret
129
109
  end
130
110
 
131
- def each_embellished_line(strs, max_width)
132
- return if (max_width = max_width.to_i) <= 0
133
- simple_each_line(strs) do |line|
134
- line = embellish(line)
135
- next yield(line, 0) if line.empty?
136
- current = String.new(encoding: line.encoding)
137
- seq = current.dup
138
- width = 0
139
- in_zero_width = false
140
- line = line.encode(UTF_8) if line.encoding != UTF_8
141
- line.scan(WIDTH_SCANNER) do |np_start, np_end, csi, osc, gc|
142
- next in_zero_width = (current << "\1") if np_start
143
- next in_zero_width = !(current << "\2") if np_end
144
- next (current << osc) && (seq << osc) if osc
145
- if csi
146
- current << csi
147
- next if in_zero_width
148
- next seq.clear if csi == "\e[m" || csi == "\e[0m"
149
- next seq << csi
150
- end
151
- next current << gc if in_zero_width
152
- cw = char_width(gc)
153
- if (width += cw) > max_width
154
- yield(current, width - cw)
155
- width = cw
156
- current = seq.dup
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 as_embellished_lines(strs, width, height = nil)
148
+ def as_lines(strs, width, height = nil)
165
149
  ret = []
166
- if height
167
- each_embellished_line(strs, width) do |*info|
168
- ret << info
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,
@@ -2,5 +2,5 @@
2
2
 
3
3
  module NattyUI
4
4
  # The version number of the gem.
5
- VERSION = '0.10.0'
5
+ VERSION = '0.11.0'
6
6
  end
@@ -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 implementing all {Features}.
8
+ # Basic visual element.
9
9
  #
10
10
  class Element
11
11
  include Features
12
12
 
13
- # @return [Element] when embedded in a section
14
- # @return [Wrapper] when not embedded in a section
15
- attr_reader :parent
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 [Integer] available columns count within the element
26
- def available_width = @parent.available_width
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 [:block, :double, :heavy, :rounded, :semi, :simple] type frame type
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[0]} ", prefix_width: 2, suffix_width: 2)
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[1]}#{type[2] * aw}")
39
- @finish = "#{type[5]}#{type[6] * aw}"
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
- # @overload puts(...)
39
- # @param [#to_s] ... objects to print
40
- # @return [Section] itself
41
- def puts(*args, **kwargs)
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
- **kwargs.merge!(
46
- prefix: "#{@prefix}#{kwargs[:prefix]}",
47
- prefix_width: @prefix_width + kwargs[:prefix_width].to_i,
48
- suffix: "#{kwargs[:suffix]}#{@suffix}",
49
- suffix_width: @suffix_width + kwargs[:suffix_width].to_i
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
- # @overload print(...)
58
- # @param [#to_s] ... objects to print
59
- # @return [Section] itself
60
- def print(*args, **kwargs)
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
- **kwargs.merge!(
65
- prefix: "#{@prefix}#{kwargs[:prefix]}",
66
- prefix_width: @prefix_width + kwargs[:prefix_width].to_i,
67
- suffix: "#{kwargs[:suffix]}#{@suffix}",
68
- suffix_width: @suffix_width + kwargs[:suffix_width].to_i
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