natty-ui 0.28.0 → 0.30.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 +3 -3
- data/examples/examples.rb +4 -5
- data/examples/options.rb +28 -0
- data/lib/natty-ui/attributes.rb +0 -3
- data/lib/natty-ui/choice.rb +21 -14
- data/lib/natty-ui/dumb_choice.rb +2 -1
- data/lib/natty-ui/dumb_options.rb +63 -0
- data/lib/natty-ui/element.rb +3 -4
- data/lib/natty-ui/features.rb +156 -65
- data/lib/natty-ui/ls_renderer.rb +0 -1
- data/lib/natty-ui/options.rb +77 -0
- data/lib/natty-ui/task.rb +1 -1
- data/lib/natty-ui/temporary.rb +5 -6
- data/lib/natty-ui/theme.rb +22 -4
- data/lib/natty-ui/utils.rb +0 -7
- data/lib/natty-ui/version.rb +1 -1
- data/lib/natty-ui.rb +1 -3
- metadata +10 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3573e7d7a6d163f278494a095198e3fd913ff6710c4984ff12279f80893c1b54
|
|
4
|
+
data.tar.gz: 9d424a564160fa470b71d857b31fa725320b21047abd8423e613335b0e0449c6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f2b6c3c4bc0c8636bbed5dcfcfb5cc9f77487ef84be7650abba172b707cf2e9eb9c59b9a4f0ba3c2209ce4902c704f8920cbd99d63d7da3cd347dbf466afe689
|
|
7
|
+
data.tar.gz: 2c514b126360dd2c2b6173e9944db8e3cc1bd5a5505d5176ebaa382cf9a810dccc59137c55d1b18d7f1229ac8cbcee5fa9553ad5763ecea30fa84cabc0430387
|
data/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
This is the beautiful, nice, nifty, fancy, neat, pretty, cool, rich, lovely, natty user interface you like to have for your command line applications. It contains elegant, simple and beautiful tools that enhance your command line interfaces functionally and aesthetically.
|
|
4
4
|
|
|
5
5
|
- Gem: [rubygems.org](https://rubygems.org/gems/natty-ui)
|
|
6
|
-
- Source: [
|
|
6
|
+
- Source: [codeberg.org](https://codeberg.org/mblumtritt/natty-ui)
|
|
7
7
|
- Help: [rubydoc.info](https://rubydoc.info/gems/natty-ui/NattyUI)
|
|
8
8
|
|
|
9
9
|
## Features
|
|
@@ -33,13 +33,13 @@ This is the beautiful, nice, nifty, fancy, neat, pretty, cool, rich, lovely, nat
|
|
|
33
33
|
|
|
34
34
|
You can execute all examples by
|
|
35
35
|
|
|
36
|
-
```
|
|
36
|
+
```shell
|
|
37
37
|
ruby ./examples/examples.rb
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
or see the non-ANSI version
|
|
41
41
|
|
|
42
|
-
```
|
|
42
|
+
```shell
|
|
43
43
|
NO_COLOR=1 ruby ./examples/examples.rb
|
|
44
44
|
```
|
|
45
45
|
|
data/examples/examples.rb
CHANGED
|
@@ -16,11 +16,10 @@ EXAMPLES = {
|
|
|
16
16
|
'vbars' => 'Print Vertical Bars',
|
|
17
17
|
'hbars' => 'Print Horizontal Bars',
|
|
18
18
|
'sections' => 'Sections',
|
|
19
|
-
'tasks' => 'Tasks'
|
|
19
|
+
'tasks' => 'Tasks',
|
|
20
|
+
'options' => 'Options and Selections'
|
|
20
21
|
}.freeze
|
|
21
22
|
|
|
22
|
-
DEFAULT_MODE = NattyUI.input_mode == :default
|
|
23
|
-
|
|
24
23
|
ui.space
|
|
25
24
|
|
|
26
25
|
while true
|
|
@@ -34,12 +33,12 @@ while true
|
|
|
34
33
|
ui.div('[faint](Abort with [\\ESC])', padding: [1, 0, 1])
|
|
35
34
|
end
|
|
36
35
|
|
|
37
|
-
ui.space unless
|
|
36
|
+
ui.space unless Terminal.ansi?
|
|
38
37
|
break unless selected
|
|
39
38
|
|
|
40
39
|
ui.temporary do
|
|
41
40
|
load("#{__dir__}/#{selected}.rb")
|
|
42
|
-
if
|
|
41
|
+
if Terminal.ansi?
|
|
43
42
|
ui.await { ui.puts '[faint][\\Press ENTER to continue...]' }
|
|
44
43
|
else
|
|
45
44
|
ui.space
|
data/examples/options.rb
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../lib/natty-ui'
|
|
4
|
+
|
|
5
|
+
ui.message '[b]ᓚᕠᗢ NattyUI[/b] [i green]Options and Selections[/]' do
|
|
6
|
+
ui.space
|
|
7
|
+
ui.puts <<~INFO, eol: false
|
|
8
|
+
[i]Options[/i] and [i]selections[/i] allow the user to select from several
|
|
9
|
+
options interactively.
|
|
10
|
+
If ANSI is available the user can use [b][Up][/b] and [b][Down][/b] keys to
|
|
11
|
+
navigate, switch the state of selected item with [b][Space][/b] and complete
|
|
12
|
+
the selection with [b][Enter][/b].
|
|
13
|
+
INFO
|
|
14
|
+
|
|
15
|
+
ui.space
|
|
16
|
+
options =
|
|
17
|
+
ui.select %w[Kitty iTerm2 Ghostty Tabby Rio] do
|
|
18
|
+
ui.puts '[i]Which terminal applications did you already tested?[/i]'
|
|
19
|
+
end
|
|
20
|
+
case options.size
|
|
21
|
+
when 0
|
|
22
|
+
ui.puts 'You selected nothing – test more in future!'
|
|
23
|
+
when 1
|
|
24
|
+
ui.puts "#{options.first} is indeed a nice terminal."
|
|
25
|
+
else
|
|
26
|
+
ui.puts "#{options.join(', ')} are worth to test!"
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/natty-ui/attributes.rb
CHANGED
|
@@ -29,7 +29,6 @@ module NattyUI
|
|
|
29
29
|
def _init = nil
|
|
30
30
|
def _assign(_opt) = self
|
|
31
31
|
def _store(opt) = opt
|
|
32
|
-
|
|
33
32
|
def as_uint(value) = [0, value.to_i].max
|
|
34
33
|
def as_nint(value) = ([0, value.to_i].max if value)
|
|
35
34
|
|
|
@@ -555,9 +554,7 @@ module NattyUI
|
|
|
555
554
|
|
|
556
555
|
module TextWithAttributes
|
|
557
556
|
include WithAttributes
|
|
558
|
-
|
|
559
557
|
attr_reader :text
|
|
560
|
-
|
|
561
558
|
def empty? = @text.empty?
|
|
562
559
|
alias _to_s to_s
|
|
563
560
|
private :_to_s
|
data/lib/natty-ui/choice.rb
CHANGED
|
@@ -7,24 +7,31 @@ module NattyUI
|
|
|
7
7
|
def select
|
|
8
8
|
yield(self) if block_given?
|
|
9
9
|
pin_line = NattyUI.lines_written
|
|
10
|
-
draw
|
|
10
|
+
draw
|
|
11
|
+
last = @current
|
|
11
12
|
while (event = Terminal.read_key_event)
|
|
12
13
|
case event.name
|
|
13
14
|
when 'Esc', 'Ctrl+c'
|
|
14
15
|
break nil if @abortable
|
|
15
|
-
when 'Enter', '
|
|
16
|
-
break @ret[current]
|
|
16
|
+
when 'Enter', 'Space'
|
|
17
|
+
break @ret[@current]
|
|
17
18
|
when 'Home'
|
|
18
|
-
current = 0
|
|
19
|
+
@current = 0
|
|
19
20
|
when 'End'
|
|
20
|
-
current = @texts.size - 1
|
|
21
|
-
when 'Up', 'Back', 'Shift+Tab', 'i'
|
|
22
|
-
current = @texts.size - 1 if (current -= 1) < 0
|
|
23
|
-
when 'Down', 'Tab', 'k'
|
|
24
|
-
current = 0 if (current += 1) == @texts.size
|
|
21
|
+
@current = @texts.size - 1
|
|
22
|
+
when 'Up', 'Back', 'Shift+Tab', 'i', 'w'
|
|
23
|
+
@current = @texts.size - 1 if (@current -= 1) < 0
|
|
24
|
+
when 'Down', 'Tab', 'k', 's'
|
|
25
|
+
@current = 0 if (@current += 1) == @texts.size
|
|
26
|
+
else
|
|
27
|
+
next unless event.simple?
|
|
28
|
+
c = event.key.ord
|
|
29
|
+
@current = (c - 48).clamp(0, @texts.size - 1) if c.between?(48, 57)
|
|
25
30
|
end
|
|
31
|
+
next if last == @current
|
|
26
32
|
pin_line = NattyUI.back_to_line(pin_line, erase: false)
|
|
27
|
-
draw
|
|
33
|
+
draw
|
|
34
|
+
last = @current
|
|
28
35
|
end
|
|
29
36
|
ensure
|
|
30
37
|
NattyUI.back_to_line(@start_line)
|
|
@@ -38,17 +45,17 @@ module NattyUI
|
|
|
38
45
|
@texts = args + kwargs.values
|
|
39
46
|
@ret = Array.new(args.size, &:itself) + kwargs.keys
|
|
40
47
|
@abortable = abortable
|
|
41
|
-
@
|
|
48
|
+
@current = @ret.index(selected) || 0
|
|
42
49
|
theme = Theme.current
|
|
43
50
|
@mark = [theme.mark(:choice), theme.choice_style]
|
|
44
51
|
@mark_current = [theme.mark(:current_choice), theme.choice_current_style]
|
|
45
52
|
end
|
|
46
53
|
|
|
47
|
-
def draw
|
|
54
|
+
def draw
|
|
48
55
|
@texts.each_with_index do |str, idx|
|
|
49
|
-
mark,
|
|
56
|
+
mark, style = idx == @current ? @mark_current : @mark
|
|
50
57
|
@parent.puts(
|
|
51
|
-
"#{
|
|
58
|
+
"#{style}#{str}",
|
|
52
59
|
first_line_prefix: mark,
|
|
53
60
|
first_line_prefix_width: mark.width
|
|
54
61
|
)
|
data/lib/natty-ui/dumb_choice.rb
CHANGED
|
@@ -11,7 +11,8 @@ module NattyUI
|
|
|
11
11
|
return if @abortable && %w[Esc Ctrl+c].include?(event.name)
|
|
12
12
|
next unless event.simple?
|
|
13
13
|
code = event.raw.upcase
|
|
14
|
-
if @ret.size <= 9
|
|
14
|
+
if @ret.size <= 9
|
|
15
|
+
next unless ('1'..'9').include?(code)
|
|
15
16
|
code = @ret[code.ord - 49] and break code
|
|
16
17
|
elsif ('A'..'Z').include?(code)
|
|
17
18
|
code = @ret[code.ord - 65] and break code
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'element'
|
|
4
|
+
|
|
5
|
+
module NattyUI
|
|
6
|
+
class DumbOptions < Element
|
|
7
|
+
def select
|
|
8
|
+
yield(self) if block_given?
|
|
9
|
+
draw
|
|
10
|
+
while (event = Terminal.read_key_event)
|
|
11
|
+
return if @abortable && %w[Esc Ctrl+c].include?(event.name)
|
|
12
|
+
return @opts.transform_values(&:last) if event.name == 'Enter'
|
|
13
|
+
next unless event.simple?
|
|
14
|
+
code = event.raw.upcase
|
|
15
|
+
if @opts.size <= 9
|
|
16
|
+
next unless ('1'..'9').include?(code)
|
|
17
|
+
offset = 49
|
|
18
|
+
elsif ('A'..'Z').include?(code)
|
|
19
|
+
offset = 65
|
|
20
|
+
else
|
|
21
|
+
next
|
|
22
|
+
end
|
|
23
|
+
key = @opts.keys[code.ord - offset] or next
|
|
24
|
+
@opts[key][-1] = !@opts[key][-1]
|
|
25
|
+
@parent.space
|
|
26
|
+
draw
|
|
27
|
+
end
|
|
28
|
+
ensure
|
|
29
|
+
@parent.space
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def initialize(parent, opts, abortable, selected)
|
|
35
|
+
super(parent)
|
|
36
|
+
@opts =
|
|
37
|
+
opts.to_h do |k, v|
|
|
38
|
+
v.is_a?(Enumerable) ? [k, [v[0], !!v[-1]]] : [k, [k, !!v]]
|
|
39
|
+
end
|
|
40
|
+
@abortable = abortable
|
|
41
|
+
@current = @opts.key?(selected) ? selected : @opts.first.first
|
|
42
|
+
theme = Theme.current
|
|
43
|
+
@marks = {
|
|
44
|
+
true => theme.mark(:checkmark),
|
|
45
|
+
false => theme.mark(:choice)
|
|
46
|
+
}.compare_by_identity
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def draw
|
|
50
|
+
glyph = @opts.size <= 9 ? 1 : 'A'
|
|
51
|
+
@opts.each_pair do |_, (str, selected)|
|
|
52
|
+
mark = @marks[selected]
|
|
53
|
+
@parent.puts(
|
|
54
|
+
str,
|
|
55
|
+
first_line_prefix: "[\\#{glyph}] #{mark}",
|
|
56
|
+
first_line_prefix_width: mark.width + 2
|
|
57
|
+
)
|
|
58
|
+
glyph = glyph.succ
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
private_constant :DumbOptions
|
|
63
|
+
end
|
data/lib/natty-ui/element.rb
CHANGED
|
@@ -32,12 +32,12 @@ module NattyUI
|
|
|
32
32
|
self
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
alias _to_s to_s
|
|
36
|
-
private :_to_s
|
|
37
|
-
|
|
38
35
|
# @!visibility private
|
|
39
36
|
alias to_s inspect
|
|
40
37
|
|
|
38
|
+
alias _to_s to_s
|
|
39
|
+
private :_to_s
|
|
40
|
+
|
|
41
41
|
private
|
|
42
42
|
|
|
43
43
|
def initialize(parent)
|
|
@@ -48,7 +48,6 @@ module NattyUI
|
|
|
48
48
|
|
|
49
49
|
module WithStatus
|
|
50
50
|
attr_reader :status
|
|
51
|
-
|
|
52
51
|
def active? = @status.nil?
|
|
53
52
|
def closed? = !active?
|
|
54
53
|
def ok? = @state == :ok
|
data/lib/natty-ui/features.rb
CHANGED
|
@@ -37,44 +37,67 @@ module NattyUI
|
|
|
37
37
|
# @return [Features]
|
|
38
38
|
# itself
|
|
39
39
|
def puts(*text, **options)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (prefix = options[:prefix])
|
|
46
|
-
prefix = Ansi.bbcode(prefix) if bbcode
|
|
47
|
-
options[:prefix_width] || Text.width(prefix, bbcode: false)
|
|
48
|
-
else
|
|
49
|
-
0
|
|
50
|
-
end
|
|
40
|
+
if (ansi = Terminal.ansi?)
|
|
41
|
+
@__eol ||= "\e[m\n"
|
|
42
|
+
else
|
|
43
|
+
@__eol ||= "\n"
|
|
44
|
+
end
|
|
51
45
|
|
|
52
|
-
if
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
46
|
+
if options.empty?
|
|
47
|
+
bbcode = true
|
|
48
|
+
max_width = Terminal.columns
|
|
49
|
+
else
|
|
50
|
+
bbcode = true if (bbcode = options[:bbcode]).nil?
|
|
51
|
+
ignore_newline = options[:eol] == false || options[:ignore_newline]
|
|
52
|
+
|
|
53
|
+
if (max_width = options[:max_width]).nil?
|
|
54
|
+
return self if (max_width = Terminal.columns).zero?
|
|
55
|
+
elsif max_width < 1
|
|
56
|
+
if max_width > 0
|
|
57
|
+
max_width *= Terminal.columns
|
|
58
|
+
elsif max_width < 0
|
|
59
|
+
max_width += Terminal.columns
|
|
60
|
+
else
|
|
61
|
+
return self
|
|
62
|
+
end
|
|
63
|
+
end
|
|
57
64
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
65
|
+
prefix_width =
|
|
66
|
+
if (prefix = options[:prefix])
|
|
67
|
+
prefix = Ansi.bbcode(prefix) if bbcode
|
|
68
|
+
options[:prefix_width] || Text.width(prefix, bbcode: false)
|
|
69
|
+
else
|
|
70
|
+
0
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
if (first_line = options[:first_line_prefix])
|
|
74
|
+
first_line = Ansi.bbcode(first_line) if bbcode
|
|
75
|
+
first_line_width =
|
|
76
|
+
options[:first_line_prefix_width] ||
|
|
77
|
+
Text.width(first_line, bbcode: false)
|
|
78
|
+
|
|
79
|
+
if prefix_width < first_line_width
|
|
80
|
+
prefix_next = "#{prefix}#{' ' * (first_line_width - prefix_width)}"
|
|
81
|
+
prefix = first_line
|
|
82
|
+
prefix_width = first_line_width
|
|
83
|
+
else
|
|
84
|
+
prefix_next = prefix
|
|
85
|
+
prefix =
|
|
86
|
+
if first_line_width < prefix_width
|
|
87
|
+
first_line + (' ' * (prefix_width - first_line_width))
|
|
88
|
+
else
|
|
89
|
+
first_line
|
|
90
|
+
end
|
|
91
|
+
end
|
|
70
92
|
end
|
|
71
|
-
end
|
|
72
93
|
|
|
73
|
-
|
|
94
|
+
max_width -= prefix_width
|
|
74
95
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
96
|
+
if (suffix = options[:suffix])
|
|
97
|
+
suffix = Ansi.bbcode(suffix) if bbcode
|
|
98
|
+
max_width -=
|
|
99
|
+
options[:suffix_width] || Text.width(suffix, bbcode: false)
|
|
100
|
+
end
|
|
78
101
|
end
|
|
79
102
|
|
|
80
103
|
return self if max_width <= 0
|
|
@@ -84,13 +107,13 @@ module NattyUI
|
|
|
84
107
|
*text,
|
|
85
108
|
limit: max_width,
|
|
86
109
|
bbcode: bbcode,
|
|
87
|
-
ansi:
|
|
88
|
-
ignore_newline:
|
|
110
|
+
ansi: ansi,
|
|
111
|
+
ignore_newline: ignore_newline
|
|
89
112
|
)
|
|
90
113
|
|
|
91
114
|
if (align = options[:align]).nil?
|
|
92
115
|
lines.each do |line|
|
|
93
|
-
Terminal.print(prefix, line, suffix,
|
|
116
|
+
Terminal.print(prefix, line, suffix, @__eol, bbcode: false)
|
|
94
117
|
@lines_written += 1
|
|
95
118
|
prefix, prefix_next = prefix_next, nil if prefix_next
|
|
96
119
|
end
|
|
@@ -110,7 +133,7 @@ module NattyUI
|
|
|
110
133
|
' ' * (max_width - width),
|
|
111
134
|
line,
|
|
112
135
|
suffix,
|
|
113
|
-
|
|
136
|
+
@__eol,
|
|
114
137
|
bbcode: false
|
|
115
138
|
)
|
|
116
139
|
@lines_written += 1
|
|
@@ -125,7 +148,7 @@ module NattyUI
|
|
|
125
148
|
line,
|
|
126
149
|
' ' * (space - lw),
|
|
127
150
|
suffix,
|
|
128
|
-
|
|
151
|
+
@__eol,
|
|
129
152
|
bbcode: false
|
|
130
153
|
)
|
|
131
154
|
@lines_written += 1
|
|
@@ -138,7 +161,7 @@ module NattyUI
|
|
|
138
161
|
line,
|
|
139
162
|
' ' * (max_width - width),
|
|
140
163
|
suffix,
|
|
141
|
-
|
|
164
|
+
@__eol,
|
|
142
165
|
bbcode: false
|
|
143
166
|
)
|
|
144
167
|
@lines_written += 1
|
|
@@ -148,6 +171,20 @@ module NattyUI
|
|
|
148
171
|
self
|
|
149
172
|
end
|
|
150
173
|
|
|
174
|
+
# Print given text with a decoration mark.
|
|
175
|
+
#
|
|
176
|
+
# @param text (see puts)
|
|
177
|
+
# @param mark [Symbol, #to_s]
|
|
178
|
+
# marker type
|
|
179
|
+
#
|
|
180
|
+
# @return (see puts)
|
|
181
|
+
def mark(*text, mark: :default, **options)
|
|
182
|
+
mark = Theme.current.mark(mark)
|
|
183
|
+
options[:first_line_prefix] = mark
|
|
184
|
+
options[:first_line_prefix_width] = mark.width
|
|
185
|
+
puts(*text, **options)
|
|
186
|
+
end
|
|
187
|
+
|
|
151
188
|
# Print given text as lines like {#puts}. Used in elements with temporary
|
|
152
189
|
# output like {#task} the text will be kept ("pinned").
|
|
153
190
|
#
|
|
@@ -169,21 +206,7 @@ module NattyUI
|
|
|
169
206
|
#
|
|
170
207
|
# @return (see puts)
|
|
171
208
|
def pin(*text, mark: nil, **options)
|
|
172
|
-
|
|
173
|
-
options[:first_line_prefix] = Theme.current.mark(mark) if mark
|
|
174
|
-
puts(*text, **options)
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
# Print given text with a decoration mark.
|
|
178
|
-
#
|
|
179
|
-
# @param text (see puts)
|
|
180
|
-
# @param mark [Symbol, #to_s]
|
|
181
|
-
# marker type
|
|
182
|
-
#
|
|
183
|
-
# @return (see puts)
|
|
184
|
-
def mark(*text, mark: :default)
|
|
185
|
-
mark = Theme.current.mark(mark)
|
|
186
|
-
puts(*text, first_line_prefix: mark, first_line_prefix_width: mark.width)
|
|
209
|
+
mark(*text, mark: mark, pin: true, **options)
|
|
187
210
|
end
|
|
188
211
|
|
|
189
212
|
# Print given text as a quotation.
|
|
@@ -198,7 +221,7 @@ module NattyUI
|
|
|
198
221
|
*text,
|
|
199
222
|
prefix: quote,
|
|
200
223
|
prefix_width: quote.width,
|
|
201
|
-
max_width: width < 20 ? nil : width.
|
|
224
|
+
max_width: width < 20 ? nil : width.round
|
|
202
225
|
)
|
|
203
226
|
end
|
|
204
227
|
|
|
@@ -293,7 +316,7 @@ module NattyUI
|
|
|
293
316
|
#
|
|
294
317
|
# @return (see puts)
|
|
295
318
|
def space(count = 1)
|
|
296
|
-
puts("\n" * count)
|
|
319
|
+
(count = count.to_i).positive? ? puts("\n" * count) : self
|
|
297
320
|
end
|
|
298
321
|
|
|
299
322
|
# Print given items as list (like 'ls' command).
|
|
@@ -735,7 +758,8 @@ module NattyUI
|
|
|
735
758
|
end
|
|
736
759
|
end
|
|
737
760
|
|
|
738
|
-
#
|
|
761
|
+
# Allows the user to select an option from a selection.
|
|
762
|
+
# The selected option is returned.
|
|
739
763
|
#
|
|
740
764
|
# @overload choice(*choices, abortable: false)
|
|
741
765
|
# @param [#to_s] choices
|
|
@@ -773,6 +797,8 @@ module NattyUI
|
|
|
773
797
|
# one or more alternatives to select from
|
|
774
798
|
# @param [true, false] abortable
|
|
775
799
|
# whether the user is allowed to abort with 'Esc' or 'Ctrl+c'
|
|
800
|
+
# @param [#to_s, nil] selected
|
|
801
|
+
# optionally pre-selected option
|
|
776
802
|
#
|
|
777
803
|
# @return [Object]
|
|
778
804
|
# key for selected choice
|
|
@@ -795,6 +821,8 @@ module NattyUI
|
|
|
795
821
|
# one or more alternatives to select from
|
|
796
822
|
# @param [true, false] abortable
|
|
797
823
|
# whether the user is allowed to abort with 'Esc' or 'Ctrl+c'
|
|
824
|
+
# @param [Integer] selected
|
|
825
|
+
# pre-selected option index
|
|
798
826
|
#
|
|
799
827
|
# @yieldparam temp [Temporary]
|
|
800
828
|
# temporary displayed section (section will be erased after input)
|
|
@@ -807,17 +835,79 @@ module NattyUI
|
|
|
807
835
|
def choice(*choices, abortable: false, selected: nil, **kwchoices, &block)
|
|
808
836
|
return if choices.empty? && kwchoices.empty?
|
|
809
837
|
choice =
|
|
810
|
-
|
|
811
|
-
when :default
|
|
838
|
+
if Terminal.ansi?
|
|
812
839
|
Choice.new(self, choices, kwchoices, abortable, selected)
|
|
813
|
-
when :dumb
|
|
814
|
-
DumbChoice.new(self, choices, kwchoices, abortable)
|
|
815
840
|
else
|
|
816
|
-
|
|
841
|
+
DumbChoice.new(self, choices, kwchoices, abortable)
|
|
817
842
|
end
|
|
818
843
|
__with(choice) { choice.select(&block) }
|
|
819
844
|
end
|
|
820
845
|
|
|
846
|
+
# Allows the user to select from several options.
|
|
847
|
+
# All options are returned with their selection status.
|
|
848
|
+
#
|
|
849
|
+
# @param [{#to_s => [true,false]}] choices
|
|
850
|
+
# Hash of options and their selection state
|
|
851
|
+
# @param [true, false] abortable
|
|
852
|
+
# whether the user is allowed to abort with 'Esc' or 'Ctrl+c'
|
|
853
|
+
# @param [#to_s, nil] selected
|
|
854
|
+
# optionally pre-selected key
|
|
855
|
+
#
|
|
856
|
+
# @yieldparam temp [Temporary]
|
|
857
|
+
# temporary displayed section (section will be erased after input)
|
|
858
|
+
#
|
|
859
|
+
# @return [{#to_s => [true,false]}]
|
|
860
|
+
# Hash of options and their selection state
|
|
861
|
+
# @return [nil]
|
|
862
|
+
# when user aborted the selection
|
|
863
|
+
def options(abortable: false, selected: nil, **choices, &block)
|
|
864
|
+
return {} if choices.empty?
|
|
865
|
+
options =
|
|
866
|
+
if Terminal.ansi?
|
|
867
|
+
Options.new(self, choices, abortable, selected)
|
|
868
|
+
else
|
|
869
|
+
DumbOptions.new(self, choices, abortable, selected)
|
|
870
|
+
end
|
|
871
|
+
__with(options) { options.select(&block) }
|
|
872
|
+
end
|
|
873
|
+
|
|
874
|
+
# Allows the user to select from several options.
|
|
875
|
+
# The selected options are returned.
|
|
876
|
+
#
|
|
877
|
+
# @example Select a terminal
|
|
878
|
+
# ui.select %w[Kitty iTerm2 Ghostty Tabby Rio] do
|
|
879
|
+
# ui.puts '[i]Which terminal applications did you already tested?[/i]'
|
|
880
|
+
# end
|
|
881
|
+
#
|
|
882
|
+
# @param [Array<#to_s>] choices
|
|
883
|
+
# selectable options
|
|
884
|
+
# @param [true, false] abortable
|
|
885
|
+
# whether the user is allowed to abort with 'Esc' or 'Ctrl+c'
|
|
886
|
+
# @param [Integer, :all, nil] selected
|
|
887
|
+
# optionally pre-selected option index or `:all` to pre-select all items
|
|
888
|
+
# @yieldparam temp [Temporary]
|
|
889
|
+
# temporary displayed section (section will be erased after input)
|
|
890
|
+
#
|
|
891
|
+
# @return [Array<#to_s>]
|
|
892
|
+
# selected options
|
|
893
|
+
# @return [nil]
|
|
894
|
+
# when user aborted the selection
|
|
895
|
+
def select(*choices, abortable: false, selected: nil, &block)
|
|
896
|
+
return [] if choices.empty?
|
|
897
|
+
choices = choices[0] if choices.size == 1 && choices[0].is_a?(Enumerable)
|
|
898
|
+
if selected == :all
|
|
899
|
+
sel = true
|
|
900
|
+
elsif selected
|
|
901
|
+
selected = choices[selected.to_i]
|
|
902
|
+
end
|
|
903
|
+
options(
|
|
904
|
+
abortable: abortable,
|
|
905
|
+
selected: selected,
|
|
906
|
+
**choices.to_h { [_1, sel] },
|
|
907
|
+
&block
|
|
908
|
+
).filter_map { |key, selected| key if selected }
|
|
909
|
+
end
|
|
910
|
+
|
|
821
911
|
#
|
|
822
912
|
# @!endgroup
|
|
823
913
|
#
|
|
@@ -870,18 +960,17 @@ module NattyUI
|
|
|
870
960
|
end
|
|
871
961
|
end
|
|
872
962
|
end
|
|
873
|
-
|
|
874
|
-
EOL__ = Terminal.ansi? ? "\e[m\n" : "\n"
|
|
875
|
-
private_constant :EOL__
|
|
876
963
|
end
|
|
877
964
|
|
|
878
965
|
dir = __dir__
|
|
879
966
|
autoload :Choice, "#{dir}/choice.rb"
|
|
880
967
|
autoload :DumbChoice, "#{dir}/dumb_choice.rb"
|
|
968
|
+
autoload :DumbOptions, "#{dir}/dumb_options.rb"
|
|
881
969
|
autoload :CompactLSRenderer, "#{dir}/ls_renderer.rb"
|
|
882
970
|
autoload :Framed, "#{dir}/framed.rb"
|
|
883
971
|
autoload :HBarsRenderer, "#{dir}/hbars_renderer.rb"
|
|
884
972
|
autoload :LSRenderer, "#{dir}/ls_renderer.rb"
|
|
973
|
+
autoload :Options, "#{dir}/options.rb"
|
|
885
974
|
autoload :Progress, "#{dir}/progress.rb"
|
|
886
975
|
autoload :DumbProgress, "#{dir}/progress.rb"
|
|
887
976
|
autoload :Section, "#{dir}/section.rb"
|
|
@@ -895,10 +984,12 @@ module NattyUI
|
|
|
895
984
|
private_constant(
|
|
896
985
|
:Choice,
|
|
897
986
|
:DumbChoice,
|
|
987
|
+
:DumbOptions,
|
|
898
988
|
:CompactLSRenderer,
|
|
899
989
|
:Framed,
|
|
900
990
|
:HBarsRenderer,
|
|
901
991
|
:LSRenderer,
|
|
992
|
+
:Options,
|
|
902
993
|
:Progress,
|
|
903
994
|
:DumbProgress,
|
|
904
995
|
:Utils,
|
data/lib/natty-ui/ls_renderer.rb
CHANGED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'element'
|
|
4
|
+
|
|
5
|
+
module NattyUI
|
|
6
|
+
class Options < Element
|
|
7
|
+
def select
|
|
8
|
+
yield(self) if block_given?
|
|
9
|
+
pin_line = NattyUI.lines_written
|
|
10
|
+
draw
|
|
11
|
+
last = @current
|
|
12
|
+
while (event = Terminal.read_key_event)
|
|
13
|
+
case event.name
|
|
14
|
+
when 'Esc', 'Ctrl+c'
|
|
15
|
+
return if @abortable
|
|
16
|
+
when 'Enter'
|
|
17
|
+
return @opts.transform_values(&:last)
|
|
18
|
+
when 'Space'
|
|
19
|
+
(last = @opts[@current])[-1] = !last[-1]
|
|
20
|
+
when 'a'
|
|
21
|
+
@opts.transform_values { _1[-1] = true }
|
|
22
|
+
last = nil
|
|
23
|
+
when 'n'
|
|
24
|
+
@opts.transform_values { _1[-1] = false }
|
|
25
|
+
last = nil
|
|
26
|
+
when 'Home'
|
|
27
|
+
@current = @opts.first.first
|
|
28
|
+
when 'End'
|
|
29
|
+
@current = @opts.keys.last
|
|
30
|
+
when 'Up', 'Back', 'Shift+Tab', 'i'
|
|
31
|
+
keys = @opts.keys
|
|
32
|
+
@current = keys[keys.index(@current) - 1]
|
|
33
|
+
when 'Down', 'Tab', 'k'
|
|
34
|
+
keys = @opts.keys
|
|
35
|
+
@current = keys[keys.index(@current) + 1] || keys[0]
|
|
36
|
+
end
|
|
37
|
+
next if last == @current
|
|
38
|
+
pin_line = NattyUI.back_to_line(pin_line, erase: false)
|
|
39
|
+
draw
|
|
40
|
+
last = @current
|
|
41
|
+
end
|
|
42
|
+
ensure
|
|
43
|
+
NattyUI.back_to_line(@start_line)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def initialize(parent, opts, abortable, selected)
|
|
49
|
+
super(parent)
|
|
50
|
+
@opts =
|
|
51
|
+
opts.to_h do |k, v|
|
|
52
|
+
v.is_a?(Enumerable) ? [k, [v[0], !!v[-1]]] : [k, [k, !!v]]
|
|
53
|
+
end
|
|
54
|
+
@abortable = abortable
|
|
55
|
+
@current = @opts.key?(selected) ? selected : @opts.first.first
|
|
56
|
+
@start_line = NattyUI.lines_written
|
|
57
|
+
theme = Theme.current
|
|
58
|
+
@style = {
|
|
59
|
+
false => theme.choice_style,
|
|
60
|
+
true => theme.choice_current_style
|
|
61
|
+
}.compare_by_identity
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def draw
|
|
65
|
+
states = NattyUI::Theme.current.option_states
|
|
66
|
+
@opts.each_pair do |key, (str, selected)|
|
|
67
|
+
mark = states.dig(current = key == @current, selected)
|
|
68
|
+
@parent.puts(
|
|
69
|
+
"#{@style[current]}#{str}",
|
|
70
|
+
first_line_prefix: mark,
|
|
71
|
+
first_line_prefix_width: mark.width
|
|
72
|
+
)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
private_constant :Options
|
|
77
|
+
end
|
data/lib/natty-ui/task.rb
CHANGED
data/lib/natty-ui/temporary.rb
CHANGED
|
@@ -8,11 +8,10 @@ module NattyUI
|
|
|
8
8
|
#
|
|
9
9
|
class Temporary < Element
|
|
10
10
|
# @!visibility private
|
|
11
|
-
def puts(*objects, **
|
|
11
|
+
def puts(*objects, **opts)
|
|
12
12
|
return self if @state
|
|
13
|
-
if
|
|
14
|
-
@pins ||= []
|
|
15
|
-
@pins << [objects, options.except(:prefix_width, :suffix_width)]
|
|
13
|
+
if opts.delete(:pin)
|
|
14
|
+
(@pins ||= []) << [objects, opts.except(:prefix_width, :suffix_width)]
|
|
16
15
|
end
|
|
17
16
|
super
|
|
18
17
|
end
|
|
@@ -20,8 +19,8 @@ module NattyUI
|
|
|
20
19
|
# @!visibility private
|
|
21
20
|
def done
|
|
22
21
|
return self if @state
|
|
23
|
-
NattyUI.back_to_line(@start_line
|
|
24
|
-
@pins&.each { |objects,
|
|
22
|
+
NattyUI.back_to_line(@start_line) if @start_line
|
|
23
|
+
@pins&.each { |objects, opts| puts(*objects, **opts) }
|
|
25
24
|
@state = :ok
|
|
26
25
|
self
|
|
27
26
|
end
|
data/lib/natty-ui/theme.rb
CHANGED
|
@@ -55,7 +55,7 @@ module NattyUI
|
|
|
55
55
|
failed: '[bright_red]𝑭[/fg]',
|
|
56
56
|
current: '[bright_green]➔[/fg]',
|
|
57
57
|
choice: '[bright_white]◦[/fg]',
|
|
58
|
-
current_choice: '[bright_green]
|
|
58
|
+
current_choice: '[bright_green]➔[/fg]'
|
|
59
59
|
)
|
|
60
60
|
theme.define_section(
|
|
61
61
|
default: :bright_blue,
|
|
@@ -166,11 +166,13 @@ module NattyUI
|
|
|
166
166
|
end
|
|
167
167
|
|
|
168
168
|
class Compiled
|
|
169
|
-
attr_reader :task_style,
|
|
169
|
+
attr_reader :task_style,
|
|
170
|
+
:choice_current_style,
|
|
171
|
+
:choice_style,
|
|
172
|
+
:option_states
|
|
170
173
|
|
|
171
174
|
def defined_marks = @mark.keys.sort!
|
|
172
175
|
def defined_borders = @border.keys.sort!
|
|
173
|
-
|
|
174
176
|
def heading(index) = @heading[index.to_i.clamp(1, 6) - 1]
|
|
175
177
|
|
|
176
178
|
def mark(value)
|
|
@@ -208,12 +210,25 @@ module NattyUI
|
|
|
208
210
|
SectionBorder.create(border(theme.section_border)),
|
|
209
211
|
theme.section_styles.dup.compare_by_identity
|
|
210
212
|
)
|
|
213
|
+
@option_states = create_option_states
|
|
211
214
|
end
|
|
212
215
|
|
|
213
216
|
private
|
|
214
217
|
|
|
215
218
|
def as_style(value) = (Ansi[*value].freeze if value)
|
|
216
219
|
|
|
220
|
+
def create_option_states
|
|
221
|
+
# [current?][selected?]
|
|
222
|
+
c = @mark[:current_choice]
|
|
223
|
+
n = @mark[:none]
|
|
224
|
+
sel = @mark[:checkmark]
|
|
225
|
+
uns = @mark[:choice]
|
|
226
|
+
{
|
|
227
|
+
false => { false => n + uns, true => n + sel }.compare_by_identity,
|
|
228
|
+
true => { false => c + uns, true => c + sel }.compare_by_identity
|
|
229
|
+
}.compare_by_identity.freeze
|
|
230
|
+
end
|
|
231
|
+
|
|
217
232
|
def create_sections(template, styles)
|
|
218
233
|
Hash
|
|
219
234
|
.new do |h, kind|
|
|
@@ -224,7 +239,9 @@ module NattyUI
|
|
|
224
239
|
|
|
225
240
|
def create_mark(mark)
|
|
226
241
|
return {} if mark.empty?
|
|
227
|
-
|
|
242
|
+
mark = mark.to_h { |n, e| [n.to_sym, Str.new("#{e} ")] }
|
|
243
|
+
mark[:none] ||= Str.new(' ', 2)
|
|
244
|
+
with_default(mark)
|
|
228
245
|
end
|
|
229
246
|
|
|
230
247
|
def create_border(border)
|
|
@@ -294,6 +311,7 @@ module NattyUI
|
|
|
294
311
|
|
|
295
312
|
private_constant :SectionBorder
|
|
296
313
|
end
|
|
314
|
+
# private_constant :Compiled
|
|
297
315
|
|
|
298
316
|
private
|
|
299
317
|
|
data/lib/natty-ui/utils.rb
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
module NattyUI
|
|
4
4
|
module Utils
|
|
5
5
|
class << self
|
|
6
|
-
# @!visibility private
|
|
7
6
|
def style(value)
|
|
8
7
|
value =
|
|
9
8
|
case value
|
|
@@ -22,27 +21,22 @@ module NattyUI
|
|
|
22
21
|
value.keep_if { Ansi.valid?(_1) }.empty? ? nil : value
|
|
23
22
|
end
|
|
24
23
|
|
|
25
|
-
# @!visibility private
|
|
26
24
|
def align(value)
|
|
27
25
|
POS_ALI.include?(value) ? value : :left
|
|
28
26
|
end
|
|
29
27
|
|
|
30
|
-
# @!visibility private
|
|
31
28
|
def position(value)
|
|
32
29
|
value if POS_ALI.include?(value)
|
|
33
30
|
end
|
|
34
31
|
|
|
35
|
-
# @!visibility private
|
|
36
32
|
def vertical(value)
|
|
37
33
|
VERT.include?(value) ? value : :top
|
|
38
34
|
end
|
|
39
35
|
|
|
40
|
-
# @!visibility private
|
|
41
36
|
def split_table_attr(values)
|
|
42
37
|
[values.slice(*TAB_ATTR), values.except(*TAB_ATTR)]
|
|
43
38
|
end
|
|
44
39
|
|
|
45
|
-
# @!visibility private
|
|
46
40
|
def padding(*value)
|
|
47
41
|
value = value.flatten.take(4).map! { [0, _1.to_i].max }
|
|
48
42
|
case value.size
|
|
@@ -60,7 +54,6 @@ module NattyUI
|
|
|
60
54
|
end
|
|
61
55
|
alias margin padding
|
|
62
56
|
|
|
63
|
-
# @!visibility private
|
|
64
57
|
def as_size(range, value)
|
|
65
58
|
return range.begin if value == :min
|
|
66
59
|
return range.end if value.nil? || value.is_a?(Symbol)
|
data/lib/natty-ui/version.rb
CHANGED
data/lib/natty-ui.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: natty-ui
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.30.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mike Blumtritt
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version:
|
|
18
|
+
version: 0.13.0
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version:
|
|
25
|
+
version: 0.13.0
|
|
26
26
|
description: |
|
|
27
27
|
This is the beautiful, nice, nifty, fancy, neat, pretty, cool, rich, lovely,
|
|
28
28
|
natty user interface tool you like to have for your command line applications.
|
|
@@ -50,6 +50,7 @@ files:
|
|
|
50
50
|
- examples/key-codes.rb
|
|
51
51
|
- examples/ls.rb
|
|
52
52
|
- examples/named-colors.rb
|
|
53
|
+
- examples/options.rb
|
|
53
54
|
- examples/sections.rb
|
|
54
55
|
- examples/tables.rb
|
|
55
56
|
- examples/tasks.rb
|
|
@@ -58,11 +59,13 @@ files:
|
|
|
58
59
|
- lib/natty-ui/attributes.rb
|
|
59
60
|
- lib/natty-ui/choice.rb
|
|
60
61
|
- lib/natty-ui/dumb_choice.rb
|
|
62
|
+
- lib/natty-ui/dumb_options.rb
|
|
61
63
|
- lib/natty-ui/element.rb
|
|
62
64
|
- lib/natty-ui/features.rb
|
|
63
65
|
- lib/natty-ui/framed.rb
|
|
64
66
|
- lib/natty-ui/hbars_renderer.rb
|
|
65
67
|
- lib/natty-ui/ls_renderer.rb
|
|
68
|
+
- lib/natty-ui/options.rb
|
|
66
69
|
- lib/natty-ui/progress.rb
|
|
67
70
|
- lib/natty-ui/section.rb
|
|
68
71
|
- lib/natty-ui/table.rb
|
|
@@ -75,15 +78,15 @@ files:
|
|
|
75
78
|
- lib/natty-ui/version.rb
|
|
76
79
|
- lib/natty-ui/width_finder.rb
|
|
77
80
|
- lib/natty_ui.rb
|
|
78
|
-
homepage: https://
|
|
81
|
+
homepage: https://codeberg.org/mblumtritt/natty-ui
|
|
79
82
|
licenses:
|
|
80
83
|
- BSD-3-Clause
|
|
81
84
|
metadata:
|
|
85
|
+
source_code_uri: https://codeberg.org/mblumtritt/natty-ui
|
|
86
|
+
bug_tracker_uri: https://codeberg.org/mblumtritt/natty-ui/issues
|
|
87
|
+
documentation_uri: https://rubydoc.info/gems/natty-ui
|
|
82
88
|
rubygems_mfa_required: 'true'
|
|
83
89
|
yard.run: yard
|
|
84
|
-
source_code_uri: https://github.com/mblumtritt/natty-ui
|
|
85
|
-
bug_tracker_uri: https://github.com/mblumtritt/natty-ui/issues
|
|
86
|
-
documentation_uri: https://rubydoc.info/gems/natty-ui/NattyUI
|
|
87
90
|
rdoc_options: []
|
|
88
91
|
require_paths:
|
|
89
92
|
- lib
|