ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
help_menu.c
Go to the documentation of this file.
1
2/* Storage capacities in elements; text buffers include the terminating NUL. */
3#define UI_HELP_MENU_KEY_BUFFER_SIZE 96
4#define UI_HELP_MENU_TEXT_COUNT 64
5#define UI_HELP_MENU_LHS_BUFFER_SIZE 1024
6#define UI_HELP_MENU_RHS_BUFFER_SIZE 1024
7#define UI_HELP_MENU_PADDED_BUFFER_SIZE 2048
8#define UI_HELP_MENU_KEY_EXTENDED_BUFFER_SIZE 256
9#define UI_HELP_MENU_DESCRIPTION_BUFFER_SIZE 768
10#define UI_HELP_MENU_STYLED_KEYS_BUFFER_SIZE 512
11#define UI_HELP_MENU_BINDING_BUFFER_SIZE 64
12#define UI_HELP_MENU_STYLED_BUFFER_SIZE 512
13#define UI_HELP_MENU_VALUES_BUFFER_SIZE 512
14
15/* Existing buffer budgets in bytes, including NUL for text storage. */
16#define HELP_MENU_TEXT_LINE_BUFFER_SIZE 2048
17
19#include <ascii-chat/ui/text.h>
22#include <stdio.h>
23#include <string.h>
24
25void ui_help_keys(char *out, size_t cap, const char *keys) {
26 if (!out || !cap) {
27 SET_ERRNO(ERROR_INVALID_PARAM, "Help keys require output storage");
28 return;
29 }
30 size_t used = 0;
31 out[0] = '\0';
32 if (!keys)
33 return;
34 for (const char *p = keys; *p;) {
35 if (used + 1 >= cap) {
36 SET_ERRNO(ERROR_BUFFER_OVERFLOW, "Help key output buffer is too small");
37 return;
38 }
39 if (*p == ' ' || *p == '/' || *p == '|' || *p == '(' || *p == ')') {
40 out[used++] = *p++;
41 out[used] = '\0';
42 } else {
43 const char *start = p;
44 while (*p && *p != ' ' && *p != '/' && *p != '|' && *p != '(' && *p != ')')
45 ++p;
47 if ((size_t)(p - start) >= sizeof(key)) {
48 SET_ERRNO(ERROR_BUFFER_OVERFLOW, "Help key token is too long");
49 return;
50 }
51 snprintf(key, sizeof(key), "%.*s", (int)(p - start), start);
52 const char *styled = colored_string(LOG_COLOR_WARN, key);
53 size_t length = strlen(styled);
54 if (length >= cap - used) {
55 SET_ERRNO(ERROR_BUFFER_OVERFLOW, "Help key output buffer is too small");
56 return;
57 }
58 memcpy(out + used, styled, length + 1);
59 used += length;
60 }
61 }
62}
63
64static void measure(const ui_help_menu_t *m, int *keys, int *left, int *right) {
65 *keys = *left = *right = 0;
66 for (size_t i = 0; i < m->count; ++i) {
67 int k = m->entries[i].keys ? display_width(m->entries[i].keys) : 0;
68 if (k > *keys)
69 *keys = k;
70 }
71 for (size_t i = 0; i < m->count; ++i) {
72 int w = display_width(m->entries[i].description);
73 if (m->entries[i].keys)
74 w += *keys + 2;
75 if (w > *left)
76 *left = w;
77 }
78 for (size_t i = 0; i < m->settings_count; ++i) {
79 int w = display_width(m->settings[i]);
80 if (w > *right)
81 *right = w;
82 }
83}
84
85terminal_size_t ui_help_menu_measure(const ui_help_menu_t *m) {
86 if (!m || (m->count && !m->entries) || (m->settings_count && !m->settings)) {
87 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid help menu entries or settings");
88 return (terminal_size_t){0};
89 }
90 int keys, left, right;
91 measure(m, &keys, &left, &right);
92 int width = left + (right ? right + 3 : 0);
93 if (m->title && display_width(m->title) > width)
95 if (m->footer && display_width(m->footer) > width)
97 size_t rows = m->count > m->settings_count ? m->count : m->settings_count;
98 return (terminal_size_t){.cols = width + 6, .rows = (int)rows + 8 + m->top_margin};
99}
100
101bool ui_help_menu_render(frame_buffer_t *out, terminal_size_t size, const ui_help_menu_t *m) {
102 if (!out || !m || (m->count && !m->entries) || (m->settings_count && !m->settings) || m->count > 62 ||
103 m->settings_count > 62) {
104 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid help output or entry counts");
105 return false;
106 }
108 const char *lines[UI_HELP_MENU_TEXT_COUNT];
109 size_t rows = m->count > m->settings_count ? m->count : m->settings_count;
110 int keys, left, right;
111 measure(m, &keys, &left, &right);
112 lines[0] = "";
113 for (size_t i = 0; i < rows; ++i) {
116 if (i < m->count) {
117 const ui_help_entry_t *entry = &m->entries[i];
118 if (!entry->keys)
119 snprintf(lhs, sizeof(lhs), "%s", entry->description);
120 else {
122 char styled_keys[UI_HELP_MENU_STYLED_KEYS_BUFFER_SIZE];
123 ui_help_keys(styled_keys, sizeof(styled_keys), entry->keys);
124 ui_text_cell(key, sizeof(key), styled_keys, keys, UI_ALIGN_LEFT);
125 ui_text_values_style(description, sizeof(description), entry->description, LOG_COLOR_FATAL);
126 snprintf(lhs, sizeof(lhs), "%s %s", key, description);
127 }
128 }
129 if (i < m->settings_count) {
130 const char *setting = m->settings[i];
131 const char *end = setting[0] == '(' ? strchr(setting, ')') : NULL;
132 if (end) {
134 snprintf(binding, sizeof(binding), "%.*s", (int)(end + 1 - setting), setting);
135 ui_help_keys(styled, sizeof(styled), binding);
137 ui_text_values_style(values, sizeof(values), end + 1, LOG_COLOR_FATAL);
138 snprintf(rhs, sizeof(rhs), "%s%s", styled, values);
139 } else
140 ui_text_values_style(rhs, sizeof(rhs), setting, LOG_COLOR_FATAL);
141 }
142 ui_text_cell(padded, sizeof(padded), lhs, left, UI_ALIGN_LEFT);
143 snprintf(text[i], sizeof(text[i]), "%s%s%s", padded, right ? " " : "", rhs);
144 lines[i + 1] = text[i];
145 }
146 ui_popup_t box = {
147 .title = m->title, .lines = lines, .line_count = rows + 1, .footer = m->footer, .top_margin = m->top_margin};
148 return ui_popup_render(out, size, &box);
149}
#define SET_ERRNO(code,...)
unsigned int width
Definition fft.c:23
struct frame_buffer frame_buffer_t
Opaque frame buffer handle.
@ ERROR_INVALID_PARAM
@ ERROR_BUFFER_OVERFLOW
@ LOG_COLOR_FATAL
Definition log/log.h:153
@ LOG_COLOR_WARN
Definition log/log.h:151
const char * colored_string(log_color_t color, const char *text)
Build a colored string for terminal output.
Definition string.c:284
int display_width(const char *text)
Calculate the visible display width of text with ANSI escape codes.
#define UI_HELP_MENU_LHS_BUFFER_SIZE
Definition help_menu.c:5
void ui_help_keys(char *out, size_t cap, const char *keys)
Definition help_menu.c:25
#define UI_HELP_MENU_TEXT_COUNT
Definition help_menu.c:4
#define UI_HELP_MENU_KEY_EXTENDED_BUFFER_SIZE
Definition help_menu.c:8
#define UI_HELP_MENU_STYLED_BUFFER_SIZE
Definition help_menu.c:12
terminal_size_t ui_help_menu_measure(const ui_help_menu_t *m)
Definition help_menu.c:85
#define UI_HELP_MENU_BINDING_BUFFER_SIZE
Definition help_menu.c:11
#define HELP_MENU_TEXT_LINE_BUFFER_SIZE
Definition help_menu.c:16
#define UI_HELP_MENU_KEY_BUFFER_SIZE
Definition help_menu.c:3
bool ui_help_menu_render(frame_buffer_t *out, terminal_size_t size, const ui_help_menu_t *m)
Definition help_menu.c:101
#define UI_HELP_MENU_PADDED_BUFFER_SIZE
Definition help_menu.c:7
#define UI_HELP_MENU_VALUES_BUFFER_SIZE
Definition help_menu.c:13
#define UI_HELP_MENU_RHS_BUFFER_SIZE
Definition help_menu.c:6
#define UI_HELP_MENU_DESCRIPTION_BUFFER_SIZE
Definition help_menu.c:9
#define UI_HELP_MENU_STYLED_KEYS_BUFFER_SIZE
Definition help_menu.c:10
🖥️ Display Width and Terminal Centering Utilities
bool ui_popup_render(frame_buffer_t *output, terminal_size_t size, const ui_popup_t *box)
Definition popup.c:56
static bool size_t gotel_query_t * out
Definition queries.h:8
🔤 String Manipulation and Shell Escaping Utilities
Definition help_menu.h:3
const char * keys
Definition help_menu.h:4
const char * description
Definition help_menu.h:5
ui help entry keeps the values that move through the subsystem: keys, description....
Definition help_menu.h:12
const char * footer
Definition help_menu.h:18
const char * title
Definition help_menu.h:13
size_t settings_count
Definition help_menu.h:17
const ui_help_entry_t * entries
Definition help_menu.h:14
const char *const * settings
Definition help_menu.h:16
When code needs to work with ui popup, it keeps title, lines, line_count, footer here instead of scat...
Definition popup.h:23
void ui_text_values_style(char *out, size_t cap, const char *text, log_color_t zero_color)
Definition text.c:95
size_t ui_text_cell(char *out, size_t cap, const char *text, int width, ui_alignment_t align)
Definition text.c:48
@ UI_ALIGN_LEFT
Definition text.h:9