grmenu 0.1.4 → 0.1.6
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/GRmenu.rb +581 -104
- data/README.md +223 -125
- data/data/borders.json +85 -0
- data/data/colors.json +16 -0
- data/data/fonts.json +3005 -0
- metadata +9 -6
data/GRmenu.rb
CHANGED
|
@@ -1,14 +1,95 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'io/console'
|
|
4
|
+
require 'json'
|
|
4
5
|
|
|
5
6
|
class GRmenu
|
|
6
7
|
CLEAR_SCREEN_SEQUENCE = "\e[H\e[2J\e[3J"
|
|
7
|
-
HIDE_CURSOR
|
|
8
|
-
SHOW_CURSOR
|
|
9
|
-
CURSOR_HOME
|
|
10
|
-
CLEAR_TO_EOL
|
|
11
|
-
CLEAR_TO_EOS
|
|
8
|
+
HIDE_CURSOR = "\e[?25l"
|
|
9
|
+
SHOW_CURSOR = "\e[?25h"
|
|
10
|
+
CURSOR_HOME = "\e[H"
|
|
11
|
+
CLEAR_TO_EOL = "\e[K"
|
|
12
|
+
CLEAR_TO_EOS = "\e[J"
|
|
13
|
+
|
|
14
|
+
module Color
|
|
15
|
+
RESET = "\e[0m"
|
|
16
|
+
BOLD = "\e[1m"
|
|
17
|
+
|
|
18
|
+
CODES = {
|
|
19
|
+
black: { 1 => "\e[30m", 2 => "\e[90m" },
|
|
20
|
+
gray: { 1 => "\e[90m", 2 => "\e[38;5;245m" },
|
|
21
|
+
grey: { 1 => "\e[90m", 2 => "\e[38;5;245m" },
|
|
22
|
+
red: { 1 => "\e[31m", 2 => "\e[91m" },
|
|
23
|
+
green: { 1 => "\e[32m", 2 => "\e[92m" },
|
|
24
|
+
yellow: { 1 => "\e[33m", 2 => "\e[93m" },
|
|
25
|
+
blue: { 1 => "\e[34m", 2 => "\e[94m" },
|
|
26
|
+
magenta: { 1 => "\e[35m", 2 => "\e[95m" },
|
|
27
|
+
purple: { 1 => "\e[38;5;129m", 2 => "\e[38;5;141m" },
|
|
28
|
+
pink: { 1 => "\e[38;5;205m", 2 => "\e[38;5;218m" },
|
|
29
|
+
cyan: { 1 => "\e[36m", 2 => "\e[96m" },
|
|
30
|
+
aqua: { 1 => "\e[38;5;45m", 2 => "\e[38;5;51m" },
|
|
31
|
+
orange: { 1 => "\e[38;5;208m", 2 => "\e[38;5;214m" },
|
|
32
|
+
white: { 1 => "\e[37m", 2 => "\e[97m" }
|
|
33
|
+
}.freeze
|
|
34
|
+
|
|
35
|
+
module_function
|
|
36
|
+
|
|
37
|
+
def paint(text, color_name, level = 1)
|
|
38
|
+
code = CODES.dig(color_name.to_sym, level) || "\e[37m"
|
|
39
|
+
"#{code}#{text}#{RESET}"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def red(s) = paint(s, :red, 1)
|
|
43
|
+
def bright_red(s) = paint(s, :red, 2)
|
|
44
|
+
def dark_red(s) = paint(s, :red, 1)
|
|
45
|
+
|
|
46
|
+
def green(s) = paint(s, :green, 1)
|
|
47
|
+
def bright_green(s) = paint(s, :green, 2)
|
|
48
|
+
def dark_green(s) = paint(s, :green, 1)
|
|
49
|
+
|
|
50
|
+
def yellow(s) = paint(s, :yellow, 1)
|
|
51
|
+
def bright_yellow(s) = paint(s, :yellow, 2)
|
|
52
|
+
|
|
53
|
+
def blue(s) = paint(s, :blue, 1)
|
|
54
|
+
def bright_blue(s) = paint(s, :blue, 2)
|
|
55
|
+
|
|
56
|
+
def magenta(s) = paint(s, :magenta, 1)
|
|
57
|
+
def bright_magenta(s) = paint(s, :magenta, 2)
|
|
58
|
+
|
|
59
|
+
def purple(s) = paint(s, :purple, 1)
|
|
60
|
+
def bright_purple(s) = paint(s, :purple, 2)
|
|
61
|
+
|
|
62
|
+
def pink(s) = paint(s, :pink, 1)
|
|
63
|
+
def bright_pink(s) = paint(s, :pink, 2)
|
|
64
|
+
|
|
65
|
+
def cyan(s) = paint(s, :cyan, 1)
|
|
66
|
+
def bright_cyan(s) = paint(s, :cyan, 2)
|
|
67
|
+
|
|
68
|
+
def aqua(s) = paint(s, :aqua, 1)
|
|
69
|
+
def bright_aqua(s) = paint(s, :aqua, 2)
|
|
70
|
+
|
|
71
|
+
def orange(s) = paint(s, :orange, 1)
|
|
72
|
+
def bright_orange(s) = paint(s, :orange, 2)
|
|
73
|
+
|
|
74
|
+
def white(s) = paint(s, :white, 1)
|
|
75
|
+
def bright_white(s) = paint(s, :white, 2)
|
|
76
|
+
|
|
77
|
+
def black(s) = paint(s, :black, 1)
|
|
78
|
+
def gray(s) = paint(s, :gray, 1)
|
|
79
|
+
def bright_gray(s) = paint(s, :gray, 2)
|
|
80
|
+
def grey(s) = gray(s)
|
|
81
|
+
|
|
82
|
+
def r(s) = bright_red(s)
|
|
83
|
+
def dr(s) = dark_red(s)
|
|
84
|
+
def g(s) = bright_green(s)
|
|
85
|
+
def y(s) = bright_yellow(s)
|
|
86
|
+
def w(s) = bright_white(s)
|
|
87
|
+
def gr(s) = gray(s)
|
|
88
|
+
def cy(s) = bright_cyan(s)
|
|
89
|
+
def mg(s) = bright_magenta(s)
|
|
90
|
+
def bl(s) = bright_blue(s)
|
|
91
|
+
end
|
|
92
|
+
C = Color
|
|
12
93
|
|
|
13
94
|
STYLES = {
|
|
14
95
|
1 => "#", 2 => "┌", 3 => "╔", 4 => "┏", 5 => "╒",
|
|
@@ -18,15 +99,21 @@ class GRmenu
|
|
|
18
99
|
}.freeze
|
|
19
100
|
|
|
20
101
|
COLORS = {
|
|
21
|
-
"black"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
102
|
+
"black" => { 1 => "\e[30m", 2 => "\e[90m" },
|
|
103
|
+
"gray" => { 1 => "\e[90m", 2 => "\e[38;5;245m" },
|
|
104
|
+
"grey" => { 1 => "\e[90m", 2 => "\e[38;5;245m" },
|
|
105
|
+
"red" => { 1 => "\e[31m", 2 => "\e[91m" },
|
|
106
|
+
"green" => { 1 => "\e[32m", 2 => "\e[92m" },
|
|
107
|
+
"yellow" => { 1 => "\e[33m", 2 => "\e[93m" },
|
|
108
|
+
"blue" => { 1 => "\e[34m", 2 => "\e[94m" },
|
|
109
|
+
"magenta" => { 1 => "\e[35m", 2 => "\e[95m" },
|
|
110
|
+
"purple" => { 1 => "\e[38;5;129m", 2 => "\e[38;5;141m" },
|
|
111
|
+
"pink" => { 1 => "\e[38;5;205m", 2 => "\e[38;5;218m" },
|
|
112
|
+
"cyan" => { 1 => "\e[36m", 2 => "\e[96m" },
|
|
113
|
+
"aqua" => { 1 => "\e[38;5;45m", 2 => "\e[38;5;51m" },
|
|
114
|
+
"orange" => { 1 => "\e[38;5;208m", 2 => "\e[38;5;214m" },
|
|
115
|
+
"white" => { 1 => "\e[37m", 2 => "\e[97m" },
|
|
116
|
+
"reset" => "\e[0m"
|
|
30
117
|
}.freeze
|
|
31
118
|
|
|
32
119
|
BORDERS = {
|
|
@@ -37,29 +124,68 @@ class GRmenu
|
|
|
37
124
|
5 => { h: "═", v: "│", tl: "╒", tr: "╕", bl: "╘", br: "╛" },
|
|
38
125
|
6 => { h: "─", v: "║", tl: "╓", tr: "╖", bl: "╙", br: "╜" },
|
|
39
126
|
7 => { h: "─", v: "│", tl: "╭", tr: "╮", bl: "╰", br: "╯" },
|
|
40
|
-
8 => { h: "▀",
|
|
127
|
+
8 => { h: "▀", hb: "▄", v: "▌", vl: "▌", vr: "▐", tl: "▛", tr: "▜", bl: "▙", br: "▟" },
|
|
41
128
|
19 => { h: "●○", v: "●", tl: "●", tr: "●", bl: "●", br: "●" },
|
|
42
129
|
20 => { h: "★☆", v: "★", tl: "★", tr: "★", bl: "★", br: "★" }
|
|
43
130
|
}.freeze
|
|
44
131
|
|
|
132
|
+
def self._normalize_font(f)
|
|
133
|
+
normalized = {}
|
|
134
|
+
f.each do |key, lines|
|
|
135
|
+
max_w = lines.map(&:length).max
|
|
136
|
+
normalized[key] = lines.map { |line| line.ljust(max_w) }.freeze
|
|
137
|
+
end
|
|
138
|
+
normalized.freeze
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def self._load_fonts
|
|
142
|
+
possible_paths = [
|
|
143
|
+
File.expand_path("data/fonts.json", __dir__),
|
|
144
|
+
File.expand_path("../data/fonts.json", __dir__),
|
|
145
|
+
File.expand_path("fonts.json", __dir__)
|
|
146
|
+
]
|
|
147
|
+
path = possible_paths.find { |p| File.file?(p) }
|
|
148
|
+
return {}.freeze unless path
|
|
149
|
+
|
|
150
|
+
raw_fonts = JSON.parse(File.read(path))
|
|
151
|
+
loaded = {}
|
|
152
|
+
raw_fonts.each do |font_key, chars|
|
|
153
|
+
font_id = font_key.to_i
|
|
154
|
+
loaded[font_id] = _normalize_font(chars)
|
|
155
|
+
end
|
|
156
|
+
loaded.freeze
|
|
157
|
+
rescue StandardError
|
|
158
|
+
{}.freeze
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
FONTS = _load_fonts
|
|
162
|
+
FONTS.each { |id, data| const_set("FONT_#{id}", data) }
|
|
163
|
+
FONT = FONTS[1] || {}.freeze
|
|
164
|
+
|
|
45
165
|
class SetStyle
|
|
46
166
|
def initialize(
|
|
47
|
-
border:
|
|
48
|
-
options:
|
|
49
|
-
focus:
|
|
167
|
+
border: { color: "cyan", level: 1 },
|
|
168
|
+
options: { color: "white", level: 1 },
|
|
169
|
+
focus: { color: "green", level: 2 },
|
|
170
|
+
title: { color: "yellow", level: 2 },
|
|
171
|
+
banner: { color: "magenta", level: 2 },
|
|
172
|
+
subtitle: { color: "cyan", level: 2 },
|
|
173
|
+
divider: { color: "blue", level: 1 },
|
|
174
|
+
font: 1
|
|
50
175
|
)
|
|
51
|
-
@border
|
|
52
|
-
@options
|
|
53
|
-
@focus
|
|
176
|
+
@border = border.dup
|
|
177
|
+
@options = options.dup
|
|
178
|
+
@focus = focus.dup
|
|
179
|
+
@title = title.dup
|
|
180
|
+
@banner = banner.dup
|
|
181
|
+
@subtitle = subtitle.dup
|
|
182
|
+
@divider = divider.dup
|
|
183
|
+
@font = font.to_i
|
|
54
184
|
end
|
|
55
185
|
|
|
56
186
|
def border(color_name = nil, brightness_level = 1)
|
|
57
187
|
return @border if color_name.nil?
|
|
58
|
-
|
|
59
|
-
@border = color_name
|
|
60
|
-
else
|
|
61
|
-
@border = { color: color_name.to_s, level: brightness_level.to_i }
|
|
62
|
-
end
|
|
188
|
+
@border = parse_color(color_name, brightness_level)
|
|
63
189
|
end
|
|
64
190
|
alias_method :Border, :border
|
|
65
191
|
alias_method :set_border, :border
|
|
@@ -67,11 +193,7 @@ class GRmenu
|
|
|
67
193
|
|
|
68
194
|
def options(color_name = nil, brightness_level = 1)
|
|
69
195
|
return @options if color_name.nil?
|
|
70
|
-
|
|
71
|
-
@options = color_name
|
|
72
|
-
else
|
|
73
|
-
@options = { color: color_name.to_s, level: brightness_level.to_i }
|
|
74
|
-
end
|
|
196
|
+
@options = parse_color(color_name, brightness_level)
|
|
75
197
|
end
|
|
76
198
|
alias_method :Options, :options
|
|
77
199
|
alias_method :set_options, :options
|
|
@@ -79,25 +201,67 @@ class GRmenu
|
|
|
79
201
|
|
|
80
202
|
def focus(color_name = nil, brightness_level = 2)
|
|
81
203
|
return @focus if color_name.nil?
|
|
82
|
-
|
|
83
|
-
@focus = color_name
|
|
84
|
-
else
|
|
85
|
-
@focus = { color: color_name.to_s, level: brightness_level.to_i }
|
|
86
|
-
end
|
|
204
|
+
@focus = parse_color(color_name, brightness_level)
|
|
87
205
|
end
|
|
88
206
|
alias_method :Focus, :focus
|
|
89
207
|
alias_method :set_focus, :focus
|
|
90
208
|
alias_method :focus=, :focus
|
|
91
209
|
|
|
210
|
+
def title(color_name = nil, brightness_level = 2)
|
|
211
|
+
return @title if color_name.nil?
|
|
212
|
+
@title = parse_color(color_name, brightness_level)
|
|
213
|
+
end
|
|
214
|
+
alias_method :Title, :title
|
|
215
|
+
alias_method :set_title, :title
|
|
216
|
+
alias_method :title=, :title
|
|
217
|
+
|
|
218
|
+
def banner(color_name = nil, brightness_level = 2)
|
|
219
|
+
return @banner if color_name.nil?
|
|
220
|
+
@banner = parse_color(color_name, brightness_level)
|
|
221
|
+
end
|
|
222
|
+
alias_method :Banner, :banner
|
|
223
|
+
alias_method :set_banner, :banner
|
|
224
|
+
alias_method :banner=, :banner
|
|
225
|
+
|
|
226
|
+
def subtitle(color_name = nil, brightness_level = 2)
|
|
227
|
+
return @subtitle if color_name.nil?
|
|
228
|
+
@subtitle = parse_color(color_name, brightness_level)
|
|
229
|
+
end
|
|
230
|
+
alias_method :Subtitle, :subtitle
|
|
231
|
+
alias_method :set_subtitle, :subtitle
|
|
232
|
+
alias_method :subtitle=, :subtitle
|
|
233
|
+
|
|
234
|
+
def divider(color_name = nil, brightness_level = 1)
|
|
235
|
+
return @divider if color_name.nil?
|
|
236
|
+
@divider = parse_color(color_name, brightness_level)
|
|
237
|
+
end
|
|
238
|
+
alias_method :Divider, :divider
|
|
239
|
+
alias_method :set_divider, :divider
|
|
240
|
+
alias_method :divider=, :divider
|
|
241
|
+
|
|
242
|
+
def font(font_id = nil)
|
|
243
|
+
return @font if font_id.nil?
|
|
244
|
+
@font = font_id.to_i
|
|
245
|
+
end
|
|
246
|
+
alias_method :Font, :font
|
|
247
|
+
alias_method :set_font, :font
|
|
248
|
+
alias_method :font=, :font
|
|
249
|
+
|
|
250
|
+
private
|
|
251
|
+
|
|
252
|
+
def parse_color(color_val, default_level = 1)
|
|
253
|
+
if color_val.is_a?(Hash)
|
|
254
|
+
{ color: (color_val[:color] || color_val["color"]).to_s, level: (color_val[:level] || color_val["level"] || default_level).to_i }
|
|
255
|
+
else
|
|
256
|
+
{ color: color_val.to_s, level: default_level.to_i }
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
92
260
|
class << self
|
|
93
261
|
def border(color_name = nil, brightness_level = 1)
|
|
94
262
|
@default_border ||= { color: "cyan", level: 1 }
|
|
95
263
|
return @default_border if color_name.nil?
|
|
96
|
-
|
|
97
|
-
@default_border = color_name
|
|
98
|
-
else
|
|
99
|
-
@default_border = { color: color_name.to_s, level: brightness_level.to_i }
|
|
100
|
-
end
|
|
264
|
+
@default_border = { color: color_name.to_s, level: brightness_level.to_i }
|
|
101
265
|
end
|
|
102
266
|
alias_method :Border, :border
|
|
103
267
|
alias_method :border=, :border
|
|
@@ -105,11 +269,7 @@ class GRmenu
|
|
|
105
269
|
def options(color_name = nil, brightness_level = 1)
|
|
106
270
|
@default_options ||= { color: "white", level: 1 }
|
|
107
271
|
return @default_options if color_name.nil?
|
|
108
|
-
|
|
109
|
-
@default_options = color_name
|
|
110
|
-
else
|
|
111
|
-
@default_options = { color: color_name.to_s, level: brightness_level.to_i }
|
|
112
|
-
end
|
|
272
|
+
@default_options = { color: color_name.to_s, level: brightness_level.to_i }
|
|
113
273
|
end
|
|
114
274
|
alias_method :Options, :options
|
|
115
275
|
alias_method :options=, :options
|
|
@@ -117,14 +277,51 @@ class GRmenu
|
|
|
117
277
|
def focus(color_name = nil, brightness_level = 2)
|
|
118
278
|
@default_focus ||= { color: "green", level: 2 }
|
|
119
279
|
return @default_focus if color_name.nil?
|
|
120
|
-
|
|
121
|
-
@default_focus = color_name
|
|
122
|
-
else
|
|
123
|
-
@default_focus = { color: color_name.to_s, level: brightness_level.to_i }
|
|
124
|
-
end
|
|
280
|
+
@default_focus = { color: color_name.to_s, level: brightness_level.to_i }
|
|
125
281
|
end
|
|
126
282
|
alias_method :Focus, :focus
|
|
127
283
|
alias_method :focus=, :focus
|
|
284
|
+
|
|
285
|
+
def title(color_name = nil, brightness_level = 2)
|
|
286
|
+
@default_title ||= { color: "yellow", level: 2 }
|
|
287
|
+
return @default_title if color_name.nil?
|
|
288
|
+
@default_title = { color: color_name.to_s, level: brightness_level.to_i }
|
|
289
|
+
end
|
|
290
|
+
alias_method :Title, :title
|
|
291
|
+
alias_method :title=, :title
|
|
292
|
+
|
|
293
|
+
def banner(color_name = nil, brightness_level = 2)
|
|
294
|
+
@default_banner ||= { color: "magenta", level: 2 }
|
|
295
|
+
return @default_banner if color_name.nil?
|
|
296
|
+
@default_banner = { color: color_name.to_s, level: brightness_level.to_i }
|
|
297
|
+
end
|
|
298
|
+
alias_method :Banner, :banner
|
|
299
|
+
alias_method :banner=, :banner
|
|
300
|
+
|
|
301
|
+
def subtitle(color_name = nil, brightness_level = 2)
|
|
302
|
+
@default_subtitle ||= { color: "cyan", level: 2 }
|
|
303
|
+
return @default_subtitle if color_name.nil?
|
|
304
|
+
@default_subtitle = { color: color_name.to_s, level: brightness_level.to_i }
|
|
305
|
+
end
|
|
306
|
+
alias_method :Subtitle, :subtitle
|
|
307
|
+
alias_method :subtitle=, :subtitle
|
|
308
|
+
|
|
309
|
+
def divider(color_name = nil, brightness_level = 1)
|
|
310
|
+
@default_divider ||= { color: "blue", level: 1 }
|
|
311
|
+
return @default_divider if color_name.nil?
|
|
312
|
+
@default_divider = { color: color_name.to_s, level: brightness_level.to_i }
|
|
313
|
+
end
|
|
314
|
+
alias_method :Divider, :divider
|
|
315
|
+
alias_method :divider=, :divider
|
|
316
|
+
|
|
317
|
+
def font(font_id = nil)
|
|
318
|
+
@default_font ||= 1
|
|
319
|
+
return @default_font if font_id.nil?
|
|
320
|
+
@default_font = font_id.to_i
|
|
321
|
+
end
|
|
322
|
+
alias_method :Font, :font
|
|
323
|
+
alias_method :set_font, :font
|
|
324
|
+
alias_method :font=, :font
|
|
128
325
|
end
|
|
129
326
|
end
|
|
130
327
|
|
|
@@ -136,7 +333,7 @@ class GRmenu
|
|
|
136
333
|
end
|
|
137
334
|
end
|
|
138
335
|
|
|
139
|
-
attr_accessor :functions, :title, :style, :index, :style_config
|
|
336
|
+
attr_accessor :functions, :title, :subtitle, :banner, :banner_style, :divider, :style, :index, :style_config, :center
|
|
140
337
|
|
|
141
338
|
alias_method :options, :functions
|
|
142
339
|
alias_method :options=, :functions=
|
|
@@ -144,37 +341,221 @@ class GRmenu
|
|
|
144
341
|
alias_method :selected_index=, :index=
|
|
145
342
|
alias_method :SetStyle, :style_config
|
|
146
343
|
alias_method :set_style, :style_config
|
|
344
|
+
alias_method :description, :subtitle
|
|
345
|
+
alias_method :description=, :subtitle=
|
|
346
|
+
|
|
347
|
+
def self.STYLES = STYLES
|
|
348
|
+
def self.COLORS = COLORS
|
|
349
|
+
def self.BORDERS = BORDERS
|
|
350
|
+
def self.FONTS = FONTS
|
|
351
|
+
def self.FONT = FONT_1
|
|
352
|
+
|
|
353
|
+
def self.terminal_width
|
|
354
|
+
cols = $stdout.winsize[1] rescue nil
|
|
355
|
+
cols = $stdin.winsize[1] rescue nil if cols.nil? || cols <= 0
|
|
356
|
+
(cols && cols > 0) ? cols : (ENV['COLUMNS'] ? ENV['COLUMNS'].to_i : 80)
|
|
357
|
+
rescue StandardError
|
|
358
|
+
80
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
def self.clear_screen
|
|
362
|
+
Kernel.print(CLEAR_SCREEN_SEQUENCE)
|
|
363
|
+
end
|
|
364
|
+
class << self
|
|
365
|
+
alias_method :clr, :clear_screen
|
|
366
|
+
end
|
|
147
367
|
|
|
148
|
-
def self.
|
|
149
|
-
|
|
368
|
+
def self.div(long = nil, color = "blue", level = 1, char = "─")
|
|
369
|
+
width = long || [terminal_width - 2, 64].min
|
|
370
|
+
color_code = COLORS.dig(color.to_s.downcase, level) || "\e[34m"
|
|
371
|
+
Kernel.print("#{color_code}#{char * width}#{COLORS['reset']}\r\n")
|
|
150
372
|
end
|
|
151
373
|
|
|
152
|
-
def self.
|
|
153
|
-
|
|
374
|
+
def self.help
|
|
375
|
+
w = [terminal_width - 4, 64].min
|
|
376
|
+
w = [w, 42].max
|
|
377
|
+
inner_w = w - 2
|
|
378
|
+
h_line = "═" * inner_w
|
|
379
|
+
s_line = "─" * w
|
|
380
|
+
|
|
381
|
+
Kernel.print "\r\n"
|
|
382
|
+
Kernel.print "#{Color.bright_cyan("╔" + h_line + "╗")}\r\n"
|
|
383
|
+
Kernel.print "#{Color.bright_cyan("║")}#{Color.bright_yellow("GRmenu - Guia y Referencia Rapida".center(inner_w))}#{Color.bright_cyan("║")}\r\n"
|
|
384
|
+
Kernel.print "#{Color.bright_cyan("║")}#{Color.gray("Navegacion interactiva en terminal TTY".center(inner_w))}#{Color.bright_cyan("║")}\r\n"
|
|
385
|
+
Kernel.print "#{Color.bright_cyan("╚" + h_line + "╝")}\r\n\r\n"
|
|
386
|
+
|
|
387
|
+
Kernel.print "#{Color.bright_magenta("[1] HELPERS NATIVOS EN MODO CRUDO")}\r\n"
|
|
388
|
+
Kernel.print "#{Color.bright_blue(s_line)}\r\n"
|
|
389
|
+
Kernel.print " #{Color.bright_green("GRmenu.clear_screen")} #{Color.gray("(o GRmenu.clr)")}\r\n"
|
|
390
|
+
Kernel.print " * Limpia la terminal al instante con secuencias ANSI.\r\n"
|
|
391
|
+
Kernel.print " #{Color.bright_green("GRmenu.continue(mensaje)")}\r\n"
|
|
392
|
+
Kernel.print " * Pausa interactiva: espera una sola tecla en modo TTY crudo.\r\n"
|
|
393
|
+
Kernel.print " #{Color.bright_green("GRmenu.banner(texto, delay, color:, level:, style:, font:)")}\r\n"
|
|
394
|
+
Kernel.print " * Renderiza banner ASCII 3D con marco y animacion opcional.\r\n"
|
|
395
|
+
Kernel.print " #{Color.bright_green("GRmenu.div(longitud, color, level, char)")}\r\n"
|
|
396
|
+
Kernel.print " * Dibuja linea divisoria horizontal adaptable a la consola.\r\n"
|
|
397
|
+
Kernel.print " #{Color.bright_green("GRmenu.help")}\r\n"
|
|
398
|
+
Kernel.print " * Imprime esta guia visual interactiva en consola.\r\n\r\n"
|
|
399
|
+
|
|
400
|
+
Kernel.print "#{Color.bright_magenta("[2] MODULO DE COLORES (Color / C)")}\r\n"
|
|
401
|
+
Kernel.print "#{Color.bright_blue(s_line)}\r\n"
|
|
402
|
+
Kernel.print " #{Color.cyan("Uso directo: ")}#{Color.bright_white("puts Color.green(\"Texto\")")} | #{Color.bright_white("puts Color.bright_cyan(\"Texto\")")}\r\n"
|
|
403
|
+
Kernel.print " #{Color.cyan("Paleta: ")}#{Color.red("red")}, #{Color.green("green")}, #{Color.yellow("yellow")}, #{Color.blue("blue")}, #{Color.magenta("magenta")}, #{Color.purple("purple")}, #{Color.pink("pink")}, #{Color.cyan("cyan")}, #{Color.aqua("aqua")}, #{Color.orange("orange")}, #{Color.white("white")}, #{Color.gray("gray")}, #{Color.black("black")}.\r\n"
|
|
404
|
+
Kernel.print " #{Color.cyan("Brillo: ")}#{Color.white("1")} = Normal, #{Color.bright_white("2")} = Brillante / Bold.\r\n\r\n"
|
|
405
|
+
|
|
406
|
+
Kernel.print "#{Color.bright_magenta("[3] FUENTES ASCII 3D DEL BANNER (font: 1 al 10)")}\r\n"
|
|
407
|
+
Kernel.print "#{Color.bright_blue(s_line)}\r\n"
|
|
408
|
+
Kernel.print " #{Color.yellow("1")} -> #{Color.bright_white("ANSI Shadow 3D (Default)")} #{Color.cyan("[██████╗ ██╗ ██╗]")}\r\n"
|
|
409
|
+
Kernel.print " #{Color.yellow("2")} -> #{Color.bright_white("Slant 3D (FIGlet)")} #{Color.cyan("[ ____ __ __]")}\r\n"
|
|
410
|
+
Kernel.print " #{Color.yellow("3")} -> #{Color.bright_white("Doom / Standard 3D")} #{Color.cyan("[ ____ _ _]")}\r\n"
|
|
411
|
+
Kernel.print " #{Color.yellow("4")} -> #{Color.bright_white("Graffiti Shadow 3D")} #{Color.cyan("[ ,---. ,--. ,--.]")}\r\n"
|
|
412
|
+
Kernel.print " #{Color.yellow("5")} -> #{Color.bright_white("Small Slant / Mini 3D")} #{Color.cyan("[ ___ _ _]")}\r\n"
|
|
413
|
+
Kernel.print " #{Color.yellow("6")} -> #{Color.bright_white("Modular Pipe 3D")} #{Color.cyan("[ _____ _____]")}\r\n"
|
|
414
|
+
Kernel.print " #{Color.yellow("7")} -> #{Color.bright_white("Bubble / Round Gothic")} #{Color.cyan("[ ____ _ _]")}\r\n"
|
|
415
|
+
Kernel.print " #{Color.yellow("8")} -> #{Color.bright_white("Double-Line Wire 3D")} #{Color.cyan("[ ╔═════╗ ║ ║]")}\r\n"
|
|
416
|
+
Kernel.print " #{Color.yellow("9")} -> #{Color.bright_white("Solid Fat 3D Block")} #{Color.cyan("[ ██████▄ ██ ██]")}\r\n"
|
|
417
|
+
Kernel.print " #{Color.yellow("10")}-> #{Color.bright_white("Arcade Stars Matrix")} #{Color.cyan("[ ★★★★ ★ ★]")}\r\n\r\n"
|
|
418
|
+
|
|
419
|
+
Kernel.print "#{Color.bright_magenta("[4] ESTILOS DE MARCO (style / banner_style: 1 al 20)")}\r\n"
|
|
420
|
+
Kernel.print "#{Color.bright_blue(s_line)}\r\n"
|
|
421
|
+
Kernel.print " #{Color.yellow("3")} -> #{Color.bright_white("Doble linea")} #{Color.cyan("╔═══╗ ║ ║ ╚═══╝")} (Default en Banner)\r\n"
|
|
422
|
+
Kernel.print " #{Color.yellow("7")} -> #{Color.bright_white("Curvas redondeadas")} #{Color.cyan("╭───╮ │ │ ╰───╯")}\r\n"
|
|
423
|
+
Kernel.print " #{Color.yellow("4")} -> #{Color.bright_white("Linea gruesa")} #{Color.cyan("┏━━━┓ ┃ ┃ ┗━━━┛")}\r\n"
|
|
424
|
+
Kernel.print " #{Color.yellow("2")} -> #{Color.bright_white("Linea simple")} #{Color.cyan("┌───┐ │ │ └───┘")}\r\n"
|
|
425
|
+
Kernel.print " #{Color.yellow("8")} -> #{Color.bright_white("Bloques outline")} #{Color.cyan("▛▀▀▀▜ ▌ ▌ ▙▄▄▄▟")}\r\n"
|
|
426
|
+
Kernel.print " #{Color.yellow("19")} -> #{Color.bright_white("Circulos")} #{Color.cyan("●○○○● ● ● ●○○○●")} (Default en Opciones)\r\n"
|
|
427
|
+
Kernel.print " #{Color.yellow("20")} -> #{Color.bright_white("Estrellas")} #{Color.cyan("★☆☆☆★ ★ ★ ★☆☆☆★")}\r\n\r\n"
|
|
428
|
+
|
|
429
|
+
Kernel.print "#{Color.bright_magenta("[5] PARAMETROS DE GRmenu.new(functions, ...)")}\r\n"
|
|
430
|
+
Kernel.print "#{Color.bright_blue(s_line)}\r\n"
|
|
431
|
+
Kernel.print " #{Color.bright_green("banner:")} #{Color.white("String")} -> Texto grande a renderizar en arte ASCII 3D.\r\n"
|
|
432
|
+
Kernel.print " #{Color.bright_green("title:")} #{Color.white("String")} -> Titulo en el encabezado del recuadro.\r\n"
|
|
433
|
+
Kernel.print " #{Color.bright_green("subtitle:")} #{Color.white("String")} -> Subtitulo / descripcion (soporta \\n).\r\n"
|
|
434
|
+
Kernel.print " #{Color.bright_green("font:")} #{Color.white("Integer")} -> Fuente ASCII del banner (1 al 10, default 1).\r\n"
|
|
435
|
+
Kernel.print " #{Color.bright_green("style:")} #{Color.white("Integer")} -> Estilo de marco de opciones (1 al 20, default 19).\r\n"
|
|
436
|
+
Kernel.print " #{Color.bright_green("banner_style:")} #{Color.white("Integer")} -> Estilo de marco del banner (1 al 20, default 3).\r\n"
|
|
437
|
+
Kernel.print " #{Color.bright_green("divider:")} #{Color.white("Boolean")} -> Divisores alineados al banner (true/false).\r\n"
|
|
438
|
+
Kernel.print " #{Color.bright_green("center:")} #{Color.white("Boolean")} -> Centrado simetrico de subtitulo y menu (default true).\r\n\r\n"
|
|
439
|
+
|
|
440
|
+
Kernel.print "#{Color.bright_magenta("[6] METODOS DE CONFIGURACION (menu.set_style)")}\r\n"
|
|
441
|
+
Kernel.print "#{Color.bright_blue(s_line)}\r\n"
|
|
442
|
+
Kernel.print " #{Color.cyan("menu.set_style.font(id)")} -> Cambia fuente ASCII (1..10)\r\n"
|
|
443
|
+
Kernel.print " #{Color.cyan("menu.set_style.banner(color, level)")} -> Color y brillo del banner ASCII\r\n"
|
|
444
|
+
Kernel.print " #{Color.cyan("menu.set_style.title(color, level)")} -> Color y brillo del titulo\r\n"
|
|
445
|
+
Kernel.print " #{Color.cyan("menu.set_style.subtitle(color, level)")} -> Color y brillo del subtitulo\r\n"
|
|
446
|
+
Kernel.print " #{Color.cyan("menu.set_style.divider(color, level)")} -> Color y brillo de las lineas divisorias\r\n"
|
|
447
|
+
Kernel.print " #{Color.cyan("menu.set_style.border(color, level)")} -> Color y brillo del marco de opciones\r\n"
|
|
448
|
+
Kernel.print " #{Color.cyan("menu.set_style.options(color, level)")} -> Color y brillo de opciones no activas\r\n"
|
|
449
|
+
Kernel.print " #{Color.cyan("menu.set_style.focus(color, level)")} -> Color y brillo de la opcion resaltada\r\n\r\n"
|
|
450
|
+
|
|
451
|
+
Kernel.print "#{Color.bright_magenta("[7] EJECUCION (menu.draw)")}\r\n"
|
|
452
|
+
Kernel.print "#{Color.bright_blue(s_line)}\r\n"
|
|
453
|
+
Kernel.print " #{Color.bright_white("menu.draw(size_max: 38)")} -> Inicia el menu interactivo con ancho minimo.\r\n"
|
|
454
|
+
Kernel.print "#{Color.bright_blue(s_line)}\r\n\r\n"
|
|
154
455
|
end
|
|
155
456
|
|
|
156
|
-
def
|
|
157
|
-
|
|
457
|
+
def help
|
|
458
|
+
self.class.help
|
|
158
459
|
end
|
|
159
460
|
|
|
160
|
-
def
|
|
461
|
+
def self.continue(text = "Presiona cualquier tecla para continuar...")
|
|
462
|
+
Kernel.print("#{Color.gray(text)} ")
|
|
463
|
+
if $stdin.respond_to?(:raw) && $stdin.respond_to?(:tty?) && $stdin.tty?
|
|
464
|
+
$stdin.raw(&:getch)
|
|
465
|
+
elsif $stdin.respond_to?(:getch)
|
|
466
|
+
$stdin.getch
|
|
467
|
+
else
|
|
468
|
+
$stdin.read(1)
|
|
469
|
+
end
|
|
470
|
+
Kernel.print("\r\n")
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
def self.build_ascii_lines(text, max_cols = terminal_width, font_id = 1)
|
|
474
|
+
target_font = FONTS[font_id.to_i] || FONTS[1]
|
|
475
|
+
clean_chars = text.to_s.upcase.chars.select { |c| target_font.key?(c) }
|
|
476
|
+
return [] if clean_chars.empty?
|
|
477
|
+
|
|
478
|
+
font_height = target_font.values.first.length
|
|
479
|
+
|
|
480
|
+
[2, 1, 0].each do |spacing|
|
|
481
|
+
lines = Array.new(font_height, "")
|
|
482
|
+
clean_chars.each_with_index do |c, idx|
|
|
483
|
+
fig = target_font[c]
|
|
484
|
+
pad = (idx == clean_chars.length - 1) ? "" : (" " * spacing)
|
|
485
|
+
font_height.times { |i| lines[i] += fig[i] + pad }
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
max_len = lines.map(&:length).max
|
|
489
|
+
return lines if (max_len + 6) <= max_cols
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
nil
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
def self.banner(text, delay = 0, color: "magenta", level: 2, style: 3, font: 1)
|
|
496
|
+
cols = terminal_width
|
|
497
|
+
color_code = COLORS.dig(color.to_s.downcase, level) || "\e[1;95m"
|
|
498
|
+
reset_code = COLORS["reset"]
|
|
499
|
+
|
|
500
|
+
ascii_rows = build_ascii_lines(text, cols, font)
|
|
501
|
+
border_cfg = BORDERS[style] || BORDERS[3]
|
|
502
|
+
|
|
503
|
+
if ascii_rows
|
|
504
|
+
max_len = ascii_rows.map(&:length).max
|
|
505
|
+
h_fill = (border_cfg[:h] * ((max_len + 4).to_f / border_cfg[:h].length).ceil)[0...(max_len + 4)]
|
|
506
|
+
|
|
507
|
+
Kernel.print("#{color_code}#{border_cfg[:tl]}#{h_fill}#{border_cfg[:tr]}#{reset_code}\r\n")
|
|
508
|
+
ascii_rows.each do |line|
|
|
509
|
+
pad = " " * (max_len - line.length)
|
|
510
|
+
Kernel.print("#{color_code}#{border_cfg[:v]} #{line}#{pad} #{border_cfg[:v]}#{reset_code}\r\n")
|
|
511
|
+
sleep(delay) if delay > 0
|
|
512
|
+
end
|
|
513
|
+
Kernel.print("#{color_code}#{border_cfg[:bl]}#{h_fill}#{border_cfg[:br]}#{reset_code}\r\n")
|
|
514
|
+
else
|
|
515
|
+
clean_t = text.to_s.strip
|
|
516
|
+
box_w = [clean_t.length + 6, cols - 2].min
|
|
517
|
+
h_fill = (border_cfg[:h] * ((box_w - 2).to_f / border_cfg[:h].length).ceil)[0...(box_w - 2)]
|
|
518
|
+
Kernel.print("#{color_code}#{border_cfg[:tl]}#{h_fill}#{border_cfg[:tr]}#{reset_code}\r\n")
|
|
519
|
+
Kernel.print("#{color_code}#{border_cfg[:v]} #{clean_t.center(box_w - 4)} #{border_cfg[:v]}#{reset_code}\r\n")
|
|
520
|
+
Kernel.print("#{color_code}#{border_cfg[:bl]}#{h_fill}#{border_cfg[:br]}#{reset_code}\r\n")
|
|
521
|
+
end
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
class << self
|
|
525
|
+
alias_method :message, :banner
|
|
526
|
+
alias_method :logo, :banner
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
def initialize(functions, *positional_arguments, title: nil, banner: nil, subtitle: nil, description: nil, divider: nil, style: nil, banner_style: nil, center: true, font: nil, **keyword_arguments)
|
|
161
530
|
@functions = functions.is_a?(Array) ? functions : Array(functions)
|
|
162
531
|
|
|
163
532
|
pos_title = positional_arguments[0]
|
|
164
533
|
pos_style = positional_arguments[1]
|
|
165
534
|
|
|
166
|
-
@title
|
|
167
|
-
@
|
|
168
|
-
@
|
|
169
|
-
@
|
|
535
|
+
@title = (title || pos_title || keyword_arguments[:title] || "").to_s
|
|
536
|
+
@banner = (banner || keyword_arguments[:banner] || "").to_s
|
|
537
|
+
@subtitle = (subtitle || description || keyword_arguments[:subtitle] || keyword_arguments[:description] || "").to_s
|
|
538
|
+
@divider = divider.nil? ? (!@banner.empty? || !@subtitle.empty?) : divider
|
|
539
|
+
@style = (style || pos_style || keyword_arguments[:style] || 19).to_i
|
|
540
|
+
@banner_style = (banner_style || keyword_arguments[:banner_style] || 3).to_i
|
|
541
|
+
@center = center.nil? ? true : center
|
|
542
|
+
@index = 0
|
|
543
|
+
@clear_seq = CLEAR_SCREEN_SEQUENCE
|
|
544
|
+
|
|
545
|
+
init_font = font || keyword_arguments[:font_style] || SetStyle.font || 1
|
|
170
546
|
|
|
171
547
|
@style_config = SetStyle.new(
|
|
172
|
-
border:
|
|
173
|
-
options:
|
|
174
|
-
focus:
|
|
548
|
+
border: SetStyle.border.dup,
|
|
549
|
+
options: SetStyle.options.dup,
|
|
550
|
+
focus: SetStyle.focus.dup,
|
|
551
|
+
title: SetStyle.title.dup,
|
|
552
|
+
banner: SetStyle.banner.dup,
|
|
553
|
+
subtitle: SetStyle.subtitle.dup,
|
|
554
|
+
divider: SetStyle.divider.dup,
|
|
555
|
+
font: init_font
|
|
175
556
|
)
|
|
176
557
|
end
|
|
177
|
-
|
|
558
|
+
|
|
178
559
|
def move_up
|
|
179
560
|
return @index if @functions.empty?
|
|
180
561
|
@index = (@index - 1) % @functions.length
|
|
@@ -209,73 +590,153 @@ class GRmenu
|
|
|
209
590
|
end
|
|
210
591
|
alias_method :_hline, :build_horizontal_line
|
|
211
592
|
|
|
212
|
-
def
|
|
213
|
-
|
|
593
|
+
def render_banner_lines(term_cols)
|
|
594
|
+
return [[], 0] if @banner.nil? || @banner.empty?
|
|
595
|
+
|
|
596
|
+
font_id = @style_config.font || 1
|
|
597
|
+
ascii_rows = self.class.build_ascii_lines(@banner, term_cols, font_id)
|
|
598
|
+
banner_border = BORDERS[@banner_style] || BORDERS[3]
|
|
599
|
+
banner_color_cfg = @style_config.banner
|
|
600
|
+
|
|
601
|
+
lines = []
|
|
602
|
+
box_w = 0
|
|
603
|
+
h_top = banner_border[:ht] || banner_border[:h]
|
|
604
|
+
h_bot = banner_border[:hb] || banner_border[:h]
|
|
605
|
+
v_l = banner_border[:vl] || banner_border[:v]
|
|
606
|
+
v_r = banner_border[:vr] || banner_border[:v]
|
|
607
|
+
|
|
608
|
+
if ascii_rows
|
|
609
|
+
content_w = ascii_rows.map(&:length).max
|
|
610
|
+
box_w = content_w + 6
|
|
611
|
+
top_fill = build_horizontal_line(h_top, content_w + 4)
|
|
612
|
+
bot_fill = build_horizontal_line(h_bot, content_w + 4)
|
|
613
|
+
|
|
614
|
+
lines << colorize("#{banner_border[:tl]}#{top_fill}#{banner_border[:tr]}", banner_color_cfg)
|
|
615
|
+
ascii_rows.each do |row|
|
|
616
|
+
pad = " " * (content_w - row.length)
|
|
617
|
+
lines << colorize("#{v_l} #{row}#{pad} #{v_r}", banner_color_cfg)
|
|
618
|
+
end
|
|
619
|
+
lines << colorize("#{banner_border[:bl]}#{bot_fill}#{banner_border[:br]}", banner_color_cfg)
|
|
620
|
+
else
|
|
621
|
+
clean_b = @banner.strip
|
|
622
|
+
box_w = [clean_b.length + 6, term_cols - 2].min
|
|
623
|
+
top_fill = build_horizontal_line(h_top, box_w - 2)
|
|
624
|
+
bot_fill = build_horizontal_line(h_bot, box_w - 2)
|
|
625
|
+
|
|
626
|
+
lines << colorize("#{banner_border[:tl]}#{top_fill}#{banner_border[:tr]}", banner_color_cfg)
|
|
627
|
+
lines << colorize("#{v_l} #{clean_b.center(box_w - 4)} #{v_r}", banner_color_cfg)
|
|
628
|
+
lines << colorize("#{banner_border[:bl]}#{bot_fill}#{banner_border[:br]}", banner_color_cfg)
|
|
629
|
+
end
|
|
630
|
+
[lines, box_w]
|
|
214
631
|
end
|
|
215
632
|
|
|
216
633
|
def render_lines(size_max = 20)
|
|
634
|
+
term_cols = self.class.terminal_width
|
|
635
|
+
rendered_lines = []
|
|
636
|
+
|
|
637
|
+
banner_box_width = 0
|
|
638
|
+
if @banner && !@banner.empty?
|
|
639
|
+
banner_lines, banner_box_width = render_banner_lines(term_cols)
|
|
640
|
+
rendered_lines.concat(banner_lines)
|
|
641
|
+
rendered_lines << ""
|
|
642
|
+
end
|
|
643
|
+
|
|
217
644
|
option_names = @functions.map { |func| extract_name_from_action(func) }
|
|
645
|
+
calculated_width = [size_max, @title.length + 4].max
|
|
646
|
+
calculated_width = ([calculated_width] + option_names.map { |name| name.length + 6 }).max
|
|
647
|
+
total_width = [calculated_width, term_cols - 2].min
|
|
648
|
+
|
|
649
|
+
reference_width = banner_box_width > 0 ? banner_box_width : total_width
|
|
650
|
+
margin_left = (@center && reference_width > total_width) ? " " * ((reference_width - total_width) / 2) : ""
|
|
651
|
+
|
|
652
|
+
if @subtitle && !@subtitle.empty?
|
|
653
|
+
subtitle_lines = @subtitle.lines.map(&:chomp)
|
|
654
|
+
div_w = @divider.is_a?(Numeric) ? @divider.to_i : [reference_width, term_cols - 2].min
|
|
655
|
+
|
|
656
|
+
if @divider
|
|
657
|
+
rendered_lines << colorize("─" * div_w, @style_config.divider)
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
subtitle_lines.each do |sub_line|
|
|
661
|
+
formatted_sub = @center ? sub_line.center(div_w) : sub_line
|
|
662
|
+
rendered_lines << colorize(formatted_sub, @style_config.subtitle)
|
|
663
|
+
end
|
|
218
664
|
|
|
219
|
-
|
|
665
|
+
if @divider
|
|
666
|
+
rendered_lines << colorize("─" * div_w, @style_config.divider)
|
|
667
|
+
end
|
|
668
|
+
rendered_lines << ""
|
|
669
|
+
end
|
|
220
670
|
|
|
221
671
|
border_color_cfg = @style_config.border
|
|
222
672
|
options_color_cfg = @style_config.options
|
|
223
673
|
focus_color_cfg = @style_config.focus
|
|
674
|
+
title_color_cfg = @style_config.title
|
|
224
675
|
|
|
225
676
|
box_border = BORDERS[@style]
|
|
226
|
-
rendered_lines = []
|
|
227
677
|
|
|
228
678
|
if box_border
|
|
229
|
-
|
|
230
|
-
|
|
679
|
+
h_top = box_border[:ht] || box_border[:h]
|
|
680
|
+
h_bot = box_border[:hb] || box_border[:h]
|
|
681
|
+
v_l_raw = box_border[:vl] || box_border[:v]
|
|
682
|
+
v_r_raw = box_border[:vr] || box_border[:v]
|
|
683
|
+
|
|
684
|
+
top_fill = build_horizontal_line(h_top, total_width - 2)
|
|
685
|
+
bot_fill = build_horizontal_line(h_bot, total_width - 2)
|
|
686
|
+
mid_fill = build_horizontal_line(h_top, total_width - 2)
|
|
231
687
|
|
|
232
|
-
|
|
233
|
-
|
|
688
|
+
v_left = colorize(v_l_raw, border_color_cfg)
|
|
689
|
+
v_right = colorize(v_r_raw, border_color_cfg)
|
|
690
|
+
|
|
691
|
+
top_border_line = box_border[:tl] + top_fill + box_border[:tr]
|
|
692
|
+
rendered_lines << "#{margin_left}#{colorize(top_border_line, border_color_cfg)}"
|
|
234
693
|
|
|
235
694
|
unless @title.empty?
|
|
236
|
-
centered_title = @title.center(total_width - 4)
|
|
237
|
-
rendered_lines << "#{
|
|
695
|
+
centered_title = colorize(@title.center(total_width - 4), title_color_cfg)
|
|
696
|
+
rendered_lines << "#{margin_left}#{v_left} #{centered_title} #{v_right}"
|
|
238
697
|
|
|
239
|
-
separator_line =
|
|
240
|
-
rendered_lines << colorize(separator_line, border_color_cfg)
|
|
698
|
+
separator_line = v_l_raw + mid_fill + v_r_raw
|
|
699
|
+
rendered_lines << "#{margin_left}#{colorize(separator_line, border_color_cfg)}"
|
|
241
700
|
end
|
|
242
701
|
|
|
243
702
|
option_names.each_with_index do |option_name, current_index|
|
|
703
|
+
avail_w = [total_width - 6, 1].max
|
|
244
704
|
if @index == current_index
|
|
245
|
-
highlighted_text = colorize("
|
|
246
|
-
rendered_lines << "#{
|
|
705
|
+
highlighted_text = colorize("> #{option_name.ljust(avail_w)}", focus_color_cfg)
|
|
706
|
+
rendered_lines << "#{margin_left}#{v_left} #{highlighted_text} #{v_right}"
|
|
247
707
|
else
|
|
248
|
-
normal_text = colorize("
|
|
249
|
-
rendered_lines << "#{
|
|
708
|
+
normal_text = colorize(" #{option_name.ljust(avail_w)}", options_color_cfg)
|
|
709
|
+
rendered_lines << "#{margin_left}#{v_left} #{normal_text} #{v_right}"
|
|
250
710
|
end
|
|
251
711
|
end
|
|
252
712
|
|
|
253
|
-
bottom_border_line = box_border[:bl] +
|
|
254
|
-
rendered_lines << colorize(bottom_border_line, border_color_cfg)
|
|
713
|
+
bottom_border_line = box_border[:bl] + bot_fill + box_border[:br]
|
|
714
|
+
rendered_lines << "#{margin_left}#{colorize(bottom_border_line, border_color_cfg)}"
|
|
255
715
|
else
|
|
256
716
|
symbol_char = STYLES[@style] || "#"
|
|
257
717
|
solid_border = colorize(symbol_char, border_color_cfg)
|
|
258
718
|
solid_line = symbol_char * total_width
|
|
259
719
|
|
|
260
|
-
rendered_lines << colorize(solid_line, border_color_cfg)
|
|
720
|
+
rendered_lines << "#{margin_left}#{colorize(solid_line, border_color_cfg)}"
|
|
261
721
|
|
|
262
722
|
unless @title.empty?
|
|
263
|
-
centered_title = @title.center(total_width - 4)
|
|
264
|
-
rendered_lines << "#{solid_border} #{centered_title} #{solid_border}"
|
|
265
|
-
rendered_lines << colorize(solid_line, border_color_cfg)
|
|
723
|
+
centered_title = colorize(@title.center(total_width - 4), title_color_cfg)
|
|
724
|
+
rendered_lines << "#{margin_left}#{solid_border} #{centered_title} #{solid_border}"
|
|
725
|
+
rendered_lines << "#{margin_left}#{colorize(solid_line, border_color_cfg)}"
|
|
266
726
|
end
|
|
267
727
|
|
|
268
728
|
option_names.each_with_index do |option_name, current_index|
|
|
729
|
+
avail_w = [total_width - 6, 1].max
|
|
269
730
|
if @index == current_index
|
|
270
|
-
highlighted_text = colorize(option_name.ljust(
|
|
271
|
-
rendered_lines << "#{solid_border} #{highlighted_text} #{solid_border}"
|
|
731
|
+
highlighted_text = colorize("> #{option_name.ljust(avail_w)}", focus_color_cfg)
|
|
732
|
+
rendered_lines << "#{margin_left}#{solid_border} #{highlighted_text} #{solid_border}"
|
|
272
733
|
else
|
|
273
|
-
normal_text = colorize(option_name.ljust(
|
|
274
|
-
rendered_lines << "#{solid_border} #{normal_text} #{solid_border}"
|
|
734
|
+
normal_text = colorize(" #{option_name.ljust(avail_w)}", options_color_cfg)
|
|
735
|
+
rendered_lines << "#{margin_left}#{solid_border} #{normal_text} #{solid_border}"
|
|
275
736
|
end
|
|
276
737
|
end
|
|
277
738
|
|
|
278
|
-
rendered_lines << colorize(solid_line, border_color_cfg)
|
|
739
|
+
rendered_lines << "#{margin_left}#{colorize(solid_line, border_color_cfg)}"
|
|
279
740
|
end
|
|
280
741
|
|
|
281
742
|
rendered_lines
|
|
@@ -330,10 +791,10 @@ class GRmenu
|
|
|
330
791
|
while (key = read_single_key(input_stream))
|
|
331
792
|
break if key == "q" || key == "Q" || key == "\x03" || key == "\x04"
|
|
332
793
|
|
|
333
|
-
if key == "\e[A" || key == "\eOA"
|
|
794
|
+
if key == "\e[A" || key == "\eOA" || key == "\xe0H" || key == "\x00H"
|
|
334
795
|
move_up
|
|
335
796
|
draw_frame(target_width)
|
|
336
|
-
elsif key == "\e[B" || key == "\eOB"
|
|
797
|
+
elsif key == "\e[B" || key == "\eOB" || key == "\xe0P" || key == "\x00P"
|
|
337
798
|
move_down
|
|
338
799
|
draw_frame(target_width)
|
|
339
800
|
elsif key == "\r" || key == "\n"
|
|
@@ -363,6 +824,14 @@ class GRmenu
|
|
|
363
824
|
first_char << extra_chars
|
|
364
825
|
rescue IO::WaitReadable, IO::EAGAINWaitReadable, EOFError
|
|
365
826
|
end
|
|
827
|
+
elsif first_char == "\x00" || first_char == "\xe0"
|
|
828
|
+
begin
|
|
829
|
+
second_char = input_stream.read_nonblock(1)
|
|
830
|
+
first_char << second_char
|
|
831
|
+
rescue IO::WaitReadable, IO::EAGAINWaitReadable, EOFError
|
|
832
|
+
second_char = input_stream.getch rescue nil
|
|
833
|
+
first_char << second_char if second_char
|
|
834
|
+
end
|
|
366
835
|
end
|
|
367
836
|
|
|
368
837
|
first_char
|
|
@@ -370,27 +839,32 @@ class GRmenu
|
|
|
370
839
|
nil
|
|
371
840
|
end
|
|
372
841
|
|
|
842
|
+
def format_auto_name(raw_name)
|
|
843
|
+
cleaned = raw_name.to_s.gsub(/[_-]+/, ' ').strip
|
|
844
|
+
cleaned.split(' ').map(&:capitalize).join(' ')
|
|
845
|
+
end
|
|
846
|
+
|
|
373
847
|
def extract_name_from_action(action)
|
|
374
848
|
case action
|
|
375
|
-
when Method
|
|
376
|
-
action.name.to_s
|
|
377
|
-
when Symbol
|
|
378
|
-
action.to_s
|
|
379
849
|
when Array
|
|
380
850
|
action[0].to_s
|
|
851
|
+
when Method
|
|
852
|
+
format_auto_name(action.name)
|
|
853
|
+
when Symbol
|
|
854
|
+
format_auto_name(action)
|
|
381
855
|
when Proc
|
|
382
856
|
if action.respond_to?(:name) && action.name
|
|
383
|
-
action.name
|
|
857
|
+
format_auto_name(action.name)
|
|
384
858
|
else
|
|
385
|
-
"
|
|
859
|
+
"Opcion"
|
|
386
860
|
end
|
|
387
861
|
else
|
|
388
862
|
if action.respond_to?(:name)
|
|
389
|
-
action.name
|
|
863
|
+
format_auto_name(action.name)
|
|
390
864
|
elsif action.respond_to?(:title)
|
|
391
865
|
action.title.to_s
|
|
392
866
|
else
|
|
393
|
-
action
|
|
867
|
+
format_auto_name(action)
|
|
394
868
|
end
|
|
395
869
|
end
|
|
396
870
|
end
|
|
@@ -414,4 +888,7 @@ class GRmenu
|
|
|
414
888
|
end
|
|
415
889
|
end
|
|
416
890
|
|
|
891
|
+
Color = GRmenu::Color unless defined?(Color)
|
|
892
|
+
Colors = GRmenu::Color unless defined?(Colors)
|
|
893
|
+
C = GRmenu::Color unless defined?(C)
|
|
417
894
|
Grmenu = GRmenu unless defined?(Grmenu)
|