natty-ui 0.6.0 → 0.8.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/.yardopts +12 -0
- data/README.md +25 -48
- data/examples/24bit-colors.rb +23 -0
- data/examples/3bit-colors.rb +13 -0
- data/examples/8bit-colors.rb +31 -0
- data/examples/attributes.rb +41 -134
- data/examples/demo.rb +217 -0
- data/examples/illustration.png +0 -0
- data/examples/illustration.rb +26 -0
- data/examples/list_in_columns.rb +10 -17
- data/examples/progress.rb +24 -29
- data/examples/query.rb +22 -21
- data/examples/table.rb +18 -0
- data/lib/natty-ui/ansi.rb +111 -92
- data/lib/natty-ui/ansi_wrapper.rb +93 -161
- data/lib/natty-ui/features.rb +12 -19
- data/lib/natty-ui/version.rb +2 -2
- data/lib/natty-ui/wrapper/ask.rb +26 -20
- data/lib/natty-ui/wrapper/element.rb +19 -23
- data/lib/natty-ui/wrapper/framed.rb +23 -18
- data/lib/natty-ui/wrapper/heading.rb +26 -53
- data/lib/natty-ui/wrapper/horizontal_rule.rb +37 -0
- data/lib/natty-ui/wrapper/list_in_columns.rb +66 -10
- data/lib/natty-ui/wrapper/message.rb +20 -27
- data/lib/natty-ui/{mixins.rb → wrapper/mixins.rb} +2 -2
- data/lib/natty-ui/wrapper/progress.rb +11 -9
- data/lib/natty-ui/wrapper/query.rb +37 -30
- data/lib/natty-ui/wrapper/quote.rb +25 -0
- data/lib/natty-ui/wrapper/request.rb +27 -5
- data/lib/natty-ui/wrapper/section.rb +42 -40
- data/lib/natty-ui/wrapper/table.rb +298 -0
- data/lib/natty-ui/wrapper/task.rb +9 -12
- data/lib/natty-ui/wrapper.rb +117 -44
- data/lib/natty-ui.rb +48 -50
- data/lib/natty_ui.rb +3 -0
- metadata +18 -9
- data/examples/basic.rb +0 -62
@@ -12,19 +12,31 @@ module NattyUI
|
|
12
12
|
@stream.flush
|
13
13
|
return self
|
14
14
|
end
|
15
|
-
(@stream <<
|
15
|
+
(@stream << Ansi::BLANK_SLATE).flush
|
16
16
|
begin
|
17
17
|
yield(self)
|
18
18
|
ensure
|
19
|
-
(@stream <<
|
19
|
+
(@stream << Ansi::UNBLANK_SLATE).flush
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
+
def cls
|
24
|
+
(@stream << Ansi::CURSOR_HOME << Ansi::SCREEN_ERASE).flush
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
23
28
|
protected
|
24
29
|
|
25
|
-
def
|
26
|
-
|
27
|
-
|
30
|
+
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}" }
|
28
40
|
end
|
29
41
|
|
30
42
|
def temp_func
|
@@ -32,7 +44,7 @@ module NattyUI
|
|
32
44
|
lambda do
|
33
45
|
count = @lines_written - count
|
34
46
|
if count.nonzero?
|
35
|
-
@stream << Ansi.cursor_line_up(count) << Ansi
|
47
|
+
@stream << Ansi.cursor_line_up(count) << Ansi::SCREEN_ERASE_BELOW
|
36
48
|
@lines_written -= count
|
37
49
|
end
|
38
50
|
@stream.flush
|
@@ -40,40 +52,47 @@ module NattyUI
|
|
40
52
|
end
|
41
53
|
end
|
42
54
|
|
43
|
-
class
|
44
|
-
|
55
|
+
class Progress < Progress
|
56
|
+
def draw(title)
|
57
|
+
@msg =
|
58
|
+
"#{@parent.prefix}#{Ansi[:bold, 39]}➔#{Ansi[:reset, 39]} " \
|
59
|
+
"#{title}#{Ansi::RESET} "
|
60
|
+
(wrapper.stream << @msg << Ansi::CURSOR_HIDE).flush
|
61
|
+
@msg = "#{Ansi::LINE_CLEAR}#{@msg}"
|
62
|
+
return @msg << BAR_COLOR if @max_value
|
63
|
+
@msg << Ansi[:bold, 220]
|
64
|
+
@indicator = 0
|
65
|
+
end
|
45
66
|
|
46
|
-
def
|
47
|
-
|
48
|
-
{
|
49
|
-
prefix:
|
50
|
-
if color
|
51
|
-
"#{Ansi[:bold, :italic, color]}#{str}" \
|
52
|
-
"#{Ansi[:reset, :bold, color]} "
|
53
|
-
else
|
54
|
-
"#{Ansi[:bold, 231]}#{str} "
|
55
|
-
end,
|
56
|
-
suffix: Ansi.reset
|
57
|
-
}
|
67
|
+
def redraw
|
68
|
+
(wrapper.stream << @msg << (@max_value ? fullbar : indicator)).flush
|
58
69
|
end
|
59
70
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
71
|
+
def end_draw
|
72
|
+
(wrapper.stream << Ansi::LINE_CLEAR << Ansi::CURSOR_SHOW).flush
|
73
|
+
end
|
74
|
+
|
75
|
+
def indicator = '◐◓◑◒'[(@indicator += 1) % 4]
|
76
|
+
|
77
|
+
def fullbar
|
78
|
+
percent = @value / @max_value
|
79
|
+
count = (30 * percent).to_i
|
80
|
+
mv = max_value.to_i.to_s
|
81
|
+
"#{'█' * count}#{BAR_BACK}#{'▁' * (30 - count)}" \
|
82
|
+
"#{BAR_INK} #{value.to_i.to_s.rjust(mv.size)}/#{mv} " \
|
83
|
+
"(#{(percent * 100).round(2).to_s.rjust(6)})"
|
84
|
+
end
|
85
|
+
|
86
|
+
BAR_COLOR = Ansi[39, 295].freeze
|
87
|
+
BAR_BACK = Ansi[236, 492].freeze
|
88
|
+
BAR_INK = Ansi[:bold, 255, :on_default].freeze
|
70
89
|
end
|
90
|
+
private_constant :Progress
|
71
91
|
|
72
|
-
|
92
|
+
module Temporary
|
73
93
|
def temporary
|
74
|
-
stream = wrapper.stream
|
75
94
|
unless block_given?
|
76
|
-
stream.flush
|
95
|
+
wrapper.stream.flush
|
77
96
|
return self
|
78
97
|
end
|
79
98
|
count = wrapper.lines_written
|
@@ -82,158 +101,71 @@ module NattyUI
|
|
82
101
|
ensure
|
83
102
|
count = wrapper.lines_written - count
|
84
103
|
if count.nonzero?
|
85
|
-
stream << Ansi.cursor_line_up(count) <<
|
104
|
+
wrapper.stream << Ansi.cursor_line_up(count) <<
|
105
|
+
Ansi::SCREEN_ERASE_BELOW
|
86
106
|
end
|
87
|
-
stream.flush
|
107
|
+
wrapper.stream.flush
|
88
108
|
end
|
89
109
|
end
|
110
|
+
end
|
111
|
+
private_constant :Temporary
|
90
112
|
|
91
|
-
|
92
|
-
|
93
|
-
def initialize(parent, prefix_attr: nil, **opts)
|
94
|
-
super
|
95
|
-
return unless @prefix && prefix_attr
|
96
|
-
@prefix = Ansi.embellish(@prefix, *prefix_attr)
|
97
|
-
end
|
113
|
+
class Section < Section
|
114
|
+
include Temporary
|
98
115
|
end
|
116
|
+
private_constant :Section
|
99
117
|
|
100
|
-
class
|
101
|
-
|
118
|
+
class Quote < Quote
|
119
|
+
include Temporary
|
102
120
|
|
103
|
-
def
|
104
|
-
|
105
|
-
|
121
|
+
def initialize(...)
|
122
|
+
super
|
123
|
+
@prefix = "#{Ansi[39]}#{@prefix}#{Ansi::RESET}"
|
106
124
|
end
|
125
|
+
end
|
126
|
+
private_constant :Quote
|
107
127
|
|
108
|
-
|
109
|
-
|
128
|
+
class Framed < Framed
|
129
|
+
def color(str) = "#{Ansi[39]}#{str}#{Ansi::RESET}"
|
110
130
|
end
|
131
|
+
private_constant :Framed
|
111
132
|
|
112
|
-
class
|
133
|
+
class Message < Section
|
113
134
|
protected
|
114
135
|
|
115
|
-
def initialize(parent, title:,
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
rcount = [width - _plain_width(title) - 6, 0].max
|
136
|
+
def initialize(parent, title:, glyph:)
|
137
|
+
wrapper = parent.wrapper
|
138
|
+
color = COLORS[glyph] || COLORS[:default]
|
139
|
+
glyph = wrapper.glyph(glyph) || glyph
|
140
|
+
prefix_width = NattyUI.display_width(glyph) + 1
|
121
141
|
parent.puts(
|
122
|
-
|
123
|
-
|
124
|
-
|
142
|
+
title,
|
143
|
+
prefix: "#{glyph} #{color}",
|
144
|
+
prefix_width: prefix_width,
|
145
|
+
suffix: Ansi::RESET,
|
146
|
+
suffix_width: 0
|
125
147
|
)
|
126
|
-
|
127
|
-
vert = "#{COLOR}#{vert}#{Ansi.reset}"
|
128
|
-
super(
|
129
|
-
parent,
|
130
|
-
prefix: "#{vert} ",
|
131
|
-
suffix:
|
132
|
-
"#{Ansi.cursor_right_aligned}" \
|
133
|
-
"#{Ansi.cursor_left(suffix_width)}#{vert}",
|
134
|
-
**opts
|
135
|
-
)
|
136
|
-
end
|
137
|
-
|
138
|
-
def suffix = "#{super} "
|
139
|
-
def finish = parent.puts(@bottom)
|
140
|
-
|
141
|
-
def components(type)
|
142
|
-
COMPONENTS[type] || raise(ArgumentError, "invalid frame type - #{type}")
|
148
|
+
super(parent, prefix: ' ' * prefix_width, prefix_width: prefix_width)
|
143
149
|
end
|
144
150
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
151
|
+
COLORS = {
|
152
|
+
default: Ansi[255],
|
153
|
+
information: Ansi[255],
|
154
|
+
warning: Ansi[221],
|
155
|
+
error: Ansi[208],
|
156
|
+
completed: Ansi[82],
|
157
|
+
failed: Ansi[196],
|
158
|
+
task: Ansi[39],
|
159
|
+
query: Ansi[255]
|
153
160
|
}.compare_by_identity.freeze
|
154
161
|
end
|
155
|
-
|
156
|
-
class Ask < Ask
|
157
|
-
protected
|
158
|
-
|
159
|
-
def query(question)
|
160
|
-
(wrapper.stream << "#{prefix}#{PREFIX} #{question}#{Ansi.reset} ").flush
|
161
|
-
end
|
162
|
-
|
163
|
-
def finish = (wrapper.stream << Ansi.line_clear).flush
|
164
|
-
|
165
|
-
PREFIX = "#{Ansi[:bold, :italic, 220]}▶︎#{Ansi[:reset, 220]}".freeze
|
166
|
-
end
|
167
|
-
|
168
|
-
class Request < Request
|
169
|
-
def prompt(question) = "#{prefix}#{PREFIX} #{question}#{Ansi.reset} "
|
170
|
-
def finish = (wrapper.stream << FINISH).flush
|
171
|
-
|
172
|
-
PREFIX = "#{Ansi[:bold, :italic, 220]}▶︎#{Ansi[:reset, 220]}".freeze
|
173
|
-
FINISH = (Ansi.cursor_line_up + Ansi.line_erase_to_end).freeze
|
174
|
-
end
|
175
|
-
|
176
|
-
class Query < Query
|
177
|
-
protected
|
178
|
-
|
179
|
-
def read(choices, result_typye)
|
180
|
-
wrapper.stream << "#{prefix}#{PROMPT} "
|
181
|
-
super
|
182
|
-
end
|
183
|
-
|
184
|
-
PROMPT = Ansi.embellish(':', :bold, 220).freeze
|
185
|
-
end
|
162
|
+
private_constant :Message
|
186
163
|
|
187
164
|
class Task < Message
|
188
165
|
include ProgressAttributes
|
189
166
|
include TaskMethods
|
190
167
|
end
|
191
|
-
|
192
|
-
class Progress < Progress
|
193
|
-
protected
|
194
|
-
|
195
|
-
def draw(title)
|
196
|
-
@prefix = "#{prefix}#{TITLE_PREFIX}#{title}#{Ansi.reset} "
|
197
|
-
(wrapper.stream << @prefix << Ansi.cursor_hide).flush
|
198
|
-
@prefix = "#{Ansi.line_clear}#{@prefix}"
|
199
|
-
if @max_value
|
200
|
-
@prefix << BAR_COLOR
|
201
|
-
else
|
202
|
-
@prefix << INDICATOR_ATTRIBUTE
|
203
|
-
@indicator = 0
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
207
|
-
def redraw
|
208
|
-
(wrapper.stream << @prefix << (@max_value ? fullbar : indicator)).flush
|
209
|
-
end
|
210
|
-
|
211
|
-
def end_draw = (wrapper.stream << ERASE).flush
|
212
|
-
def indicator = '─╲│╱'[(@indicator += 1) % 4]
|
213
|
-
# def indicator = '⣷⣯⣟⡿⢿⣻⣽⣾'[(@indicator += 1) % 8]
|
214
|
-
|
215
|
-
def fullbar
|
216
|
-
percent = @value / @max_value
|
217
|
-
count = (30 * percent).to_i
|
218
|
-
mv = max_value.to_i.to_s
|
219
|
-
"#{'█' * count}#{BAR_BACK}#{'▁' * (30 - count)}" \
|
220
|
-
"#{BAR_INK} #{value.to_i.to_s.rjust(mv.size)}/#{mv} " \
|
221
|
-
"(#{(percent * 100).round(2).to_s.rjust(6)})"
|
222
|
-
end
|
223
|
-
|
224
|
-
TITLE_PREFIX = "#{Ansi[:bold, :italic, 117]}➔#{Ansi[:reset, 117]} ".freeze
|
225
|
-
INDICATOR_ATTRIBUTE = Ansi[:bold, 220].freeze
|
226
|
-
BAR_COLOR = Ansi[39, 295].freeze
|
227
|
-
BAR_BACK = Ansi[236, 492].freeze
|
228
|
-
BAR_INK = Ansi[:bold, 255, :on_default].freeze
|
229
|
-
ERASE = (Ansi.line_clear + Ansi.cursor_show).freeze
|
230
|
-
end
|
231
|
-
|
232
|
-
PAGE_BEGIN =
|
233
|
-
"#{Ansi.reset}#{Ansi.cursor_save_pos}#{Ansi.screen_save}" \
|
234
|
-
"#{Ansi.cursor_home}#{Ansi.screen_erase}".freeze
|
235
|
-
PAGE_END =
|
236
|
-
"#{Ansi.screen_restore}#{Ansi.cursor_restore_pos}#{Ansi.reset}".freeze
|
168
|
+
private_constant :Task
|
237
169
|
end
|
238
170
|
|
239
171
|
private_constant :AnsiWrapper
|
data/lib/natty-ui/features.rb
CHANGED
@@ -5,32 +5,25 @@ module NattyUI
|
|
5
5
|
# Features of {NattyUI} - methods to display natty elements.
|
6
6
|
#
|
7
7
|
module Features
|
8
|
-
#
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
size = _plain_width(symbol)
|
15
|
-
return self if size.zero?
|
16
|
-
msg = symbol * ((available_width - 1) / size)
|
17
|
-
return puts(msg, prefix: Ansi[39], suffix: Ansi.reset) if wrapper.ansi?
|
18
|
-
puts(msg)
|
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
|
19
14
|
end
|
20
15
|
|
21
16
|
protected
|
22
17
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
def _element(type, *args)
|
27
|
-
wrapper.class.const_get(type).__send__(:new, self).__send__(:_call, *args)
|
18
|
+
def _element(type, ...)
|
19
|
+
wrapper.class.const_get(type).__send__(:new, self).__send__(:call, ...)
|
28
20
|
end
|
29
21
|
|
30
|
-
def _section(
|
31
|
-
sec = wrapper.class.const_get(type).__send__(:new, owner, **opts)
|
22
|
+
def _section(type, args = nil, owner: nil, **opts, &block)
|
23
|
+
sec = wrapper.class.const_get(type).__send__(:new, owner || self, **opts)
|
32
24
|
sec.puts(*args) if args && !args.empty?
|
33
|
-
|
25
|
+
sec.__send__(:call, &block) if block
|
26
|
+
sec
|
34
27
|
end
|
35
28
|
end
|
36
29
|
end
|
data/lib/natty-ui/version.rb
CHANGED
data/lib/natty-ui/wrapper/ask.rb
CHANGED
@@ -13,13 +13,13 @@ module NattyUI
|
|
13
13
|
# The default for `yes` includes `ENTER` and `RETURN` key
|
14
14
|
#
|
15
15
|
# @example
|
16
|
-
# case
|
16
|
+
# case ui.ask('Do you like the NattyUI gem?')
|
17
17
|
# when true
|
18
|
-
#
|
18
|
+
# ui.info('Yeah!!')
|
19
19
|
# when false
|
20
|
-
#
|
20
|
+
# ui.write("That's pity!")
|
21
21
|
# else
|
22
|
-
#
|
22
|
+
# ui.failed('You should have an opinion!')
|
23
23
|
# end
|
24
24
|
#
|
25
25
|
# @see NattyUI.in_stream
|
@@ -28,7 +28,7 @@ module NattyUI
|
|
28
28
|
# @param yes [#to_s] chars which will be used to answer 'Yes'
|
29
29
|
# @param no [#to_s] chars which will be used to answer 'No'
|
30
30
|
# @return [Boolean] whether the answer is yes or no
|
31
|
-
# @return [nil] when input was aborted with
|
31
|
+
# @return [nil] when input was aborted with `^C` or `^D`
|
32
32
|
def ask(question, yes: "jotsyd\r\n", no: 'n')
|
33
33
|
_element(:Ask, question, yes, no)
|
34
34
|
end
|
@@ -42,27 +42,33 @@ module NattyUI
|
|
42
42
|
class Ask < Element
|
43
43
|
protected
|
44
44
|
|
45
|
-
def
|
45
|
+
def call(question, yes, no)
|
46
46
|
yes, no = grab(yes, no)
|
47
|
-
|
48
|
-
read(yes, no)
|
49
|
-
ensure
|
50
|
-
finish
|
51
|
-
end
|
52
|
-
|
53
|
-
def query(question)
|
54
|
-
(wrapper.stream << prefix << "▶︎ #{question} ").flush
|
55
|
-
end
|
56
|
-
|
57
|
-
def finish = (wrapper.stream << "\n").flush
|
58
|
-
|
59
|
-
def read(yes, no)
|
47
|
+
draw(question)
|
60
48
|
while true
|
61
49
|
char = NattyUI.in_stream.getch
|
62
|
-
return if "\
|
50
|
+
return if "\3\4".include?(char)
|
63
51
|
return true if yes.include?(char)
|
64
52
|
return false if no.include?(char)
|
65
53
|
end
|
54
|
+
rescue Interrupt, SystemCallError
|
55
|
+
nil
|
56
|
+
ensure
|
57
|
+
if @parent.ansi?
|
58
|
+
(wrapper.stream << Ansi::LINE_CLEAR).flush
|
59
|
+
else
|
60
|
+
@parent.puts
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def draw(question)
|
65
|
+
glyph = wrapper.glyph(:query)
|
66
|
+
@parent.print(
|
67
|
+
question,
|
68
|
+
prefix: "#{glyph} #{Ansi[255]}",
|
69
|
+
prefix_width: NattyUI.display_width(glyph) + 1,
|
70
|
+
suffix_width: 0
|
71
|
+
)
|
66
72
|
end
|
67
73
|
|
68
74
|
def grab(yes, no)
|
@@ -10,7 +10,7 @@ module NattyUI
|
|
10
10
|
class Element
|
11
11
|
include Features
|
12
12
|
|
13
|
-
# @return [
|
13
|
+
# @return [Element] when embedded in a section
|
14
14
|
# @return [Wrapper] when not embedded in a section
|
15
15
|
attr_reader :parent
|
16
16
|
|
@@ -22,6 +22,9 @@ module NattyUI
|
|
22
22
|
# @return [Boolean] whether its closed or not
|
23
23
|
def closed? = (@status != nil)
|
24
24
|
|
25
|
+
# @return [Integer] available columns count within the element
|
26
|
+
def available_width = @parent.available_width
|
27
|
+
|
25
28
|
# Close the element.
|
26
29
|
#
|
27
30
|
# @return [Element] itself
|
@@ -29,28 +32,11 @@ module NattyUI
|
|
29
32
|
|
30
33
|
alias _to_s to_s
|
31
34
|
private :_to_s
|
32
|
-
|
33
35
|
# @!visibility private
|
34
|
-
def inspect =
|
36
|
+
def inspect = _to_s
|
35
37
|
|
36
38
|
protected
|
37
39
|
|
38
|
-
def prefix = "#{@parent.__send__(:prefix)}#{@prefix}"
|
39
|
-
def suffix = "#{@suffix}#{@parent.__send__(:suffix)}"
|
40
|
-
def prefix_width = _blemish_width(prefix)
|
41
|
-
def suffix_width = _blemish_width(suffix)
|
42
|
-
def available_width = wrapper.screen_columns - prefix_width - suffix_width
|
43
|
-
def finish = nil
|
44
|
-
|
45
|
-
def wrapper
|
46
|
-
return @wrapper if @wrapper
|
47
|
-
@wrapper = self
|
48
|
-
@wrapper = @wrapper.parent until @wrapper.is_a?(Wrapper)
|
49
|
-
@wrapper
|
50
|
-
end
|
51
|
-
|
52
|
-
def initialize(parent) = (@parent = parent)
|
53
|
-
|
54
40
|
def _close(state)
|
55
41
|
return self if @status
|
56
42
|
@status = state
|
@@ -58,18 +44,28 @@ module NattyUI
|
|
58
44
|
@raise ? raise(BREAK) : self
|
59
45
|
end
|
60
46
|
|
61
|
-
def
|
47
|
+
def call
|
48
|
+
NattyUI.instance_variable_set(:@element, self)
|
62
49
|
@raise = true
|
63
50
|
yield(self)
|
64
|
-
|
51
|
+
closed? ? self : close
|
65
52
|
rescue BREAK
|
66
53
|
nil
|
54
|
+
ensure
|
55
|
+
NattyUI.instance_variable_set(:@element, @parent)
|
67
56
|
end
|
68
57
|
|
69
|
-
|
70
|
-
|
58
|
+
def finish = nil
|
59
|
+
def prefix = "#{@parent.instance_variable_get(:@prefix)}#{@prefix}"
|
60
|
+
|
61
|
+
def initialize(parent)
|
62
|
+
@parent = parent
|
63
|
+
end
|
71
64
|
|
72
65
|
private_class_method :new
|
66
|
+
|
67
|
+
BREAK = Class.new(StandardError)
|
68
|
+
private_constant :BREAK
|
73
69
|
end
|
74
70
|
end
|
75
71
|
end
|
@@ -10,15 +10,14 @@ module NattyUI
|
|
10
10
|
# When no block is given, the section must be closed, see
|
11
11
|
# {Wrapper::Element#close}.
|
12
12
|
#
|
13
|
-
# @param [#to_s] title object to print as section title
|
14
13
|
# @param [Array<#to_s>] args more objects to print
|
15
14
|
# @param [Symbol] type frame type;
|
16
15
|
# valid types are `:rounded`, `:simple`, `:heavy`, `:semi`, `:double`
|
17
16
|
# @yieldparam [Wrapper::Framed] framed the created section
|
18
17
|
# @return [Object] the result of the code block
|
19
18
|
# @return [Wrapper::Framed] itself, when no code block is given
|
20
|
-
def framed(
|
21
|
-
_section(
|
19
|
+
def framed(*args, type: :rounded, &block)
|
20
|
+
_section(:Framed, args, type: type, &block)
|
22
21
|
end
|
23
22
|
end
|
24
23
|
|
@@ -30,25 +29,31 @@ module NattyUI
|
|
30
29
|
class Framed < Section
|
31
30
|
protected
|
32
31
|
|
33
|
-
def initialize(parent,
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
32
|
+
def initialize(parent, type:)
|
33
|
+
@type = FRAME_PARTS[type] or
|
34
|
+
raise(ArgumentError, "invalid frame type - #{type.inspect}")
|
35
|
+
parent.puts(
|
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
|
38
41
|
end
|
39
42
|
|
40
|
-
def finish
|
41
|
-
|
42
|
-
|
43
|
-
|
43
|
+
def finish
|
44
|
+
@parent.puts(
|
45
|
+
color("#{@type[3]}#{@type[1] * (@parent.available_width - 2)}")
|
46
|
+
)
|
44
47
|
end
|
45
48
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
def color(str) = str
|
50
|
+
|
51
|
+
FRAME_PARTS = {
|
52
|
+
rounded: '╭─╮╰│╯',
|
53
|
+
simple: '┌─┐└│┘',
|
54
|
+
heavy: '┏━┓┗┃┛',
|
55
|
+
double: '╔═╗╚║╝',
|
56
|
+
semi: '╒═╕╘│╛'
|
52
57
|
}.compare_by_identity.freeze
|
53
58
|
end
|
54
59
|
end
|