UI: automatically indent lines in dialogues

This commit is contained in:
2026-06-01 18:49:53 +03:00
parent cc179e1b72
commit 10442f3101
7 changed files with 149 additions and 23 deletions
+3 -3
View File
@@ -1,13 +1,13 @@
# TODO: # TODO:
## Current: ## Current:
- [ ] Break line automatically in modal widgets
- [ ] Cancel upload/download
- [ ] Show download/upload progress in percentages - [ ] Show download/upload progress in percentages
- [ ] File upload multiline description (window) - [ ] File upload multiline description (window)
- [ ] Separate common structures in the common.h
- [ ] Sorting in the file list - [ ] Sorting in the file list
- [ ] Resume download - [ ] 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] Input: fix non-Latin letters
- [x] Get rid of unsigned integers - [x] Get rid of unsigned integers
- [x] Check registration / enter server name again - [x] Check registration / enter server name again
+2
View File
@@ -17,6 +17,7 @@
#include "main.h" #include "main.h"
#include "../app.h" #include "../app.h"
#include "alert.h"
#include "connection.h" #include "connection.h"
#include "file_list.h" #include "file_list.h"
#include "query.h" #include "query.h"
@@ -41,6 +42,7 @@ int main(int argc, char **argv) {
/* init alert */ /* init alert */
w_alert_init(app); 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); init_query_args(q_args, app->params);
q_args->buf = malloc(INBUFSIZE); q_args->buf = malloc(INBUFSIZE);
+3 -3
View File
@@ -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, int32_t u_utf8_convert_to_wide(const char *utf8_str,
wchar_t **restrict wide_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) // Step 1: Convert UTF-8 (MBCS) to wchar_t (wide)
size_t wide_len = size_t wide_len =
u_utf8_code_points_count(utf8_str); // Get required length (-1 on error) u_utf8_code_points_count(utf8_str); // Get required length (-1 on error)
if (wide_len == (size_t)-1) { if (wide_len == (size_t)-1) {
*len = 0; *o_len = 0;
perror("mbstowcs failed"); perror("mbstowcs failed");
return 1; return 1;
} }
*wide_str = *wide_str =
malloc((wide_len + 1) * sizeof(wchar_t)); // +1 for null terminator malloc((wide_len + 1) * sizeof(wchar_t)); // +1 for null terminator
mbstowcs(*wide_str, utf8_str, wide_len + 1); // Perform conversion mbstowcs(*wide_str, utf8_str, wide_len + 1); // Perform conversion
*len = wide_len; *o_len = wide_len;
return 0; return 0;
} }
+1 -1
View File
@@ -8,7 +8,7 @@
const char *u_utf8_wind_n(const char *s, int n); const char *u_utf8_wind_n(const char *s, int n);
size_t u_utf8_code_points_count(const char *s); 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, 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); int32_t u_utf8_curs_printw(WINDOW *win, int32_t *y, int32_t *x, const char* utf8_str, int max_len, bool multiline);
#endif #endif
+12 -4
View File
@@ -1,11 +1,14 @@
/* SPDX-License-Identifier: MIT */ /* SPDX-License-Identifier: MIT */
/* Copyright (c) 2026 Oleksandr Zhylin */ /* Copyright (c) 2026 Oleksandr Zhylin */
#include <ncursesw/ncurses.h> #include <ncursesw/ncurses.h>
#include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
#include <wchar.h>
#include <widget.h> #include <widget.h>
#include <widget_core.h> #include <widget_core.h>
@@ -237,7 +240,11 @@ int32_t w_dialogue_draw(w_dialogue_t *d) {
/* analyze text content */ /* analyze text content */
int32_t line_count = 0; int32_t line_count = 0;
int32_t line_max_len = 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 += line_count;
y += 2; /* margin for text */ 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); DETECT_GROUP_SIZE(d->g_action, line_max_len, y, x);
x += line_max_len; x += line_max_len;
x += 1; /* when uses box */ x += 2; /* when uses box */
y += 1; /* when uses box */ y += 2; /* when uses box */
d->w.sz.x = x; d->w.sz.x = x;
d->w.sz.y = y; d->w.sz.y = y;
@@ -283,7 +290,8 @@ int32_t w_dialogue_draw(w_dialogue_t *d) {
/* text */ /* text */
wattroff(d->win, A_BOLD); wattroff(d->win, A_BOLD);
/* mvwhline(d->win, d->w.sz.y - 3, 1, 0, d->w.sz.x - 2); */ /* 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); wattroff(d->win, A_REVERSE);
if (d->g_content != NULL) { if (d->g_content != NULL) {
+115 -5
View File
@@ -8,10 +8,12 @@
#include "widget_core.h" #include "widget_core.h"
#include "../main.h" #include "../main.h"
#include <ncursesw/ncurses.h> #include <ncursesw/ncurses.h>
#include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <utils.h> #include <utils.h>
#include <wchar.h>
extern int32_t m_id; 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, \ /* #define PRINT_TEXT(win, l_buf, text, i, c_line_len, win_width, line_v_pos, \
attrs) \ attrs) \
strncpy(l_buf, text + i - c_line_len, c_line_len); \ 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 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 attrs, int32_t x, int32_t max_line_len) {
int32_t utf8len = 0; 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, int32_t w_print_multiline_text(WINDOW *win, const char *_text,
const int32_t win_width, const int32_t y, const int32_t win_width, const int32_t y,
const int32_t x, const int16_t attrs) { const int32_t x, const int16_t attrs) {
int16_t line_v_pos = y; int16_t line_v_pos = y;
int32_t c_line_len = 0; 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++) { for (; text[i]; i++, c_line_len++) {
if (text[i] == '\n') { 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); x, m_line_len);
c_line_len = -1; c_line_len = -1;
line_v_pos++; 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); m_line_len);
free(text); free(text);
return 1; 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;
}
+13 -7
View File
@@ -59,8 +59,8 @@ enum w_cbrp_code {
/* widget callback response data */ /* widget callback response data */
typedef struct { typedef struct {
enum w_cbrp_code code; enum w_cbrp_code code;
struct w_val_t val; struct w_val_t val;
} w_cbrp_data; } w_cbrp_data;
typedef struct { typedef struct {
@@ -103,10 +103,16 @@ enum w_pmt_attrs {
PMT_ALIGN_CENTER = 03, PMT_ALIGN_CENTER = 03,
}; };
void w_init(w_t *w, w_t *w_parent, WINDOW **win, char *title); 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_get_max_line_len(const char *text, int32_t *line_count);
int32_t w_print_multiline_text(WINDOW *win, const char *text, int32_t w_print_multiline_text(WINDOW *win, const char *text,
const int32_t win_width, const int32_t y, const int32_t win_width, const int32_t y,
const int32_t x, const int16_t attrs); 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 #endif