rcurses 4.9.3 → 4.9.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.
@@ -0,0 +1,160 @@
1
+ # string_extensions.rb
2
+
3
+ class String
4
+ # 256-color or truecolor RGB foregroundbreset only the fg (SGR 39)
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[39m"]
9
+ else
10
+ ["\e[38;5;#{color}m", "\e[39m"]
11
+ end
12
+ color(self, sp, ep)
13
+ end
14
+
15
+ # 256-color or truecolor RGB backgroundbreset only the bg (SGR 49)
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[49m"]
20
+ else
21
+ ["\e[48;5;#{color}m", "\e[49m"]
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[39;49m")
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.
56
+ def color(text, sp, ep = "\e[0m")
57
+ t = text.gsub("\n", "#{ep}\n#{sp}")
58
+ "#{sp}#{t}#{ep}"
59
+ end
60
+
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
63
+ def c(code)
64
+ parts = code.split(',')
65
+ seq = []
66
+
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"
92
+ end
93
+
94
+ # Strip all ANSI SGR sequences
95
+ def pure
96
+ gsub(/\e\[\d+(?:;\d+)*m/, '')
97
+ end
98
+
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
105
+ def shorten(n)
106
+ count = 0
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
114
+ else
115
+ out << self[i]
116
+ i += 1
117
+ count += 1
118
+ end
119
+ end
120
+
121
+ out
122
+ end
123
+
124
+ # Insert +insertion+ at visible position +pos+ (negative → end),
125
+ # respecting and re-inserting existing ANSI sequences.
126
+ def inject(insertion, pos)
127
+ pure_txt = pure
128
+ visible_len = pure_txt.length
129
+ pos = visible_len if pos < 0
130
+
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
137
+ else
138
+ if count == pos && !injected
139
+ out << insertion
140
+ injected = true
141
+ end
142
+ out << self[i]
143
+ count += 1
144
+ i += 1
145
+ end
146
+ end
147
+
148
+ unless injected
149
+ if out =~ /(\e\[\d+(?:;\d+)*m)\z/
150
+ trailing = $1
151
+ out = out[0...-trailing.length] + insertion + trailing
152
+ else
153
+ out << insertion
154
+ end
155
+ end
156
+
157
+ out
158
+ end
159
+ end
160
+
data/rcurses-README.md ADDED
@@ -0,0 +1,257 @@
1
+ # rcurses - An alternative curses library written in pure Ruby
2
+
3
+ ![Ruby](https://img.shields.io/badge/language-Ruby-red) [![Gem Version](https://badge.fury.io/rb/rcurses.svg)](https://badge.fury.io/rb/rcurses) ![Unlicense](https://img.shields.io/badge/license-Unlicense-green) ![Stay Amazing](https://img.shields.io/badge/Stay-Amazing-important)
4
+
5
+ <img src="img/rcurses-logo.png" width="150" height="150">
6
+
7
+ Create curses applications for the terminal easier than ever.
8
+
9
+ Here's a somewhat simple example of a TUI program using rcurses: The [T-REX](https://github.com/isene/T-REX) calculator.
10
+
11
+ And here's a much more involved example: The [RTFM](https://github.com/isene/RTFM) terminal file manager.
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
+
16
+ # Why?
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.
18
+
19
+ # Design principles
20
+ Simple and with minimum of external dependencies.
21
+
22
+ # Installation
23
+ Simply run `gem install rcurses`.
24
+
25
+ To use this library do:
26
+ ```
27
+ require 'rcurses'
28
+ ```
29
+
30
+ # Features
31
+ * Create panes (with the colors and(or border), manipulate the panes and add content
32
+ * Dress up text (in panes or anywhere in the terminal) in bold, italic, underline, reverse color, blink and in any 256 terminal colors for foreground and background
33
+ * Use a simple editor to let users edit text in panes
34
+ * Left, right or center align text in panes
35
+ * Cursor movement around the terminal
36
+
37
+ # The elements
38
+ `rcurses` gives you the following elements:
39
+ * The class `Pane` to create and manilpulate panes/boxes
40
+ * Extensions to the class String to print text in various degrees of fancy and also strip any fanciness
41
+ * A module `Cursor` to give you cursor movements around the terminal
42
+ * A module `Rinput` providing the function `getchr` to capture a single character input from the user (much better than any Ruby built-ins)
43
+
44
+ # class Pane
45
+ To create a pane do something like this:
46
+ ```
47
+ mypane = Rcurses::Pane.new(80, 30, 30, 10, 19, 229)
48
+ ```
49
+ This will create a pane/box starting at terminal column/x 80 and row/y 30 with the width of 30 characters and a hight of 10 characters and with the foreground color 19 and background color 229 (from the 256 choices available)
50
+
51
+ The format for creating a pane is:
52
+ ```
53
+ Rcurses::Pane.new(x, y, w, h, fg, bg)
54
+ ```
55
+ You can drop the last two 256-color codes to create a pane with the defaults for your terminal.
56
+
57
+ By adding values for the terminal size in your program:
58
+ ```
59
+ @max_h, @max_w = IO.console.winsize
60
+ ```
61
+ ...you can use these values to create proportinally sized panes. So, a hight value of "@max_h/2" is valid to create a pane with the height of half the terminal height (the integer corresponding to half the terminal height will then be accessible as the variable `h`). Use the variables `@max_h` for terminal height and `@max_w` for terminal width.
62
+
63
+ Avaliable properties/variables:
64
+
65
+ Property | Description
66
+ ---------------|---------------------------------------------------------------
67
+ x | The x (column) position of the Pane
68
+ y | The y (row) position of the Pane
69
+ w | The width of the Pane
70
+ h | The heigth of the Pane
71
+ fg | Foreground color for the Pane
72
+ bg | Background color for the Pane
73
+ border | Draw border around the Pane (=true) or not (=false), default being false
74
+ scroll | Whether to indicate more text to be shown above/below the Pane, default is true
75
+ text | The text/content of the Pane
76
+ ix | The line number at the top of the Pane, starts at 0, the first line of text in the Pane
77
+ index | An attribute that can be used to track the selected line/element in the pane
78
+ align | Text alignment in the Pane: "l" = lefts aligned, "c" = center, "r" = right, with the default "l"
79
+ prompt | The prompt to print at the beginning of a one-liner Pane used as an input box
80
+ moreup | Set to true when there is more text above what is shown (top scroll bar i showing)
81
+ moredown | Set to true when there is more text below what is shown (bottom scroll bar i showing)
82
+
83
+ The methods for Pane:
84
+
85
+ Method | Description
86
+ ---------------|---------------------------------------------------------------
87
+ new/init | Initializes a Pane with optional arguments `x, y, w, h, fg and bg`
88
+ 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)
89
+ refresh | Refreshes/redraws the Pane with content
90
+ border_refresh | Refresh the Pane border only
91
+ full_refresh | Refreshes/redraws the Pane with content completely (without diff rendering)
92
+ 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
93
+ 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
94
+ clear | Clears the pane
95
+ say(text) | Short form for setting panel.text, then doing a refresh of that panel
96
+ ask(prompt,text) | Short form of setting panel.prompt, then panel.text, doing a panel.editline and then returning panel.text
97
+ pagedown | Scroll down one page height in the text (minus one line), but not longer than the length of the text
98
+ pageup | Scroll up one page height in the text (minus one line)
99
+ linedown | Scroll down one line in the text
100
+ lineup | Scroll up one line in the text
101
+ bottom | Scroll to the bottom of the text in the pane
102
+ top | Scroll to the top of the text in the pane
103
+
104
+ # class String extensions
105
+ Method extensions provided for the class String.
106
+
107
+ 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")`.
108
+
109
+ Method | Description
110
+ ---------------|---------------------------------------------------------------
111
+ fg(fg) | Set text to be printed with the foreground color `fg` (example: `"TEST".fg(84)`)
112
+ bg(bg) | Set text to be printed with the background color `bg` (example: `"TEST".bg("dd32a9")`)
113
+ fb(fg, bg) | Set text to be printed with the foreground color `fg` and background color `bg` (example: `"TEST".fb(84,196)`)
114
+ b | Set text to be printed in bold (example: `"TEST".b`)
115
+ i | Set text to be printed in italic (example: `"TEST".i`)
116
+ u | Set text to be printed underlined (example: `"TEST".u`)
117
+ l | Set text to be printed blinking (example: `"TEST".l`)
118
+ r | Set text to be printed in reverse colors (example: `"TEST".r`)
119
+ c(code) | Use coded format like "TEST".c("204,45,bui") to print "TEST" in bold, underline italic, fg=204 and bg=45 (the format is `.c("fg,bg,biulr")`)
120
+ pure | Strip text of any "dressing" (example: with `text = "TEST".b`, you will have bold text in the variable `text`, then with `text.pure` it will show "uncoded" or pure text)
121
+ clean_ansi | Strip seemingly uncolored strings of ansi code (those that are enclosed in "\e[0m"
122
+ shorten(n) | Shorten the pure version of the string to 'n' characters, preserving any ANSI coding
123
+ inject("chars",pos) | Inject "chars" at position 'pos' in the pure version of the string (if 'pos' is '-1', then append at end). Preserves any ANSI code
124
+
125
+ PS: Blink does not work in conjunction with setting a background color in urxvt. It does work in gnome-terminal. But the overall performance in urxvt as orders of magnitude better than gnome-terminal.
126
+
127
+ # Cleaning up upon exit
128
+ End a program with `Rcurses.clear_screen` to clear the screen for any rcurses residues.
129
+
130
+ # module Cursor
131
+ To use this module, first do `include Rcurses::Cursor`. Create a new cursor object with `mycursor = Rcurses::Cursor`. Then you can apply the following methods to `mycursor`:
132
+
133
+ Method | Description
134
+ ------------------|---------------------------------------------------------------
135
+ save | Save current position
136
+ restore | Restore cursor position
137
+ pos | Query cursor current position (example: `row,col = mycursor.pos`)
138
+ colget | Query cursor current cursor col/x position (example: `row = mycursor.rowget`)
139
+ rowget | Query cursor current cursor row/y position (example: `row = mycursor.rowget`)
140
+ set(c = 1, r = 1) | Set the position of the cursor to row/y,col/x (example: `mycursor.set(row,col)`) (default = top row, first column)
141
+ col(c = 1) | Cursor moves to the nth position horizontally in the current line (default = first column)
142
+ row(r = 1) | Cursor moves to the nth position vertically in the current column (default = first/top row)
143
+ up(n = 1) | Move cursor up by n (default is 1 character up)
144
+ down(n = 1) | Move cursor down by n (default is 1 character down)
145
+ left(n = 1) | Move cursor backward by n (default is one character)
146
+ right(n = 1) | Move cursor forward by n (default is one character)
147
+ next_line | Move cursor down to beginning of next line
148
+ prev_line | Move cursor up to beginning of previous line
149
+ clear_char(n = 1) | Erase n characters from the current cursor position (default is one character)
150
+ clear_line | Erase the entire current line and return to beginning of the line
151
+ clear_line_before | Erase from the beginning of the line up to and including the current cursor position
152
+ clear_line_after | Erase from the current position (inclusive) to the end of the line
153
+ scroll_up | Scroll display up one line
154
+ scroll_down | Scroll display down one line
155
+ clear_screen_down | Clear screen down from current row
156
+ hide | Hide the cursor
157
+ show | Show cursor
158
+
159
+ # The function getchr
160
+ rcurses provides a vital extension to Ruby in reading characters entered by the user. This is especially needed for curses applications where readline inputs are required.
161
+ The function getchr is automatically included in your arsenal when you first do `include Rcurses::Input`.
162
+
163
+ Simply use `chr = getchr` in a program to read any character input by the user. The returning code (the content of `chr` in this example) could be any of the following:
164
+
165
+ Key pressed | string returned
166
+ ----------------|----------------------------------------------------------
167
+ `esc` | "ESC"
168
+ `up` | "UP"
169
+ `shift-up` | "S-UP"
170
+ `ctrl-up` | "C-UP"
171
+ `down` | "DOWN"
172
+ `shift-down` | "S-DOWN"
173
+ `ctrl-down` | "C-DOWN"
174
+ `right` | "RIGHT"
175
+ `shift-right` | "S-RIGHT"
176
+ `ctrl-right` | "C-RIGHT"
177
+ `left` | "LEFT"
178
+ `shifth-left` | "S-LEFT"
179
+ `ctrl-left` | "C-LEFT"
180
+ `shift-tab` | "S-TAB"
181
+ `insert` | "INS"
182
+ `ctrl-insert` | "C-INS"
183
+ `del` | "DEL"
184
+ `ctrl-del` | "C-DEL"
185
+ `pageup` | "PgUP"
186
+ `ctrl-pageup` | "C-PgUP"
187
+ `pagedown` | "PgDOWN"
188
+ `ctrl-pagedown` | "C-PgDOWN"
189
+ `home` | "HOME"
190
+ `ctrl-home` | "C-HOME"
191
+ `end` | "END"
192
+ `ctrl-end` | "C-END"
193
+ `backspace` | "BACK"
194
+ `ctrl- ` | "C-SPACE"
195
+ `ctrl-h` | "BACK"
196
+ `ctrl-a` | "C-A"
197
+ `ctrl-b` | "C-B"
198
+ `ctrl-c` | "C-C"
199
+ `ctrl-d` | "C-D"
200
+ `ctrl-e` | "C-E"
201
+ `ctrl-f` | "C-F"
202
+ `ctrl-g` | "C-G"
203
+ `ctrl-i` | "C-I"
204
+ `ctrl-j` | "C-J"
205
+ `ctrl-k` | "C-K"
206
+ `ctrl-l` | "C-L"
207
+ `ctrl-m` | "C-M"
208
+ `ctrl-n` | "C-N"
209
+ `ctrl-o` | "C-O"
210
+ `ctrl-p` | "C-P"
211
+ `ctrl-q` | "C-Q"
212
+ `ctrl-r` | "C-R"
213
+ `ctrl-s` | "C-S"
214
+ `ctrl-t` | "C-T"
215
+ `ctrl-u` | "C-U"
216
+ `ctrl-v` | "C-V"
217
+ `ctrl-a` | "WBACK"
218
+ `ctrl-x` | "C-X"
219
+ `ctrl-y` | "C-Y"
220
+ `ctrl-z` | "C-Z"
221
+ `enter` | "ENTER"
222
+ `tab` | "TAB"
223
+ `F1` - `F12` | "F1" - "F12"
224
+
225
+ Any other character enter will be returned (to `chr` in the example above).
226
+
227
+ In order to handle several character pased into STDIN by the user (and not only returned the first character only, your program should empty the STDIN like this:
228
+
229
+ ```
230
+ while $stdin.ready?
231
+ chr += $stdin.getc
232
+ end
233
+ ```
234
+ You can also pass a timeout to `getchr` with `getchr(time)` to wait for `time` number of seconds and returning `nil` if the user does not press a key.
235
+
236
+
237
+ # Example
238
+
239
+ Try this in `irb`:
240
+ ```
241
+ require 'rcurses'
242
+ @max_h, @max_w = IO.console.winsize
243
+ mypane = Pane.new(@max_w/2, 30, 30, 10, 19, 229)
244
+ mypane.border = true
245
+ mypane.text = "Hello".i + " World!".b.i + "\n \n" + "rcurses".r + " " + "is cool".c("16,212")
246
+ mypane.refresh
247
+ mypane.edit
248
+ ```
249
+ ... and then try to add some bold text by enclosing it in '*' and italics by enclosing text in '/'. Then press 'ctrl-s' to save your edited text - and then type `mypane.refresh` to see the result.
250
+
251
+ And - try running the example file `rcurses_example.rb`.
252
+
253
+ # Not yet implemented
254
+ Let me know what other features you like to see.
255
+
256
+ # License and copyright
257
+ Just steal or borrow anything you like. This is now Public Domain.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rcurses
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.9.3
4
+ version: 4.9.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Geir Isene
@@ -29,15 +29,23 @@ description: 'Create curses applications for the terminal easier than ever. Crea
29
29
  up text (in panes or anywhere in the terminal) in bold, italic, underline, reverse
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
- in panes. Cursor movement around the terminal. 4.9.3: Reverted to stable 4.8.3 codebase
33
- after 4.9.0-4.9.2 color issues.'
32
+ in panes. Cursor movement around the terminal. 4.9.5: Emergency fix - properly built
33
+ gem with color handling restored.'
34
34
  email: g@isene.com
35
35
  executables: []
36
36
  extensions: []
37
37
  extra_rdoc_files: []
38
38
  files:
39
39
  - LICENSE
40
- - README.md
40
+ - examples/basic_panes.rb
41
+ - examples/focus_panes.rb
42
+ - lib/rcurses.rb
43
+ - lib/rcurses/cursor.rb
44
+ - lib/rcurses/general.rb
45
+ - lib/rcurses/input.rb
46
+ - lib/rcurses/pane.rb
47
+ - lib/string_extensions.rb
48
+ - rcurses-README.md
41
49
  homepage: https://isene.com/
42
50
  licenses:
43
51
  - Unlicense