mirror of
https://github.com/dankamongmen/notcurses
synced 2025-03-09 17:19:03 -04:00
First go at ncplane_puttext() (#690)
Very simple take at ncplane_puttext(), a new function for linebroken text. Also some very basic unit tests. I doubt this works very well yet, but it handles the simplest cases #682. Added nclog(), internal function for logging. #520
This commit is contained in:
parent
7736e4a7dc
commit
28431914a8
34
.drone.yml
34
.drone.yml
@ -25,22 +25,22 @@ steps:
|
||||
- export LANG=en_US.UTF-8
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake .. -DCMAKE_BUILD_TYPE=Release
|
||||
- cmake -DCMAKE_BUILD_TYPE=Release -DUSE_MULTIMEDIA=none ..
|
||||
- make
|
||||
- ./notcurses-tester -p ../data
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: fedora-rawhide
|
||||
|
||||
steps:
|
||||
- name: fedora-rawhide
|
||||
image: dankamongmen/fedora33:2020-06-08a
|
||||
commands:
|
||||
- export LANG=en_US.UTF-8
|
||||
- dnf install -y doctest-devel
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake -DCMAKE_BUILD_TYPE=Release -DUSE_MULTIMEDIA=oiio ..
|
||||
- make
|
||||
- ./notcurses-tester -p ../data
|
||||
#---
|
||||
#kind: pipeline
|
||||
#type: docker
|
||||
#name: fedora-rawhide
|
||||
#
|
||||
#steps:
|
||||
#- name: fedora-rawhide
|
||||
# image: dankamongmen/fedora33:2020-05-29a
|
||||
# commands:
|
||||
# - export LANG=en_US.UTF-8
|
||||
# - mkdir build
|
||||
# - cd build
|
||||
# - ls
|
||||
# - cmake -DCMAKE_BUILD_TYPE=Release -DUSE_MULTIMEDIA=oiio ..
|
||||
# - make
|
||||
# - ./notcurses-tester -p ../data
|
||||
|
3
NEWS.md
3
NEWS.md
@ -6,6 +6,9 @@ rearrangements of Notcurses.
|
||||
that `struct`'s `flags` field. Each `bool` has its own `NCOPTION_`.
|
||||
* Added a Pixel API for working directly with the contents of `ncvisual`s,
|
||||
including `ncvisual_at_yx()` and `ncvisual_set_yx()`.
|
||||
* Added `ncplane_puttext()` for writing multiline, line-broken text.
|
||||
* Added `ncplane_putnstr()`, `ncplane_putnstr_yx()`, and
|
||||
`ncplane_putnstr_aligned()` for byte-limited output of UTF-8.
|
||||
|
||||
* 1.4.5 (2020-06-04)
|
||||
* `ncblit_rgba()` and `ncblit_bgrx()` have replaced most of their arguments
|
||||
|
41
USAGE.md
41
USAGE.md
@ -902,12 +902,11 @@ int ncplane_putwegc_stainable(struct ncplane* n, const wchar_t* gclust, int* sby
|
||||
|
||||
// Write a series of EGCs to the current location, using the current style.
|
||||
// They will be interpreted as a series of columns (according to the definition
|
||||
// of ncplane_putc()). Advances the cursor by some positive number of cells
|
||||
// of ncplane_putc()). Advances the cursor by some positive number of columns
|
||||
// (though not beyond the end of the plane); this number is returned on success.
|
||||
// On error, a non-positive number is returned, indicating the number of cells
|
||||
// On error, a non-positive number is returned, indicating the number of columns
|
||||
// which were written before the error.
|
||||
static inline int
|
||||
ncplane_putstr_yx(struct ncplane* n, int y, int x, const char* gclusters);
|
||||
int ncplane_putstr_yx(struct ncplane* n, int y, int x, const char* gclusters);
|
||||
|
||||
static inline int
|
||||
ncplane_putstr(struct ncplane* n, const char* gclustarr){
|
||||
@ -935,6 +934,22 @@ ncplane_putwstr_yx(struct ncplane* n, int y, int x, const wchar_t* gclustarr){
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Write a series of EGCs to the current location, using the current style.
|
||||
// They will be interpreted as a series of columns (according to the definition
|
||||
// of ncplane_putc()). Advances the cursor by some positive number of columns
|
||||
// (though not beyond the end of the plane); this number is returned on success.
|
||||
// On error, a non-positive number is returned, indicating the number of columns
|
||||
// which were written before the error. No more than 's' bytes will be written.
|
||||
int ncplane_putnstr_yx(struct ncplane* n, int y, int x, size_t s, const char* gclusters);
|
||||
|
||||
static inline int
|
||||
ncplane_putnstr(struct ncplane* n, size_t s, const char* gclustarr){
|
||||
return ncplane_putnstr_yx(n, -1, -1, s, gclustarr);
|
||||
}
|
||||
|
||||
int ncplane_putnstr_aligned(struct ncplane* n, int y, ncalign_e align,
|
||||
size_t s, const char* s);
|
||||
|
||||
static inline int
|
||||
ncplane_putwstr_aligned(struct ncplane* n, int y, ncalign_e align,
|
||||
const wchar_t* gclustarr){
|
||||
@ -1001,6 +1016,24 @@ ncplane_printf_aligned(struct ncplane* n, int y, ncalign_e align, const char* fo
|
||||
}
|
||||
```
|
||||
|
||||
Multiline chunks of human-readable text can be written with
|
||||
`ncplane_puttext()` even if the plane does not have scrolling enabled. Such
|
||||
text will be broken up across lines using the Unicode line-breaking algorithm
|
||||
of [Unicode Annex #14](http://www.unicode.org/reports/tr14/tr14-34.html).
|
||||
|
||||
```c
|
||||
// Write the specified text to the plane, breaking lines sensibly, beginning at
|
||||
// the specified line. Returns the number of columns written. When breaking a
|
||||
// line, the line will be cleared to the end of the plane (the last line will
|
||||
// *not* be so cleared). The number of bytes written from the input is written
|
||||
// to '*bytes' if it is not NULL. Cleared columns are included in the return
|
||||
// value, but *not* included in the number of bytes written. Leaves the cursor
|
||||
// at the end of output. A partial write will be accomplished as far as it can;
|
||||
// determine whether the write completed by inspecting '*bytes'.
|
||||
int ncplane_puttext(struct ncplane* n, int y, ncalign_e align,
|
||||
const char* text, size_t* bytes);
|
||||
```
|
||||
|
||||
Lines and boxes can be drawn, interpolating their colors between their two
|
||||
endpoints. For a line of a single color, be sure to specify the same channels
|
||||
on both sides. Boxes allow fairly detailed specification of how they're drawn.
|
||||
|
@ -10,93 +10,83 @@ notcurses_output - output to ncplanes
|
||||
|
||||
**#include <notcurses/notcurses.h>**
|
||||
|
||||
**static inline int
|
||||
ncplane_putc(struct ncplane* n, const cell* c);**
|
||||
**static inline int ncplane_putc(struct ncplane* n, const cell* c);**
|
||||
|
||||
**int ncplane_putc_yx(struct ncplane* n, int y, int x, const cell* c);**
|
||||
|
||||
**static inline int
|
||||
ncplane_putsimple(struct ncplane* n, char c);**
|
||||
**static inline int ncplane_putsimple(struct ncplane* n, char c);**
|
||||
|
||||
**static inline int
|
||||
ncplane_putsimple_yx(struct ncplane* n, int y, int x, char c);**
|
||||
**static inline int ncplane_putsimple_yx(struct ncplane* n, int y, int x, char c);**
|
||||
|
||||
**int ncplane_putsimple_stainable(struct ncplane* n, char c);**
|
||||
|
||||
**static inline int
|
||||
ncplane_putwc(struct ncplane* n, wchar_t w);**
|
||||
**static inline int ncplane_putwc(struct ncplane* n, wchar_t w);**
|
||||
|
||||
**int ncplane_putwc_yx(struct ncplane* n, int y, int x, wchar_t w);**
|
||||
|
||||
**static inline int
|
||||
ncplane_putegc(struct ncplane* n, const char* gclust, int* sbytes);**
|
||||
**static inline int ncplane_putegc(struct ncplane* n, const char* gclust, int* sbytes);**
|
||||
|
||||
**int ncplane_putegc_yx(struct ncplane* n, int y, int x, const char* gclust, int* sbytes);**
|
||||
|
||||
**int ncplane_putegc_stainable(struct ncplane* n, const char* gclust, int* sbytes);**
|
||||
|
||||
**static inline int
|
||||
ncplane_putwegc(struct ncplane* n, const wchar_t* gclust, int* sbytes);**
|
||||
**static inline int ncplane_putwegc(struct ncplane* n, const wchar_t* gclust, int* sbytes);**
|
||||
|
||||
**static inline int
|
||||
ncplane_putwegc_yx(struct ncplane* n, int y, int x, const wchar_t* gclust, int* sbytes);**
|
||||
**static inline int ncplane_putwegc_yx(struct ncplane* n, int y, int x, const wchar_t* gclust, int* sbytes);**
|
||||
|
||||
**int ncplane_putwegc_stainable(struct ncplane* n, const wchar_t* gclust, int* sbytes);**
|
||||
|
||||
**static inline int
|
||||
ncplane_putstr_yx(struct ncplane* n, int y, int x, const char* gclustarr);**
|
||||
**int ncplane_putstr_yx(struct ncplane* n, int y, int x, const char* gclustarr);**
|
||||
|
||||
**static inline int
|
||||
ncplane_putstr(struct ncplane* n, const char* gclustarr);**
|
||||
**static inline int ncplane_putstr(struct ncplane* n, const char* gclustarr);**
|
||||
|
||||
**int ncplane_putstr_aligned(struct ncplane* n, int y, ncalign_e align,
|
||||
const char* s);**
|
||||
**int ncplane_putstr_aligned(struct ncplane* n, int y, ncalign_e align, const char* s);**
|
||||
|
||||
**int ncplane_putwstr_yx(struct ncplane* n, int y, int x, const wchar_t* gclustarr);**
|
||||
|
||||
**static inline int
|
||||
ncplane_putwstr_aligned(struct ncplane* n, int y, ncalign_e align,
|
||||
const wchar_t* gclustarr);**
|
||||
**static inline int ncplane_putwstr_aligned(struct ncplane* n, int y, ncalign_e align, const wchar_t* gclustarr);**
|
||||
|
||||
**static inline int
|
||||
ncplane_putwstr(struct ncplane* n, const wchar_t* gclustarr);**
|
||||
**static inline int ncplane_putwstr(struct ncplane* n, const wchar_t* gclustarr);**
|
||||
|
||||
**int ncplane_vprintf_aligned(struct ncplane* n, int y, ncalign_e align,
|
||||
const char* format, va_list ap);**
|
||||
**int ncplane_putnstr_yx(struct ncplane* n, int y, int x, size_t s, const char* gclusters);**
|
||||
|
||||
**int ncplane_vprintf_yx(struct ncplane* n, int y, int x,
|
||||
const char* format, va_list ap);**
|
||||
**static inline int ncplane_putnstr(struct ncplane* n, size_t s, const char* gclustarr);**
|
||||
|
||||
**static inline int
|
||||
ncplane_vprintf(struct ncplane* n, const char* format, va_list ap);**
|
||||
**int ncplane_putnstr_aligned(struct ncplane* n, int y, ncalign_e align, size_t s, const char* s);**
|
||||
|
||||
**static inline int
|
||||
ncplane_printf(struct ncplane* n, const char* format, ...);**
|
||||
**int ncplane_vprintf_aligned(struct ncplane* n, int y, ncalign_e align, const char* format, va_list ap);**
|
||||
|
||||
**static inline int
|
||||
ncplane_printf_yx(struct ncplane* n, int y, int x, const char* format, ...);**
|
||||
**int ncplane_vprintf_yx(struct ncplane* n, int y, int x, const char* format, va_list ap);**
|
||||
|
||||
**static inline int
|
||||
ncplane_printf_aligned(struct ncplane* n, int y, ncalign_e align, const char* format, ...);**
|
||||
**static inline int ncplane_vprintf(struct ncplane* n, const char* format, va_list ap);**
|
||||
|
||||
**static inline int ncplane_printf(struct ncplane* n, const char* format, ...);**
|
||||
|
||||
**static inline int ncplane_printf_yx(struct ncplane* n, int y, int x, const char* format, ...);**
|
||||
|
||||
**static inline int ncplane_printf_aligned(struct ncplane* n, int y, ncalign_e align, const char* format, ...);**
|
||||
|
||||
**int ncplane_cursor_move_yx(struct ncplane* n, int y, int x);**
|
||||
|
||||
**int ncplane_puttext(struct ncplane* n, int y, ncalign_e align, const char* text, size_t* bytes);**
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
These functions write EGCs (Extended Grapheme Clusters) to the specified
|
||||
**struct ncplane**s. The following inputs are supported:
|
||||
|
||||
* **ncplane_putc(3)**: writes a single cell (see **notcurses_cell(3)**)
|
||||
* **ncplane_put_simple(3)**: writes a single 7-bit ASCII character
|
||||
* **ncplane_putwc(3)**: writes a single **wchar_t** (following UTF-8 conversion)
|
||||
* **ncplane_putwegc(3)**: writes a single EGC from an array of **wchar_t**
|
||||
* **ncplane_putegc(3)**: writes a single EGC from an array of UTF-8
|
||||
* **ncplane_putstr(3)**: writes a set of EGCs from an array of UTF-8
|
||||
* **ncplane_putwstr(3)**: writes a set of EGCs from an array of **wchar_t**
|
||||
* **ncplane_vprintf(3)**: formatted output using **va_list**
|
||||
* **ncplane_printf(3)**: formatted output using variadic arguments
|
||||
* **ncplane_putc()**: writes a single cell (see **notcurses_cell(3)**)
|
||||
* **ncplane_put_simple()**: writes a single 7-bit ASCII character
|
||||
* **ncplane_putwc()**: writes a single **wchar_t** (following UTF-8 conversion)
|
||||
* **ncplane_putwegc()**: writes a single EGC from an array of **wchar_t**
|
||||
* **ncplane_putegc()**: writes a single EGC from an array of UTF-8
|
||||
* **ncplane_putstr()**: writes a set of EGCs from an array of UTF-8
|
||||
* **ncplane_putwstr()**: writes a set of EGCs from an array of **wchar_t**
|
||||
* **ncplane_vprintf()**: formatted output using **va_list**
|
||||
* **ncplane_printf()**: formatted output using variadic arguments
|
||||
* **ncplane_puttext()**: multi-line, line-broken, aligned text
|
||||
|
||||
All of these use the **ncplane**'s active styling, save **notcurses_putc(3)**,
|
||||
All of these use the **ncplane**'s active styling, save **notcurses_putc()**,
|
||||
which uses the cell's styling. Functions accepting a single EGC expect a series
|
||||
of **wchar_t** terminated by **L'\0'** or a series of UTF-8 **char** terminated
|
||||
by **'\0'**. The EGC must be well-formed, and must not contain any cluster
|
||||
@ -104,10 +94,10 @@ breaks. For more information, consult [Unicode® Standard Annex #29](https://uni
|
||||
Functions accepting a set of EGCs must consist of a series of well-formed EGCs,
|
||||
broken by cluster breaks, terminated by the appropriate NUL terminator.
|
||||
|
||||
These functions output to the `ncplane`'s current cursor location. They *do not*
|
||||
move to the next line upon reaching the right extreme of the containing plane.
|
||||
If the entirety of the content cannot be output, they will output as much as
|
||||
possible.
|
||||
These functions output to the `ncplane`'s current cursor location. Aside from
|
||||
**ncplane_puttext()**, they *do not* move to the next line upon reaching the
|
||||
right extreme of the containing plane. If the entirety of the content cannot be
|
||||
output, they will output as much as possible.
|
||||
|
||||
Each of these base functions has two additional forms:
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
* Review the testing checklist (doc/testing-checklist.md)
|
||||
* clang-tidy check with something like:
|
||||
* `cmake "-DCMAKE_CXX_CLANG_TIDY=/usr/bin/clang-tidy-11;-checks=-*,clang-analyzer-*,modernize-*,performance-*" ..`
|
||||
* `scan-build cmake .. && scan-build make`
|
||||
|
22
doc/testing-checklist.md
Normal file
22
doc/testing-checklist.md
Normal file
@ -0,0 +1,22 @@
|
||||
# Release testing
|
||||
|
||||
## Unit tests
|
||||
|
||||
Run unit tests (`make && make test`):
|
||||
* In each multimedia configuration (`ffmpeg`, `oiio`, `none`)
|
||||
* With LANG set to `fr_FR.UTF-8` (to test comma as decimal separator)
|
||||
* All must pass
|
||||
|
||||
## Manual tests
|
||||
|
||||
Run, using `valgrind --tool=memcheck --leak-check=full`:
|
||||
* `notcurses-demo` in each of the three multimedia configurations
|
||||
* `notcurses-demo` with `USE_QRCODEGEN=off`
|
||||
* `notcurses-ncreel`
|
||||
* `notcurses-input`
|
||||
* `notcurses-view` with each scaling mode and an image + video, in three
|
||||
terminal geometries: square, tall, wide
|
||||
* `notcurses-demo` with margins
|
||||
* `notcurses-demo` with FPS plot and HUD up
|
||||
* Play a game of `notcurses-tetris`
|
||||
* Run each PoC binary, including `ncpp_build` and `ncpp_build_exceptions`
|
@ -1438,32 +1438,11 @@ API int ncplane_putwegc_stainable(struct ncplane* n, const wchar_t* gclust, int*
|
||||
|
||||
// Write a series of EGCs to the current location, using the current style.
|
||||
// They will be interpreted as a series of columns (according to the definition
|
||||
// of ncplane_putc()). Advances the cursor by some positive number of cells
|
||||
// of ncplane_putc()). Advances the cursor by some positive number of columns
|
||||
// (though not beyond the end of the plane); this number is returned on success.
|
||||
// On error, a non-positive number is returned, indicating the number of cells
|
||||
// On error, a non-positive number is returned, indicating the number of columns
|
||||
// which were written before the error.
|
||||
static inline int
|
||||
ncplane_putstr_yx(struct ncplane* n, int y, int x, const char* gclusters){
|
||||
int ret = 0;
|
||||
// FIXME speed up this blissfully naive solution
|
||||
while(*gclusters){
|
||||
int wcs;
|
||||
int cols = ncplane_putegc_yx(n, y, x, gclusters, &wcs);
|
||||
if(cols < 0){
|
||||
return -ret;
|
||||
}
|
||||
if(wcs == 0){
|
||||
break;
|
||||
}
|
||||
// after the first iteration, just let the cursor code control where we
|
||||
// print, so that scrolling is taken into account
|
||||
y = -1;
|
||||
x = -1;
|
||||
gclusters += wcs;
|
||||
ret += wcs;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
API int ncplane_putstr_yx(struct ncplane* n, int y, int x, const char* gclusters);
|
||||
|
||||
static inline int
|
||||
ncplane_putstr(struct ncplane* n, const char* gclustarr){
|
||||
@ -1473,6 +1452,22 @@ ncplane_putstr(struct ncplane* n, const char* gclustarr){
|
||||
API int ncplane_putstr_aligned(struct ncplane* n, int y, ncalign_e align,
|
||||
const char* s);
|
||||
|
||||
// Write a series of EGCs to the current location, using the current style.
|
||||
// They will be interpreted as a series of columns (according to the definition
|
||||
// of ncplane_putc()). Advances the cursor by some positive number of columns
|
||||
// (though not beyond the end of the plane); this number is returned on success.
|
||||
// On error, a non-positive number is returned, indicating the number of columns
|
||||
// which were written before the error. No more than 's' bytes will be written.
|
||||
API int ncplane_putnstr_yx(struct ncplane* n, int y, int x, size_t s, const char* gclusters);
|
||||
|
||||
static inline int
|
||||
ncplane_putnstr(struct ncplane* n, size_t s, const char* gclustarr){
|
||||
return ncplane_putnstr_yx(n, -1, -1, s, gclustarr);
|
||||
}
|
||||
|
||||
API int ncplane_putnstr_aligned(struct ncplane* n, int y, ncalign_e align,
|
||||
size_t s, const char* gclustarr);
|
||||
|
||||
// ncplane_putstr(), but following a conversion from wchar_t to UTF-8 multibyte.
|
||||
static inline int
|
||||
ncplane_putwstr_yx(struct ncplane* n, int y, int x, const wchar_t* gclustarr){
|
||||
@ -1572,6 +1567,17 @@ ncplane_printf_aligned(struct ncplane* n, int y, ncalign_e align, const char* fo
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Write the specified text to the plane, breaking lines sensibly, beginning at
|
||||
// the specified line. Returns the number of columns written. When breaking a
|
||||
// line, the line will be cleared to the end of the plane (the last line will
|
||||
// *not* be so cleared). The number of bytes written from the input is written
|
||||
// to '*bytes' if it is not NULL. Cleared columns are included in the return
|
||||
// value, but *not* included in the number of bytes written. Leaves the cursor
|
||||
// at the end of output. A partial write will be accomplished as far as it can;
|
||||
// determine whether the write completed by inspecting '*bytes'.
|
||||
API int ncplane_puttext(struct ncplane* n, int y, ncalign_e align,
|
||||
const char* text, size_t* bytes);
|
||||
|
||||
// Draw horizontal or vertical lines using the specified cell, starting at the
|
||||
// current cursor position. The cursor will end at the cell following the last
|
||||
// cell output (even, perhaps counter-intuitively, when drawing vertical
|
||||
|
@ -497,6 +497,9 @@ struct ncplane* ncreader_plane(struct ncreader* n);
|
||||
bool ncreader_offer_input(struct ncreader* n, const struct ncinput* ni);
|
||||
char* ncreader_contents(const struct ncreader* n);
|
||||
void ncreader_destroy(struct ncreader* n, char** contents);
|
||||
int ncplane_puttext(struct ncplane* n, int y, ncalign_e align, const char* text, size_t* bytes);
|
||||
int ncplane_putnstr_yx(struct ncplane* n, int y, int x, size_t s, const char* gclusters);
|
||||
int ncplane_putnstr_aligned(struct ncplane* n, int y, ncalign_e align, size_t s, const char* gclustarr);
|
||||
""")
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -315,7 +315,7 @@ typedef struct notcurses {
|
||||
|
||||
// desired margins (best-effort only), copied in from notcurses_options
|
||||
int margin_t, margin_b, margin_r, margin_l;
|
||||
|
||||
int loglevel;
|
||||
palette256 palette; // 256-indexed palette can be used instead of/with RGB
|
||||
bool palette_damage[NCPALETTESIZE];
|
||||
struct esctrie* inputescapes; // trie of input escapes -> ncspecial_keys
|
||||
@ -757,6 +757,11 @@ calc_gradient_channels(uint64_t* channels, uint64_t ul, uint64_t ur,
|
||||
}
|
||||
}
|
||||
|
||||
void nclog(const char* fmt, ...);
|
||||
|
||||
// logging
|
||||
#define logerror(nc, fmt, ...) do{ if(nc->loglevel >= NCLOGLEVEL_ERROR){ nclog(__FILE__ "%d" fmt, __LINE__, ##__VA_ARGS__); } }while(0);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -854,7 +854,8 @@ notcurses* notcurses_init(const notcurses_options* opts, FILE* outfp){
|
||||
term_verify_seq(&ret->tcache.rmcup, "rmcup");
|
||||
}
|
||||
ret->bottom = ret->top = ret->stdscr = NULL;
|
||||
if(ncvisual_init(ffmpeg_log_level(opts->loglevel))){
|
||||
ret->loglevel = opts->loglevel;
|
||||
if(ncvisual_init(ffmpeg_log_level(ret->loglevel))){
|
||||
goto err;
|
||||
}
|
||||
if((ret->stdscr = create_initial_ncplane(ret, dimy, dimx)) == NULL){
|
||||
@ -1477,6 +1478,73 @@ int ncplane_hline_interp(ncplane* n, const cell* c, int len,
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ncplane_puttext(ncplane* n, int y, ncalign_e align, const char* text, size_t* bytes){
|
||||
int totalcols = 0;
|
||||
// save the beginning for diagnostic
|
||||
const char* beginning = text;
|
||||
// text points to the text we have *not* yet output. at each step, we see
|
||||
// how much space we have available, and begin iterating from text. remember
|
||||
// the most recent linebreaker that we see. when we exhaust our line, print
|
||||
// through the linebreaker, and advance text.
|
||||
// FIXME what about a long word? do we want to leave the big gap?
|
||||
const int dimx = ncplane_dim_x(n);
|
||||
const char* linestart = text;
|
||||
int x = 0; // number of columns consumed for this line
|
||||
do{
|
||||
const char* breaker = NULL;
|
||||
// figure how much text to output on this line
|
||||
mbstate_t mbstate = {};
|
||||
int width;
|
||||
// verified columns thus far (carried, and through breaker)
|
||||
size_t verifiedcols = x;
|
||||
while(*text && x < dimx - 1){
|
||||
wchar_t w;
|
||||
size_t consumed = mbrtowc(&w, text, MB_CUR_MAX, &mbstate);
|
||||
if(consumed == (size_t)-2 || consumed == (size_t)-1){
|
||||
logerror(n->nc, "Invalid UTF-8 after %zu bytes\n", text - beginning);
|
||||
*bytes = text - beginning;
|
||||
return -1;
|
||||
}
|
||||
width = wcwidth(w);
|
||||
if(width < 0){
|
||||
logerror(n->nc, "Non-printable UTF-8 after %zu bytes\n", text - beginning);
|
||||
*bytes = text - beginning;
|
||||
return -1;
|
||||
}
|
||||
if(x + width >= dimx){
|
||||
break;
|
||||
}
|
||||
// FIXME use the more advanced unicode functionality to break lines
|
||||
if(iswspace(w)){
|
||||
breaker = text;
|
||||
verifiedcols = x;
|
||||
}
|
||||
x += width;
|
||||
text += consumed;
|
||||
}
|
||||
int carrycols = 0;
|
||||
if(x >= dimx){
|
||||
// the last character was one past the amount we can print. set linestart
|
||||
// back to breaker, set carrycols to the amount since breaker
|
||||
carrycols = x - verifiedcols;
|
||||
}
|
||||
totalcols += verifiedcols;
|
||||
const int xpos = ncplane_align(n, align, x);
|
||||
if(breaker == NULL){
|
||||
breaker = text;
|
||||
}
|
||||
if(ncplane_putnstr_yx(n, y, xpos, breaker - linestart, linestart) < 0){
|
||||
*bytes = linestart - beginning;
|
||||
return -1;
|
||||
}
|
||||
x = carrycols;
|
||||
linestart = breaker + 1;
|
||||
++y; // FIXME scrolling!
|
||||
}while(*text);
|
||||
*bytes = text - beginning;
|
||||
return totalcols;
|
||||
}
|
||||
|
||||
int ncplane_vline_interp(ncplane* n, const cell* c, int len,
|
||||
uint64_t c1, uint64_t c2){
|
||||
unsigned ur, ug, ub;
|
||||
@ -2097,3 +2165,54 @@ void ncplane_center_abs(const ncplane* n, int* RESTRICT y, int* RESTRICT x){
|
||||
*x += n->absx;
|
||||
}
|
||||
}
|
||||
|
||||
void nclog(const char* fmt, ...){
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
vfprintf(stderr, fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
int ncplane_putstr_yx(struct ncplane* n, int y, int x, const char* gclusters){
|
||||
int ret = 0;
|
||||
// FIXME speed up this blissfully naive solution
|
||||
while(*gclusters){
|
||||
int wcs;
|
||||
int cols = ncplane_putegc_yx(n, y, x, gclusters, &wcs);
|
||||
if(cols < 0){
|
||||
return -ret;
|
||||
}
|
||||
if(wcs == 0){
|
||||
break;
|
||||
}
|
||||
// after the first iteration, just let the cursor code control where we
|
||||
// print, so that scrolling is taken into account
|
||||
y = -1;
|
||||
x = -1;
|
||||
gclusters += wcs;
|
||||
ret += wcs;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ncplane_putnstr_yx(struct ncplane* n, int y, int x, size_t s, const char* gclusters){
|
||||
size_t ret = 0;
|
||||
// FIXME speed up this blissfully naive solution
|
||||
while(ret < s && *gclusters){
|
||||
int wcs;
|
||||
int cols = ncplane_putegc_yx(n, y, x, gclusters, &wcs);
|
||||
if(cols < 0){
|
||||
return -ret;
|
||||
}
|
||||
if(wcs == 0){
|
||||
break;
|
||||
}
|
||||
// after the first iteration, just let the cursor code control where we
|
||||
// print, so that scrolling is taken into account
|
||||
y = -1;
|
||||
x = -1;
|
||||
gclusters += wcs;
|
||||
ret += wcs;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -120,7 +120,11 @@ void cell_release(ncplane* n, cell* c){
|
||||
|
||||
// Duplicate one cell onto another when they share a plane. Convenience wrapper.
|
||||
int cell_duplicate(ncplane* n, cell* targ, const cell* c){
|
||||
return cell_duplicate_far(&n->pool, targ, n, c);
|
||||
int r = cell_duplicate_far(&n->pool, targ, n, c);
|
||||
if(r < 0){
|
||||
logerror(n->nc, "Failed duplicating cell");
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// the heart of damage detection. compare two cells (from two different planes)
|
||||
|
50
tests/layout.cpp
Normal file
50
tests/layout.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
#include "main.h"
|
||||
#include "internal.h"
|
||||
|
||||
TEST_CASE("TextLayout") {
|
||||
notcurses_options nopts{};
|
||||
notcurses* nc_ = notcurses_init(&nopts, nullptr);
|
||||
if(!nc_){
|
||||
return;
|
||||
}
|
||||
ncplane* ncp_ = notcurses_stdplane(nc_);
|
||||
REQUIRE(ncp_);
|
||||
|
||||
const char str[] = "this is going to be broken up";
|
||||
|
||||
SUBCASE("LayoutLeft") {
|
||||
auto sp = ncplane_new(nc_, 2, 20, 0, 0, nullptr);
|
||||
REQUIRE(sp);
|
||||
size_t bytes;
|
||||
CHECK(0 < ncplane_puttext(sp, 0, NCALIGN_LEFT, str, &bytes));
|
||||
CHECK(0 == notcurses_render(nc_));
|
||||
CHECK(bytes == strlen(str));
|
||||
// FIXME inspect layout
|
||||
ncplane_destroy(sp);
|
||||
}
|
||||
|
||||
SUBCASE("LayoutRight") {
|
||||
auto sp = ncplane_new(nc_, 2, 20, 0, 0, nullptr);
|
||||
REQUIRE(sp);
|
||||
size_t bytes;
|
||||
CHECK(0 < ncplane_puttext(sp, 0, NCALIGN_RIGHT, str, &bytes));
|
||||
CHECK(0 == notcurses_render(nc_));
|
||||
CHECK(bytes == strlen(str));
|
||||
// FIXME inspect layout
|
||||
ncplane_destroy(sp);
|
||||
}
|
||||
|
||||
SUBCASE("LayoutCenter") {
|
||||
auto sp = ncplane_new(nc_, 2, 20, 0, 0, nullptr);
|
||||
REQUIRE(sp);
|
||||
size_t bytes;
|
||||
CHECK(0 < ncplane_puttext(sp, 0, NCALIGN_CENTER, str, &bytes));
|
||||
CHECK(0 == notcurses_render(nc_));
|
||||
CHECK(bytes == strlen(str));
|
||||
// FIXME inspect layout
|
||||
ncplane_destroy(sp);
|
||||
}
|
||||
|
||||
CHECK(0 == notcurses_stop(nc_));
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user