natty-ui 0.8.0 → 0.9.1
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/.yardopts +3 -2
- data/examples/24bit-colors.rb +11 -7
- data/examples/3bit-colors.rb +3 -2
- data/examples/8bit-colors.rb +2 -2
- data/examples/animate.rb +24 -0
- data/examples/attributes.rb +20 -65
- data/examples/demo.rb +39 -203
- data/examples/illustration.rb +27 -23
- data/examples/{list_in_columns.rb → ls.rb} +11 -11
- data/examples/message.rb +30 -0
- data/examples/progress.rb +2 -2
- data/examples/query.rb +6 -2
- data/examples/read_key.rb +13 -0
- data/examples/table.rb +26 -8
- data/lib/natty-ui/ansi.rb +364 -328
- data/lib/natty-ui/ansi_constants.rb +73 -0
- data/lib/natty-ui/ansi_wrapper.rb +68 -23
- data/lib/natty-ui/key_map.rb +119 -0
- data/lib/natty-ui/line_animation/default.rb +35 -0
- data/lib/natty-ui/line_animation/matrix.rb +28 -0
- data/lib/natty-ui/line_animation/rainbow.rb +30 -0
- data/lib/natty-ui/line_animation/test.rb +29 -0
- data/lib/natty-ui/line_animation/type_writer.rb +64 -0
- data/lib/natty-ui/line_animation.rb +54 -0
- data/lib/natty-ui/version.rb +1 -1
- data/lib/natty-ui/wrapper/animate.rb +17 -0
- data/lib/natty-ui/wrapper/ask.rb +2 -8
- data/lib/natty-ui/wrapper/framed.rb +26 -21
- data/lib/natty-ui/wrapper/heading.rb +1 -1
- data/lib/natty-ui/wrapper/horizontal_rule.rb +3 -3
- data/lib/natty-ui/wrapper/list_in_columns.rb +7 -4
- data/lib/natty-ui/wrapper/message.rb +4 -4
- data/lib/natty-ui/wrapper/progress.rb +33 -8
- data/lib/natty-ui/wrapper/query.rb +9 -13
- data/lib/natty-ui/wrapper/request.rb +2 -2
- data/lib/natty-ui/wrapper/section.rb +25 -11
- data/lib/natty-ui/wrapper/table.rb +11 -11
- data/lib/natty-ui/wrapper.rb +19 -14
- data/lib/natty-ui.rb +64 -32
- metadata +16 -5
- data/examples/illustration.png +0 -0
@@ -11,8 +11,7 @@ module NattyUI
|
|
11
11
|
# {Wrapper::Element#close}.
|
12
12
|
#
|
13
13
|
# @param [Array<#to_s>] args more objects to print
|
14
|
-
# @param [
|
15
|
-
# valid types are `:rounded`, `:simple`, `:heavy`, `:semi`, `:double`
|
14
|
+
# @param [:block, :double, :heavy, :rounded, :semi, :simple] type frame type
|
16
15
|
# @yieldparam [Wrapper::Framed] framed the created section
|
17
16
|
# @return [Object] the result of the code block
|
18
17
|
# @return [Wrapper::Framed] itself, when no code block is given
|
@@ -30,30 +29,36 @@ module NattyUI
|
|
30
29
|
protected
|
31
30
|
|
32
31
|
def initialize(parent, type:)
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
color("#{@type[0]}#{@type[1] * (parent.available_width - 2)}")
|
37
|
-
)
|
38
|
-
super(parent, prefix: "#{color(@type[4])} ", prefix_width: 2)
|
39
|
-
@suffix = ' '
|
40
|
-
@suffix_width = 1
|
32
|
+
deco = as_deco(type)
|
33
|
+
super(parent, prefix: "#{deco[0]} ", prefix_width: 2, suffix_width: 2)
|
34
|
+
init(deco)
|
41
35
|
end
|
42
36
|
|
43
|
-
def
|
44
|
-
|
45
|
-
|
46
|
-
)
|
37
|
+
def as_deco(type)
|
38
|
+
if type.is_a?(Symbol)
|
39
|
+
ret = DECO[type] and return ret
|
40
|
+
elsif type.is_a?(String)
|
41
|
+
return type if type.size == 8
|
42
|
+
return type * 8 if type.size == 1
|
43
|
+
end
|
44
|
+
raise(ArgumentError, "invalid frame type - #{type.inspect}")
|
47
45
|
end
|
48
46
|
|
49
|
-
def
|
47
|
+
def init(deco)
|
48
|
+
aw = @parent.available_width - 1
|
49
|
+
parent.puts("#{deco[1]}#{deco[2] * aw}")
|
50
|
+
@finish = "#{deco[5]}#{deco[6] * aw}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def finish = @parent.puts(@finish)
|
50
54
|
|
51
|
-
|
52
|
-
rounded: '
|
53
|
-
simple: '
|
54
|
-
heavy: '
|
55
|
-
double: '
|
56
|
-
semi: '
|
55
|
+
DECO = {
|
56
|
+
rounded: '│╭─╮│╰─╯',
|
57
|
+
simple: '│┌─┐│└─┘',
|
58
|
+
heavy: '┃┏━┓┃┗━┛',
|
59
|
+
double: '║╔═╗║╚═╝',
|
60
|
+
semi: '│╒═╕│╘═╛',
|
61
|
+
block: '▌▛▀▜▐▙▄▟'
|
57
62
|
}.compare_by_identity.freeze
|
58
63
|
end
|
59
64
|
end
|
@@ -51,7 +51,7 @@ module NattyUI
|
|
51
51
|
@parent.puts(
|
52
52
|
title,
|
53
53
|
prefix: "#{Ansi[39]}#{enclose} #{Ansi[:bold, 255]}",
|
54
|
-
suffix: " #{Ansi[:
|
54
|
+
suffix: " #{Ansi[:bold_off, 39]}#{enclose}#{Ansi::RESET}",
|
55
55
|
max_width: available_width - 2 - (enclose.size * 2)
|
56
56
|
)
|
57
57
|
end
|
@@ -8,7 +8,7 @@ module NattyUI
|
|
8
8
|
#
|
9
9
|
# @param [#to_s] symbol string to build the horizontal rule
|
10
10
|
# @return [Wrapper::Section, Wrapper] it's parent object
|
11
|
-
def hr(symbol = '
|
11
|
+
def hr(symbol = '─') = _element(:HorizontalRule, symbol)
|
12
12
|
end
|
13
13
|
|
14
14
|
class Wrapper
|
@@ -23,8 +23,8 @@ module NattyUI
|
|
23
23
|
size = NattyUI.display_width(symbol = symbol.to_s)
|
24
24
|
return @parent.puts if size == 0
|
25
25
|
max_width = available_width
|
26
|
-
@parent.
|
27
|
-
symbol * ((max_width / size)
|
26
|
+
@parent.print(
|
27
|
+
symbol * ((max_width / size)),
|
28
28
|
max_width: max_width,
|
29
29
|
prefix: Ansi[39],
|
30
30
|
prefix_width: 0,
|
@@ -25,7 +25,7 @@ module NattyUI
|
|
25
25
|
#
|
26
26
|
# @example ordered list, start at 100
|
27
27
|
# ui.ls('apple', 'banana', 'blueberry', 'pineapple', 'strawberry', glyph: 100)
|
28
|
-
# # =>
|
28
|
+
# # => 100 apple 101 banana 102 blueberry 103 pineapple 104 strawberry
|
29
29
|
#
|
30
30
|
# @example ordered list using, uppercase characters
|
31
31
|
# ui.ls('apple', 'banana', 'blueberry', 'pineapple', 'strawberry', glyph: :A)
|
@@ -84,7 +84,11 @@ module NattyUI
|
|
84
84
|
->(s) { "#{(glyph += 1).to_s.rjust(pad)} #{NattyUI.embellish(s)}" }
|
85
85
|
when Symbol
|
86
86
|
lambda do |s|
|
87
|
-
"#{
|
87
|
+
"#{
|
88
|
+
t = glyph
|
89
|
+
glyph = glyph.succ
|
90
|
+
t
|
91
|
+
} #{NattyUI.embellish(s)}"
|
88
92
|
end
|
89
93
|
else
|
90
94
|
->(s) { "#{glyph} #{NattyUI.embellish(s)}" }
|
@@ -122,8 +126,7 @@ module NattyUI
|
|
122
126
|
end
|
123
127
|
|
124
128
|
def fill(ary, size)
|
125
|
-
diff = size - ary.size
|
126
|
-
ary.fill(nil, ary.size, diff) if diff.positive?
|
129
|
+
(diff = size - ary.size).positive? && ary.fill(nil, ary.size, diff)
|
127
130
|
end
|
128
131
|
|
129
132
|
Item =
|
@@ -96,13 +96,13 @@ module NattyUI
|
|
96
96
|
def initialize(parent, title:, glyph:)
|
97
97
|
glyph = parent.wrapper.glyph(glyph) || glyph
|
98
98
|
prefix_width = NattyUI.display_width(glyph) + 1
|
99
|
-
|
100
|
-
|
101
|
-
prefix:
|
99
|
+
super(
|
100
|
+
parent,
|
101
|
+
prefix: ' ' * prefix_width,
|
102
102
|
prefix_width: prefix_width,
|
103
103
|
suffix_width: 0
|
104
104
|
)
|
105
|
-
|
105
|
+
parent.puts(title, prefix: "#{glyph} ", prefix_width: prefix_width)
|
106
106
|
end
|
107
107
|
end
|
108
108
|
end
|
@@ -7,14 +7,23 @@ module NattyUI
|
|
7
7
|
module Features
|
8
8
|
# Creates progress element implementing additional {ProgressAttributes}.
|
9
9
|
#
|
10
|
-
#
|
11
|
-
#
|
10
|
+
# When a `max_value` is given, the progress will by displayed as a bar.
|
11
|
+
# Otherwise the `spinner` is used for a little animation.
|
12
|
+
#
|
13
|
+
# When no pre-defined spinner is specified then spinner will be
|
14
|
+
# used char-wise as a string for the progress animation.
|
15
|
+
#
|
16
|
+
# A progress element has additional states and can be closed with
|
17
|
+
# {#completed} or {#failed}.
|
12
18
|
#
|
13
19
|
# @param [#to_s] title object to print as progress title
|
14
|
-
# @param [
|
20
|
+
# @param [#to_f] max_value maximum value of the progress
|
21
|
+
# @param [:bar, :blink, :blocks, :braile, :circle, :colors, :pulse,
|
22
|
+
# :snake, :swap, :triangles, :vintage, #to_s] spinner type of spinner or
|
23
|
+
# spinner elements
|
15
24
|
# @return [Wrapper::Progress] the created progress element
|
16
|
-
def progress(title, max_value: nil)
|
17
|
-
_element(:Progress, title, max_value)
|
25
|
+
def progress(title, max_value: nil, spinner: :pulse)
|
26
|
+
_element(:Progress, title, max_value, spinner)
|
18
27
|
end
|
19
28
|
end
|
20
29
|
|
@@ -29,15 +38,31 @@ module NattyUI
|
|
29
38
|
|
30
39
|
protected
|
31
40
|
|
32
|
-
def call(title, max_value)
|
41
|
+
def call(title, max_value, spinner)
|
33
42
|
@final_text = [title]
|
34
43
|
@max_value = [0, max_value.to_f].max if max_value
|
35
44
|
@value = @progress = 0
|
36
|
-
draw(title)
|
45
|
+
draw(title, SPINNER[spinner] || spinner.to_s)
|
37
46
|
self
|
38
47
|
end
|
39
48
|
|
40
|
-
|
49
|
+
SPINNER = {
|
50
|
+
bar: '▁▂▃▄▅▆▇█▇▆▅▄▃▂',
|
51
|
+
blink: '■□▪▫',
|
52
|
+
blocks: '▖▘▝▗',
|
53
|
+
braile: '⣷⣯⣟⡿⢿⣻⣽⣾',
|
54
|
+
braile_reverse: '⡿⣟⣯⣷⣾⣽⣻⢿',
|
55
|
+
circle: '◐◓◑◒',
|
56
|
+
colors: '🟨🟧🟥🟦🟪🟩',
|
57
|
+
pulse: '•✺◉●◉✺',
|
58
|
+
snake: '⠁⠉⠙⠸⢰⣠⣄⡆⠇⠃',
|
59
|
+
swap: '㊂㊀㊁',
|
60
|
+
triangles: '◢◣◤◥',
|
61
|
+
vintage: '-\\|/'
|
62
|
+
}.compare_by_identity.freeze
|
63
|
+
private_constant :SPINNER
|
64
|
+
|
65
|
+
def draw(title, _spinner)
|
41
66
|
(wrapper.stream << @parent.prefix << "➔ #{title} ").flush
|
42
67
|
end
|
43
68
|
|
@@ -63,28 +63,24 @@ module NattyUI
|
|
63
63
|
end
|
64
64
|
|
65
65
|
def as_choices(choices, kw_choices)
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
.transform_values! { _1.to_s.tr("\r\n\t", ' ') }
|
74
|
-
)
|
66
|
+
choices
|
67
|
+
.take(9)
|
68
|
+
.each_with_index
|
69
|
+
.to_h { |str, i| [i + 1, str] }
|
70
|
+
.merge!(kw_choices)
|
71
|
+
.transform_keys!(&:to_s)
|
72
|
+
.transform_values! { _1.to_s.gsub(/[[:space:]]/, ' ') }
|
75
73
|
end
|
76
74
|
|
77
75
|
def read(choices, result)
|
78
76
|
while true
|
79
|
-
char = NattyUI.
|
80
|
-
return if
|
77
|
+
char = NattyUI.read_key
|
78
|
+
return if char == 'Ctrl+C'
|
81
79
|
next unless choices.key?(char)
|
82
80
|
return char if result == :char
|
83
81
|
return choices[char] if result == :title
|
84
82
|
return char, choices[char]
|
85
83
|
end
|
86
|
-
rescue Interrupt, SystemCallError
|
87
|
-
nil
|
88
84
|
end
|
89
85
|
|
90
86
|
CHOICE_MARK = Ansi[:bold, 34]
|
@@ -46,8 +46,8 @@ module NattyUI
|
|
46
46
|
(wrapper.stream << ANSI_PREFIX).flush if wrapper.ansi?
|
47
47
|
end
|
48
48
|
|
49
|
-
ANSI_PREFIX = Ansi::RESET + Ansi
|
50
|
-
ANSI_FINISH = Ansi::RESET + Ansi::
|
49
|
+
ANSI_PREFIX = Ansi::RESET + Ansi::ITALIC
|
50
|
+
ANSI_FINISH = Ansi::RESET + Ansi::LINE_PREVIOUS + Ansi::LINE_ERASE
|
51
51
|
private_constant :ANSI_PREFIX, :ANSI_FINISH
|
52
52
|
end
|
53
53
|
end
|
@@ -8,10 +8,14 @@ module NattyUI
|
|
8
8
|
# into the section.
|
9
9
|
#
|
10
10
|
# @param [Array<#to_s>] args optional objects to print
|
11
|
+
# @param [String] prefix used for each printed line
|
12
|
+
# @param [String] suffix used for each printed line
|
11
13
|
# @yieldparam [Wrapper::Section] section the created section
|
12
14
|
# @return [Object] the result of the code block
|
13
15
|
# @return [Wrapper::Section] itself, when no code block is given
|
14
|
-
def section(*args,
|
16
|
+
def section(*args, prefix: ' ', suffix: ' ', &block)
|
17
|
+
_section(:Section, args, prefix: prefix, suffix: suffix, &block)
|
18
|
+
end
|
15
19
|
alias sec section
|
16
20
|
end
|
17
21
|
|
@@ -38,10 +42,12 @@ module NattyUI
|
|
38
42
|
return self if @status
|
39
43
|
@parent.puts(
|
40
44
|
*args,
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
50
|
+
)
|
45
51
|
)
|
46
52
|
self
|
47
53
|
end
|
@@ -55,10 +61,12 @@ module NattyUI
|
|
55
61
|
return self if @status
|
56
62
|
@parent.print(
|
57
63
|
*args,
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
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
|
69
|
+
)
|
62
70
|
)
|
63
71
|
self
|
64
72
|
end
|
@@ -87,17 +95,23 @@ module NattyUI
|
|
87
95
|
# @!visibility private
|
88
96
|
def inspect = @status ? "#{_to_s[..-2]} status=#{@status}>" : _to_s
|
89
97
|
|
98
|
+
# @!visibility private
|
99
|
+
def rcol = @parent.rcol - @suffix_width
|
100
|
+
|
90
101
|
protected
|
91
102
|
|
92
103
|
def initialize(
|
93
104
|
parent,
|
94
105
|
prefix:,
|
95
|
-
prefix_width: NattyUI.display_width(prefix)
|
106
|
+
prefix_width: NattyUI.display_width(prefix),
|
107
|
+
suffix: nil,
|
108
|
+
suffix_width: NattyUI.display_width(suffix)
|
96
109
|
)
|
97
110
|
super(parent)
|
98
111
|
@prefix = prefix
|
112
|
+
@suffix = suffix
|
99
113
|
@prefix_width = prefix_width
|
100
|
-
@suffix_width =
|
114
|
+
@suffix_width = suffix_width
|
101
115
|
end
|
102
116
|
end
|
103
117
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require_relative 'element'
|
3
4
|
|
4
5
|
module NattyUI
|
@@ -10,11 +11,13 @@ module NattyUI
|
|
10
11
|
# construction. This means table features are not complete defined and
|
11
12
|
# may change in near future.
|
12
13
|
#
|
14
|
+
# Defined values for `type` are
|
15
|
+
# :double, :heavy, :semi, :simple
|
16
|
+
#
|
13
17
|
# @overload table(type: simple)
|
14
18
|
# Construct and display a table.
|
15
19
|
#
|
16
|
-
# @param [Symbol] type frame type
|
17
|
-
# valid types are `:simple`, `:heavy`, `:semi`, `:double`
|
20
|
+
# @param [Symbol] type frame type
|
18
21
|
# @yieldparam [Table] table construction helper
|
19
22
|
# @return [Wrapper::Section, Wrapper] it's parent object
|
20
23
|
#
|
@@ -39,8 +42,7 @@ module NattyUI
|
|
39
42
|
# Display the given arrays as rows of a table.
|
40
43
|
#
|
41
44
|
# @param [Array<#to_s>] args one or more arrays representing rows of the table
|
42
|
-
# @param [Symbol] type frame type
|
43
|
-
# valid types are `:simple`, `:heavy`, `:semi`, `:double`
|
45
|
+
# @param [Symbol] type frame type
|
44
46
|
# @return [Wrapper::Section, Wrapper] it's parent object
|
45
47
|
#
|
46
48
|
# @example
|
@@ -115,7 +117,7 @@ module NattyUI
|
|
115
117
|
raise(ArgumentError, "invalid table type - #{type.inspect}"),
|
116
118
|
Ansi[39],
|
117
119
|
Ansi::RESET
|
118
|
-
) {
|
120
|
+
) { @parent.puts(_1) }
|
119
121
|
@parent
|
120
122
|
end
|
121
123
|
|
@@ -142,7 +144,7 @@ module NattyUI
|
|
142
144
|
@parent.available_width - 1,
|
143
145
|
seperator,
|
144
146
|
NattyUI.plain(seperator, ansi: false)[-1] == ' '
|
145
|
-
) {
|
147
|
+
) { @parent.puts(_1) }
|
146
148
|
@parent
|
147
149
|
end
|
148
150
|
end
|
@@ -217,7 +219,7 @@ module NattyUI
|
|
217
219
|
|
218
220
|
def align(str, width, alignment)
|
219
221
|
return str unless (width -= NattyUI.display_width(str)).positive?
|
220
|
-
return str
|
222
|
+
return str + (' ' * width) if alignment == :left
|
221
223
|
(' ' * width) << str
|
222
224
|
end
|
223
225
|
|
@@ -268,10 +270,8 @@ module NattyUI
|
|
268
270
|
|
269
271
|
def adjusted_widths(col_widths)
|
270
272
|
ret = col_widths.dup
|
271
|
-
|
272
|
-
|
273
|
-
return ret
|
274
|
-
end
|
273
|
+
left = @max_width - (@col_div_size * (col_widths.size - 1))
|
274
|
+
return ret if ret.sum <= left
|
275
275
|
indexed = ret.each_with_index.to_a
|
276
276
|
# TODO: optimize this!
|
277
277
|
until ret.sum <= left
|
data/lib/natty-ui/wrapper.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'io/console'
|
4
4
|
require_relative 'ansi'
|
5
|
+
require_relative 'wrapper/animate'
|
5
6
|
require_relative 'wrapper/ask'
|
6
7
|
require_relative 'wrapper/framed'
|
7
8
|
require_relative 'wrapper/heading'
|
@@ -65,7 +66,7 @@ module NattyUI
|
|
65
66
|
def print(*args, **kwargs)
|
66
67
|
args = prepare_print(args, kwargs).to_a
|
67
68
|
@lines_written += args.size - 1
|
68
|
-
@stream.print(args
|
69
|
+
@stream.print(*args)
|
69
70
|
@stream.flush
|
70
71
|
self
|
71
72
|
end
|
@@ -148,7 +149,10 @@ module NattyUI
|
|
148
149
|
alias available_width screen_columns
|
149
150
|
|
150
151
|
# @!visibility private
|
151
|
-
|
152
|
+
alias rcol screen_columns
|
153
|
+
|
154
|
+
# @!visibility private
|
155
|
+
def prefix = nil
|
152
156
|
|
153
157
|
# @return [Array<Symbol>] available glyph names
|
154
158
|
def glyph_names = GLYPHS.keys
|
@@ -164,25 +168,26 @@ module NattyUI
|
|
164
168
|
protected
|
165
169
|
|
166
170
|
def prepare_print(args, kwargs)
|
167
|
-
|
168
|
-
|
171
|
+
_prepare_print(args, kwargs) { NattyUI.plain(_1, ansi: false) }
|
172
|
+
end
|
173
|
+
|
174
|
+
def _prepare_print(args, kwargs, &cvt)
|
175
|
+
prefix = kwargs[:prefix] and prefix = prefix.empty? ? '' : cvt[prefix]
|
176
|
+
suffix = kwargs[:suffix] and suffix = suffix.empty? ? '' : cvt[suffix]
|
169
177
|
return ["#{prefix}#{suffix}"] if args.empty?
|
170
178
|
NattyUI
|
171
179
|
.each_line(
|
172
|
-
*args.map!
|
173
|
-
max_width:
|
180
|
+
*args.map!(&cvt),
|
181
|
+
max_width:
|
182
|
+
kwargs.fetch(:max_width) do
|
183
|
+
screen_columns -
|
184
|
+
kwargs.fetch(:prefix_width) { NattyUI.display_width(prefix) } -
|
185
|
+
kwargs.fetch(:suffix_width) { NattyUI.display_width(suffix) }
|
186
|
+
end
|
174
187
|
)
|
175
188
|
.map { "#{prefix}#{_1}#{suffix}" }
|
176
189
|
end
|
177
190
|
|
178
|
-
def max_with(prefix, suffix, kwargs)
|
179
|
-
mw = kwargs[:max_width] and return mw
|
180
|
-
mw = screen_columns
|
181
|
-
mw -= kwargs[:prefix_width] || NattyUI.display_width(prefix) if prefix
|
182
|
-
mw -= kwargs[:suffix_width] || NattyUI.display_width(suffix) if suffix
|
183
|
-
mw
|
184
|
-
end
|
185
|
-
|
186
191
|
def temp_func
|
187
192
|
lambda do
|
188
193
|
@stream.flush
|
data/lib/natty-ui.rb
CHANGED
@@ -83,18 +83,19 @@ module NattyUI
|
|
83
83
|
def embellish(str)
|
84
84
|
return +'' if (str = str.to_s).empty?
|
85
85
|
reset = false
|
86
|
-
|
86
|
+
str =
|
87
87
|
str.gsub(/(\[\[((?~\]\]))\]\])/) do
|
88
88
|
match = Regexp.last_match[2]
|
89
|
-
|
89
|
+
if match[0] == '/'
|
90
|
+
next "[[#{match[1..]}]]" if match.size > 1
|
91
|
+
reset = false
|
92
|
+
Ansi::RESET
|
93
|
+
else
|
90
94
|
ansi = Ansi.try_convert(match)
|
91
|
-
|
95
|
+
ansi ? reset = ansi : "[[#{match}]]"
|
92
96
|
end
|
93
|
-
match.empty? or next "[[#{match}]]"
|
94
|
-
reset = false
|
95
|
-
Ansi::RESET
|
96
97
|
end
|
97
|
-
reset ? "#{
|
98
|
+
reset ? "#{str}#{Ansi::RESET}" : str
|
98
99
|
end
|
99
100
|
|
100
101
|
# Remove embedded attribute descriptions from given string.
|
@@ -103,16 +104,13 @@ module NattyUI
|
|
103
104
|
# @param [:keep,:remove] ansi keep or remove ANSI codes too
|
104
105
|
# @return [String] edited string
|
105
106
|
def plain(str, ansi: :keep)
|
107
|
+
return +'' if (str = str.to_s).empty?
|
106
108
|
str =
|
107
|
-
str
|
108
|
-
.
|
109
|
-
.
|
110
|
-
|
111
|
-
|
112
|
-
next match.empty? ? nil : "[[#{match}]]"
|
113
|
-
end
|
114
|
-
Ansi.try_convert(match) ? nil : "[[#{match}]]"
|
115
|
-
end
|
109
|
+
str.gsub(/(\[\[((?~\]\]))\]\])/) do
|
110
|
+
match = Regexp.last_match[2]
|
111
|
+
next match.size == 1 ? nil : "[[#{match[1..]}]]" if match[0] == '/'
|
112
|
+
Ansi.try_convert(match) ? nil : "[[#{match}]]"
|
113
|
+
end
|
116
114
|
ansi == :keep ? str : Ansi.blemish(str)
|
117
115
|
end
|
118
116
|
|
@@ -122,8 +120,21 @@ module NattyUI
|
|
122
120
|
# @param [#to_s] str string to calculate
|
123
121
|
# @return [Integer] the display size
|
124
122
|
def display_width(str)
|
125
|
-
|
126
|
-
|
123
|
+
str = plain(str).encode(Encoding::UTF_8)
|
124
|
+
return 0 if str.empty?
|
125
|
+
width = 0
|
126
|
+
in_zero_width = false
|
127
|
+
str.scan(
|
128
|
+
Reline::Unicode::WIDTH_SCANNER
|
129
|
+
) do |non_printing_start, non_printing_end, _csi, _osc, gc|
|
130
|
+
if in_zero_width
|
131
|
+
in_zero_width = false if non_printing_end
|
132
|
+
next
|
133
|
+
end
|
134
|
+
next in_zero_width = true if non_printing_start
|
135
|
+
width += Reline::Unicode.get_mbchar_width(gc) if gc
|
136
|
+
end
|
137
|
+
width
|
127
138
|
end
|
128
139
|
|
129
140
|
# Convert given arguments into strings and yield each line.
|
@@ -149,9 +160,12 @@ module NattyUI
|
|
149
160
|
str
|
150
161
|
.to_s
|
151
162
|
.each_line(chomp: true) do |line|
|
152
|
-
|
153
|
-
|
154
|
-
|
163
|
+
next yield(line) if line.empty?
|
164
|
+
lines, _height = Reline::Unicode.split_by_width(line, max_width)
|
165
|
+
lines.compact!
|
166
|
+
next if lines.empty?
|
167
|
+
lines.pop if lines[-1].empty?
|
168
|
+
lines.each(&block)
|
155
169
|
end
|
156
170
|
end
|
157
171
|
nil
|
@@ -159,26 +173,38 @@ module NattyUI
|
|
159
173
|
|
160
174
|
# Read next raw key (keyboard input) from {in_stream}.
|
161
175
|
#
|
176
|
+
# The input will be returned as named key codes like "Ctrl+C" by default.
|
177
|
+
# This can be changed by the `mode` parameter:
|
178
|
+
#
|
179
|
+
# - `:named` - name if available (fallback to raw)
|
180
|
+
# - `:raw` - key code "as is"
|
181
|
+
# - `:both` - key code and name if available
|
182
|
+
#
|
183
|
+
# @param [:named, :raw, :both] mode modfies the result
|
162
184
|
# @return [String] read key
|
163
|
-
def read_key
|
185
|
+
def read_key(mode: :named)
|
164
186
|
return @in_stream.getch unless defined?(@in_stream.getc)
|
165
187
|
return @in_stream.getc unless defined?(@in_stream.raw)
|
166
|
-
@in_stream.raw do |
|
167
|
-
key =
|
168
|
-
while (nc =
|
188
|
+
@in_stream.raw do |raw_stream|
|
189
|
+
key = raw_stream.getc
|
190
|
+
while (nc = raw_stream.read_nonblock(1, exception: false))
|
169
191
|
nc.is_a?(String) ? key += nc : break
|
170
192
|
end
|
171
|
-
key
|
193
|
+
return key if mode == :raw
|
194
|
+
return key, KEY_MAP[key]&.dup if mode == :both
|
195
|
+
KEY_MAP[key]&.dup || key
|
172
196
|
end
|
197
|
+
rescue Interrupt, SystemCallError
|
198
|
+
nil
|
173
199
|
end
|
174
200
|
|
175
201
|
private
|
176
202
|
|
177
203
|
def wrapper_class(stream, ansi)
|
178
204
|
return AnsiWrapper if ansi == true
|
179
|
-
if ansi == false || ENV.key?('NO_COLOR')
|
180
|
-
|
181
|
-
|
205
|
+
return Wrapper if ansi == false || ENV.key?('NO_COLOR')
|
206
|
+
return AnsiWrapper if ENV['ANSI'] == '1'
|
207
|
+
return Wrapper if ENV['TERM'] == 'dumb'
|
182
208
|
stream.tty? ? AnsiWrapper : Wrapper
|
183
209
|
end
|
184
210
|
|
@@ -197,8 +223,14 @@ module NattyUI
|
|
197
223
|
|
198
224
|
@element = StdOut
|
199
225
|
self.in_stream = STDIN
|
226
|
+
|
227
|
+
autoload(:KEY_MAP, File.join(__dir__, 'natty-ui', 'key_map'))
|
228
|
+
private_constant :KEY_MAP
|
200
229
|
end
|
201
230
|
|
202
|
-
#
|
203
|
-
|
204
|
-
|
231
|
+
# @!visibility private
|
232
|
+
module Kernel
|
233
|
+
# @see NattyUI.element
|
234
|
+
# @return [NattyUI::Wrapper, NattyUI::Wrapper::Element] active UI element
|
235
|
+
def ui = NattyUI.element unless defined?(ui)
|
236
|
+
end
|