ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
layout.c File Reference

Two-column layout implementation. More...

Go to the source code of this file.

Functions

void layout_print_wrapped_description (FILE *stream, const char *text, int indent_width, int term_width)
 
void layout_print_two_column_row (FILE *stream, const char *first_column, const char *second_column, int first_col_len, int term_width)
 

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 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 
)

Definition at line 206 of file layout.c.

207 {
208 if (!stream || !first_column || !second_column)
209 return;
210
211 // Strip ANSI escape codes before calculating display width
212 char *stripped = ansi_strip_escapes(first_column, strlen(first_column));
213
214 // Calculate actual display width of first column (accounts for UTF-8, excluding ANSI codes)
215 int display_width = utf8_display_width(stripped ? stripped : first_column);
216 if (display_width < 0)
217 display_width = first_col_len; // Fallback to provided value if available
218
219 SAFE_FREE(stripped);
220
221 // Use calculated first_col_len if provided (accounts for UTF-8 and ANSI codes),
222 // otherwise default to 35. The provided width respects both colored text and UTF-8 characters.
223 // Allow the first column to expand up to 60% of terminal width, but leave room for description.
224 int fixed_first_col_width = (first_col_len > 0) ? first_col_len : 35;
225 int max_first_col_width = (term_width * 6) / 10;
226 if (max_first_col_width < 40)
227 max_first_col_width = 40;
228 if (fixed_first_col_width > max_first_col_width) {
229 fixed_first_col_width = max_first_col_width;
230 }
231
232 // Second column starts after first column (with 2 spaces padding)
233 // This allows descriptions to be quite wide for wrapping
234 int second_col_start = 2 + fixed_first_col_width + 2;
235
236 // At narrow terminal widths, force single-column layout
237 bool force_single_column = (term_width <= 90);
238
239 // Position where first column ends (including leading spaces)
240 int first_col_end = 2 + display_width;
241
242 // Check if first column fits within the fixed width
243 bool fits_in_first_column_width = (display_width <= fixed_first_col_width);
244
245 // If first column fits within its width and we're not forcing single column, try same-line layout
246 if (fits_in_first_column_width && !force_single_column) {
247 // Print first column
248 fprintf(stream, " %s", first_column);
249
250 // Pad to second column start position
251 int padding = second_col_start - first_col_end;
252 if (padding > 0) {
253 fprintf(stream, "%*s", padding, "");
254 } else {
255 fprintf(stream, " ");
256 }
257
258 // Print description with wrapping at second column position
259 layout_print_wrapped_description(stream, second_column, second_col_start, term_width);
260 fprintf(stream, "\n");
261 } else if (force_single_column) {
262 // Terminal too narrow, put description on next line with indent
263 fprintf(stream, " %s\n", first_column);
264
265 // Only print description if it exists
266 if (second_column && second_column[0] != '\0') {
267 int description_indent = 4;
268 for (int i = 0; i < description_indent; i++)
269 fprintf(stream, " ");
270
271 layout_print_wrapped_description(stream, second_column, description_indent, term_width);
272 fprintf(stream, "\n");
273 }
274 } else {
275 // First column overflows its fixed width - bump description to next line
276 // Print first column as-is (it will overflow)
277 fprintf(stream, " %s\n", first_column);
278
279 // Only print description if it exists
280 if (second_column && second_column[0] != '\0') {
281 // Print description on next line at second column position, with wide wrapping
282 for (int i = 0; i < second_col_start; i++)
283 fprintf(stream, " ");
284 layout_print_wrapped_description(stream, second_column, second_col_start, term_width);
285 fprintf(stream, "\n");
286 }
287 }
288}
char * ansi_strip_escapes(const char *input, size_t input_len)
Definition ansi.c:13
void layout_print_wrapped_description(FILE *stream, const char *text, int indent_width, int term_width)
Definition layout.c:98
int display_width(const char *text)
int utf8_display_width(const char *str)
Definition utf8.c:46

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

Referenced by options_config_print_options_sections_with_width(), options_config_print_usage(), and options_print_help_for_mode().

◆ layout_print_wrapped_description()

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

Definition at line 98 of file layout.c.

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

References utf8_next_char_bytes().

Referenced by layout_print_two_column_row(), and options_print_help_for_mode().