Notcurses 3.0.13
a blingful library for TUIs and character graphics
Loading...
Searching...
No Matches
layout.c
Go to the documentation of this file.
1#include "internal.h"
2
3// print the first 'bytes' bytes of 'text' to 'n', using alignment 'align'
4// and requiring 'cols' columns, relative to the current cursor position.
5// it is an error to call ncplane_putline() with more data than can be printed
6// on the current row.
7static inline int
8ncplane_putline(ncplane* n, ncalign_e align, int cols, const char* text, size_t bytes){
9 const int avail = ncplane_dim_x(n) - n->x - 1;
10 const int offset = (align == NCALIGN_UNALIGNED ? 0 :
11 notcurses_align(avail, align, cols));
12 return ncplane_putnstr_yx(n, -1, n->x + offset, bytes, text);
13}
14
15static int
16puttext_advance_line(ncplane* n, unsigned truebreak){
17//fprintf(stderr, "ADVANCING LINE FROM %d/%d\n", n->y, n->x);
18 if(n->scrolling || n->autogrow){
19 if(truebreak){
20 if(ncplane_putchar(n, '\n') < 1){
21 return -1;
22 }
23 }else{
25 }
26 return 0;
27 }
28 // will fail on last line in the absence of scrolling, which is proper
29 return ncplane_cursor_move_yx(n, n->y + 1, 0);
30}
31
32// put up to a line of text down at the current cursor position. returns the
33// number of columns consumed, or -1 on error. the number of bytes consumed is
34// added to '*bytes', if 'bytes' is not NULL. any alignment is done relative to
35// the current cursor position. any line-breaking character will immediately
36// end the output, and move the cursor to the beginning of the next row. on an
37// error, '*bytes' is not updated, and nothing is printed.
38//
39// an input with C columns available on the row can be one of a few things:
40// * text wholly within C columns -- print it, advance x
41// * text + newline within C columns -- print through newline, ++y, x = 0
42// * text + wordbreak at C columns -- print through C, ++y, x = 0
43// * text + text at C columns:
44// * breaker (some text followed by whitespace): print through breaker, ++y, x = 0
45// * no breaker (all one word, with possible leading whitespace):
46// * leading whitespace? dump it, ++y, x = 0
47// * C == dimx: print through C, ++y, x = 0
48// * C < dimx: ++y, x = 0
49static int
50puttext_line(ncplane* n, ncalign_e align, const char* text, size_t* bytes){
51 unsigned cursx; // current cursor location
52 ncplane_cursor_yx(n, NULL, &cursx);
53 const int dimx = ncplane_dim_x(n);
54 const int avail = dimx - cursx - 1;
55//fprintf(stderr, "LINE %d starts at %d, len %d, avail %d\n", n->y, cursx, dimx, avail);
56 int bytes_leading_ws; // bytes thus far of leading whitespace
57 int cols_leading_ws; // cols thus far of leading whitespace
58 int bytes_leading_break; // bytes through last wordbreaker, 0 for no break yet
59 int cols_leading_break; // cols through last wordbreaker, 0 for no break yet
60 int cols = 0; // columns consumed thus far, cols > cols_leading_ws -> got_glyph
61 int b = 0; // bytes consumed thus far
62 bytes_leading_ws = cols_leading_ws = 0;
63 bytes_leading_break = cols_leading_break = 0;
64 while(cols <= avail){ // we can print everything we've read, if desired
65 mbstate_t mbstate = {0};
66 wchar_t w;
67 const size_t consumed = mbrtowc(&w, text + b, MB_CUR_MAX, &mbstate);
68 if(consumed == (size_t)-2 || consumed == (size_t)-1){
69 logerror("invalid UTF-8 after %d bytes", b);
70 return -1;
71 }
72//fprintf(stderr, "converted [%s] -> %lc\n", text + b, w);
73 if(consumed == 0){ // text was wholly within destination row, print it
74 if(ncplane_putline(n, align, cols, text, b) < 0){
75 return -1;
76 }
77 if(bytes){
78 *bytes = b;
79 }
80 return cols;
81 }
82 // if w is a linebreaker, print what we have, advance, and return
83 if(islinebreak(w)){
84//fprintf(stderr, "LINEBREAK at %d/%d\n", n->y, n->x);
85 if(b){
86 if(ncplane_putline(n, align, cols, text, b) < 0){
87 return -1;
88 }
89 }
90 if(puttext_advance_line(n, true)){
91 return -1;
92 }
93 if(bytes){
94 *bytes += b + consumed;
95 }
96 return cols;
97 }
98 b += consumed;
99 int width = wcwidth(w);
100 if(width < 0){
101 width = 0; // FIXME
102 }
103 cols += width;
104 if(iswordbreak(w)){
105 if(cols > cols_leading_ws){
106 bytes_leading_break = b;
107 cols_leading_break = cols;
108 }else{
109 bytes_leading_ws = b;
110 cols_leading_ws = cols;
111 }
112 }
113//fprintf(stderr, "%d approved [%lc] (tbytes: %d tcols: %d)\n", n->y, w, b, cols);
114 }
115 int colsreturn = 0;
116 if(bytes_leading_break){
117 if(ncplane_putline(n, align, cols, text, bytes_leading_break) < 0){
118 return -1;
119 }
120 if(bytes){
121 *bytes += bytes_leading_break;
122 }
123 colsreturn = cols_leading_break;
124 }else if(bytes_leading_ws){
125 if(ncplane_putline(n, align, cols, text, bytes_leading_ws) < 0){
126 return -1;
127 }
128 if(bytes){
129 *bytes += bytes_leading_ws;
130 }
131 colsreturn = cols_leading_ws;
132 }else if(cols == dimx){
133 if(ncplane_putline(n, align, cols, text, b) < 0){
134 return -1;
135 }
136 if(bytes){
137 *bytes = b;
138 }
139 colsreturn = cols;
140 }
141//fprintf(stderr, "FELL OFF line %d after %d cols %dB returning %d\n", n->y, cols, b, colsreturn);
142 if(puttext_advance_line(n, false)){
143 return -1;
144 }
145 return colsreturn;
146}
147
148// FIXME probably best to use u8_wordbreaks() and get all wordbreaks at once...
149int ncplane_puttext(ncplane* n, int y, ncalign_e align, const char* text, size_t* bytes){
150 if(bytes){
151 *bytes = 0;
152 }
153 int totalcols = 0;
154 // text points to the text we have *not* yet output. at each step, we see
155 // how much space we have available, and begin iterating from text. remember
156 // the most recent linebreaker that we see. when we exhaust our line, print
157 // through the linebreaker, and advance text.
158 // if we're using NCALIGN_LEFT, we'll be printing with x==-1, i.e. wherever
159 // the cursor is. if there's insufficient room to print anything, we need to
160 // try moving to the next line first. FIXME this ought actually apply to all
161 // alignments, which ought be taken relative to n->x. no change for
162 // NCALIGN_RIGHT, but NCALIGN_CENTER needs explicitly handle it...
163 do{
164 if(y != -1){
165 if(ncplane_cursor_move_yx(n, y, -1)){
166 return -1;
167 }
168 }
169 size_t linebytes = 0;
170 int cols = puttext_line(n, align, text, &linebytes);
171 if(cols < 0){
172 return -1;
173 }
174 totalcols += cols;
175 if(bytes){
176 *bytes += linebytes;
177 }
178 text += linebytes;
179//fprintf(stderr, "new cursor: %d/%d consumed: %zu\n", n->y, n->x, linebytes);
180 y = n->y;
181 }while(*text);
182 return totalcols;
183}
184
void scroll_down(ncplane *n)
Definition notcurses.c:1758
int ncplane_puttext(ncplane *n, int y, ncalign_e align, const char *text, size_t *bytes)
Definition layout.c:149
#define logerror(fmt,...)
Definition logging.h:32
int ncplane_cursor_move_yx(ncplane *n, int y, int x)
Definition notcurses.c:720
void ncplane_cursor_yx(const ncplane *n, unsigned *y, unsigned *x)
Definition notcurses.c:1741
int y
Definition notcurses.h:1905
ncalign_e
Definition notcurses.h:80
@ NCALIGN_UNALIGNED
Definition notcurses.h:81
vopts n
Definition notcurses.h:3502
return NULL
Definition termdesc.h:229