diff --git a/USAGE.md b/USAGE.md
index be78a9c64..fc3da539b 100644
--- a/USAGE.md
+++ b/USAGE.md
@@ -9,7 +9,7 @@ and the project is committed to backwards compatibility.
* [Planes](#planes) ([Plane Channels API](#plane-channels-api))
* [Cells](#cells) ([Cell Channels API](#cell-channels-api))
* [Reels](#reels) ([ncreel Examples](#ncreel-examples))
-* [Widgets](#widgets) ([Plots](#plots)) ([Readers](#readers))
+* [Widgets](#widgets) ([Plots](#plots)) ([Readers](#readers)) ([Progbars](#progbars))
* [Channels](#channels)
* [Visuals](#visuals) ([QR codes](#qrcodes)) ([Multimedia](#multimedia)) ([Pixels](#pixels))
* [Stats](#stats)
@@ -2337,6 +2337,9 @@ xxxxxxxxxxxxxxxx╰─────────────╯xxxxxxxxxxxxxxxxxxx
### Readers
+ncreaders provide freeform input in a (possibly multiline) region, supporting
+optional readline keybindings.
+
```c
typedef struct ncreader_options {
uint64_t tchannels; // channels used for input
@@ -2344,9 +2347,8 @@ typedef struct ncreader_options {
bool scroll; // allow more than the physical area's worth of input
} ncreader_options;
-// ncreaders provide freeform input in a (possibly multiline) region, supporting
-// optional readline keybindings. takes ownership of 'n', destroying it on any
-// error (ncreader_destroy() otherwise destroys the ncplane).
+// takes ownership of 'n', destroying it on any error (ncreader_destroy()
+// otherwise destroys the ncplane).
struct ncreader* ncreader_create(struct ncplane* n, const ncreader_options* opts);
// empty the ncreader of any user input, and home the cursor.
@@ -2367,6 +2369,57 @@ char* ncreader_contents(const struct ncreader* n);
void ncreader_destroy(struct ncreader* n, char** contents);
```
+### Progbars
+
+Progress bars proceed linearly in any of four directions. The entirety of the
+provided plane will be used -- any border should be provided by the caller on
+another plane. The plane will not be erased; text preloaded into the plane
+will be consumed by the progress indicator. The bar is redrawn for each
+provided progress report (a double between 0 and 1), and can regress with
+lower values. The procession will take place along the longer dimension (at
+the time of each redraw), with the horizontal length scaled by 2 for
+purposes of comparison. I.e. for a plane of 20 rows and 50 columns, the
+progress will be to the right (50 > 40) or left with `OPTION_RETROGRADE`.
+If `NCPROGBAR_OPTION_LOCK_ORIENTATION` is provided, the initial orientation
+is locked in, despite any resizes. It locks horizontal progression by
+default; `NCPROGBAR_OPTION_FORCE_VERTICAL` locks vertical progression. These
+are recommended if you provide custom EGCs.
+
+```
+// Takes ownership of the ncplane 'n', which will be destroyed by
+// ncprogbar_destroy(). The progress bar is initially at 0%.
+struct ncuplot* ncprogbar_create(struct ncplane* n, const ncprogbar_options* opts);
+
+// Return a reference to the ncprogbar's underlying ncplane.
+#define NCPROGBAR_OPTION_RETROGRADE 0x0001u // proceed left/down
+#define NCPROGBAR_OPTION_LOCK_ORIENTATION 0x0002u // lock in orientation
+#define NCPROGBAR_OPTION_FORCE_VERTICAL 0x0003u // lock in vert
+
+typedef struct ncprogbar_options {
+ // channels for the maximum and minimum points. linear interpolation will be
+ // applied across the domain between these two.
+ uint64_t maxchannels;
+ uint64_t minchannels;
+ // provide NULL for default (geometric) glyphs. otherwise, provide one or
+ // more EGCs to be used for the progress bar. the last EGC provided will be
+ // at the head of the progress. the first will be used for the entirety of
+ // the tail. i.e. "▃▅🭂🭍" might yield "▃▃▃▃▅🭂🭍". note that such a set of EGCs
+ // would not work well for a vertical progress bar.
+ const char egcs;
+ uint64_t flags;
+} ncprogbar_options;
+struct ncplane* ncprogbar_plane(struct ncprogbar* n);
+
+// Set the progress bar's completion, a double 0 <= 'p' <= 1.
+int ncprogbar_set_progress(struct ncprogbar* n, double p);
+
+// Get the progress bar's completion, a double on [0, 1].
+double ncprogbar_progress(const struct ncprogbar* n);
+
+// Destroy the progress bar and its underlying ncplane.
+void ncprogbar_destroy(struct ncprogbar* n);
+```
+
## Channels
A channel encodes 24 bits of RGB color, using 8 bits for each component. It
diff --git a/doc/man/index.html b/doc/man/index.html
index 96c85ac6d..23f14860e 100644
--- a/doc/man/index.html
+++ b/doc/man/index.html
@@ -57,6 +57,7 @@
notcurses_palette—operations on notcurses palettes
notcurses_plane—operations on ncplane objects
notcurses_plot—drawing histograms and lineplots
+ notcurses_plot—drawing progress bars
notcurses_reader—high-level widget for collecting input
notcurses_refresh—refresh an externally-damaged display
notcurses_render—sync the physical display
diff --git a/doc/man/man3/notcurses.3.md b/doc/man/man3/notcurses.3.md
index 2ccc2df6e..5f2b60746 100644
--- a/doc/man/man3/notcurses.3.md
+++ b/doc/man/man3/notcurses.3.md
@@ -100,6 +100,7 @@ A few high-level widgets are included, all built atop ncplanes:
* **notcurses_menu(3)** for menu bars at the top or bottom of the screen
* **notcurses_multiselector(3)** for selecting one or more items from a set
* **notcurses_plot(3)** for drawing histograms and lineplots
+* **notcurses_progbar(3)** for drawing progress bars
* **notcurses_reader(3)** for free-form input data
* **notcurses_reel(3)** for hierarchal display of data
* **notcurses_selector(3)** for selecting one item from a set
@@ -156,6 +157,7 @@ order to turn most error returns into exceptions.
**notcurses_palette(3)**,
**notcurses_plane(3)**,
**notcurses_plot(3)**,
+**notcurses_progbar(3)**,
**notcurses_reader(3)**,
**notcurses_reel(3)**,
**notcurses_refresh(3)**,
diff --git a/doc/man/man3/notcurses_progbar.3.md b/doc/man/man3/notcurses_progbar.3.md
new file mode 100644
index 000000000..2aa339608
--- /dev/null
+++ b/doc/man/man3/notcurses_progbar.3.md
@@ -0,0 +1,72 @@
+% notcurses_progbar(3)
+% nick black
+% v2.0.11
+
+# NAME
+
+notcurses_progbar - high level widget for progress bars
+
+# SYNOPSIS
+
+**#include **
+
+```c
+#define NCPROGBAR_OPTION_RETROGRADE 0x0001u // proceed left/down
+#define NCPROGBAR_OPTION_LOCK_ORIENTATION 0x0002u // lock in orientation
+#define NCPROGBAR_OPTION_FORCE_VERTICAL 0x0003u // lock in vert
+
+typedef struct ncprogbar_options {
+ uint64_t maxchannels;
+ uint64_t minchannels;
+ const char egcs;
+ uint64_t flags;
+} ncprogbar_options;
+```
+
+**struct ncuplot* ncprogbar_create(struct ncplane* ***n***, const ncprogbar_options* ***opts***)**
+
+**struct ncplane* ncprogbar_plane(struct ncprogbar* ***n***)**
+
+**int ncprogbar_set_progress(struct ncprogbar* ***n***, double ***p***)**
+
+**double ncprogbar_progress(const struct ncprogbar* ***n***)**
+
+**void ncprogbar_destroy(struct ncprogbar* ***n***)**
+
+# DESCRIPTION
+
+These functions draw progress bars in any of four directions. The progress
+measure is a **double** between zero and one, inclusive, provided to
+**ncprogbar_set_progress**. This will be scaled to the size of the provided
+ncplane ***n***. By default, the axis of progression is the longer element
+of the plane's geometry, but it can be explicitly chosen with
+**NCPROGBAR_OPTION_LOCK_ORIENTATION** and **NCPROGBAR_OPTION_FORCE_VERTICAL**.
+Horizontal bars proceed to the right by default, and vertical bars proceed up.
+This can be changed with **NCPROGBAR_OPTION_RETROGRADE**.
+
+# NOTES
+
+**ncprogbar_create** takes ownership of ***n*** in all cases. On failure,
+***n*** will be destroyed immediately. It is otherwise destroyed by
+**ncprogbar_destroy**.
+
+# RETURN VALUES
+
+**ncprogbar_plane** returns the **ncplane** on which the progress bar is drawn.
+**ncprogbar_progress** returns the current progress, a value between zero and
+one, inclusive. They cannot fail.
+
+**ncprogbar_set_progress** returns -1 if ***p*** is less than zero or greater
+than one, or if there is an internal error redrawing the progress bar. It
+returns 0 otherwise.
+
+# BUGS
+
+Whether progression is to the left or right by default probably ought be an
+aspect of the current locale.
+
+# SEE ALSO
+
+**notcurses(3)**,
+**notcurses_plane(3)**,
+**notcurses_visual(3)**
diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h
index 8e21730fd..0ac48f675 100644
--- a/include/notcurses/notcurses.h
+++ b/include/notcurses/notcurses.h
@@ -43,8 +43,9 @@ API void notcurses_version_components(int* major, int* minor, int* patch, int* t
struct notcurses; // Notcurses state for a given terminal, composed of ncplanes
struct ncplane; // a drawable Notcurses surface, composed of cells
struct ncvisual; // a visual bit of multimedia opened with LibAV|OIIO
-struct ncuplot; // a histogram, bound to a plane (uint64_ts)
-struct ncdplot; // a histogram, bound to a plane (non-negative doubles)
+struct ncuplot; // uint64_t histogram
+struct ncdplot; // double histogram
+struct ncprogbar; // progress bar
struct ncfdplane; // i/o wrapper to dump file descriptor to plane
struct ncsubproc; // ncfdplane wrapper with subprocess management
struct ncselector;// widget supporting selecting 1 from a list of options
@@ -3018,6 +3019,59 @@ API bool ncmenu_offer_input(struct ncmenu* n, const struct ncinput* nc);
// Destroy a menu created with ncmenu_create().
API int ncmenu_destroy(struct ncmenu* n);
+// Progress bars. They proceed linearly in any of four directions. The entirety
+// of the plane will be used -- any border should be provided by the caller on
+// another plane. The plane will not be erased; text preloaded into the plane
+// will be consumed by the progress indicator. The bar is redrawn for each
+// provided progress report (a double between 0 and 1), and can regress with
+// lower values. The procession will take place along the longer dimension (at
+// the time of each redraw), with the horizontal length scaled by 2 for
+// purposes of comparison. I.e. for a plane of 20 rows and 50 columns, the
+// progress will be to the right (50 > 40) or left with OPTION_RETROGRADE.
+// If NCPROGBAR_OPTION_LOCK_ORIENTATION is provided, the initial orientation
+// is locked in, despite any resizes. It locks horizontal progression by
+// default; NCPROGBAR_OPTION_FORCE_VERTICAL locks vertical progression. These
+// are recommended if you provide custom EGCs.
+
+#define NCPROGBAR_OPTION_RETROGRADE 0x0001u // proceed left/down
+#define NCPROGBAR_OPTION_LOCK_ORIENTATION 0x0002u // lock in orientation
+#define NCPROGBAR_OPTION_FORCE_VERTICAL 0x0003u // lock in vert
+
+typedef struct ncprogbar_options {
+ // channels for the maximum and minimum points. linear interpolation will be
+ // applied across the domain between these two.
+ uint64_t maxchannels;
+ uint64_t minchannels;
+ // provide NULL for default (geometric) glyphs. otherwise, provide one or
+ // more EGCs to be used for the progress bar. the last EGC provided will be
+ // at the head of the progress. the first will be used for the entirety of
+ // the tail. i.e. "▃▅🭂🭍" might yield "▃▃▃▃▅🭂🭍". note that such a set of EGCs
+ // would not work well for a vertical progress bar.
+ const char egcs;
+ uint64_t flags;
+} ncprogbar_options;
+
+// Takes ownership of the ncplane 'n', which will be destroyed by
+// ncprogbar_destroy(). The progress bar is initially at 0%.
+API struct ncuplot* ncprogbar_create(struct ncplane* n, const ncprogbar_options* opts)
+ __attribute__ ((nonnull (1)));
+
+// Return a reference to the ncprogbar's underlying ncplane.
+API struct ncplane* ncprogbar_plane(struct ncprogbar* n)
+ __attribute__ ((nonnull (1)));
+
+// Set the progress bar's completion, a double 0 <= 'p' <= 1.
+API int ncprogbar_set_progress(struct ncprogbar* n, double p)
+ __attribute__ ((nonnull (1)));
+
+// Get the progress bar's completion, a double on [0, 1].
+API double ncprogbar_progress(const struct ncprogbar* n)
+ __attribute__ ((nonnull (1)));
+
+// Destroy the progress bar and its underlying ncplane.
+API void ncprogbar_destroy(struct ncprogbar* n)
+ __attribute__ ((nonnull (1)));
+
// Plots. Given a rectilinear area, an ncplot can graph samples along some axis.
// There is some underlying independent variable--this could be e.g. measurement
// sequence number, or measurement time. Samples are tagged with this variable, which