mirror of
https://github.com/dankamongmen/notcurses
synced 2025-03-09 17:19:03 -04:00
parent
ee93d1cf2c
commit
c677a4fd8a
@ -2,6 +2,7 @@ Copyright 2019-2021 Nick Black
|
||||
Copyright 2019-2021 Marek Habersack
|
||||
Copyright 2020-2021 José Luis Cruz
|
||||
Copyright 2020-2021 igo95862
|
||||
Copyright 2021 Łukasz Drukała
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
187
USAGE.md
187
USAGE.md
@ -9,7 +9,7 @@ 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)) ([Progbars](#progbars))
|
||||
* [Widgets](#widgets) ([Plots](#plots)) ([Readers](#readers)) ([Progbars](#progbars)) ([Tabs](#tabs))
|
||||
* [Channels](#channels)
|
||||
* [Visuals](#visuals) ([QR codes](#qrcodes)) ([Multimedia](#multimedia)) ([Pixels](#pixels))
|
||||
* [Stats](#stats)
|
||||
@ -2486,7 +2486,7 @@ 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`.
|
||||
|
||||
```
|
||||
```c
|
||||
// Takes ownership of the ncplane 'n', which will be destroyed by
|
||||
// ncprogbar_destroy(). The progress bar is initially at 0%.
|
||||
struct ncprogbar* ncprogbar_create(struct ncplane* n, const ncprogbar_options* opts);
|
||||
@ -2514,6 +2514,189 @@ double ncprogbar_progress(const struct ncprogbar* n);
|
||||
void ncprogbar_destroy(struct ncprogbar* n);
|
||||
```
|
||||
|
||||
### Tabs
|
||||
|
||||
Tabbed widgets. The tab list is displayed at the top or at the bottom of the
|
||||
plane, and only one tab is visible at a time.
|
||||
|
||||
```c
|
||||
// Display the tab list at the bottom instead of at the top of the plane
|
||||
#define NCTABBED_OPTION_BOTTOM 0x0001ull
|
||||
|
||||
typedef struct nctabbed_options {
|
||||
uint64_t selchan; // channel for the selected tab header
|
||||
uint64_t hdrchan; // channel for unselected tab headers
|
||||
uint64_t sepchan; // channel for the tab separator
|
||||
char* separator; // separator string
|
||||
uint64_t flags; // bitmask of NCTABBED_OPTION_*
|
||||
} nctabbed_options;
|
||||
|
||||
// Tab content drawing callback. Takes the tab it was associated to, the ncplane
|
||||
// on which tab content is to be drawn, and the user pointer of the tab.
|
||||
// It is called during nctabbed_redraw().
|
||||
typedef void (*tabcb)(struct nctab* t, struct ncplane* ncp, void* curry);
|
||||
|
||||
// Creates a new nctabbed widget, associated with the given ncplane 'n', and with
|
||||
// additional options given in 'opts'. When 'opts' is NULL, it acts as if it were
|
||||
// called with an all-zero opts. The widget takes ownership of 'n', and destroys
|
||||
// it when the widget is destroyed. Returns the newly created widget. Returns
|
||||
// NULL on failure, also destroying 'n'.
|
||||
struct nctabbed* nctabbed_create(struct ncplane* n, const nctabbed_options* opts);
|
||||
|
||||
// Destroy an nctabbed widget. All memory belonging to 'nt' is deallocated,
|
||||
// including all tabs and their names. The plane associated with 'nt' is also
|
||||
// destroyed. Calling this with NULL does nothing.
|
||||
void nctabbed_destroy(struct nctabbed* nt);
|
||||
|
||||
// Redraw the widget. This calls the tab callback of the currently selected tab
|
||||
// to draw tab contents, and draws tab headers. The tab content plane is not
|
||||
// modified by this function, apart from resizing the plane is necessary.
|
||||
void nctabbed_redraw(struct nctabbed* nt);
|
||||
|
||||
// Make sure the tab header of the currently selected tab is at least partially
|
||||
// visible. (by rotating tabs until at least one column is displayed)
|
||||
// Does nothing if there are no tabs.
|
||||
void nctabbed_ensure_selected_header_visible(struct nctabbed* nt);
|
||||
|
||||
// Returns the currently selected tab, or NULL if there are no tabs.
|
||||
struct nctab* nctabbed_selected(struct nctabbed* nt);
|
||||
|
||||
// Returns the leftmost tab, or NULL if there are no tabs.
|
||||
struct nctab* nctabbed_leftmost(struct nctabbed* nt);
|
||||
|
||||
// Returns the number of tabs in the widget.
|
||||
int nctabbed_tabcount(struct nctabbed* nt);
|
||||
|
||||
// Returns the plane associated to 'nt'.
|
||||
struct ncplane* nctabbed_plane(struct nctabbed* nt);
|
||||
|
||||
// Returns the tab content plane.
|
||||
struct ncplane* nctabbed_content_plane(struct nctabbed* nt);
|
||||
|
||||
// Returns the tab callback.
|
||||
tabcb nctab_cb(struct nctab* t)
|
||||
__attribute__ ((nonnull (1)));
|
||||
|
||||
// Returns the tab name. This is not a copy and it should not be stored.
|
||||
const char* nctab_name(struct nctab* t);
|
||||
|
||||
// Returns the width (in columns) of the tab's name.
|
||||
int nctab_name_width(struct nctab* t);
|
||||
|
||||
// Returns the tab's user pointer.
|
||||
void* nctab_userptr(struct nctab* t);
|
||||
|
||||
// Returns the tab to the right of 't'. This does not change which tab is selected.
|
||||
struct nctab* nctab_next(struct nctab* t);
|
||||
|
||||
// Returns the tab to the left of 't'. This does not change which tab is selected.
|
||||
struct nctab* nctab_prev(struct nctab* t);
|
||||
|
||||
// Add a new tab to 'nt' with the given tab callback, name, and user pointer.
|
||||
// If both 'before' and 'after' are NULL, the tab is inserted after the selected
|
||||
// tab. Otherwise, it gets put after 'after' (if not NULL) and before 'before'
|
||||
// (if not NULL). If both 'after' and 'before' are given, they must be two
|
||||
// neighboring tabs (the tab list is circular, so the last tab is immediately
|
||||
// before the leftmost tab), otherwise the function returns NULL. If 'name' is
|
||||
// NULL or a string containing illegal characters, the function returns NULL.
|
||||
// On all other failures the function also returns NULL. If it returns NULL,
|
||||
// none of the arguments are modified, and the widget state is not altered.
|
||||
struct nctab* nctabbed_add(struct nctabbed* nt, struct nctab* after,
|
||||
struct nctab* before, tabcb tcb,
|
||||
const char* name, void* opaque);
|
||||
|
||||
// Remove a tab 't' from 'nt'. Its neighboring tabs become neighbors to each
|
||||
// other. If 't' if the selected tab, the tab after 't' becomes selected.
|
||||
// Likewise if 't' is the leftmost tab, the tab after 't' becomes leftmost.
|
||||
// If 't' is the only tab, there will no more be a selected or leftmost tab,
|
||||
// until a new tab is added. Returns -1 if 't' is NULL, and 0 otherwise.
|
||||
int nctabbed_del(struct nctabbed* nt, struct nctab* t);
|
||||
|
||||
// Move 't' after 'after' (if not NULL) and before 'before' (if not NULL).
|
||||
// If both 'after' and 'before' are NULL, the function returns -1, otherwise
|
||||
// it returns 0.
|
||||
int nctab_move(struct nctabbed* nt, struct nctab* t, struct nctab* after,
|
||||
struct nctab* before);
|
||||
|
||||
// Move 't' to the right by one tab, looping around to become leftmost if needed.
|
||||
void nctab_move_right(struct nctabbed* nt, struct nctab* t);
|
||||
|
||||
// Move 't' to the right by one tab, looping around to become the last tab if needed.
|
||||
void nctab_move_left(struct nctabbed* nt, struct nctab* t);
|
||||
|
||||
// Rotate the tabs of 'nt' right by 'amt' tabs, or '-amt' tabs left if 'amt' is
|
||||
// negative. Tabs are rotated only by changing the leftmost tab; the selected tab
|
||||
// stays the same. If there are no tabs, nothing happens.
|
||||
void nctabbed_rotate(struct nctabbed* nt, int amt);
|
||||
|
||||
// Select the tab after the currently selected tab, and return the newly selected
|
||||
// tab. Returns NULL if there are no tabs.
|
||||
struct nctab* nctabbed_next(struct nctabbed* nt);
|
||||
|
||||
// Select the tab before the currently selected tab, and return the newly selected
|
||||
// tab. Returns NULL if there are no tabs.
|
||||
struct nctab* nctabbed_prev(struct nctabbed* nt);
|
||||
|
||||
// Change the selected tab to be 't'. Returns the previously selected tab.
|
||||
struct nctab* nctabbed_select(struct nctabbed* nt, struct nctab* t);
|
||||
|
||||
// Write the channels for tab headers, the selected tab header, and the separator
|
||||
// to '*hdrchan', '*selchan', and '*sepchan' respectively.
|
||||
void nctabbed_channels(struct nctabbed* nt, uint64_t* RESTRICT hdrchan,
|
||||
uint64_t* RESTRICT selchan, uint64_t* RESTRICT sepchan);
|
||||
|
||||
static inline uint64_t
|
||||
nctabbed_hdrchan(struct nctabbed* nt){
|
||||
uint64_t ch;
|
||||
nctabbed_channels(nt, &ch, NULL, NULL);
|
||||
return ch;
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
nctabbed_selchan(struct nctabbed* nt){
|
||||
uint64_t ch;
|
||||
nctabbed_channels(nt, NULL, &ch, NULL);
|
||||
return ch;
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
nctabbed_sepchan(struct nctabbed* nt){
|
||||
uint64_t ch;
|
||||
nctabbed_channels(nt, NULL, NULL, &ch);
|
||||
return ch;
|
||||
}
|
||||
|
||||
// Returns the tab separator. This is not a copy and it should not be stored.
|
||||
// This can be NULL, if the separator was set to NULL in ncatbbed_create() or
|
||||
// nctabbed_set_separator().
|
||||
const char* nctabbed_separator(struct nctabbed* nt);
|
||||
|
||||
// Returns the tab separator width, or zero if there is no separator.
|
||||
int nctabbed_separator_width(struct nctabbed* nt);
|
||||
|
||||
// Set the tab headers channel for 'nt'.
|
||||
void nctabbed_set_hdrchan(struct nctabbed* nt, uint64_t chan);
|
||||
|
||||
// Set the selected tab header channel for 'nt'.
|
||||
void nctabbed_set_selchan(struct nctabbed* nt, uint64_t chan);
|
||||
|
||||
// Set the tab separator channel for 'nt'.
|
||||
void nctabbed_set_sepchan(struct nctabbed* nt, uint64_t chan);
|
||||
|
||||
// Set the tab callback function for 't'. Returns the previous tab callback.
|
||||
tabcb nctab_set_cb(struct nctab* t, tabcb newcb);
|
||||
|
||||
// Change the name of 't'. Returns -1 if 'newname' is NULL, and 0 otherwise.
|
||||
int nctab_set_name(struct nctab* t, const char* newname);
|
||||
|
||||
// Set the user pointer of 't'. Returns the previous user pointer.
|
||||
void* nctab_set_userptr(struct nctab* t, void* newopaque);
|
||||
|
||||
// Change the tab separator for 'nt'. Returns -1 if 'separator' is not NULL and
|
||||
// is not a valid string, and 0 otherwise.
|
||||
int nctabbed_set_separator(struct nctabbed* nt, const char* separator);
|
||||
```
|
||||
|
||||
## Channels
|
||||
|
||||
A channel encodes 24 bits of RGB color, using 8 bits for each component. It
|
||||
|
@ -73,6 +73,7 @@
|
||||
<a href="notcurses_stats.3.html">notcurses_stats</a>—notcurses runtime statistics<br/>
|
||||
<a href="notcurses_stdplane.3.html">notcurses_stdplane</a>—acquire the standard <tt>ncplane</tt><br/>
|
||||
<a href="notcurses_stop.3.html">notcurses_stop</a>—collapse the context<br/>
|
||||
<a href="notcurses_tabbed.3.html">notcurses_tabbed</a>—tabbed interface widget<br/>
|
||||
<a href="notcurses_tree.3.html">notcurses_tree</a>—line-based widget for hierarchical data<br/>
|
||||
<a href="notcurses_visual.3.html">notcurses_visual</a>—operations on <tt>ncvisual</tt> objects<br/>
|
||||
</div>
|
||||
|
@ -117,6 +117,7 @@ A few high-level widgets are included, all built atop ncplanes:
|
||||
* **notcurses_progbar(3)** for drawing progress bars
|
||||
* **notcurses_reader(3)** for free-form input data
|
||||
* **notcurses_reel(3)** for hierarchal display of block-based data
|
||||
* **notcurses_tabbed(3)** for tabbed interfaces
|
||||
* **notcurses_selector(3)** for selecting one item from a set
|
||||
* **notcurses_tree(3)** for hierarchal display of line-based data
|
||||
|
||||
@ -187,6 +188,7 @@ order to turn most error returns into exceptions.
|
||||
**notcurses_stats(3)**,
|
||||
**notcurses_stdplane(3)**,
|
||||
**notcurses_stop(3)**,
|
||||
**notcurses_tabbed(3)**,
|
||||
**notcurses_tree(3)**,
|
||||
**notcurses_visual(3)**,
|
||||
**terminfo(5)**, **ascii(7)**, **utf-8(7)**,
|
||||
|
161
doc/man/man3/notcurses_tabbed.3.md
Normal file
161
doc/man/man3/notcurses_tabbed.3.md
Normal file
@ -0,0 +1,161 @@
|
||||
% notcurses_tabbed(3)
|
||||
% v2.2.3
|
||||
|
||||
# NAME
|
||||
|
||||
notcurses_tabbed – tabbed interface widget
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
**#include <notcurses/notcurses.h>**
|
||||
|
||||
```c
|
||||
#define NCTABBED_OPTION_BOTTOM 0x0001
|
||||
|
||||
struct nctabbed;
|
||||
struct ncplane;
|
||||
struct nctab;
|
||||
|
||||
typedef struct nctabbed_options {
|
||||
uint64_t selchan; // channel for the selected tab header
|
||||
uint64_t hdrchan; // channel for unselected tab headers
|
||||
uint64_t sepchan; // channel for the tab separator
|
||||
char* separator; // separator string (copied by nctabbed_create())
|
||||
uint64_t flags; // bitmask of NCTABBED_OPTION_*
|
||||
} nctabbed_options;
|
||||
|
||||
typedef void (*tabcb)(struct nctab* t, struct ncplane* ncp, void* userptr);
|
||||
```
|
||||
|
||||
**struct nctabbed* nctabbed_create(struct ncplane* ***ncp***, const nctabbed_options* ***opts***);**
|
||||
|
||||
**void nctabbed_destroy(struct nctabbed* ***nt***);**
|
||||
|
||||
**void nctabbed_redraw(struct nctabbed* ***nt***);**
|
||||
|
||||
**void nctabbed_ensure_selected_header_visible(struct nctabbed* ***nt***);**
|
||||
|
||||
**struct nctab* nctabbed_selected(struct nctabbed* ***nt***);**
|
||||
|
||||
**struct nctab* nctabbed_leftmost(struct nctabbed* ***nt***);**
|
||||
|
||||
**int nctabbed_tabcount(struct nctabbed* ***nt***);**
|
||||
|
||||
**struct ncplane* nctabbed_plane(struct nctabbed* ***nt***);**
|
||||
|
||||
**struct ncplane* nctabbed_content_plane(struct nctabbed* ***nt***);**
|
||||
|
||||
**tabcb nctab_cb(struct nctab* ***t***);**
|
||||
|
||||
**const char* nctab_name(struct nctab* ***t***);**
|
||||
|
||||
**int nctab_name_width(struct nctab* ***t***);**
|
||||
|
||||
**void* nctab_userptr(struct nctab* ***t***);**
|
||||
|
||||
**struct nctab* nctab_next(struct nctab* ***t***);**
|
||||
|
||||
**struct nctab* nctab_prev(struct nctab* ***t***);**
|
||||
|
||||
**struct nctab* nctabbed_add(struct nctabbed* ***nt***, struct nctab* ***after***, struct nctab* ***before***, tabcb ***tcb***, const char* ***name***, void* ***opaque***);
|
||||
|
||||
**int nctabbed_del(struct nctabbed* ***nt***, struct nctab* ***t***);**
|
||||
|
||||
**int nctab_move(struct nctabbed* ***nt***, struct nctab* ***t***, struct nctab* ***after***,
|
||||
struct nctab* ***before***);**
|
||||
|
||||
**void nctab_move_right(struct nctabbed* ***nt***, struct nctab* ***t***);**
|
||||
|
||||
**void nctab_move_left(struct nctabbed* ***nt***, struct nctab* ***t***);**
|
||||
|
||||
**void nctabbed_rotate(struct nctabbed* ***nt***, int ***amt***);**
|
||||
|
||||
**struct nctab* nctabbed_next(struct nctabbed* ***nt***);**
|
||||
|
||||
**struct nctab* nctabbed_prev(struct nctabbed* ***nt***);**
|
||||
|
||||
**struct nctab* nctabbed_select(struct nctabbed* ***nt***, struct nctab* ***t***);**
|
||||
|
||||
**void nctabbed_channels(struct nctabbed* ***nt***, uint64_t* RESTRICT ***hdrchan***, uint64_t* RESTRICT ***selchan***, uint64_t* RESTRICT ***sepchan***);**
|
||||
|
||||
**uint64_t nctabbed_hdrchan(struct nctabbed* ***nt***);**
|
||||
|
||||
**uint64_t nctabbed_selchan(struct nctabbed* ***nt***);**
|
||||
|
||||
**uint64_t nctabbed_sepchan(struct nctabbed* ***nt***);**
|
||||
|
||||
**const char* nctabbed_separator(struct nctabbed* ***nt***);**
|
||||
|
||||
**int nctabbed_separator_width(struct nctabbed* ***nt***);**
|
||||
|
||||
**void nctabbed_set_hdrchan(struct nctabbed* ***nt***, uint64_t ***chan***);**
|
||||
|
||||
**void nctabbed_set_selchan(struct nctabbed* ***nt***, uint64_t ***chan***);**
|
||||
|
||||
**void nctabbed_set_sepchan(struct nctabbed* ***nt***, uint64_t ***chan***);**
|
||||
|
||||
**tabcb nctab_set_cb(struct nctab* ***t***, tabcb ***newcb***);**
|
||||
|
||||
**int nctab_set_name(struct nctab* ***t***, const char* ***newname***);**
|
||||
|
||||
**void* nctab_set_userptr(struct nctab* ***t***, void* ***newopaque***);**
|
||||
|
||||
**int nctabbed_set_separator(struct nctabbed* ***nt***, const char* ***separator***);**
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
An **nctabbed** is a widget with one area for data display and a bar with
|
||||
names of tabs. Unless there are no tabs, exactly one tab is "selected", and
|
||||
exactly one tab is "leftmost". The selected tab is the one that controls
|
||||
the tab content plane. The leftmost tab is the one of which the header is
|
||||
visible furthest to the left. Any tab can be moved to and from anywhere in the
|
||||
list. The tabs can be "rotated", which really means the leftmost tab gets
|
||||
shifted. The widget is drawn only when **nctabbed_redraw** or **ntabbed_create**
|
||||
are called.
|
||||
|
||||
## LAYOUT
|
||||
|
||||
The widget has a tab list either on the top or the bottom, 1 row thick. The tab
|
||||
list contains tab headers (i.e. their names), separated with the separator
|
||||
specified in **nctabbed_create** or **nctabbed_set_separator**. The channels
|
||||
for the selected tab's header, other tab headers and the separator can be
|
||||
set independently of each other. The tab separator can be 0-length, or NULL,
|
||||
in which case there is no visible separator between tab headers. The selected
|
||||
tab can be made sure to be visible when drawn (by changing the leftmost tab)
|
||||
by calling the very long-named **nctabbed_ensure_selected_header_visible**.
|
||||
The rest of the widget is an **ncplane** housing the selected tab content. (if any)
|
||||
|
||||
## THE TAB CALLBACK
|
||||
|
||||
The tab callback (of type **tabcb**) takes a tab, the tab content plane, and
|
||||
the opaque pointer given to **nctabbed_add** or **nctabbed_set_userptr**.
|
||||
It is called when the tab content is supposed to be drawn, that is when
|
||||
the whole widget is redrawn. It should draw the tab content and possibly
|
||||
make other actions, but it should not assume anything about the current state
|
||||
of the tab content plane, nor should it modify the widget's or the tab's state.
|
||||
|
||||
# RETURN VALUES
|
||||
|
||||
**nctabbed_create** returns the newly created widget, or **NULL** when the widget
|
||||
failed to be created. This destroys the **ncplane** given to it even if it fails.
|
||||
|
||||
**nctabbed_selected** and **nctabbed_leftmost** return the selected and
|
||||
leftmost tabs, respectively. If there are no tabs, these return **NULL**.
|
||||
|
||||
**nctab_name** returns the tab's name. This is not a copy, and it should not be
|
||||
stored, since it is freed when the tab's name is changed or the tab is deleted.
|
||||
|
||||
**nctabbed_next**, **nctabbed_prev** and **nctabbed_select** return the newly
|
||||
selected tab.
|
||||
|
||||
**nctabbed_separator** returns the tab separator. This is not a copy, and it
|
||||
should not be stored, since it is freed when the separator is changed or the
|
||||
widget is deleted.
|
||||
|
||||
Functions returning **int** return **-1** on failure.
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
**notcurses(3)**,
|
||||
**notcurses_channels(3)**,
|
||||
**notcurses_plane(3)**
|
@ -55,6 +55,8 @@ struct ncreader; // widget supporting free string input ala readline
|
||||
struct ncfadectx; // context for a palette fade operation
|
||||
struct nctablet; // grouped item within an ncreel
|
||||
struct ncreel; // hierarchical block-based data browser
|
||||
struct nctab; // grouped item within an nctabbed
|
||||
struct nctabbed; // widget with one tab visible at a time
|
||||
|
||||
// we never blit full blocks, but instead spaces (more efficient) with the
|
||||
// background set to the desired foreground.
|
||||
@ -3188,6 +3190,217 @@ API double ncprogbar_progress(const struct ncprogbar* n)
|
||||
// Destroy the progress bar and its underlying ncplane.
|
||||
API void ncprogbar_destroy(struct ncprogbar* n);
|
||||
|
||||
// Tabbed widgets. The tab list is displayed at the top or at the bottom of the
|
||||
// plane, and only one tab is visible at a time.
|
||||
|
||||
// Display the tab list at the bottom instead of at the top of the plane
|
||||
#define NCTABBED_OPTION_BOTTOM 0x0001ull
|
||||
|
||||
typedef struct nctabbed_options {
|
||||
uint64_t selchan; // channel for the selected tab header
|
||||
uint64_t hdrchan; // channel for unselected tab headers
|
||||
uint64_t sepchan; // channel for the tab separator
|
||||
char* separator; // separator string (copied by nctabbed_create())
|
||||
uint64_t flags; // bitmask of NCTABBED_OPTION_*
|
||||
} nctabbed_options;
|
||||
|
||||
// Tab content drawing callback. Takes the tab it was associated to, the ncplane
|
||||
// on which tab content is to be drawn, and the user pointer of the tab.
|
||||
// It is called during nctabbed_redraw().
|
||||
typedef void (*tabcb)(struct nctab* t, struct ncplane* ncp, void* curry);
|
||||
|
||||
// Creates a new nctabbed widget, associated with the given ncplane 'n', and with
|
||||
// additional options given in 'opts'. When 'opts' is NULL, it acts as if it were
|
||||
// called with an all-zero opts. The widget takes ownership of 'n', and destroys
|
||||
// it when the widget is destroyed. Returns the newly created widget. Returns
|
||||
// NULL on failure, also destroying 'n'.
|
||||
API ALLOC struct nctabbed* nctabbed_create(struct ncplane* n, const nctabbed_options* opts)
|
||||
__attribute ((nonnull (1)));
|
||||
|
||||
// Destroy an nctabbed widget. All memory belonging to 'nt' is deallocated,
|
||||
// including all tabs and their names. The plane associated with 'nt' is also
|
||||
// destroyed. Calling this with NULL does nothing.
|
||||
API void nctabbed_destroy(struct nctabbed* nt);
|
||||
|
||||
// Redraw the widget. This calls the tab callback of the currently selected tab
|
||||
// to draw tab contents, and draws tab headers. The tab content plane is not
|
||||
// modified by this function, apart from resizing the plane is necessary.
|
||||
API void nctabbed_redraw(struct nctabbed* nt)
|
||||
__attribute__ ((nonnull (1)));
|
||||
|
||||
// Make sure the tab header of the currently selected tab is at least partially
|
||||
// visible. (by rotating tabs until at least one column is displayed)
|
||||
// Does nothing if there are no tabs.
|
||||
API void nctabbed_ensure_selected_header_visible(struct nctabbed* nt)
|
||||
__attribute__ ((nonnull (1)));
|
||||
|
||||
// Returns the currently selected tab, or NULL if there are no tabs.
|
||||
API struct nctab* nctabbed_selected(struct nctabbed* nt)
|
||||
__attribute__ ((nonnull (1)));
|
||||
|
||||
// Returns the leftmost tab, or NULL if there are no tabs.
|
||||
API struct nctab* nctabbed_leftmost(struct nctabbed* nt)
|
||||
__attribute__ ((nonnull (1)));
|
||||
|
||||
// Returns the number of tabs in the widget.
|
||||
API int nctabbed_tabcount(struct nctabbed* nt)
|
||||
__attribute__ ((nonnull (1)));
|
||||
|
||||
// Returns the plane associated to 'nt'.
|
||||
API struct ncplane* nctabbed_plane(struct nctabbed* nt)
|
||||
__attribute__ ((nonnull (1)));
|
||||
|
||||
// Returns the tab content plane.
|
||||
API struct ncplane* nctabbed_content_plane(struct nctabbed* nt)
|
||||
__attribute__ ((nonnull (1)));
|
||||
|
||||
// Returns the tab callback.
|
||||
API tabcb nctab_cb(struct nctab* t)
|
||||
__attribute__ ((nonnull (1)));
|
||||
|
||||
// Returns the tab name. This is not a copy and it should not be stored.
|
||||
API const char* nctab_name(struct nctab* t)
|
||||
__attribute__ ((nonnull (1)));
|
||||
|
||||
// Returns the width (in columns) of the tab's name.
|
||||
API int nctab_name_width(struct nctab* t)
|
||||
__attribute__ ((nonnull (1)));
|
||||
|
||||
// Returns the tab's user pointer.
|
||||
API void* nctab_userptr(struct nctab* t)
|
||||
__attribute__ ((nonnull (1)));
|
||||
|
||||
// Returns the tab to the right of 't'. This does not change which tab is selected.
|
||||
API struct nctab* nctab_next(struct nctab* t)
|
||||
__attribute__ ((nonnull (1)));
|
||||
|
||||
// Returns the tab to the left of 't'. This does not change which tab is selected.
|
||||
API struct nctab* nctab_prev(struct nctab* t)
|
||||
__attribute__ ((nonnull (1)));
|
||||
|
||||
// Add a new tab to 'nt' with the given tab callback, name, and user pointer.
|
||||
// If both 'before' and 'after' are NULL, the tab is inserted after the selected
|
||||
// tab. Otherwise, it gets put after 'after' (if not NULL) and before 'before'
|
||||
// (if not NULL). If both 'after' and 'before' are given, they must be two
|
||||
// neighboring tabs (the tab list is circular, so the last tab is immediately
|
||||
// before the leftmost tab), otherwise the function returns NULL. If 'name' is
|
||||
// NULL or a string containing illegal characters, the function returns NULL.
|
||||
// On all other failures the function also returns NULL. If it returns NULL,
|
||||
// none of the arguments are modified, and the widget state is not altered.
|
||||
API ALLOC struct nctab* nctabbed_add(struct nctabbed* nt, struct nctab* after,
|
||||
struct nctab* before, tabcb tcb,
|
||||
const char* name, void* opaque)
|
||||
__attribute__ ((nonnull (1, 5)));
|
||||
|
||||
// Remove a tab 't' from 'nt'. Its neighboring tabs become neighbors to each
|
||||
// other. If 't' if the selected tab, the tab after 't' becomes selected.
|
||||
// Likewise if 't' is the leftmost tab, the tab after 't' becomes leftmost.
|
||||
// If 't' is the only tab, there will no more be a selected or leftmost tab,
|
||||
// until a new tab is added. Returns -1 if 't' is NULL, and 0 otherwise.
|
||||
API int nctabbed_del(struct nctabbed* nt, struct nctab* t)
|
||||
__attribute__ ((nonnull (1)));
|
||||
|
||||
// Move 't' after 'after' (if not NULL) and before 'before' (if not NULL).
|
||||
// If both 'after' and 'before' are NULL, the function returns -1, otherwise
|
||||
// it returns 0.
|
||||
API int nctab_move(struct nctabbed* nt, struct nctab* t, struct nctab* after,
|
||||
struct nctab* before)
|
||||
__attribute__ ((nonnull (1, 2)));
|
||||
|
||||
// Move 't' to the right by one tab, looping around to become leftmost if needed.
|
||||
API void nctab_move_right(struct nctabbed* nt, struct nctab* t)
|
||||
__attribute__ ((nonnull (1, 2)));
|
||||
|
||||
// Move 't' to the right by one tab, looping around to become the last tab if needed.
|
||||
API void nctab_move_left(struct nctabbed* nt, struct nctab* t)
|
||||
__attribute__ ((nonnull (1, 2)));
|
||||
|
||||
// Rotate the tabs of 'nt' right by 'amt' tabs, or '-amt' tabs left if 'amt' is
|
||||
// negative. Tabs are rotated only by changing the leftmost tab; the selected tab
|
||||
// stays the same. If there are no tabs, nothing happens.
|
||||
API void nctabbed_rotate(struct nctabbed* nt, int amt)
|
||||
__attribute__ ((nonnull (1)));
|
||||
|
||||
// Select the tab after the currently selected tab, and return the newly selected
|
||||
// tab. Returns NULL if there are no tabs.
|
||||
API struct nctab* nctabbed_next(struct nctabbed* nt)
|
||||
__attribute__ ((nonnull (1)));
|
||||
|
||||
// Select the tab before the currently selected tab, and return the newly selected
|
||||
// tab. Returns NULL if there are no tabs.
|
||||
API struct nctab* nctabbed_prev(struct nctabbed* nt)
|
||||
__attribute__ ((nonnull (1)));
|
||||
|
||||
// Change the selected tab to be 't'. Returns the previously selected tab.
|
||||
API struct nctab* nctabbed_select(struct nctabbed* nt, struct nctab* t)
|
||||
__attribute__ ((nonnull (1, 2)));
|
||||
|
||||
// Write the channels for tab headers, the selected tab header, and the separator
|
||||
// to '*hdrchan', '*selchan', and '*sepchan' respectively.
|
||||
API void nctabbed_channels(struct nctabbed* nt, uint64_t* RESTRICT hdrchan,
|
||||
uint64_t* RESTRICT selchan, uint64_t* RESTRICT sepchan)
|
||||
__attribute__ ((nonnull (1)));
|
||||
|
||||
static inline uint64_t
|
||||
nctabbed_hdrchan(struct nctabbed* nt){
|
||||
uint64_t ch;
|
||||
nctabbed_channels(nt, &ch, NULL, NULL);
|
||||
return ch;
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
nctabbed_selchan(struct nctabbed* nt){
|
||||
uint64_t ch;
|
||||
nctabbed_channels(nt, NULL, &ch, NULL);
|
||||
return ch;
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
nctabbed_sepchan(struct nctabbed* nt){
|
||||
uint64_t ch;
|
||||
nctabbed_channels(nt, NULL, NULL, &ch);
|
||||
return ch;
|
||||
}
|
||||
|
||||
// Returns the tab separator. This is not a copy and it should not be stored.
|
||||
// This can be NULL, if the separator was set to NULL in ncatbbed_create() or
|
||||
// nctabbed_set_separator().
|
||||
API const char* nctabbed_separator(struct nctabbed* nt)
|
||||
__attribute__ ((nonnull (1)));
|
||||
|
||||
// Returns the tab separator width, or zero if there is no separator.
|
||||
API int nctabbed_separator_width(struct nctabbed* nt)
|
||||
__attribute__ ((nonnull (1)));
|
||||
|
||||
// Set the tab headers channel for 'nt'.
|
||||
API void nctabbed_set_hdrchan(struct nctabbed* nt, uint64_t chan)
|
||||
__attribute__ ((nonnull (1)));
|
||||
|
||||
// Set the selected tab header channel for 'nt'.
|
||||
API void nctabbed_set_selchan(struct nctabbed* nt, uint64_t chan)
|
||||
__attribute__ ((nonnull (1)));
|
||||
|
||||
// Set the tab separator channel for 'nt'.
|
||||
API void nctabbed_set_sepchan(struct nctabbed* nt, uint64_t chan)
|
||||
__attribute__ ((nonnull (1)));
|
||||
|
||||
// Set the tab callback function for 't'. Returns the previous tab callback.
|
||||
API tabcb nctab_set_cb(struct nctab* t, tabcb newcb)
|
||||
__attribute__ ((nonnull (1)));
|
||||
|
||||
// Change the name of 't'. Returns -1 if 'newname' is NULL, and 0 otherwise.
|
||||
API int nctab_set_name(struct nctab* t, const char* newname)
|
||||
__attribute__ ((nonnull (1, 2)));
|
||||
|
||||
// Set the user pointer of 't'. Returns the previous user pointer.
|
||||
API void* nctab_set_userptr(struct nctab* t, void* newopaque)
|
||||
__attribute__ ((nonnull (1)));
|
||||
|
||||
// Change the tab separator for 'nt'. Returns -1 if 'separator' is not NULL and
|
||||
// is not a valid string, and 0 otherwise.
|
||||
API int nctabbed_set_separator(struct nctabbed* nt, const char* separator)
|
||||
__attribute__ ((nonnull (1, 2)));
|
||||
|
||||
// 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
|
||||
|
@ -256,6 +256,28 @@ typedef struct ncprogbar {
|
||||
bool retrograde;
|
||||
} ncprogbar;
|
||||
|
||||
typedef struct nctab {
|
||||
struct nctabbed* nt; // The nctabbed this belongs to
|
||||
tabcb cb; // tab callback
|
||||
char* name; // tab name
|
||||
int namecols; // tab name width in columns
|
||||
void* curry; // user pointer
|
||||
struct nctab* prev;
|
||||
struct nctab* next;
|
||||
} nctab;
|
||||
|
||||
typedef struct nctabbed {
|
||||
ncplane* ncp; // widget ncplane
|
||||
ncplane* p; // tab content ncplane
|
||||
ncplane* hp; // tab headers ncplane
|
||||
// a doubly-linked circular list of tabs
|
||||
nctab* leftmost; // the tab most to the left
|
||||
nctab* selected; // the currently selected tab
|
||||
int tabcount; // tab separator (can be NULL)
|
||||
int sepcols; // separator with in columns
|
||||
nctabbed_options opts; // copied in nctabbed_create()
|
||||
} nctabbed;
|
||||
|
||||
// terminfo cache
|
||||
typedef struct tinfo {
|
||||
unsigned colors;// number of colors terminfo reported usable for this screen
|
||||
|
456
src/lib/tabbed.c
Normal file
456
src/lib/tabbed.c
Normal file
@ -0,0 +1,456 @@
|
||||
#include "internal.h"
|
||||
|
||||
void nctabbed_redraw(nctabbed* nt){
|
||||
nctab* t;
|
||||
int drawn_cols = 0;
|
||||
int rows, cols;
|
||||
if(nt->tabcount == 0){
|
||||
// no tabs = nothing to draw
|
||||
ncplane_erase(nt->hp);
|
||||
return;
|
||||
}
|
||||
// update sizes for planes
|
||||
ncplane_dim_yx(nt->ncp, &rows, &cols);
|
||||
if(nt->opts.flags & NCTABBED_OPTION_BOTTOM){
|
||||
ncplane_resize_simple(nt->hp, -1, cols);
|
||||
ncplane_resize_simple(nt->p, rows - 1, cols);
|
||||
ncplane_move_yx(nt->hp, rows - 2, 0);
|
||||
}else{
|
||||
ncplane_resize_simple(nt->hp, -1, cols);
|
||||
ncplane_resize_simple(nt->p, rows - 1, cols);
|
||||
}
|
||||
// the callback draws the tab contents
|
||||
if(nt->selected->cb){
|
||||
nt->selected->cb(nt->selected, nt->p, nt->selected->curry);
|
||||
}
|
||||
// now we draw the headers
|
||||
t = nt->leftmost;
|
||||
ncplane_erase(nt->hp);
|
||||
ncplane_set_channels(nt->hp, nt->opts.hdrchan);
|
||||
do{
|
||||
if(t == nt->selected){
|
||||
ncplane_set_channels(nt->hp, nt->opts.selchan);
|
||||
drawn_cols += ncplane_putstr(nt->hp, t->name);
|
||||
ncplane_set_channels(nt->hp, nt->opts.hdrchan);
|
||||
}else{
|
||||
drawn_cols += ncplane_putstr(nt->hp, t->name);
|
||||
}
|
||||
// avoid drawing the separator after the last tab, or when we
|
||||
// ran out of space, or when it's not set
|
||||
if((t->next != nt->leftmost || drawn_cols >= cols) && nt->opts.separator){
|
||||
ncplane_set_channels(nt->hp, nt->opts.sepchan);
|
||||
drawn_cols += ncplane_putstr(nt->hp, nt->opts.separator);
|
||||
ncplane_set_channels(nt->hp, nt->opts.hdrchan);
|
||||
}
|
||||
t = t->next;
|
||||
}while(t != nt->leftmost && drawn_cols < cols);
|
||||
}
|
||||
|
||||
void nctabbed_ensure_selected_header_visible(nctabbed* nt){
|
||||
nctab* t = nt->leftmost;
|
||||
int cols = ncplane_dim_x(nt->hp);
|
||||
int takencols = 0;
|
||||
if(!t){
|
||||
return;
|
||||
}
|
||||
//fprintf(stderr, "ensuring selected header visible\n");
|
||||
do{
|
||||
if(t == nt->selected){
|
||||
break;
|
||||
}
|
||||
takencols += t->namecols + nt->sepcols;
|
||||
if(takencols >= cols){
|
||||
//fprintf(stderr, "not enough space, rotating\n");
|
||||
takencols -= nt->leftmost->namecols + nt->sepcols;
|
||||
nctabbed_rotate(nt, -1);
|
||||
}
|
||||
t = t->next;
|
||||
//fprintf(stderr, "iteration over: takencols = %d, cols = %d\n", takencols, cols);
|
||||
}while(t != nt->leftmost);
|
||||
//fprintf(stderr, "ensuring done\n");
|
||||
}
|
||||
|
||||
static bool
|
||||
nctabbed_validate_opts(ncplane* n, const nctabbed_options* opts){
|
||||
notcurses* nc = ncplane_notcurses(n);
|
||||
if(opts->flags > NCTABBED_OPTION_BOTTOM){
|
||||
logwarn(nc, "Provided unsupported flags 0x%016jx\n", (uint64_t)opts->flags);
|
||||
}
|
||||
if(opts->sepchan && !opts->separator){
|
||||
logwarn(nc, "Provided non-zero separator channel when separator is NULL")
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
nctab* nctabbed_selected(nctabbed* nt){
|
||||
return nt->selected;
|
||||
}
|
||||
|
||||
nctab* nctabbed_leftmost(nctabbed* nt){
|
||||
return nt->leftmost;
|
||||
}
|
||||
|
||||
int nctabbed_tabcount(nctabbed* nt){
|
||||
return nt->tabcount;
|
||||
}
|
||||
|
||||
ncplane* nctabbed_plane(nctabbed* nt){
|
||||
return nt->ncp;
|
||||
}
|
||||
|
||||
ncplane* nctabbed_content_plane(nctabbed* nt){
|
||||
return nt->p;
|
||||
}
|
||||
|
||||
tabcb nctab_cb(nctab* t){
|
||||
return t->cb;
|
||||
}
|
||||
|
||||
const char* nctab_name(nctab* t){
|
||||
return t->name;
|
||||
}
|
||||
|
||||
int nctab_name_width(nctab* t){
|
||||
return t->namecols;
|
||||
}
|
||||
|
||||
void* nctab_userptr(nctab* t){
|
||||
return t->curry;
|
||||
}
|
||||
|
||||
nctab* nctab_next(nctab* t){
|
||||
return t->next;
|
||||
}
|
||||
|
||||
nctab* nctab_prev(nctab* t){
|
||||
return t->prev;
|
||||
}
|
||||
|
||||
nctabbed* nctabbed_create(ncplane* n, const nctabbed_options* topts){
|
||||
nctabbed_options zeroed = {};
|
||||
ncplane_options nopts = {};
|
||||
int nrows, ncols;
|
||||
nctabbed* nt;
|
||||
notcurses* nc = ncplane_notcurses(n);
|
||||
if(!topts){
|
||||
topts = &zeroed;
|
||||
}
|
||||
if(!nctabbed_validate_opts(n, topts)){
|
||||
return NULL;
|
||||
}
|
||||
if((nt = malloc(sizeof(*nt))) == NULL){
|
||||
logerror(nc, "Couldn't allocate nctabbed");
|
||||
return NULL;
|
||||
}
|
||||
nt->ncp = n;
|
||||
nt->leftmost = nt->selected = NULL;
|
||||
nt->tabcount = 0;
|
||||
memcpy(&nt->opts, topts, sizeof(*topts));
|
||||
if(nt->opts.separator){
|
||||
if((nt->opts.separator = strdup(nt->opts.separator)) == NULL){
|
||||
logerror(nc, "Couldn't allocate nctabbed separator");
|
||||
free(nt);
|
||||
return NULL;
|
||||
}
|
||||
if((nt->sepcols = ncstrwidth(nt->opts.separator)) < 0){
|
||||
logerror(nc, "Separator string contains illegal characters");
|
||||
free(nt->opts.separator);
|
||||
free(nt);
|
||||
return NULL;
|
||||
}
|
||||
}else{
|
||||
nt->sepcols = 0;
|
||||
}
|
||||
ncplane_dim_yx(n, &nrows, &ncols);
|
||||
if(topts->flags & NCTABBED_OPTION_BOTTOM){
|
||||
nopts.y = nopts.x = 0;
|
||||
nopts.cols = ncols;
|
||||
nopts.rows = nrows - 1;
|
||||
if((nt->p = ncplane_create(n, &nopts)) == NULL){
|
||||
logerror(nc, "Couldn't create the tab content plane");
|
||||
ncplane_genocide(n);
|
||||
free(nt);
|
||||
return NULL;
|
||||
}
|
||||
nopts.y = nrows - 2;
|
||||
nopts.rows = 1;
|
||||
if((nt->hp = ncplane_create(n, &nopts)) == NULL){
|
||||
logerror(nc, "Couldn't create the tab headers plane");
|
||||
ncplane_genocide(n);
|
||||
free(nt);
|
||||
return NULL;
|
||||
}
|
||||
}else{
|
||||
nopts.y = nopts.x = 0;
|
||||
nopts.cols = ncols;
|
||||
nopts.rows = 1;
|
||||
if((nt->hp = ncplane_create(n, &nopts)) == NULL){
|
||||
logerror(nc, "Couldn't create the tab headers plane");
|
||||
ncplane_genocide(n);
|
||||
free(nt);
|
||||
return NULL;
|
||||
}
|
||||
nopts.y = 1;
|
||||
nopts.rows = nrows - 1;
|
||||
if((nt->p = ncplane_create(n, &nopts)) == NULL){
|
||||
logerror(nc, "Couldn't create the tab content plane");
|
||||
ncplane_genocide(n);
|
||||
free(nt);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
nctabbed_redraw(nt);
|
||||
return nt;
|
||||
}
|
||||
|
||||
nctab* nctabbed_add(nctabbed* nt, nctab* after, nctab* before, tabcb cb,
|
||||
const char* name, void* opaque){
|
||||
nctab* t;
|
||||
notcurses* nc = ncplane_notcurses(nt->ncp);
|
||||
if(after && before){
|
||||
if(after->prev != before || before->next != after){
|
||||
logerror(ncplane_notcurses(nt->ncp), "bad before (%p) / after (%p) spec\n", before, after);
|
||||
return NULL;
|
||||
}
|
||||
}else if(!after && !before){
|
||||
// add it to the right of the selected tab
|
||||
after = nt->selected;
|
||||
}
|
||||
if((t = malloc(sizeof(*t))) == NULL){
|
||||
logerror(nc, "Couldn't alocate nctab")
|
||||
return NULL;
|
||||
}
|
||||
if((t->name = strdup(name)) == NULL){
|
||||
logerror(nc, "Couldn't allocate the tab name");
|
||||
free(t);
|
||||
return NULL;
|
||||
}
|
||||
if((t->namecols = ncstrwidth(name)) < 0){
|
||||
logerror(nc, "Tab name contains illegal characters")
|
||||
free(t->name);
|
||||
free(t);
|
||||
return NULL;
|
||||
}
|
||||
if(after){
|
||||
t->next = after->next;
|
||||
t->prev = after;
|
||||
after->next = t;
|
||||
t->next->prev = t;
|
||||
}else if(before){
|
||||
t->next = before;
|
||||
t->prev = before->prev;
|
||||
before->prev = t;
|
||||
t->prev->next = t;
|
||||
}else{
|
||||
// the first tab
|
||||
t->prev = t->next = t;
|
||||
nt->leftmost = nt->selected = t;
|
||||
}
|
||||
t->nt = nt;
|
||||
t->cb = cb;
|
||||
t->curry = opaque;
|
||||
++nt->tabcount;
|
||||
return t;
|
||||
}
|
||||
|
||||
int nctabbed_del(nctabbed* nt, nctab* t){
|
||||
if(!t){
|
||||
logerror(ncplane_notcurses(nt->ncp), "Provided NULL nctab");
|
||||
return -1;
|
||||
}
|
||||
if(nt->tabcount == 1){
|
||||
nt->leftmost = nt->selected = NULL;
|
||||
}else{
|
||||
if(nt->selected == t){
|
||||
nt->selected = t->next;
|
||||
}
|
||||
if(nt->leftmost == t){
|
||||
nt->leftmost = t->next;
|
||||
}
|
||||
t->next->prev = t->prev;
|
||||
t->prev->next = t->next;
|
||||
}
|
||||
free(t);
|
||||
--nt->tabcount;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nctab_move(nctabbed* nt, nctab* t, nctab* after, nctab* before){
|
||||
if(after && before){
|
||||
if(after->prev != before || before->next != after){
|
||||
logerror(ncplane_notcurses(nt->ncp), "bad before (%p) / after (%p) spec\n", before, after);
|
||||
return -1;
|
||||
}
|
||||
}else if(!after && !before){
|
||||
logerror(ncplane_notcurses(nt->ncp), "bad before (%p) / after (%p) spec\n", before, after);
|
||||
return -1;
|
||||
}
|
||||
// bad things would happen
|
||||
if(t == after || t == before){
|
||||
logerror(ncplane_notcurses(nt->ncp), "Cannot move a tab before or after itself.");
|
||||
return -1;
|
||||
}
|
||||
t->prev->next = t->next;
|
||||
t->next->prev = t->prev;
|
||||
if(after){
|
||||
t->next = after->next;
|
||||
t->prev = after;
|
||||
after->next = t;
|
||||
t->next->prev = t;
|
||||
}else{
|
||||
t->next = before;
|
||||
t->prev = before->prev;
|
||||
before->prev = t;
|
||||
t->prev->next = t;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nctab_move_right(nctabbed* nt, nctab* t){
|
||||
if(t == nt->leftmost->prev){
|
||||
nctab_move(nt, t, NULL, nt->leftmost);
|
||||
nt->leftmost = t;
|
||||
return;
|
||||
}else if(t == nt->leftmost){
|
||||
nt->leftmost = t->next;
|
||||
}
|
||||
nctab_move(nt, t, t->next, NULL);
|
||||
}
|
||||
|
||||
void nctab_move_left(nctabbed* nt, nctab* t){
|
||||
if(t == nt->leftmost){
|
||||
nt->leftmost = t->next;
|
||||
nctab_move(nt, t, nt->leftmost->prev, NULL);
|
||||
return;
|
||||
}else if(t == nt->leftmost->next){
|
||||
nt->leftmost = t;
|
||||
}
|
||||
nctab_move(nt, t, NULL, t->prev);
|
||||
}
|
||||
|
||||
void nctabbed_rotate(nctabbed* nt, int amt){
|
||||
if(amt > 0){
|
||||
for(int i = 0 ; i < amt ; ++i){
|
||||
nt->leftmost = nt->leftmost->prev;
|
||||
}
|
||||
}else{
|
||||
for(int i = 0 ; i < -amt ; ++i){
|
||||
nt->leftmost = nt->leftmost->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nctab* nctabbed_next(nctabbed* nt){
|
||||
if(nt->tabcount == 0){
|
||||
return NULL;
|
||||
}
|
||||
nt->selected = nt->selected->next;
|
||||
return nt->selected;
|
||||
}
|
||||
|
||||
nctab* nctabbed_prev(nctabbed* nt){
|
||||
if(nt->tabcount == 0){
|
||||
return NULL;
|
||||
}
|
||||
nt->selected = nt->selected->prev;
|
||||
return nt->selected;
|
||||
}
|
||||
|
||||
nctab* nctabbed_select(nctabbed* nt, nctab* t){
|
||||
nctab* prevsel = nt->selected;
|
||||
nt->selected = t;
|
||||
return prevsel;
|
||||
}
|
||||
|
||||
void nctabbed_channels(nctabbed* nt, uint64_t* RESTRICT hdrchan,
|
||||
uint64_t* RESTRICT selchan, uint64_t* RESTRICT sepchan){
|
||||
memcpy(&nt->opts.hdrchan, hdrchan, sizeof(*hdrchan));
|
||||
memcpy(&nt->opts.selchan, selchan, sizeof(*selchan));
|
||||
memcpy(&nt->opts.sepchan, sepchan, sizeof(*sepchan));
|
||||
}
|
||||
|
||||
const char* nctabbed_separator(nctabbed* nt){
|
||||
return nt->opts.separator;
|
||||
}
|
||||
|
||||
int nctabbed_separator_width(nctabbed* nt){
|
||||
return nt->sepcols;
|
||||
}
|
||||
|
||||
void nctabbed_destroy(nctabbed* nt){
|
||||
if(!nt){
|
||||
return;
|
||||
}
|
||||
nctab* t = nt->leftmost;
|
||||
nctab* tmp;
|
||||
t->prev->next = NULL;
|
||||
while(t){
|
||||
tmp = t->next;
|
||||
free(t->name);
|
||||
free(t);
|
||||
t = tmp;
|
||||
}
|
||||
ncplane_genocide(nt->ncp);
|
||||
free(nt->opts.separator);
|
||||
free(nt);
|
||||
}
|
||||
|
||||
void nctabbed_set_hdrchan(nctabbed* nt, uint64_t chan){
|
||||
nt->opts.hdrchan = chan;
|
||||
}
|
||||
|
||||
void nctabbed_set_selchan(nctabbed* nt, uint64_t chan){
|
||||
nt->opts.selchan = chan;
|
||||
}
|
||||
|
||||
void nctabbed_set_sepchan(nctabbed* nt, uint64_t chan){
|
||||
nt->opts.sepchan = chan;
|
||||
}
|
||||
|
||||
tabcb nctab_set_cb(nctab* t, tabcb newcb){
|
||||
tabcb prevcb = t->cb;
|
||||
t->cb = newcb;
|
||||
return prevcb;
|
||||
}
|
||||
|
||||
int nctab_set_name(nctab* t, const char* newname){
|
||||
int newnamecols;
|
||||
char* prevname = t->name;
|
||||
notcurses* nc = ncplane_notcurses(t->nt->ncp);
|
||||
if((newnamecols = ncstrwidth(newname)) < 0){
|
||||
logerror(nc, "New tab name contains illegal characters");
|
||||
return -1;
|
||||
}
|
||||
if((t->name = strdup(newname)) == NULL){
|
||||
logerror(nc, "Couldn't allocate new tab name");
|
||||
t->name = prevname;
|
||||
return -1;
|
||||
}
|
||||
free(prevname);
|
||||
t->namecols = newnamecols;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* nctab_set_userptr(nctab* t, void* newopaque){
|
||||
void* prevcurry = t->curry;
|
||||
t->curry = newopaque;
|
||||
return prevcurry;
|
||||
}
|
||||
|
||||
int nctabbed_set_separator(nctabbed* nt, const char* separator){
|
||||
int newsepcols;
|
||||
char* prevsep = nt->opts.separator;
|
||||
notcurses* nc = ncplane_notcurses(nt->ncp);
|
||||
if((newsepcols = ncstrwidth(separator)) < 0){
|
||||
logerror(nc, "New tab separator contains illegal characters");
|
||||
return -1;
|
||||
}
|
||||
if((nt->opts.separator = strdup(separator)) == NULL){
|
||||
logerror(nc, "Couldn't allocate new tab separator");
|
||||
nt->opts.separator = prevsep;
|
||||
return -1;
|
||||
}
|
||||
free(prevsep);
|
||||
nt->sepcols = newsepcols;
|
||||
return 0;
|
||||
}
|
143
src/poc/tabbed.c
Normal file
143
src/poc/tabbed.c
Normal file
@ -0,0 +1,143 @@
|
||||
#include <notcurses/notcurses.h>
|
||||
|
||||
#define REDRAW() \
|
||||
do{ \
|
||||
nctabbed_redraw(nct); \
|
||||
if(notcurses_render(nc) < 0){ \
|
||||
goto ded; \
|
||||
} \
|
||||
notcurses_getc_blocking(nc, NULL); \
|
||||
}while(0)
|
||||
|
||||
void tabcbfn(struct nctab* t, struct ncplane* ncp, void* curry){
|
||||
(void) t;
|
||||
(void) curry;
|
||||
ncplane_erase(ncp);
|
||||
ncplane_puttext(ncp, -1, NCALIGN_LEFT,
|
||||
"Use left/right arrow keys for navigation, "
|
||||
"'[' and ']' to rotate tabs, "
|
||||
"'a' to add a tab, 'r' to remove a tab, "
|
||||
"',' and '.' to move the selected tab, "
|
||||
"and 'q' to quit",
|
||||
NULL);
|
||||
}
|
||||
|
||||
void print_usage(char** argv){
|
||||
printf("Usage: %s [ -bht | --bottom | --help | --top ]...\n", argv[0]);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv){
|
||||
struct notcurses* nc;
|
||||
bool bottom = false;
|
||||
for(int i = 1 ; i < argc ; ++i){
|
||||
if(strcmp(argv[i], "--help") == 0){
|
||||
print_usage(argv);
|
||||
return EXIT_SUCCESS;
|
||||
}else if(strcmp(argv[i], "--bottom") == 0){
|
||||
bottom = true;
|
||||
}else if(strcmp(argv[i], "--top") == 0){
|
||||
bottom = false;
|
||||
}else if(argv[i][0] == '-'){
|
||||
for(char* c = &argv[i][1] ; *c ; ++c){
|
||||
switch(*c){
|
||||
case 'h':
|
||||
print_usage(argv);
|
||||
return EXIT_SUCCESS;
|
||||
case 'b':
|
||||
bottom = true;
|
||||
break;
|
||||
case 't':
|
||||
bottom = false;
|
||||
break;
|
||||
default:
|
||||
print_usage(argv);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
print_usage(argv);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
nc = notcurses_core_init(NULL, NULL);
|
||||
if(!nc){
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
int rows, cols;
|
||||
struct ncplane* stdp = notcurses_stddim_yx(nc, &rows, &cols);
|
||||
struct ncplane_options popts = {
|
||||
.y = 3,
|
||||
.x = 5,
|
||||
.rows = rows - 10,
|
||||
.cols = cols - 10
|
||||
};
|
||||
struct ncplane* ncp = ncplane_create(stdp, &popts);
|
||||
struct nctabbed_options topts = {
|
||||
.hdrchan = CHANNELS_RGB_INITIALIZER(255, 0, 0, 60, 60, 60),
|
||||
.selchan = CHANNELS_RGB_INITIALIZER(0, 255, 0, 0, 0, 0),
|
||||
.sepchan = CHANNELS_RGB_INITIALIZER(255, 255, 255, 100, 100, 100),
|
||||
.separator = " || ",
|
||||
.flags = bottom ? NCTABBED_OPTION_BOTTOM : 0
|
||||
};
|
||||
struct nctabbed* nct = nctabbed_create(ncp, &topts);
|
||||
struct nctab* t_; // stupid unused result warnings
|
||||
(void) t_;
|
||||
ncplane_set_base(nctabbed_content_plane(nct), " ", 0, CHANNELS_RGB_INITIALIZER(255, 255, 255, 15, 60, 15));
|
||||
REDRAW();
|
||||
t_ = nctabbed_add(nct, NULL, NULL, tabcbfn, "Tab #1", NULL);
|
||||
REDRAW();
|
||||
t_ = nctabbed_add(nct, NULL, NULL, tabcbfn, "Tab #2", NULL);
|
||||
REDRAW();
|
||||
t_ = nctabbed_add(nct, NULL, NULL, tabcbfn, "Tab #3", NULL);
|
||||
REDRAW();
|
||||
t_ = nctabbed_add(nct, NULL, NULL, tabcbfn, "alpha", NULL);
|
||||
REDRAW();
|
||||
t_ = nctabbed_add(nct, NULL, NULL, tabcbfn, "beta", NULL);
|
||||
REDRAW();
|
||||
t_ = nctabbed_add(nct, NULL, NULL, tabcbfn, "gamma", NULL);
|
||||
REDRAW();
|
||||
char32_t c;
|
||||
while((c = notcurses_getc_blocking(nc, NULL)) != 'q'){
|
||||
switch(c){
|
||||
case NCKEY_RIGHT:
|
||||
nctabbed_next(nct);
|
||||
break;
|
||||
case NCKEY_LEFT:
|
||||
nctabbed_prev(nct);
|
||||
break;
|
||||
case '[':
|
||||
nctabbed_rotate(nct, -1);
|
||||
break;
|
||||
case ']':
|
||||
nctabbed_rotate(nct, 1);
|
||||
break;
|
||||
case ',':
|
||||
nctab_move_left(nct, nctabbed_selected(nct));
|
||||
break;
|
||||
case '.':
|
||||
nctab_move_right(nct, nctabbed_selected(nct));
|
||||
break;
|
||||
case 'a':
|
||||
t_ = nctabbed_add(nct, NULL, NULL, tabcbfn, "added tab", NULL);
|
||||
break;
|
||||
case 'r':
|
||||
nctabbed_del(nct, nctabbed_selected(nct));
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
nctabbed_ensure_selected_header_visible(nct);
|
||||
nctabbed_redraw(nct);
|
||||
if(notcurses_render(nc)){
|
||||
goto ded;
|
||||
}
|
||||
}
|
||||
goto fin;
|
||||
ded:
|
||||
notcurses_stop(nc);
|
||||
return EXIT_FAILURE;
|
||||
fin:
|
||||
if(notcurses_stop(nc) < 0){
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
394
src/tests/tabbed.cpp
Normal file
394
src/tests/tabbed.cpp
Normal file
@ -0,0 +1,394 @@
|
||||
#include "main.h"
|
||||
|
||||
void tabbedcb(struct nctab*, struct ncplane*, void*){
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE("Tabbed") {
|
||||
auto nc_ = testing_notcurses();
|
||||
if(!nc_){
|
||||
return;
|
||||
}
|
||||
struct ncplane* n_ = notcurses_stdplane(nc_);
|
||||
REQUIRE(n_);
|
||||
REQUIRE(0 == ncplane_cursor_move_yx(n_, 0, 0));
|
||||
|
||||
SUBCASE("CreateNullOpts") {
|
||||
struct ncplane_options nopts = {
|
||||
.y = 1, .x = 2, .rows = ncplane_dim_y(n_) - 2, .cols = ncplane_dim_x(n_) - 4,
|
||||
.userptr = nullptr, .name = nullptr, .resizecb = nullptr, .flags = 0
|
||||
};
|
||||
auto ncp = ncplane_create(n_, &nopts);
|
||||
REQUIRE(nullptr != ncp);
|
||||
auto nt = nctabbed_create(ncp, nullptr);
|
||||
REQUIRE(nullptr != nt);
|
||||
CHECK(0 == nctabbed_hdrchan(nt));
|
||||
CHECK(0 == nctabbed_selchan(nt));
|
||||
CHECK(0 == nctabbed_sepchan(nt));
|
||||
CHECK(nullptr == nctabbed_separator(nt));
|
||||
nctabbed_destroy(nt);
|
||||
}
|
||||
|
||||
SUBCASE("Create") {
|
||||
struct nctabbed_options opts = {
|
||||
.selchan = CHANNELS_RGB_INITIALIZER(0, 255, 0, 70, 70, 70),
|
||||
.hdrchan = CHANNELS_RGB_INITIALIZER(255, 0, 0, 60, 60, 60),
|
||||
.sepchan = CHANNELS_RGB_INITIALIZER(0, 0, 255, 60, 60, 60),
|
||||
.separator = const_cast<char*>("-separator-"),
|
||||
.flags = NCTABBED_OPTION_BOTTOM
|
||||
};
|
||||
struct ncplane_options nopts = {
|
||||
.y = 1, .x = 2, .rows = ncplane_dim_y(n_) - 2, .cols = ncplane_dim_x(n_) - 4,
|
||||
.userptr = nullptr, .name = nullptr, .resizecb = nullptr, .flags = 0
|
||||
};
|
||||
auto ncp = ncplane_create(n_, &nopts);
|
||||
auto nt = nctabbed_create(ncp, &opts);
|
||||
REQUIRE(nullptr != nt);
|
||||
CHECK(opts.hdrchan == nctabbed_hdrchan(nt));
|
||||
CHECK(opts.selchan == nctabbed_selchan(nt));
|
||||
CHECK(opts.sepchan == nctabbed_sepchan(nt));
|
||||
CHECK(0 == strcmp("-separator-", nctabbed_separator(nt)));
|
||||
nctabbed_destroy(nt);
|
||||
}
|
||||
|
||||
SUBCASE("Add") {
|
||||
struct ncplane_options nopts = {
|
||||
.y = 1, .x = 2, .rows = ncplane_dim_y(n_) - 2, .cols = ncplane_dim_x(n_) - 4,
|
||||
.userptr = nullptr, .name = nullptr, .resizecb = nullptr, .flags = 0
|
||||
};
|
||||
auto ncp = ncplane_create(n_, &nopts);
|
||||
auto nt = nctabbed_create(ncp, nullptr);
|
||||
REQUIRE(nullptr != nt);
|
||||
CHECK(0 == nctabbed_tabcount(nt));
|
||||
auto t1 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "amazingtab123", nullptr);
|
||||
CHECK(nullptr != t1);
|
||||
CHECK(t1 == nctabbed_selected(nt));
|
||||
CHECK(t1 == nctabbed_leftmost(nt));
|
||||
CHECK(1 == nctabbed_tabcount(nt));
|
||||
CHECK(t1 == nctab_next(t1));
|
||||
CHECK(t1 == nctab_prev(t1));
|
||||
CHECK(nullptr == nctab_userptr(t1));
|
||||
CHECK(0 == strcmp("amazingtab123", nctab_name(t1)));
|
||||
auto t2 = nctabbed_add(nt, nullptr, nullptr, nullptr, "nullcbtab", nullptr);
|
||||
CHECK(nullptr != t2);
|
||||
CHECK(t1 == nctabbed_selected(nt));
|
||||
CHECK(t1 == nctabbed_leftmost(nt));
|
||||
CHECK(2 == nctabbed_tabcount(nt));
|
||||
CHECK(t2 == nctab_next(t1));
|
||||
CHECK(t2 == nctab_prev(t1));
|
||||
CHECK(t1 == nctab_next(t2));
|
||||
CHECK(t1 == nctab_prev(t2));
|
||||
// this one gets put in the middle
|
||||
auto t3 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab3", nullptr);
|
||||
CHECK(nullptr != t3);
|
||||
CHECK(3 == nctabbed_tabcount(nt));
|
||||
CHECK(t2 == nctab_next(t3));
|
||||
CHECK(t1 == nctab_prev(t3));
|
||||
// this one at the end
|
||||
auto t4 = nctabbed_add(nt, t2, t1, tabbedcb, "tab4", nullptr);
|
||||
CHECK(nullptr != t4);
|
||||
CHECK(4 == nctabbed_tabcount(nt));
|
||||
CHECK(t1 == nctab_next(t4));
|
||||
CHECK(t2 == nctab_prev(t4));
|
||||
// second to last
|
||||
auto t5 = nctabbed_add(nt, nullptr, t4, tabbedcb, "tab5", nullptr);
|
||||
CHECK(nullptr != t5);
|
||||
CHECK(t4 == nctab_next(t5));
|
||||
CHECK(t2 == nctab_prev(t5));
|
||||
// second
|
||||
auto t6 = nctabbed_add(nt, t1, nullptr, tabbedcb, "tab6", nullptr);
|
||||
CHECK(nullptr != t6);
|
||||
CHECK(t3 == nctab_next(t6));
|
||||
CHECK(t1 == nctab_prev(t6));
|
||||
nctabbed_destroy(nt);
|
||||
}
|
||||
|
||||
SUBCASE("Del") {
|
||||
struct ncplane_options nopts = {
|
||||
.y = 1, .x = 2, .rows = ncplane_dim_y(n_) - 2, .cols = ncplane_dim_x(n_) - 4,
|
||||
.userptr = nullptr, .name = nullptr, .resizecb = nullptr, .flags = 0
|
||||
};
|
||||
auto ncp = ncplane_create(n_, &nopts);
|
||||
auto nt = nctabbed_create(ncp, nullptr);
|
||||
REQUIRE(nullptr != nt);
|
||||
auto t1 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab1", nullptr);
|
||||
auto t5 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab5", nullptr);
|
||||
auto t4 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab4", nullptr);
|
||||
auto t3 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab3", nullptr);
|
||||
auto t2 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab2", nullptr);
|
||||
CHECK(5 == nctabbed_tabcount(nt));
|
||||
// tabs: t1 t2 t3 t4 t5
|
||||
CHECK(0 == nctabbed_del(nt, t5));
|
||||
CHECK(4 == nctabbed_tabcount(nt));
|
||||
CHECK(t1 == nctab_next(t4));
|
||||
CHECK(t4 == nctab_prev(t1));
|
||||
// tabs: t1 t2 t3 t4
|
||||
CHECK(0 == nctabbed_del(nt, t1));
|
||||
CHECK(3 == nctabbed_tabcount(nt));
|
||||
CHECK(t2 == nctab_next(t4));
|
||||
CHECK(t4 == nctab_prev(t2));
|
||||
CHECK(t2 == nctabbed_selected(nt));
|
||||
CHECK(t2 == nctabbed_leftmost(nt));
|
||||
// tabs: t2 t3 t4
|
||||
CHECK(0 == nctabbed_del(nt, t3));
|
||||
CHECK(2 == nctabbed_tabcount(nt));
|
||||
CHECK(t2 == nctab_next(t4));
|
||||
CHECK(t4 == nctab_prev(t2));
|
||||
// tabs: t2 t4
|
||||
CHECK(0 == nctabbed_del(nt, t2));
|
||||
CHECK(1 == nctabbed_tabcount(nt));
|
||||
CHECK(t4 == nctab_next(t4));
|
||||
CHECK(t4 == nctab_prev(t4));
|
||||
CHECK(t4 == nctabbed_selected(nt));
|
||||
CHECK(t4 == nctabbed_leftmost(nt));
|
||||
// only t4 left
|
||||
CHECK(0 == nctabbed_del(nt, t4));
|
||||
CHECK(0 == nctabbed_tabcount(nt));
|
||||
CHECK(nullptr == nctabbed_selected(nt));
|
||||
CHECK(nullptr == nctabbed_leftmost(nt));
|
||||
nctabbed_destroy(nt);
|
||||
}
|
||||
|
||||
SUBCASE("Move") {
|
||||
struct ncplane_options nopts = {
|
||||
.y = 1, .x = 2, .rows = ncplane_dim_y(n_) - 2, .cols = ncplane_dim_x(n_) - 4,
|
||||
.userptr = nullptr, .name = nullptr, .resizecb = nullptr, .flags = 0
|
||||
};
|
||||
auto ncp = ncplane_create(n_, &nopts);
|
||||
auto nt = nctabbed_create(ncp, nullptr);
|
||||
REQUIRE(nullptr != nt);
|
||||
auto t1 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab1", nullptr);
|
||||
auto t5 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab5", nullptr);
|
||||
auto t4 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab4", nullptr);
|
||||
auto t3 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab3", nullptr);
|
||||
auto t2 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab2", nullptr);
|
||||
// tabs: t1 t2 t3 t4 t5
|
||||
nctab_move(nt, t4, t5, nullptr);
|
||||
CHECK(t1 == nctab_next(t4));
|
||||
CHECK(t5 == nctab_prev(t4));
|
||||
CHECK(t2 == nctab_next(t1));
|
||||
CHECK(t4 == nctab_prev(t1));
|
||||
CHECK(t4 == nctab_next(t5));
|
||||
CHECK(t3 == nctab_prev(t5));
|
||||
// tabs: t1 t2 t3 t5 t4
|
||||
nctab_move(nt, t1, t2, nullptr);
|
||||
CHECK(t3 == nctab_next(t1));
|
||||
CHECK(t2 == nctab_prev(t1));
|
||||
CHECK(t1 == nctab_next(t2));
|
||||
CHECK(t4 == nctab_prev(t2));
|
||||
CHECK(t5 == nctab_next(t3));
|
||||
CHECK(t1 == nctab_prev(t3));
|
||||
CHECK(t1 == nctabbed_selected(nt));
|
||||
CHECK(t1 == nctabbed_leftmost(nt));
|
||||
// tabs: t1 t3 t5 t4 t2
|
||||
nctab_move(nt, t2, nullptr, t3);
|
||||
CHECK(t2 == nctab_next(t1));
|
||||
CHECK(t4 == nctab_prev(t1));
|
||||
CHECK(t3 == nctab_next(t2));
|
||||
CHECK(t1 == nctab_prev(t2));
|
||||
CHECK(t5 == nctab_next(t3));
|
||||
CHECK(t2 == nctab_prev(t3));
|
||||
// tabs: t1 t2 t3 t5 t4
|
||||
nctab_move(nt, t1, nullptr, t4);
|
||||
CHECK(t1 == nctab_next(t5));
|
||||
CHECK(t3 == nctab_prev(t5));
|
||||
CHECK(t4 == nctab_next(t1));
|
||||
CHECK(t5 == nctab_prev(t1));
|
||||
CHECK(t2 == nctab_next(t4));
|
||||
CHECK(t1 == nctab_prev(t4));
|
||||
CHECK(t1 == nctabbed_selected(nt));
|
||||
CHECK(t1 == nctabbed_leftmost(nt));
|
||||
// tabs: t1 t4 t2 t3 t5
|
||||
nctabbed_destroy(nt);
|
||||
}
|
||||
|
||||
SUBCASE("Rotate") {
|
||||
struct ncplane_options nopts = {
|
||||
.y = 1, .x = 2, .rows = ncplane_dim_y(n_) - 2, .cols = ncplane_dim_x(n_) - 4,
|
||||
.userptr = nullptr, .name = nullptr, .resizecb = nullptr, .flags = 0
|
||||
};
|
||||
auto ncp = ncplane_create(n_, &nopts);
|
||||
auto nt = nctabbed_create(ncp, nullptr);
|
||||
REQUIRE(nullptr != nt);
|
||||
auto t1 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab1", nullptr);
|
||||
auto t5 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab5", nullptr);
|
||||
auto t4 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab4", nullptr);
|
||||
auto t3 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab3", nullptr);
|
||||
auto t2 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab2", nullptr);
|
||||
// tabs: t1 t2 t3 t4 t5
|
||||
nctabbed_rotate(nt, 1);
|
||||
CHECK(t5 == nctabbed_leftmost(nt));
|
||||
CHECK(t1 == nctabbed_selected(nt));
|
||||
// tabs: t5 t1 t2 t3 t4
|
||||
nctabbed_rotate(nt, 7);
|
||||
CHECK(t3 == nctabbed_leftmost(nt));
|
||||
CHECK(t1 == nctabbed_selected(nt));
|
||||
// tabs: t3 t4 t5 t1 t2
|
||||
nctabbed_rotate(nt, -1);
|
||||
CHECK(t4 == nctabbed_leftmost(nt));
|
||||
CHECK(t1 == nctabbed_selected(nt));
|
||||
// tabs: t4 t5 t1 t2 t3
|
||||
nctabbed_rotate(nt, -13);
|
||||
CHECK(t2 == nctabbed_leftmost(nt));
|
||||
CHECK(t1 == nctabbed_selected(nt));
|
||||
// tabs: t2 t3 t4 t5 t1
|
||||
nctabbed_destroy(nt);
|
||||
}
|
||||
|
||||
SUBCASE("MoveLeftRight") {
|
||||
struct ncplane_options nopts = {
|
||||
.y = 1, .x = 2, .rows = ncplane_dim_y(n_) - 2, .cols = ncplane_dim_x(n_) - 4,
|
||||
.userptr = nullptr, .name = nullptr, .resizecb = nullptr, .flags = 0
|
||||
};
|
||||
auto ncp = ncplane_create(n_, &nopts);
|
||||
auto nt = nctabbed_create(ncp, nullptr);
|
||||
REQUIRE(nullptr != nt);
|
||||
auto t1 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab1", nullptr);
|
||||
auto t5 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab5", nullptr);
|
||||
auto t4 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab4", nullptr);
|
||||
auto t3 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab3", nullptr);
|
||||
auto t2 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab2", nullptr);
|
||||
// tabs: t1 t2 t3 t4 t5
|
||||
nctab_move_right(nt, t1);
|
||||
CHECK(t3 == nctab_next(t1));
|
||||
CHECK(t2 == nctab_prev(t1));
|
||||
CHECK(t2 == nctabbed_leftmost(nt));
|
||||
CHECK(t1 == nctabbed_selected(nt));
|
||||
// tabs: t2 t1 t3 t4 t5
|
||||
nctab_move_right(nt, t1);
|
||||
CHECK(t4 == nctab_next(t1));
|
||||
CHECK(t3 == nctab_prev(t1));
|
||||
CHECK(t2 == nctabbed_leftmost(nt));
|
||||
CHECK(t1 == nctabbed_selected(nt));
|
||||
// tabs: t2 t3 t1 t4 t5
|
||||
nctab_move_right(nt, t5);
|
||||
CHECK(t2 == nctab_next(t5));
|
||||
CHECK(t4 == nctab_prev(t5));
|
||||
CHECK(t5 == nctabbed_leftmost(nt));
|
||||
CHECK(t1 == nctabbed_selected(nt));
|
||||
// tabs: t5 t2 t3 t1 t4
|
||||
nctab_move_right(nt, t5);
|
||||
CHECK(t3 == nctab_next(t5));
|
||||
CHECK(t2 == nctab_prev(t5));
|
||||
CHECK(t2 == nctabbed_leftmost(nt));
|
||||
CHECK(t1 == nctabbed_selected(nt));
|
||||
// tabs: t2 t5 t3 t1 t4
|
||||
nctab_move_left(nt, t5);
|
||||
CHECK(t2 == nctab_next(t5));
|
||||
CHECK(t4 == nctab_prev(t5));
|
||||
CHECK(t5 == nctabbed_leftmost(nt));
|
||||
CHECK(t1 == nctabbed_selected(nt));
|
||||
// tabs: t5 t2 t3 t1 t4
|
||||
nctab_move_left(nt, t5);
|
||||
CHECK(t2 == nctab_next(t5));
|
||||
CHECK(t4 == nctab_prev(t5));
|
||||
CHECK(t2 == nctabbed_leftmost(nt));
|
||||
CHECK(t1 == nctabbed_selected(nt));
|
||||
// tabs: t2 t3 t1 t4 t5
|
||||
nctab_move_left(nt, t5);
|
||||
CHECK(t4 == nctab_next(t5));
|
||||
CHECK(t1 == nctab_prev(t5));
|
||||
CHECK(t2 == nctabbed_leftmost(nt));
|
||||
CHECK(t1 == nctabbed_selected(nt));
|
||||
// tabs: t2 t3 t1 t5 t4
|
||||
nctab_move_left(nt, t5);
|
||||
CHECK(t1 == nctab_next(t5));
|
||||
CHECK(t3 == nctab_prev(t5));
|
||||
CHECK(t2 == nctabbed_leftmost(nt));
|
||||
CHECK(t1 == nctabbed_selected(nt));
|
||||
// tabs: t2 t3 t5 t1 t4
|
||||
nctabbed_destroy(nt);
|
||||
}
|
||||
|
||||
SUBCASE("Select") {
|
||||
struct ncplane_options nopts = {
|
||||
.y = 1, .x = 2, .rows = ncplane_dim_y(n_) - 2, .cols = ncplane_dim_x(n_) - 4,
|
||||
.userptr = nullptr, .name = nullptr, .resizecb = nullptr, .flags = 0
|
||||
};
|
||||
auto ncp = ncplane_create(n_, &nopts);
|
||||
auto nt = nctabbed_create(ncp, nullptr);
|
||||
REQUIRE(nullptr != nt);
|
||||
auto t1 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab1", nullptr);
|
||||
auto t5 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab5", nullptr);
|
||||
auto t4 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab4", nullptr);
|
||||
auto t3 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab3", nullptr);
|
||||
auto t2 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab2", nullptr);
|
||||
(void)t3;
|
||||
(void)t4;
|
||||
CHECK(t1 == nctabbed_selected(nt));
|
||||
nctabbed_select(nt, t2);
|
||||
CHECK(t2 == nctabbed_selected(nt));
|
||||
nctabbed_select(nt, t5);
|
||||
CHECK(t5 == nctabbed_selected(nt));
|
||||
nctabbed_select(nt, t5);
|
||||
CHECK(t5 == nctabbed_selected(nt));
|
||||
nctabbed_select(nt, t1);
|
||||
CHECK(t1 == nctabbed_selected(nt));
|
||||
nctabbed_destroy(nt);
|
||||
}
|
||||
|
||||
SUBCASE("NextPrev") {
|
||||
struct ncplane_options nopts = {
|
||||
.y = 1, .x = 2, .rows = ncplane_dim_y(n_) - 2, .cols = ncplane_dim_x(n_) - 4,
|
||||
.userptr = nullptr, .name = nullptr, .resizecb = nullptr, .flags = 0
|
||||
};
|
||||
auto ncp = ncplane_create(n_, &nopts);
|
||||
auto nt = nctabbed_create(ncp, nullptr);
|
||||
REQUIRE(nullptr != nt);
|
||||
auto t1 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab1", nullptr);
|
||||
auto t5 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab5", nullptr);
|
||||
auto t4 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab4", nullptr);
|
||||
auto t3 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab3", nullptr);
|
||||
auto t2 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab2", nullptr);
|
||||
CHECK(t1 == nctabbed_selected(nt));
|
||||
CHECK(t2 == nctabbed_next(nt));
|
||||
CHECK(t2 == nctabbed_selected(nt));
|
||||
CHECK(t1 == nctabbed_leftmost(nt));
|
||||
CHECK(t3 == nctabbed_next(nt));
|
||||
CHECK(t3 == nctabbed_selected(nt));
|
||||
CHECK(t1 == nctabbed_leftmost(nt));
|
||||
CHECK(t4 == nctabbed_next(nt));
|
||||
CHECK(t4 == nctabbed_selected(nt));
|
||||
CHECK(t1 == nctabbed_leftmost(nt));
|
||||
CHECK(t5 == nctabbed_next(nt));
|
||||
CHECK(t5 == nctabbed_selected(nt));
|
||||
CHECK(t1 == nctabbed_leftmost(nt));
|
||||
CHECK(t1 == nctabbed_next(nt));
|
||||
CHECK(t1 == nctabbed_selected(nt));
|
||||
CHECK(t1 == nctabbed_leftmost(nt));
|
||||
nctabbed_destroy(nt);
|
||||
}
|
||||
|
||||
SUBCASE("Setters") {
|
||||
struct ncplane_options nopts = {
|
||||
.y = 1, .x = 2, .rows = ncplane_dim_y(n_) - 2, .cols = ncplane_dim_x(n_) - 4,
|
||||
.userptr = nullptr, .name = nullptr, .resizecb = nullptr, .flags = 0
|
||||
};
|
||||
auto ncp = ncplane_create(n_, &nopts);
|
||||
auto nt = nctabbed_create(ncp, nullptr);
|
||||
REQUIRE(nullptr != nt);
|
||||
uint64_t hdrchan = CHANNELS_RGB_INITIALIZER(255, 127, 63, 31, 15, 7);
|
||||
uint64_t selchan = CHANNELS_RGB_INITIALIZER(127, 63, 31, 15, 7, 3);
|
||||
uint64_t sepchan = CHANNELS_RGB_INITIALIZER(63, 31, 15, 7, 3, 1);
|
||||
nctabbed_set_hdrchan(nt, hdrchan);
|
||||
nctabbed_set_selchan(nt, selchan);
|
||||
nctabbed_set_sepchan(nt, sepchan);
|
||||
uint64_t hdrchan2, selchan2, sepchan2;
|
||||
nctabbed_channels(nt, &hdrchan2, &selchan2, &sepchan2);
|
||||
CHECK(hdrchan == hdrchan2);
|
||||
CHECK(selchan == selchan2);
|
||||
CHECK(sepchan == sepchan2);
|
||||
const char* sep = "separateur";
|
||||
nctabbed_set_separator(nt, sep);
|
||||
CHECK(0 == strcmp(sep, nctabbed_separator(nt)));
|
||||
auto t1 = nctabbed_add(nt, nullptr, nullptr, tabbedcb, "tab1", nullptr);
|
||||
nctab_set_cb(t1, tabbedcb);
|
||||
CHECK(tabbedcb == nctab_cb(t1));
|
||||
nctab_set_userptr(t1, (void*) sep);
|
||||
CHECK((void*) sep == nctab_userptr(t1));
|
||||
const char* tname = "tab name";
|
||||
nctab_set_name(t1, tname);
|
||||
CHECK(0 == strcmp(tname, nctab_name(t1)));
|
||||
nctabbed_destroy(nt);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user