natty-ui 0.8.0 → 0.9.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/.yardopts +3 -2
  3. data/examples/24bit-colors.rb +11 -7
  4. data/examples/3bit-colors.rb +3 -2
  5. data/examples/8bit-colors.rb +2 -2
  6. data/examples/animate.rb +24 -0
  7. data/examples/attributes.rb +20 -65
  8. data/examples/demo.rb +39 -203
  9. data/examples/illustration.png +0 -0
  10. data/examples/illustration.rb +26 -23
  11. data/examples/{list_in_columns.rb → ls.rb} +11 -11
  12. data/examples/message.rb +30 -0
  13. data/examples/progress.rb +2 -2
  14. data/examples/query.rb +6 -2
  15. data/examples/read_key.rb +13 -0
  16. data/examples/table.rb +26 -8
  17. data/lib/natty-ui/ansi.rb +354 -328
  18. data/lib/natty-ui/ansi_constants.rb +73 -0
  19. data/lib/natty-ui/ansi_wrapper.rb +68 -23
  20. data/lib/natty-ui/key_map.rb +119 -0
  21. data/lib/natty-ui/line_animation/default.rb +35 -0
  22. data/lib/natty-ui/line_animation/matrix.rb +28 -0
  23. data/lib/natty-ui/line_animation/rainbow.rb +30 -0
  24. data/lib/natty-ui/line_animation/test.rb +29 -0
  25. data/lib/natty-ui/line_animation/type_writer.rb +64 -0
  26. data/lib/natty-ui/line_animation.rb +54 -0
  27. data/lib/natty-ui/version.rb +1 -1
  28. data/lib/natty-ui/wrapper/animate.rb +17 -0
  29. data/lib/natty-ui/wrapper/ask.rb +2 -8
  30. data/lib/natty-ui/wrapper/framed.rb +26 -21
  31. data/lib/natty-ui/wrapper/heading.rb +1 -1
  32. data/lib/natty-ui/wrapper/horizontal_rule.rb +3 -3
  33. data/lib/natty-ui/wrapper/list_in_columns.rb +7 -4
  34. data/lib/natty-ui/wrapper/message.rb +4 -4
  35. data/lib/natty-ui/wrapper/progress.rb +33 -8
  36. data/lib/natty-ui/wrapper/query.rb +9 -13
  37. data/lib/natty-ui/wrapper/request.rb +2 -2
  38. data/lib/natty-ui/wrapper/section.rb +25 -11
  39. data/lib/natty-ui/wrapper/table.rb +11 -11
  40. data/lib/natty-ui/wrapper.rb +19 -14
  41. data/lib/natty-ui.rb +33 -12
  42. metadata +16 -4
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NattyUI
4
+ module Ansi
5
+ CURSOR_SHOW = "\e[?25h"
6
+ CURSOR_HIDE = "\e[?25l"
7
+ CURSOR_HOME = cursor_pos(nil, nil)
8
+ CURSOR_FIRST_ROW = cursor_pos(1).freeze
9
+ CURSOR_FIRST_COLUMN = cursor_column(1).freeze
10
+ CURSOR_SAFE_POS_SCO = "\e[s"
11
+ CURSOR_RESTORE_POS_SCO = "\e[u"
12
+ CURSOR_SAFE_POS_DEC = "\e7"
13
+ CURSOR_RESTORE_POS_DEC = "\e8"
14
+ CURSOR_SAFE_POS = CURSOR_SAFE_POS_DEC
15
+ CURSOR_RESTORE_POS = CURSOR_RESTORE_POS_DEC
16
+
17
+ CURSOR_ALIGN_RIGHT = "\e[9999G\e[1D\e[1C" # @!visibility private
18
+
19
+ SCREEN_ERASE = screen_erase(:entire)
20
+ SCREEN_SAVE = "\e[?47h"
21
+ SCREEN_RESTORE = "\e[?47l"
22
+ SCREEN_ALTERNATE = "\e[?1049h"
23
+ SCREEN_ALTERNATE_OFF = "\e[?1049l"
24
+ SCREEN_SAVE_ATTRIBUTES = '\e[#{'
25
+ SCREEN_RESTORE_ATTRIBUTES = '\e[#}'
26
+
27
+ SCREEN_BLANK = "\e[0m\e[s\e[?47h\e[H\e[2J" # @!visibility private
28
+ SCREEN_UNBLANK = "\e[?47l\e[u\e[0m" # @!visibility private
29
+
30
+ LINE_PREVIOUS = cursor_previous_line(1).freeze
31
+ LINE_NEXT = cursor_next_line(1).freeze
32
+ LINE_ERASE = line_erase(:entire)
33
+
34
+ # ANSI control code sequence to erase the screen and set cursor position on
35
+ # upper left corner.
36
+ CLS = (CURSOR_HOME + SCREEN_ERASE).freeze
37
+
38
+ # ANSI control code sequence to erase current line and position to first
39
+ # column.
40
+ CLL = (LINE_ERASE + CURSOR_FIRST_COLUMN).freeze
41
+
42
+ # ANSI control code to reset all attributes.
43
+ RESET = self[:reset].freeze
44
+
45
+ BOLD = self[:bold].freeze
46
+ BOLD_OFF = self[:bold_off].freeze
47
+
48
+ FAINT = self[:faint].freeze
49
+ FAINT_OFF = self[:faint_off].freeze
50
+
51
+ ITALIC = self[:italic].freeze
52
+ ITALIC_OFF = self[:italic_off].freeze
53
+
54
+ STRIKE = self[:strike].freeze
55
+ STRIKE_OFF = self[:strike_off].freeze
56
+
57
+ UNDERLINE = self[:underline].freeze
58
+ DOUBLE_UNDERLINE = self[:double_underline].freeze
59
+ UNDERLINE_OFF = self[:underline_off].freeze
60
+
61
+ CURLY_UNDERLINE = self[:curly_underline].freeze
62
+ CURLY_UNDERLINE_OFF = self[:curly_underline_off].freeze
63
+
64
+ BLINK = self[:blink].freeze
65
+ BLINK_OFF = self[:blink_off].freeze
66
+
67
+ INVERT = self[:invert].freeze
68
+ INVERT_OFF = self[:invert_off].freeze
69
+
70
+ HIDE = self[:hide].freeze
71
+ REVEAL = self[:reveal].freeze
72
+ end
73
+ end
@@ -2,41 +2,77 @@
2
2
 
3
3
  require_relative 'ansi'
4
4
  require_relative 'wrapper'
5
+ require_relative 'line_animation'
5
6
 
6
7
  module NattyUI
7
8
  class AnsiWrapper < Wrapper
8
9
  def ansi? = true
9
10
 
11
+ def puts(*args, **kwargs)
12
+ return super if args.empty? || (animation = kwargs[:animation]).nil?
13
+
14
+ prefix = kwargs[:prefix]
15
+ if prefix && !prefix.empty?
16
+ prefix = NattyUI.embellish(prefix)
17
+ prefix_width = kwargs[:prefix_width] || NattyUI.display_width(prefix)
18
+ else
19
+ prefix = nil
20
+ prefix_width = 0
21
+ end
22
+ kwargs[:prefix] = prefix
23
+ kwargs[:prefix_width] = prefix_width
24
+
25
+ suffix = kwargs[:suffix]
26
+ suffix = suffix.empty? ? nil : NattyUI.embellish(suffix) if suffix
27
+
28
+ mw = kwargs[:max_width]
29
+ unless mw
30
+ mw = screen_columns - prefix_width
31
+ mw -= kwargs[:suffix_width] || NattyUI.display_width(suffix) if suffix
32
+ end
33
+ kwargs[:max_width] = mw
34
+
35
+ (@stream << Ansi::CURSOR_HIDE).flush
36
+ animation = LineAnimation[animation].new(@stream, kwargs)
37
+ prefix = "#{Ansi::RESET}#{Ansi::CLL}#{prefix}"
38
+
39
+ NattyUI
40
+ .each_line(
41
+ *args.map! { NattyUI.embellish(Ansi.blemish(_1)) },
42
+ max_width: mw
43
+ )
44
+ .each do |line|
45
+ @stream << prefix
46
+ animation.print(line)
47
+ (@stream << "#{prefix}#{line}#{suffix}\n").flush
48
+ @lines_written += 1
49
+ end
50
+ (@stream << Ansi::CURSOR_SHOW).flush
51
+ self
52
+ end
53
+
10
54
  def page
11
55
  unless block_given?
12
56
  @stream.flush
13
57
  return self
14
58
  end
15
- (@stream << Ansi::BLANK_SLATE).flush
59
+ (@stream << Ansi::SCREEN_BLANK).flush
16
60
  begin
17
61
  yield(self)
18
62
  ensure
19
- (@stream << Ansi::UNBLANK_SLATE).flush
63
+ (@stream << Ansi::SCREEN_UNBLANK).flush
20
64
  end
21
65
  end
22
66
 
23
67
  def cls
24
- (@stream << Ansi::CURSOR_HOME << Ansi::SCREEN_ERASE).flush
68
+ (@stream << Ansi::CLS).flush
25
69
  self
26
70
  end
27
71
 
28
72
  protected
29
73
 
30
74
  def prepare_print(args, kwargs)
31
- prefix = kwargs[:prefix] and prefix = NattyUI.embellish(prefix)
32
- suffix = kwargs[:suffix] and suffix = NattyUI.embellish(suffix)
33
- return ["#{prefix}#{suffix}"] if args.empty?
34
- NattyUI
35
- .each_line(
36
- *args.map! { NattyUI.embellish(_1) },
37
- max_width: max_with(prefix, suffix, kwargs)
38
- )
39
- .map { "#{prefix}#{_1}#{suffix}" }
75
+ _prepare_print(args, kwargs) { NattyUI.embellish(_1) }
40
76
  end
41
77
 
42
78
  def temp_func
@@ -44,7 +80,7 @@ module NattyUI
44
80
  lambda do
45
81
  count = @lines_written - count
46
82
  if count.nonzero?
47
- @stream << Ansi.cursor_line_up(count) << Ansi::SCREEN_ERASE_BELOW
83
+ @stream << Ansi.cursor_prev_line(count) << Ansi.display(:erase_below)
48
84
  @lines_written -= count
49
85
  end
50
86
  @stream.flush
@@ -53,27 +89,28 @@ module NattyUI
53
89
  end
54
90
 
55
91
  class Progress < Progress
56
- def draw(title)
92
+ def draw(title, spinner)
57
93
  @msg =
58
94
  "#{@parent.prefix}#{Ansi[:bold, 39]}➔#{Ansi[:reset, 39]} " \
59
95
  "#{title}#{Ansi::RESET} "
60
96
  (wrapper.stream << @msg << Ansi::CURSOR_HIDE).flush
61
- @msg = "#{Ansi::LINE_CLEAR}#{@msg}"
97
+ @msg = "#{Ansi::CLL}#{@msg}"
62
98
  return @msg << BAR_COLOR if @max_value
63
- @msg << Ansi[:bold, 220]
64
- @indicator = 0
99
+ spinner_color = Ansi[:bold, 220]
100
+ @spinner =
101
+ Enumerator.new do |y|
102
+ spinner.each_char { y << "#{spinner_color}#{_1}" } while true
103
+ end
65
104
  end
66
105
 
67
106
  def redraw
68
- (wrapper.stream << @msg << (@max_value ? fullbar : indicator)).flush
107
+ (wrapper.stream << @msg << (@max_value ? fullbar : @spinner.next)).flush
69
108
  end
70
109
 
71
110
  def end_draw
72
- (wrapper.stream << Ansi::LINE_CLEAR << Ansi::CURSOR_SHOW).flush
111
+ (wrapper.stream << Ansi::CLL << Ansi::CURSOR_SHOW).flush
73
112
  end
74
113
 
75
- def indicator = '◐◓◑◒'[(@indicator += 1) % 4]
76
-
77
114
  def fullbar
78
115
  percent = @value / @max_value
79
116
  count = (30 * percent).to_i
@@ -101,8 +138,8 @@ module NattyUI
101
138
  ensure
102
139
  count = wrapper.lines_written - count
103
140
  if count.nonzero?
104
- wrapper.stream << Ansi.cursor_line_up(count) <<
105
- Ansi::SCREEN_ERASE_BELOW
141
+ wrapper.stream << Ansi.cursor_prev_line(count) <<
142
+ Ansi.display(:erase_below)
106
143
  end
107
144
  wrapper.stream.flush
108
145
  end
@@ -127,6 +164,14 @@ module NattyUI
127
164
 
128
165
  class Framed < Framed
129
166
  def color(str) = "#{Ansi[39]}#{str}#{Ansi::RESET}"
167
+
168
+ def init(deco)
169
+ @prefix = "#{color(deco[0])} "
170
+ @suffix = "#{Ansi.cursor_column(parent.rcol)}#{color(deco[4])}"
171
+ aw = @parent.available_width - 2
172
+ parent.puts(color("#{deco[1]}#{deco[2] * aw}#{deco[3]}"))
173
+ @finish = color("#{deco[5]}#{deco[6] * aw}#{deco[7]}")
174
+ end
130
175
  end
131
176
  private_constant :Framed
132
177
 
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NattyUI
4
+ KEY_MAP =
5
+ Module # generator
6
+ .new do
7
+ def self.add_mods(name, code)
8
+ @mods.each_pair do |mod, prefix|
9
+ @map["\e[1;#{mod}#{code}"] = "#{prefix}+#{name}"
10
+ end
11
+ end
12
+
13
+ def self.add_akey(name, code)
14
+ @map["\e[#{code}"] = name
15
+ add_mods(name, code)
16
+ end
17
+
18
+ def self.add_fkey(name, code)
19
+ @map["\e[#{code}~"] = name
20
+ @mods.each_pair do |mod, prefix|
21
+ @map["\e[#{code};#{mod}~"] = "#{prefix}+#{name}"
22
+ end
23
+ end
24
+
25
+ def self.to_hash
26
+ # control codes
27
+ num = 0
28
+ @map = ('A'..'Z').to_h { [(num += 1).chr, "Ctrl+#{_1}"] }
29
+
30
+ add_akey('Up', 'A')
31
+ add_akey('Down', 'B')
32
+ add_akey('Right', 'C')
33
+ add_akey('Left', 'D')
34
+ add_akey('End', 'F')
35
+ add_akey('Home', 'H')
36
+
37
+ add_mods('F1', 'P')
38
+ add_mods('F2', 'Q')
39
+ add_mods('F3', 'R')
40
+ add_mods('F4', 'S')
41
+
42
+ add_fkey('DEL', '3')
43
+ add_fkey('PgUp', '5')
44
+ add_fkey('PgDown', '6')
45
+ add_fkey('F1', 'F1')
46
+ add_fkey('F2', 'F2')
47
+ add_fkey('F3', 'F3')
48
+ add_fkey('F4', 'F4')
49
+ add_fkey('F5', '15')
50
+ add_fkey('F6', '17')
51
+ add_fkey('F7', '18')
52
+ add_fkey('F8', '19')
53
+ add_fkey('F9', '20')
54
+ add_fkey('F10', '21')
55
+ add_fkey('F11', '23')
56
+ add_fkey('F12', '24')
57
+ add_fkey('F13', '25')
58
+ add_fkey('F14', '26')
59
+ add_fkey('F15', '28')
60
+ add_fkey('F16', '29')
61
+ add_fkey('F17', '31')
62
+ add_fkey('F18', '32')
63
+ add_fkey('F19', '33')
64
+ add_fkey('F20', '34')
65
+
66
+ # overrides and additionals
67
+ @map.merge!(
68
+ "\e" => 'ESC',
69
+ "\4" => 'DEL', # = Ctrl+D
70
+ "\u007F" => 'BACK',
71
+ "\b" => 'Ctrl+BACK',
72
+ "\r" => 'ENTER', # = Ctrl+M
73
+ "\t" => 'TAB',
74
+ "\e[Z" => 'Shift+TAB',
75
+ "\e[5" => 'PgUp',
76
+ "\e[6" => 'PgDown',
77
+ # SS3 control (VT 100 etc)
78
+ "\eOA" => 'Up',
79
+ "\eOB" => 'Down',
80
+ "\eOC" => 'Right',
81
+ "\eOD" => 'Left',
82
+ "\eOP" => 'F1',
83
+ "\eOQ" => 'F2',
84
+ "\eOR" => 'F3',
85
+ "\eOS" => 'F4',
86
+ "\eO2P" => 'Shift+F1',
87
+ "\eO2Q" => 'Shift+F2',
88
+ "\eO2R" => 'Shift+F3',
89
+ "\eO2S" => 'Shift+F4',
90
+ "\eOt" => 'F5',
91
+ "\eOu" => 'F6',
92
+ "\eOv" => 'F7',
93
+ "\eOw" => 'F8',
94
+ "\eOl" => 'F9',
95
+ "\eOx" => 'F10'
96
+ )
97
+ end
98
+
99
+ @mods = {
100
+ 2 => 'Shift',
101
+ 3 => 'Alt',
102
+ 4 => 'Alt-Shift',
103
+ 5 => 'Ctrl',
104
+ 6 => 'Ctrl-Shift',
105
+ 7 => 'Ctrl-Alt',
106
+ 8 => 'Ctrl-Alt-Shift',
107
+ 9 => 'Meta',
108
+ 10 => 'Meta-Shift',
109
+ 11 => 'Meta-Alt',
110
+ 12 => 'Meta-Alt-Shift',
111
+ 13 => 'Ctrl-Meta',
112
+ 14 => 'Ctrl-Meta-Shift',
113
+ 15 => 'Ctrl-Meta-Alt',
114
+ 16 => 'Ctrl-Meta-Alt-Shift'
115
+ }.compare_by_identity
116
+ end
117
+ .to_hash
118
+ .freeze
119
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NattyUI
4
+ module LineAnimation
5
+ class Default < None
6
+ def initialize(*_)
7
+ super
8
+ @column = @options[:prefix_width] + 1
9
+ @color = color
10
+ @num = 0
11
+ end
12
+
13
+ def print(line)
14
+ line = plain(line)
15
+ time = 0.5 / line.size
16
+ @stream << @color
17
+ if (@num += 1).odd?
18
+ line.each_char do |char|
19
+ (@stream << char).flush
20
+ sleep(time)
21
+ end
22
+ else
23
+ pos = @column + line.size
24
+ line.reverse!
25
+ line.each_char do |char|
26
+ (@stream << Ansi.cursor_column(pos -= 1) << char).flush
27
+ sleep(time)
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ define default: Default
34
+ end
35
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NattyUI
4
+ module LineAnimation
5
+ class Matrix < None
6
+ def initialize(*_)
7
+ super
8
+ @prefix = "#{to_column}#{color}"
9
+ end
10
+
11
+ def print(line)
12
+ line = plain(line)
13
+ str = Array.new(line.size) { CHARS.sample }.join
14
+ pos = Array.new(line.size, &:itself).shuffle
15
+ until pos.size < 4
16
+ pos.shift(pos.size / 4).each { str[_1] = line[_1] }
17
+ pos.sample(pos.size / 2).each { str[_1] = CHARS.sample }
18
+ (@stream << "#{@prefix}#{str}").flush
19
+ sleep(0.08)
20
+ end
21
+ end
22
+
23
+ CHARS = '2598Z*):.\=+-¦|_ヲアウエオカキケコサシスセソタツテナニヌネハヒホマミムメモヤユラリワ'.chars
24
+ end
25
+
26
+ define matrix: Matrix
27
+ end
28
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NattyUI
4
+ module LineAnimation
5
+ class Rainbow < None
6
+ def initialize(*_)
7
+ super
8
+ @prefix = to_column
9
+ end
10
+
11
+ def print(line)
12
+ line = plain(line)
13
+ 11.upto(200) do |spread|
14
+ (
15
+ @stream << @prefix <<
16
+ Ansi.rainbow(
17
+ line,
18
+ frequence: 0.1,
19
+ spread: spread / 100.0,
20
+ seed: 0
21
+ )
22
+ ).flush
23
+ sleep(0.01)
24
+ end
25
+ end
26
+ end
27
+
28
+ define rainbow: Rainbow
29
+ end
30
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NattyUI
4
+ module LineAnimation
5
+ class Test < None
6
+ def initialize(*_)
7
+ super
8
+ @color = attribute(:color, :default)
9
+ end
10
+
11
+ def print(line, column)
12
+ prefix = "#{Ansi.cursor_column(column)}#{@color}"
13
+ line = plain(line)
14
+ str = Array.new(line.size) { CHARS.sample }.join
15
+ pos = Array.new(line.size, &:itself).shuffle
16
+ until pos.size < 4
17
+ pos.shift(pos.size / 4).each { str[_1] = line[_1] }
18
+ pos.sample(pos.size / 2).each { str[_1] = CHARS.sample }
19
+ (@stream << "#{prefix}#{str}").flush
20
+ sleep(0.08)
21
+ end
22
+ end
23
+
24
+ CHARS = '2598Z*):.\=+-¦|_ヲアウエオカキケコサシスセソタツテナニヌネハヒホマミムメモヤユラリワ'.chars
25
+ end
26
+
27
+ define test: Test
28
+ end
29
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NattyUI
4
+ module LineAnimation
5
+ class TypeWriter < None
6
+ def initialize(*_)
7
+ super
8
+ @color = color
9
+ @cursor_color = attribute(:cursor_color, 0x2e)
10
+ @column = @options[:prefix_width] + 1
11
+ @num = 0
12
+ end
13
+
14
+ def print(line)
15
+ line = plain(line)
16
+ if (@num += 1).odd?
17
+ line.each_char do |char|
18
+ cursor(char)
19
+ (@stream << char).flush
20
+ end
21
+ else
22
+ pos = @column + line.size
23
+ line.reverse!
24
+ line.each_char do |char|
25
+ @stream << Ansi.cursor_column(pos -= 1)
26
+ cursor(char)
27
+ (@stream << char).flush
28
+ end
29
+ end
30
+ end
31
+
32
+ def cursor(char)
33
+ return sleep(0.016) if SPACE.match?(char)
34
+ @stream << @cursor_color
35
+ '▁▂▃▄▅▆▇█'.each_char do |cursor|
36
+ (@stream << cursor).flush
37
+ sleep(0.002)
38
+ @stream << CURSOR_BACK
39
+ end
40
+ @stream << @color
41
+ end
42
+
43
+ def print_org(line)
44
+ plain(line).each_char do |char|
45
+ if SPACE.match?(char)
46
+ sleep(0.016)
47
+ else
48
+ @stream << @cursor_color
49
+ '▁▂▃▄▅▆▇█'.each_char do |cursor|
50
+ (@stream << cursor).flush
51
+ sleep(0.002)
52
+ @stream << CURSOR_BACK
53
+ end
54
+ end
55
+ (@stream << @color << char).flush
56
+ end
57
+ end
58
+
59
+ CURSOR_BACK = Ansi.cursor_back(1)
60
+ end
61
+
62
+ define type_writer: TypeWriter
63
+ end
64
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'ansi'
4
+
5
+ module NattyUI
6
+ module LineAnimation
7
+ def self.defined = @defined.keys
8
+ def self.defined?(name) = @defined.key?(name)
9
+ def self.define(**kwargs) = @defined.merge!(kwargs)
10
+
11
+ def self.[](name)
12
+ return if name.nil?
13
+ klass = @defined[name] || @defined[:default]
14
+ return klass unless klass.is_a?(String)
15
+ require(klass)
16
+ klass = @defined[name] and return klass
17
+ raise(LoadError, "unknown animation - #{name}")
18
+ end
19
+
20
+ class None
21
+ def initialize(stream, options)
22
+ @stream = stream
23
+ @options = options
24
+ end
25
+
26
+ def print(_line) = nil
27
+
28
+ protected
29
+
30
+ def to_column = Ansi.cursor_column(@options[:prefix_width] + 1)
31
+ def color = attribute(:color, :default)
32
+ def plain(str) = NattyUI.plain(str, ansi: false)
33
+
34
+ def attribute(name, *default)
35
+ att = @options[name] or return Ansi[*default]
36
+ return Ansi[*att] if att.is_a?(Enumerable)
37
+ Ansi.try_convert(att.to_s) || Ansi[*default]
38
+ end
39
+
40
+ SPACE = /[[:space:]]/
41
+ end
42
+
43
+ dir = __dir__
44
+ @defined = {
45
+ default: "#{dir}/line_animation/default",
46
+ matrix: "#{dir}/line_animation/matrix",
47
+ rainbow: "#{dir}/line_animation/rainbow",
48
+ type_writer: "#{dir}/line_animation/type_writer",
49
+ test: "#{dir}/line_animation/test"
50
+ }.compare_by_identity
51
+ end
52
+
53
+ private_constant :LineAnimation
54
+ end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module NattyUI
4
4
  # The version number of the gem.
5
- VERSION = '0.8.0'
5
+ VERSION = '0.9.0'
6
6
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NattyUI
4
+ module Features
5
+ # Print given arguments line-wise with animation.
6
+ #
7
+ # @overload animate(..., animation: :default)
8
+ # @param [#to_s] ... objects to print
9
+ # @param [:default, :matrix, :rainbow, :type_writer]
10
+ # animation type of animation
11
+ # @return [Wrapper::Section, Wrapper] it's parent object
12
+ def animate(*args, **kwargs)
13
+ kwargs[:animation] ||= :default
14
+ puts(*args, **kwargs)
15
+ end
16
+ end
17
+ end
@@ -46,19 +46,13 @@ module NattyUI
46
46
  yes, no = grab(yes, no)
47
47
  draw(question)
48
48
  while true
49
- char = NattyUI.in_stream.getch
49
+ char = NattyUI.read_key(mode: :raw)
50
50
  return if "\3\4".include?(char)
51
51
  return true if yes.include?(char)
52
52
  return false if no.include?(char)
53
53
  end
54
- rescue Interrupt, SystemCallError
55
- nil
56
54
  ensure
57
- if @parent.ansi?
58
- (wrapper.stream << Ansi::LINE_CLEAR).flush
59
- else
60
- @parent.puts
61
- end
55
+ wrapper.ansi? ? (wrapper.stream << Ansi::CLL).flush : @parent.puts
62
56
  end
63
57
 
64
58
  def draw(question)