Rcurses
An alternative curses library written in pure Ruby
Install / Use
/learn @isene/RcursesREADME
rcurses - An alternative curses library written in pure Ruby
<img src="img/rcurses-logo.png" width="150" height="150">Create curses applications for the terminal easier than ever.
Here's a somewhat simple example of a TUI program using rcurses: The T-REX calculator.
And here's a much more involved example: The RTFM terminal file manager.
What's new in 6.2.0
- Popup widget - New
Rcurses::Popupclass for modal dialogs with auto-centering, border, scrolling, and built-in key handling (see Popup section below) - Built-in emoji picker - Panes with
emoji = trueget aCtrl-Eshortcut in editline that opens an emoji overlay with category tabs, search, and cursor-positioned grid rendering - Pane color cache invalidation - Changing
fgorbgon a Pane now forces a full repaint automatically (no more stale diff-rendering artifacts) - Startup stdin flush -
Rcurses.init!now flushes pending stdin data (cursor position responses) to prevent first-keypress blocking - Pane update flag - Set
pane.update = falseto skip rendering during batch operations - Wider ESC timeout - Escape sequence detection timeout increased from 50ms to 150ms for better terminal compatibility
- Unicode display_width fixes - Improved handling of FE0F variation selectors, ZWJ sequences, and dingbat characters
- Editline improvements - Unicode-aware padding, content truncation, multiline paste joins all lines
Previous notable versions: 6.1.1 (error logging), 6.1.0 (safe ANSI methods), 6.0.0 (explicit init!), 5.0.0 (memory fixes), 4.5 (RGB colors)
Why?
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.
Design principles
Simple and with minimum of external dependencies.
Installation
Simply run gem install rcurses.
To use this library do:
require 'rcurses'
Features
- Create panes (with the colors and(or border), manipulate the panes and add content
- 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
- Use a simple editor to let users edit text in panes
- Left, right or center align text in panes
- Cursor movement around the terminal
The elements
rcurses gives you the following elements:
- The class
Paneto create and manilpulate panes/boxes - Extensions to the class String to print text in various degrees of fancy and also strip any fanciness
- A module
Cursorto give you cursor movements around the terminal - A module
Rinputproviding the functiongetchrto capture a single character input from the user (much better than any Ruby built-ins)
class Pane
To create a pane do something like this:
mypane = Rcurses::Pane.new(80, 30, 30, 10, 19, 229)
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)
The format for creating a pane is:
Rcurses::Pane.new(x, y, w, h, fg, bg)
You can drop the last two 256-color codes to create a pane with the defaults for your terminal.
By adding values for the terminal size in your program:
@max_h, @max_w = IO.console.winsize
...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.
Avaliable properties/variables:
Property | Description ---------------|--------------------------------------------------------------- x | The x (column) position of the Pane y | The y (row) position of the Pane w | The width of the Pane h | The heigth of the Pane fg | Foreground color for the Pane bg | Background color for the Pane border | Draw border around the Pane (=true) or not (=false), default being false scroll | Whether to indicate more text to be shown above/below the Pane, default is true scroll_fg | Color for scroll indicators (∆/∇), defaults to pane fg color if nil text | The text/content of the Pane ix | The line number at the top of the Pane, starts at 0, the first line of text in the Pane index | An attribute that can be used to track the selected line/element in the pane align | Text alignment in the Pane: "l" = lefts aligned, "c" = center, "r" = right, with the default "l" prompt | The prompt to print at the beginning of a one-liner Pane used as an input box moreup | Set to true when there is more text above what is shown (top scroll bar i showing) moredown | Set to true when there is more text below what is shown (bottom scroll bar i showing) update | When false, refresh is a no-op (default: true). Useful during batch operations emoji | When true, Ctrl-E in editline opens the built-in emoji picker (default: false) emoji_refresh | Optional lambda/proc called after emoji picker closes, to redraw the UI
The methods for Pane:
Method | Description
---------------|---------------------------------------------------------------
new/init | Initializes a Pane with optional arguments x, y, w, h, fg and bg
move(x,y) | Move the pane by xand y (mypane.move(-4,5) will move the pane left four characters and five characters down)
refresh | Refreshes/redraws the Pane with content
border_refresh | Refresh the Pane border only - CRITICAL: Use this after changing border property
full_refresh | Refreshes/redraws the Pane with content completely (without diff rendering)
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
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
clear | Clears the pane
cleanup | Cleans up pane memory (history, caches) - useful for memory management
say(text) | Short form for setting panel.text, then doing a refresh of that panel
ask(prompt,text) | Short form of setting panel.prompt, then panel.text, doing a panel.editline and then returning panel.text
pagedown | Scroll down one page height in the text (minus one line), but not longer than the length of the text
pageup | Scroll up one page height in the text (minus one line)
linedown | Scroll down one line in the text
lineup | Scroll up one line in the text
bottom | Scroll to the bottom of the text in the pane
top | Scroll to the top of the text in the pane
class Popup
A reusable popup overlay widget for modal dialogs, selection menus, and informational displays. Extends Pane with auto-centering, built-in scrolling, and keyboard handling.
# Auto-centered popup with default size
popup = Rcurses::Popup.new(w: 50, h: 20)
# Explicit position
popup = Rcurses::Popup.new(x: 10, y: 5, w: 50, h: 20, fg: 255, bg: 236)
Modal usage (blocks until ESC or ENTER):
result = popup.modal(content_string)
# Returns selected line index on ENTER, or nil on ESC
# Built-in: UP/DOWN/PgUP/PgDN/HOME/END for scrolling
Custom key handling:
result = popup.modal(content) do |chr, index|
case chr
when 'd'
delete_item(index)
:dismiss # Close popup
when 'e'
"edit:#{index}" # Return custom value
end
end
Manual control (non-blocking):
popup.show(content_string)
# ... your own input loop ...
popup.dismiss(refresh_panes: [pane1, pane2]) # Clears area, refreshes underlying panes
Emoji Picker
Panes can opt in to a built-in emoji picker that opens with Ctrl-E during editline:
pane.emoji = true # Enable Ctrl-E shortcut
pane.emoji_refresh = -> { render } # Optional: redraw UI after picker closes
pane.editline # Ctrl-E now opens emoji overlay
The picker features category tabs (Tab/Shift-Tab), arrow navigation, search-by-keyword, and cursor-positioned grid rendering for pixel-perfect alignment regardless of Unicode width variations.
class String extensions
Method extensions provided for the class String.
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").
Method | Description ---------------|----------------------
