rcurses 4.0 → 4.5
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 +8 -2
- data/lib/rcurses.rb +1 -1
- data/lib/string_extensions.rb +124 -88
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 494b705528b9bb3e5040c9ecf8c1ff31c7cf0db13e04736140f35298d49b48b0
|
4
|
+
data.tar.gz: 6f74dc4880397c55461c6ff0f5a6371edec67edfdfb3eb3d65d7dafdc5ac6295
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0cbed1ef8307fd124d035dce74501d58f648668884ae50190d82460d33b3825557ccd1f07b4c0a64d7b69c81a0c1ef240cf1828adc769e96ffd9bfb47d3e7f08
|
7
|
+
data.tar.gz: 0f7747ce65c0f8103759b8df5e64bceee0bf7593d137380a08b6002ec6cbaeb3540236f9ebc65b1f28f293ed009b76565fe4e0fd63cdcb2bf597dd32430107cc
|
data/README.md
CHANGED
@@ -10,6 +10,9 @@ Here's a somewhat simple example of a TUI program using rcurses: The [T-REX](htt
|
|
10
10
|
|
11
11
|
And here's a much more involved example: The [RTFM](https://github.com/isene/RTFM) terminal file manager.
|
12
12
|
|
13
|
+
# NOTE: Version 4.5 gives full RGB support in addition to 256-colors
|
14
|
+
Just write a color as a string - e.g. `"d533e0"` for a hexadecimal RGB color (or use the terminal 256 colors by supplying an integer in the range 0-255)
|
15
|
+
|
13
16
|
# Why?
|
14
17
|
Having struggled with the venerable curses library and the ruby interface to it for many years, I finally got around to write an alternative - in pure Ruby.
|
15
18
|
|
@@ -83,6 +86,7 @@ Method | Description
|
|
83
86
|
new/init | Initializes a Pane with optional arguments `x, y, w, h, fg and bg`
|
84
87
|
move(x,y) | Move the pane by `x`and `y` (`mypane.move(-4,5)` will move the pane left four characters and five characters down)
|
85
88
|
refresh | Refreshes/redraws the Pane with content
|
89
|
+
border_refresh | Refresh the Pane border only
|
86
90
|
full_refresh | Refreshes/redraws the Pane with content completely (without diff rendering)
|
87
91
|
edit | An editor for the Pane. When this is invoked, all existing font dressing is stripped and the user gets to edit the raw text. The user can add font effects similar to Markdown; Use an asterisk before and after text to be drawn in bold, text between forward-slashes become italic, and underline before and after text means the text will be underlined, a hash-sign before and after text makes the text reverse colored. You can also combine a whole set of dressings in this format: `<23,245,biurl|Hello World!>` - this will make "Hello World!" print in the color 23 with the background color 245 (regardless of the Pane's fg/bg setting) in bold, italic, underlined, reversed colored and blinking. Hitting `ESC` while in edit mode will disregard the edits, while `Ctrl-S` will save the edits
|
88
92
|
editline | Used for one-line Panes. It will print the content of the property `prompt` and then the property `text` that can then be edited by the user. Hitting `ESC` will disregard the edits, while `ENTER` will save the edited text
|
@@ -97,12 +101,14 @@ bottom | Scroll to the bottom of the text in the pane
|
|
97
101
|
top | Scroll to the top of the text in the pane
|
98
102
|
|
99
103
|
# class String extensions
|
100
|
-
Method extensions provided for the class String
|
104
|
+
Method extensions provided for the class String.
|
105
|
+
|
106
|
+
A color can either be an integer in the range 0-255 for the usual 256 colors in a terminal, or it can be a string representing RGB. So both of these are valid: `string.fg(219)` and `string.fg("4d22a0")`.
|
101
107
|
|
102
108
|
Method | Description
|
103
109
|
---------------|---------------------------------------------------------------
|
104
110
|
fg(fg) | Set text to be printed with the foreground color `fg` (example: `"TEST".fg(84)`)
|
105
|
-
bg(bg) | Set text to be printed with the background color `bg` (example: `"TEST".bg(
|
111
|
+
bg(bg) | Set text to be printed with the background color `bg` (example: `"TEST".bg("dd32a9")`)
|
106
112
|
fb(fg, bg) | Set text to be printed with the foreground color `fg` and background color `bg` (example: `"TEST".fb(84,196)`)
|
107
113
|
b | Set text to be printed in bold (example: `"TEST".b`)
|
108
114
|
i | Set text to be printed in italic (example: `"TEST".i`)
|
data/lib/rcurses.rb
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
# Web_site: http://isene.com/
|
6
6
|
# Github: https://github.com/isene/rcurses
|
7
7
|
# License: Public domain
|
8
|
-
# Version: 4.
|
8
|
+
# Version: 4.5: Full RGB support in addition to 256-colors
|
9
9
|
|
10
10
|
require 'io/console' # Basic gem for rcurses
|
11
11
|
require 'io/wait' # stdin handling
|
data/lib/string_extensions.rb
CHANGED
@@ -1,124 +1,160 @@
|
|
1
|
+
# string_extensions.rb
|
2
|
+
|
1
3
|
class String
|
2
|
-
#
|
3
|
-
def fg(
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
4
|
+
# 256-color or truecolor RGB foreground
|
5
|
+
def fg(color)
|
6
|
+
sp, ep = if color.to_s =~ /\A[0-9A-Fa-f]{6}\z/
|
7
|
+
r, g, b = color.scan(/../).map { |c| c.to_i(16) }
|
8
|
+
["\e[38;2;#{r};#{g};#{b}m", "\e[0m"]
|
9
|
+
else
|
10
|
+
["\e[38;5;#{color}m", "\e[0m"]
|
11
|
+
end
|
12
|
+
color(self, sp, ep)
|
13
|
+
end
|
14
|
+
|
15
|
+
# 256-color or truecolor RGB background
|
16
|
+
def bg(color)
|
17
|
+
sp, ep = if color.to_s =~ /\A[0-9A-Fa-f]{6}\z/
|
18
|
+
r, g, b = color.scan(/../).map { |c| c.to_i(16) }
|
19
|
+
["\e[48;2;#{r};#{g};#{b}m", "\e[0m"]
|
20
|
+
else
|
21
|
+
["\e[48;5;#{color}m", "\e[0m"]
|
22
|
+
end
|
23
|
+
color(self, sp, ep)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Both fg and bg in one go
|
27
|
+
def fb(fg_color, bg_color)
|
28
|
+
parts = []
|
29
|
+
if fg_color.to_s =~ /\A[0-9A-Fa-f]{6}\z/
|
30
|
+
r, g, b = fg_color.scan(/../).map { |c| c.to_i(16) }
|
31
|
+
parts << "38;2;#{r};#{g};#{b}"
|
32
|
+
else
|
33
|
+
parts << "38;5;#{fg_color}"
|
34
|
+
end
|
35
|
+
|
36
|
+
if bg_color.to_s =~ /\A[0-9A-Fa-f]{6}\z/
|
37
|
+
r, g, b = bg_color.scan(/../).map { |c| c.to_i(16) }
|
38
|
+
parts << "48;2;#{r};#{g};#{b}"
|
39
|
+
else
|
40
|
+
parts << "48;5;#{bg_color}"
|
41
|
+
end
|
42
|
+
|
43
|
+
sp = "\e[#{parts.join(';')}m"
|
44
|
+
color(self, sp, "\e[0m")
|
45
|
+
end
|
46
|
+
|
47
|
+
# bold, italic, underline, blink, reverse
|
48
|
+
def b; color(self, "\e[1m", "\e[22m"); end
|
49
|
+
def i; color(self, "\e[3m", "\e[23m"); end
|
50
|
+
def u; color(self, "\e[4m", "\e[24m"); end
|
51
|
+
def l; color(self, "\e[5m", "\e[25m"); end
|
52
|
+
def r; color(self, "\e[7m", "\e[27m"); end
|
53
|
+
|
54
|
+
# Internal helper - wraps +text+ in start/end sequences,
|
55
|
+
# and re-applies start on every newline.
|
13
56
|
def color(text, sp, ep = "\e[0m")
|
14
|
-
|
15
|
-
#
|
16
|
-
text = text.gsub("\n", "#{ep}\n#{sp}")
|
17
|
-
"#{sp}#{text}#{ep}"
|
57
|
+
t = text.gsub("\n", "#{ep}\n#{sp}")
|
58
|
+
"#{sp}#{t}#{ep}"
|
18
59
|
end
|
19
60
|
|
20
|
-
#
|
61
|
+
# Combined code: "foo".c("FF0000,00FF00,bui")
|
62
|
+
# — 6-hex or decimal for fg, then for bg, then letters b/i/u/l/r
|
21
63
|
def c(code)
|
22
|
-
|
23
|
-
|
24
|
-
prop += "48;5;#{code.match(/(?<=,)\d+/).to_s};" if code.match(/(?<=,)\d+/)
|
25
|
-
prop += "1;" if code.include?('b')
|
26
|
-
prop += "3;" if code.include?('i')
|
27
|
-
prop += "4;" if code.include?('u')
|
28
|
-
prop += "5;" if code.include?('l')
|
29
|
-
prop += "7;" if code.include?('r')
|
30
|
-
prop.chop! if prop[-1] == ";"
|
31
|
-
prop += "m"
|
32
|
-
prop += self
|
33
|
-
prop += "\e[0m"
|
34
|
-
prop
|
35
|
-
end
|
64
|
+
parts = code.split(',')
|
65
|
+
seq = []
|
36
66
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
67
|
+
fg = parts.shift
|
68
|
+
if fg =~ /\A[0-9A-Fa-f]{6}\z/
|
69
|
+
r,g,b = fg.scan(/../).map{|c|c.to_i(16)}
|
70
|
+
seq << "38;2;#{r};#{g};#{b}"
|
71
|
+
elsif fg =~ /\A\d+\z/
|
72
|
+
seq << "38;5;#{fg}"
|
73
|
+
end
|
74
|
+
|
75
|
+
if parts.any?
|
76
|
+
bg = parts.shift
|
77
|
+
if bg =~ /\A[0-9A-Fa-f]{6}\z/
|
78
|
+
r,g,b = bg.scan(/../).map{|c|c.to_i(16)}
|
79
|
+
seq << "48;2;#{r};#{g};#{b}"
|
80
|
+
elsif bg =~ /\A\d+\z/
|
81
|
+
seq << "48;5;#{bg}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
seq << '1' if code.include?('b')
|
86
|
+
seq << '3' if code.include?('i')
|
87
|
+
seq << '4' if code.include?('u')
|
88
|
+
seq << '5' if code.include?('l')
|
89
|
+
seq << '7' if code.include?('r')
|
90
|
+
|
91
|
+
"\e[#{seq.join(';')}m#{self}\e[0m"
|
46
92
|
end
|
47
93
|
|
94
|
+
# Strip all ANSI SGR sequences
|
48
95
|
def pure
|
49
|
-
|
96
|
+
gsub(/\e\[\d+(?:;\d+)*m/, '')
|
50
97
|
end
|
51
98
|
|
52
|
-
#
|
99
|
+
# Remove stray leading/trailing reset if the string has no other styling
|
100
|
+
def clean_ansi
|
101
|
+
gsub(/\A(?:\e\[0m)+/, '').gsub(/\e\[0m\z/, '')
|
102
|
+
end
|
103
|
+
|
104
|
+
# Truncate the *visible* length to n, but preserve embedded ANSI
|
53
105
|
def shorten(n)
|
54
106
|
count = 0
|
55
|
-
|
56
|
-
i
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
else
|
63
|
-
# Fallback if pattern doesn’t match: treat as a normal character.
|
64
|
-
result << self[i]
|
65
|
-
i += 1
|
66
|
-
count += 1
|
67
|
-
end
|
107
|
+
out = ''
|
108
|
+
i = 0
|
109
|
+
|
110
|
+
while i < length && count < n
|
111
|
+
if self[i] == "\e" && (m = self[i..-1].match(/\A(\e\[\d+(?:;\d+)*m)/))
|
112
|
+
out << m[1]
|
113
|
+
i += m[1].length
|
68
114
|
else
|
69
|
-
|
115
|
+
out << self[i]
|
116
|
+
i += 1
|
70
117
|
count += 1
|
71
|
-
i += 1
|
72
118
|
end
|
73
119
|
end
|
74
|
-
|
120
|
+
|
121
|
+
out
|
75
122
|
end
|
76
123
|
|
77
|
-
#
|
78
|
-
#
|
79
|
-
# When inserting at the end and the string ends with an ANSI escape sequence,
|
80
|
-
# the insertion is placed before that trailing ANSI sequence.
|
124
|
+
# Insert +insertion+ at visible position +pos+ (negative → end),
|
125
|
+
# respecting and re-inserting existing ANSI sequences.
|
81
126
|
def inject(insertion, pos)
|
82
|
-
|
83
|
-
|
84
|
-
pos
|
127
|
+
pure_txt = pure
|
128
|
+
visible_len = pure_txt.length
|
129
|
+
pos = visible_len if pos < 0
|
85
130
|
|
86
|
-
count = 0
|
87
|
-
|
88
|
-
i
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
if self[i] == "\e" # Start of an ANSI sequence.
|
93
|
-
if m = self[i..-1].match(/\A(\e\[\d+(?:;\d+)*m)/)
|
94
|
-
result << m[1]
|
95
|
-
i += m[1].length
|
96
|
-
else
|
97
|
-
result << self[i]
|
98
|
-
i += 1
|
99
|
-
end
|
131
|
+
count, out, i, injected = 0, '', 0, false
|
132
|
+
|
133
|
+
while i < length
|
134
|
+
if self[i] == "\e" && (m = self[i..-1].match(/\A(\e\[\d+(?:;\d+)*m)/))
|
135
|
+
out << m[1]
|
136
|
+
i += m[1].length
|
100
137
|
else
|
101
138
|
if count == pos && !injected
|
102
|
-
|
139
|
+
out << insertion
|
103
140
|
injected = true
|
104
141
|
end
|
105
|
-
|
106
|
-
count
|
107
|
-
i
|
142
|
+
out << self[i]
|
143
|
+
count += 1
|
144
|
+
i += 1
|
108
145
|
end
|
109
146
|
end
|
110
147
|
|
111
|
-
# If we haven't injected (i.e. pos equals visible_length),
|
112
|
-
# check for a trailing ANSI sequence and insert before it.
|
113
148
|
unless injected
|
114
|
-
if
|
149
|
+
if out =~ /(\e\[\d+(?:;\d+)*m)\z/
|
115
150
|
trailing = $1
|
116
|
-
|
151
|
+
out = out[0...-trailing.length] + insertion + trailing
|
117
152
|
else
|
118
|
-
|
153
|
+
out << insertion
|
119
154
|
end
|
120
155
|
end
|
121
156
|
|
122
|
-
|
157
|
+
out
|
123
158
|
end
|
124
159
|
end
|
160
|
+
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rcurses
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '4.
|
4
|
+
version: '4.5'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Geir Isene
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-05-
|
11
|
+
date: 2025-05-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: clipboard
|
@@ -30,8 +30,7 @@ description: 'Create curses applications for the terminal easier than ever. Crea
|
|
30
30
|
color, blink and in any 256 terminal colors for foreground and background. Use a
|
31
31
|
simple editor to let users edit text in panes. Left, right or center align text
|
32
32
|
in panes. Cursor movement around the terminal. New in 3.8: Fixed border fragments
|
33
|
-
upon utf-8 characters. 4.
|
34
|
-
pane.'
|
33
|
+
upon utf-8 characters. 4.5: Full RGB support in addition to 256-colors.'
|
35
34
|
email: g@isene.com
|
36
35
|
executables: []
|
37
36
|
extensions: []
|