ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
layout.c File Reference

Two-column layout implementation. More...

Go to the source code of this file.

Macros

#define OPTIONS_LAYOUT_BUF_BUFFER_SIZE   256

Functions

void layout_print_wrapped_description (FILE *stream, const char *text, int indent_width, int term_width, int continuation_indent_extra)
 Print text with wrapping and proper indentation.
void layout_print_two_column_row (FILE *stream, const char *first_column, const char *second_column, int first_col_len, int term_width, int continuation_indent_extra)
 Print two-column row with automatic wrapping.

Detailed Description

Two-column layout implementation.

Implements reusable layout functions for two-column formatting with text wrapping. Uses utf8proc library for accurate UTF-8 display width calculation.

Definition in file options/layout.c.

Macro Definition Documentation

◆ OPTIONS_LAYOUT_BUF_BUFFER_SIZE

#define OPTIONS_LAYOUT_BUF_BUFFER_SIZE   256

Definition at line 10 of file options/layout.c.

Function Documentation

◆ layout_print_two_column_row()

void layout_print_two_column_row ( FILE * stream,
const char * first_column,
const char * second_column,
int first_col_len,
int term_width,
int continuation_indent_extra )

Print two-column row with automatic wrapping.

Prints first column text, then second column description with proper alignment. If first column is too long (>45 chars), description moves to next line. Second column wraps at description start column.

Parameters
streamOutput file stream
first_columnText for first column
second_columnDescription for second column
first_col_lenLength of first column text (excluding ANSI codes)
term_widthTerminal width
continuation_indent_extraExtra spaces for continuation lines (0 for none, 2 for options)

Definition at line 223 of file options/layout.c.

224 {
225 if (!stream || !first_column || !second_column)
226 return;
227
228 // Strip ANSI escape codes before calculating display width
229 char *stripped = ansi_strip_escapes(first_column, strlen(first_column));
230
231 // Calculate actual display width of first column (accounts for UTF-8, excluding ANSI codes)
232 int display_width = utf8_display_width(stripped ? stripped : first_column);
233 if (display_width < 0)
234 display_width = first_col_len; // Fallback to provided value if available
235
236 SAFE_FREE(stripped);
237
238 // Use calculated first_col_len if provided (accounts for UTF-8 and ANSI codes),
239 // otherwise default to 35. The provided width respects both colored text and UTF-8 characters.
240 // Allow the first column to expand up to 60% of terminal width, but leave room for description.
241 int fixed_first_col_width = (first_col_len > 0) ? first_col_len : 35;
242 int max_first_col_width = (term_width * 6) / 10;
243 if (max_first_col_width < 40)
244 max_first_col_width = 40;
245 if (fixed_first_col_width > max_first_col_width) {
246 fixed_first_col_width = max_first_col_width;
247 }
248
249 // Second column starts after first column (with 2 spaces padding)
250 // This allows descriptions to be quite wide for wrapping
251 int second_col_start = 2 + fixed_first_col_width + 2;
252
253 // At narrow terminal widths, force single-column layout
254 bool force_single_column = (term_width <= 90);
255
256 // Position where first column ends (including leading spaces)
257 int first_col_end = 2 + display_width;
258
259 // Check if first column fits within the fixed width
260 bool fits_in_first_column_width = (display_width <= fixed_first_col_width);
261
262 // If first column fits within its width and we're not forcing single column, try same-line layout
263 if (fits_in_first_column_width && !force_single_column) {
264 // Print first column
265 fprintf(stream, " %s", first_column);
266
267 // Pad to second column start position
268 int padding = second_col_start - first_col_end;
269 if (padding > 0) {
270 fprintf(stream, "%*s", padding, "");
271 } else {
272 fprintf(stream, " ");
273 }
274
275 // Print description with wrapping at second column position
276 layout_print_wrapped_description(stream, second_column, second_col_start, term_width, continuation_indent_extra);
277 fprintf(stream, "\n");
278 } else if (force_single_column) {
279 // Terminal too narrow, put description on next line with indent
280 fprintf(stream, " %s\n", first_column);
281
282 // Only print description if it exists
283 if (second_column && second_column[0] != '\0') {
284 int description_indent = 4;
285 for (int i = 0; i < description_indent; i++)
286 fprintf(stream, " ");
287
288 layout_print_wrapped_description(stream, second_column, description_indent, term_width,
289 continuation_indent_extra);
290 fprintf(stream, "\n");
291 }
292 } else {
293 // First column overflows its fixed width - bump description to next line
294 // Print first column as-is (it will overflow)
295 fprintf(stream, " %s\n", first_column);
296
297 // Only print description if it exists
298 if (second_column && second_column[0] != '\0') {
299 // Print description on next line at second column position, with wide wrapping
300 for (int i = 0; i < second_col_start; i++)
301 fprintf(stream, " ");
302 layout_print_wrapped_description(stream, second_column, second_col_start, term_width, continuation_indent_extra);
303 fprintf(stream, "\n");
304 }
305 }
306}
char * ansi_strip_escapes(const char *input, size_t input_len)
When code needs to work with rgb error, it keeps r, g, b here instead of scattering it across globals...
Definition ansi.c:27
#define SAFE_FREE(ptr)
Definition common.h:377
int display_width(const char *text)
Calculate the visible display width of text with ANSI escape codes.
int utf8_display_width(const char *str)
Calculate terminal display width of a UTF-8 string.
Definition utf8.c:98
void layout_print_wrapped_description(FILE *stream, const char *text, int indent_width, int term_width, int continuation_indent_extra)
Print text with wrapping and proper indentation.

References ansi_strip_escapes(), display_width(), layout_print_wrapped_description(), SAFE_FREE, and utf8_display_width().

◆ layout_print_wrapped_description()

void layout_print_wrapped_description ( FILE * stream,
const char * text,
int indent_width,
int term_width,
int continuation_indent_extra )

Print text with wrapping and proper indentation.

Wraps text to fit within terminal width, maintaining specified indentation for wrapped lines. Applies color formatting to metadata labels.

Parameters
streamOutput file stream
textPlain text to print (may contain wrapping markers)
indent_widthNumber of spaces to indent wrapped lines
term_widthTerminal width (0 for default 80)
continuation_indent_extraExtra spaces to add to continuation lines (0 for none, 2 for options)

Definition at line 101 of file options/layout.c.

102 {
103 if (!text || !stream)
104 return;
105
106 // Default terminal width if not specified
107 if (term_width <= 0)
108 term_width = 80;
109
110 // Available width for text after indentation, capped at 130 for readability
111 // Account for continuation lines having extra indent, so reduce available width accordingly
112 // This allows the description column itself to be up to 130 chars wide,
113 // starting from indent_width, for a max total line of indent_width + 130 (first line)
114 // or indent_width + continuation_indent_extra + 130 (continuation lines)
115 int available_width = term_width - indent_width - continuation_indent_extra;
116 if (available_width > 130)
117 available_width = 130;
118 if (available_width < 20)
119 available_width = 20;
120
121 const char *line_start = text;
122 const char *last_space = NULL;
123 const char *p = text;
124 bool inside_metadata = false;
125
126 while (*p) {
127 // Check for explicit newline
128 if (*p == '\n') {
129 // Print everything up to the newline
130 int text_len = p - line_start;
131 if (text_len > 0) {
132 char seg[BUFFER_SIZE_MEDIUM];
133 SAFE_STRNCPY(seg, line_start, text_len + 1);
134 layout_print_colored_segment(stream, seg);
135 }
136
137 fprintf(stream, "\n");
138 if (*(p + 1)) {
139 // Continuation lines get extra indent if specified
140 int continuation_indent = indent_width + continuation_indent_extra;
141 for (int i = 0; i < continuation_indent; i++)
142 fprintf(stream, " ");
143 }
144 p++;
145 line_start = p;
146 last_space = NULL;
147 inside_metadata = false;
148 continue;
149 }
150
151 // Check for start of metadata blocks (must check before space tracking)
152 // Use helper that accounts for ANSI codes in the metadata
153 if (!inside_metadata && is_metadata_start(p)) {
154 inside_metadata = true;
155 }
156
157 // Check for end of metadata blocks - always exit on )
158 // We'll re-enter when we see the next metadata marker
159 if (inside_metadata && *p == ')') {
160 inside_metadata = false;
161 }
162
163 // Track spaces for word wrapping, including inside metadata blocks
164 // Prefer breaks before metadata blocks by updating last_space even if we already have one
165 if (*p == ' ') {
166 const char *next = p + 1;
167 while (*next == ' ')
168 next++;
169 // Track space as break point if it's before metadata or a normal space (not before parenthetical)
170 if (is_metadata_start(next) || !last_space || *next != '(') {
171 last_space = p;
172 }
173 }
174
175 // Decode UTF-8 character length
176 int char_bytes = utf8_next_char_bytes(p, strlen(p));
177
178 if (char_bytes <= 0) {
179 // Invalid UTF-8 or end of string, stop
180 break;
181 }
182
183 // Advance to next character first
184 p += char_bytes;
185
186 // Calculate actual display width from line_start to current position (excluding ANSI codes)
187 int actual_width = calculate_segment_display_width(line_start, p - line_start);
188
189 // Check if we need to wrap
190 if (actual_width >= available_width && last_space && last_space > line_start) {
191 // Print text up to break point with colors applied
192 int text_len = last_space - line_start;
193 char seg[BUFFER_SIZE_MEDIUM];
194 SAFE_STRNCPY(seg, line_start, text_len + 1);
195 layout_print_colored_segment(stream, seg);
196
197 fprintf(stream, "\n");
198 // Continuation lines get extra indent if specified
199 int continuation_indent = indent_width + continuation_indent_extra;
200 for (int i = 0; i < continuation_indent; i++)
201 fprintf(stream, " ");
202
203 p = last_space + 1;
204 line_start = p;
205 last_space = NULL;
206 continue;
207 }
208 }
209
210 // Print remaining text
211 if (p > line_start) {
212 char seg[BUFFER_SIZE_MEDIUM];
213 int text_len = p - line_start;
214 SAFE_STRNCPY(seg, line_start, text_len + 1);
215 layout_print_colored_segment(stream, seg);
216 }
217}
#define BUFFER_SIZE_MEDIUM
Medium buffer size (512 bytes).
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:423
int utf8_next_char_bytes(const char *str, size_t max_bytes)
Get byte length of next UTF-8 character.
Definition utf8.c:177

References BUFFER_SIZE_MEDIUM, SAFE_STRNCPY, and utf8_next_char_bytes().

Referenced by layout_print_two_column_row().