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