From 10442f31011e93d0764d1c24b36845c31a117e64 Mon Sep 17 00:00:00 2001 From: Alexander Zhylin Date: Mon, 1 Jun 2026 18:49:53 +0300 Subject: [PATCH] UI: automatically indent lines in dialogues --- TODO.md | 6 +- src/main.c | 2 + src/utils/utf8.c | 6 +- src/utils/utf8.h | 2 +- src/widget/dialogue.c | 16 +++-- src/widget_core/widget_core.c | 120 ++++++++++++++++++++++++++++++++-- src/widget_core/widget_core.h | 20 ++++-- 7 files changed, 149 insertions(+), 23 deletions(-) diff --git a/TODO.md b/TODO.md index ac18a5d..7ca0858 100644 --- a/TODO.md +++ b/TODO.md @@ -1,13 +1,13 @@ # TODO: ## Current: -- [ ] Break line automatically in modal widgets -- [ ] Cancel upload/download - [ ] Show download/upload progress in percentages - [ ] File upload multiline description (window) -- [ ] Separate common structures in the common.h - [ ] Sorting in the file list - [ ] Resume download +- [ ] Separate common structures in the common.h +- [x] Break line automatically in modal widgets +- [x] Cancel upload/download - [x] Input: fix non-Latin letters - [x] Get rid of unsigned integers - [x] Check registration / enter server name again diff --git a/src/main.c b/src/main.c index bfc883f..390a1ce 100644 --- a/src/main.c +++ b/src/main.c @@ -17,6 +17,7 @@ #include "main.h" #include "../app.h" +#include "alert.h" #include "connection.h" #include "file_list.h" #include "query.h" @@ -41,6 +42,7 @@ int main(int argc, char **argv) { /* init alert */ w_alert_init(app); + w_alert("Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem."); init_query_args(q_args, app->params); q_args->buf = malloc(INBUFSIZE); diff --git a/src/utils/utf8.c b/src/utils/utf8.c index 312f29d..d4aa37f 100644 --- a/src/utils/utf8.c +++ b/src/utils/utf8.c @@ -28,19 +28,19 @@ const char *u_utf8_wind_n(const char *s, int n) { int32_t u_utf8_convert_to_wide(const char *utf8_str, wchar_t **restrict wide_str, - size_t *restrict len) { + size_t *restrict o_len) { // Step 1: Convert UTF-8 (MBCS) to wchar_t (wide) size_t wide_len = u_utf8_code_points_count(utf8_str); // Get required length (-1 on error) if (wide_len == (size_t)-1) { - *len = 0; + *o_len = 0; perror("mbstowcs failed"); return 1; } *wide_str = malloc((wide_len + 1) * sizeof(wchar_t)); // +1 for null terminator mbstowcs(*wide_str, utf8_str, wide_len + 1); // Perform conversion - *len = wide_len; + *o_len = wide_len; return 0; } diff --git a/src/utils/utf8.h b/src/utils/utf8.h index 490b52f..d81ae68 100644 --- a/src/utils/utf8.h +++ b/src/utils/utf8.h @@ -8,7 +8,7 @@ const char *u_utf8_wind_n(const char *s, int n); size_t u_utf8_code_points_count(const char *s); int32_t u_utf8_convert_to_wide(const char *utf8_str, wchar_t **restrict wide_str, - size_t *restrict len); + size_t *restrict o_len); int32_t u_utf8_curs_printw(WINDOW *win, int32_t *y, int32_t *x, const char* utf8_str, int max_len, bool multiline); #endif \ No newline at end of file diff --git a/src/widget/dialogue.c b/src/widget/dialogue.c index f9d0a99..f9de915 100644 --- a/src/widget/dialogue.c +++ b/src/widget/dialogue.c @@ -1,11 +1,14 @@ /* SPDX-License-Identifier: MIT */ /* Copyright (c) 2026 Oleksandr Zhylin */ #include +#include #include #include +#include #include #include +#include #include #include @@ -237,7 +240,11 @@ int32_t w_dialogue_draw(w_dialogue_t *d) { /* analyze text content */ int32_t line_count = 0; int32_t line_max_len = 0; - line_max_len = w_get_max_line_len(d->text, &line_count); + wchar_t *main_text_wide = malloc(sizeof(wchar_t) * strlen(d->text)); + size_t mwt_len; + u_utf8_convert_to_wide(d->text, &main_text_wide, &mwt_len); + line_count = w_text_divide_by_lines( + main_text_wide, mwt_len, d->p_coordinates->max_x * 0.77, &line_max_len); y += line_count; y += 2; /* margin for text */ @@ -246,8 +253,8 @@ int32_t w_dialogue_draw(w_dialogue_t *d) { DETECT_GROUP_SIZE(d->g_action, line_max_len, y, x); x += line_max_len; - x += 1; /* when uses box */ - y += 1; /* when uses box */ + x += 2; /* when uses box */ + y += 2; /* when uses box */ d->w.sz.x = x; d->w.sz.y = y; @@ -283,7 +290,8 @@ int32_t w_dialogue_draw(w_dialogue_t *d) { /* text */ wattroff(d->win, A_BOLD); /* mvwhline(d->win, d->w.sz.y - 3, 1, 0, d->w.sz.x - 2); */ - w_print_multiline_text(d->win, d->text, d->w.sz.x, 2, 1, PMT_ALIGN_CENTER); + w_print_multiline_text_wide(d->win, main_text_wide, mwt_len, line_max_len, + d->w.sz.x, 2, 0, PMT_ALIGN_CENTER); wattroff(d->win, A_REVERSE); if (d->g_content != NULL) { diff --git a/src/widget_core/widget_core.c b/src/widget_core/widget_core.c index 41b347e..272ee4f 100644 --- a/src/widget_core/widget_core.c +++ b/src/widget_core/widget_core.c @@ -8,10 +8,12 @@ #include "widget_core.h" #include "../main.h" #include +#include #include #include #include #include +#include extern int32_t m_id; @@ -59,6 +61,49 @@ int32_t w_get_max_line_len(const char *text, int32_t *line_count) { } } +/* returns number of lines */ +int32_t w_text_divide_by_lines(wchar_t *restrict text, size_t src_len, + int max_x, int *max_len) { + int cl_pos = 0; + int last_space = -1; /* space position in the current line */ + int lines_num = 1; + + for (int i = 0; text[i] != '\0'; i++, cl_pos++) { + switch (text[i]) { + case ' ': /* space symbol */ + last_space = i; + break; + case '\n': + if (cl_pos > *max_len) { + *max_len = cl_pos; + } + last_space = -1; + cl_pos = 0; + lines_num++; + break; + default: + if (cl_pos > max_x) { + if (last_space > 0) { + int line_size = last_space - (i - cl_pos); + if (line_size > *max_len) { + *max_len = line_size; + } + text[last_space] = '\n'; + i = last_space; + last_space = -1; + cl_pos = 0; + lines_num++; + } + } + } + } + cl_pos--; + if (cl_pos > *max_len) { /* for the last line */ + *max_len = cl_pos; + } + return lines_num; +} + /* #define PRINT_TEXT(win, l_buf, text, i, c_line_len, win_width, line_v_pos, \ attrs) \ strncpy(l_buf, text + i - c_line_len, c_line_len); \ @@ -72,7 +117,7 @@ int32_t w_get_max_line_len(const char *text, int32_t *line_count) { } */ -void PRINT_TEXT(WINDOW *win, char *l_buf, char *text, int32_t i, +void line_print(WINDOW *win, char *l_buf, char *text, int32_t i, int32_t c_line_len, int32_t win_width, int32_t line_v_pos, int32_t attrs, int32_t x, int32_t max_line_len) { int32_t utf8len = 0; @@ -90,9 +135,27 @@ void PRINT_TEXT(WINDOW *win, char *l_buf, char *text, int32_t i, } } +void wline_print(WINDOW *win, wchar_t *w_text, int32_t i, int32_t c_line_len, + int32_t win_width, int32_t y, int32_t attrs, int32_t x, + int32_t max_line_len) { + wchar_t l_buf[DIALOGUE_TEXT]; + int32_t b_len = c_line_len * sizeof(wchar_t); + memcpy(l_buf, w_text + i - c_line_len, b_len); + l_buf[c_line_len] = '\0'; + if ((attrs & PMT_ALIGN_CENTER) == PMT_ALIGN_CENTER) { + int32_t _x = x + (win_width - c_line_len) / 2; + mvwaddnwstr(win, y, _x, l_buf, c_line_len); + } else if (attrs & PMT_POS_CENTER) { + int32_t _x = x + (win_width - max_line_len) / 2; + mvwaddnwstr(win, y, _x, l_buf, c_line_len); + } else { + mvwaddnwstr(win, y, x, l_buf, c_line_len); + } +} + int32_t w_print_multiline_text(WINDOW *win, const char *_text, - const int32_t win_width, const int32_t y, - const int32_t x, const int16_t attrs) { + const int32_t win_width, const int32_t y, + const int32_t x, const int16_t attrs) { int16_t line_v_pos = y; int32_t c_line_len = 0; @@ -126,17 +189,64 @@ int32_t w_print_multiline_text(WINDOW *win, const char *_text, for (; text[i]; i++, c_line_len++) { if (text[i] == '\n') { - PRINT_TEXT(win, l_buf, text, i, c_line_len, win_width, line_v_pos, attrs, + line_print(win, l_buf, text, i, c_line_len, win_width, line_v_pos, attrs, x, m_line_len); c_line_len = -1; line_v_pos++; } } - PRINT_TEXT(win, l_buf, text, i, c_line_len, win_width, line_v_pos, attrs, x, + line_print(win, l_buf, text, i, c_line_len, win_width, line_v_pos, attrs, x, m_line_len); free(text); + return 1; +} + +int32_t w_print_multiline_text_wide(WINDOW *win, const wchar_t *_text, + size_t text_len, int32_t max_line_size, + const int32_t win_width, const int32_t y, + const int32_t x, const int16_t attrs) { + + int16_t line_v_pos = y; + int32_t c_line_len = 0; + int32_t i = 0; + int text_bytes_size = sizeof(wchar_t) * (text_len + 1); + wchar_t *text = malloc(text_bytes_size); + mempcpy(text, _text, text_bytes_size); + + int l_size = 0; + for (int m = 0; text[m] != '\0'; m++) { + if (text[m] != '\n') { + if (l_size >= win_width) { + int j = m; + while (text[j] != ' ') { + j--; + } + text[j] = '\n'; + l_size = 0; + } else { + l_size++; + } + } else { + l_size = 0; + } + } + + for (; text[i]; i++, c_line_len++) { + if (text[i] == '\n') { + wline_print(win, text, i, c_line_len, win_width, line_v_pos, attrs, x, + max_line_size); + c_line_len = -1; + line_v_pos++; + } + } + + wline_print(win, text, i, c_line_len, win_width, line_v_pos, attrs, x, + max_line_size); + + free(text); + return 1; } \ No newline at end of file diff --git a/src/widget_core/widget_core.h b/src/widget_core/widget_core.h index 829d5ee..9a676dd 100644 --- a/src/widget_core/widget_core.h +++ b/src/widget_core/widget_core.h @@ -59,8 +59,8 @@ enum w_cbrp_code { /* widget callback response data */ typedef struct { - enum w_cbrp_code code; - struct w_val_t val; + enum w_cbrp_code code; + struct w_val_t val; } w_cbrp_data; typedef struct { @@ -103,10 +103,16 @@ enum w_pmt_attrs { PMT_ALIGN_CENTER = 03, }; -void w_init(w_t *w, w_t *w_parent, WINDOW **win, char *title); -int32_t w_get_max_line_len(const char *text, int32_t *line_count); -int32_t w_print_multiline_text(WINDOW *win, const char *text, - const int32_t win_width, const int32_t y, - const int32_t x, const int16_t attrs); +void w_init(w_t *w, w_t *w_parent, WINDOW **win, char *title); +int32_t w_get_max_line_len(const char *text, int32_t *line_count); +int32_t w_print_multiline_text(WINDOW *win, const char *text, + const int32_t win_width, const int32_t y, + const int32_t x, const int16_t attrs); +int32_t w_print_multiline_text_wide(WINDOW *win, const wchar_t *_text, + size_t text_len, int32_t max_line_size, const int32_t win_width, + const int32_t y, const int32_t x, + const int16_t attrs); +int32_t w_text_divide_by_lines(wchar_t *restrict text, size_t src_len, + int max_x, int *max_len); #endif \ No newline at end of file