From 8c765191fb623abab5726bced698a52280d0c93e Mon Sep 17 00:00:00 2001 From: Alexander Zhylin Date: Fri, 20 Feb 2026 15:19:52 +0200 Subject: [PATCH 1/6] UI: nested groups. not finished --- src/main.c | 1 + src/modals/register.c | 32 +++++++++++++++-------- src/widget/button.c | 12 +++++++-- src/widget/dialogue.c | 58 +++++++++++------------------------------- src/widget/file_list.c | 11 ++++++-- src/widget/group.c | 30 +++++++++++++++++----- src/widget/group.h | 11 ++++---- src/widget/input.c | 24 +++++++++++------ 8 files changed, 102 insertions(+), 77 deletions(-) diff --git a/src/main.c b/src/main.c index 6fe37e7..7d89c4f 100644 --- a/src/main.c +++ b/src/main.c @@ -44,6 +44,7 @@ int main(int argc, char **argv) { q_args->buf = malloc(INBUFSIZE); app->query_args = q_args; q_args->file_list_ui = init_file_list(&(app->left_win), &(app->right_win)); + draw_file_list(q_args->file_list_ui); /* init client to connect to the server */ app->params->sd = init_client(); diff --git a/src/modals/register.c b/src/modals/register.c index bac1657..bf6704f 100644 --- a/src/modals/register.c +++ b/src/modals/register.c @@ -8,6 +8,8 @@ #include #include "alert.h" +#include "group.h" +#include "widget_core.h" typedef struct { button_t *element; @@ -46,8 +48,8 @@ void init_register_modal_cb(callback_args_t *args) { app->params->uname[in_name->value_len] = 0; strncpy(app->params->pass, in_pass->value, in_pass->value_len); app->params->pass[in_pass->value_len] = 0; - sprintf(query, "register %s %s\n%n", app->params->uname, app->params->pass, - &qlen); + sprintf(query, "register %s %s\n%n", app->params->uname, + app->params->pass, &qlen); write(app->params->sd, query, qlen); app->query_args->state = S_WAIT_REGISTER_CONFIRMATION; break; @@ -61,16 +63,24 @@ void init_register_modal_cb(callback_args_t *args) { dialogue_t *init_register_modal(app_t *app) { if (app == NULL) return NULL; + group_el_init_t content_name_email[] = { + {.type = w_input, .label = "Username", .length = 18}, + {.type = w_input, .label = "Email", .length = 18}, + {.type = w_end}}; + + group_el_init_t content_pass[] = {{.type = w_input, + .label = "Password", + .length = 18, + .is_hidden_value = true}, + {.type = w_input, + .label = "Password (repeat)", + .length = 18, + .is_hidden_value = true}, + {.type = w_end}}; + group_el_init_t content[] = { - {.type = w_input, .label = "Username", .length = 15}, - {.type = w_input, - .label = "Password", - .length = 15, - .is_hidden_value = true}, - {.type = w_input, - .label = "Password (repeat)", - .length = 15, - .is_hidden_value = true}, + {.type = w_group, .direction = vertical, .children = content_name_email}, + {.type = w_group, .direction = vertical, .children = content_pass}, {.type = w_end}}; group_el_init_t actions[] = { diff --git a/src/widget/button.c b/src/widget/button.c index 8852d75..246c581 100644 --- a/src/widget/button.c +++ b/src/widget/button.c @@ -21,8 +21,16 @@ button_t *init_button(WINDOW **win, widget_t *w_parent, char *label) { int32_t draw_button(button_t *btn, uint32_t active_id) { char title[DIALOGUE_TITLE + 6]; WINDOW *win = *(btn->w.parent_win); - uint32_t margin_y = btn->w.m_y + btn->w.w_parent->m_y; - uint32_t margin_x = btn->w.m_x + btn->w.w_parent->m_x; + uint32_t margin_y = btn->w.m_y; + uint32_t margin_x = btn->w.m_x; + + widget_t *w_par = btn->w.w_parent; + + while (w_par->parent_win == btn->w.parent_win) { + margin_y += w_par->m_y; + margin_x += w_par->m_x; + w_par = w_par->w_parent; + } if (btn->w.id == active_id) { sprintf(title, "[< %s >]", btn->w.title); diff --git a/src/widget/dialogue.c b/src/widget/dialogue.c index 8de483f..31228ec 100644 --- a/src/widget/dialogue.c +++ b/src/widget/dialogue.c @@ -179,7 +179,8 @@ void init_dialogue(dialogue_t *dialogue, const char title[], const char text[], t_size = strlen(text); strcpy(dialogue->text, text); /* trim the last new line symbol */ - if (t_size && dialogue->text[t_size-1] == '\n') dialogue->text[t_size-1] = '\0'; + if (t_size && dialogue->text[t_size - 1] == '\n') + dialogue->text[t_size - 1] = '\0'; } void dialogue_init_active_id(dialogue_t *dialogue) { @@ -211,34 +212,6 @@ void dialogue_init_active_id(dialogue_t *dialogue) { } } -// void dialogue_init_active_id(dialogue_t *dialogue) { -// enum w_type widget_type; - -// if (dialogue->g_content != NULL) { -// for (int i = 0; i < dialogue->g_content->count; i++) { -// widget_type = dialogue->g_content->elements[i].type; -// if (widget_type == w_button || widget_type == w_input || -// widget_type == w_fs_file_list) { /* Add here new types */ -// dialogue->active.type = g_content; -// widget_t *w = (widget_t *)dialogue->g_content->elements[i].element; -// dialogue->active.id = w->id; -// break; -// } -// } -// } else if (dialogue->g_action != NULL) { -// for (int i = 0; i < dialogue->g_action->count; i++) { -// widget_type = dialogue->g_action->elements[i].type; -// if (widget_type == w_button || widget_type == w_input || -// widget_type == w_fs_file_list) { -// dialogue->active.type = g_action; -// widget_t *w = (widget_t *)dialogue->g_action->elements[i].element; -// dialogue->active.id = w->id; -// break; -// } -// } -// } -// } - #define DETECT_GROUP_SIZE(group, line_max_len, y, x) \ if (group) { \ group->w.m_y = y; \ @@ -317,10 +290,10 @@ int32_t draw_dialogue(dialogue_t *d) { wattroff(d->win, A_REVERSE); if (d->g_content != NULL) { - draw_group(d->win, d->g_content, d->active.id, &(d->w)); + draw_group(d->win, d->g_content, d->active.id); } if (d->g_action != NULL) { - draw_group(d->win, d->g_action, d->active.id, &(d->w)); + draw_group(d->win, d->g_action, d->active.id); } /* move cursor */ @@ -331,19 +304,18 @@ int32_t draw_dialogue(dialogue_t *d) { FIND_ACTIVE_ELEMENT(d->g_action, d->active.id, ae_ptr, ae_idx); } - if (ae_ptr != NULL && ae_ptr->id == d->active.id) { - if (ae_ptr->type == w_input) { - input_t *input = ae_ptr->element; - d->w.cur.y = input->w.cur.y; - d->w.cur.x = input->w.cur.x + input->value_len; - d->w.cur.x -= input->cur_pos; - if (d->w.cur.y || d->w.cur.x) { - wmove(d->win, d->w.cur.y, d->w.cur.x); - curs_set(true); - } - } else { - curs_set(false); + if (ae_ptr != NULL && ae_ptr->id == d->active.id && ae_ptr->type == w_input) { + input_t *input = ae_ptr->element; + d->w.cur.y = input->w.cur.y; + d->w.cur.x = input->w.cur.x + input->value_len; + d->w.cur.x -= input->cur_pos; + if (d->w.cur.y || d->w.cur.x) { + wmove(d->win, d->w.cur.y, d->w.cur.x); + curs_set(true); } + + } else { + curs_set(false); } d->needs_update = false; diff --git a/src/widget/file_list.c b/src/widget/file_list.c index 3156e18..e4ed0dc 100644 --- a/src/widget/file_list.c +++ b/src/widget/file_list.c @@ -114,6 +114,8 @@ ui_file_list_t *init_file_list(WINDOW **win, WINDOW *const *info_win) { fui->max_lines = getmaxy(parent_win) - 3; /* 2+1 (bottom info line) */ fui->active_search = false; fui->search_key = bfromcstrrangealloc(12, 64, ""); + fui->start = NULL; + fui->current = NULL; return fui; } @@ -135,11 +137,16 @@ void draw_file_list(ui_file_list_t *fui) { int32_t sz_y_f = sz_y - 2; /* TODO: можно заменить на поле max_lines */ p_y = 1; p_x = 1; - fl_item_t *el = *(fui->start); - fl_item_t *active_el = *(fui->start); + fl_item_t *el = NULL; + fl_item_t *active_el = NULL; int32_t cur_el_idx = 0; curs_set(false); + if (fui->start != NULL) { + el = *(fui->start); + active_el = el; + } + if (fui->activate_last) { fui->current_idx = fui->current_count - 1; fui->activate_last = false; diff --git a/src/widget/group.c b/src/widget/group.c index c8b5971..2a04bbc 100644 --- a/src/widget/group.c +++ b/src/widget/group.c @@ -3,12 +3,14 @@ #include "fs_file_list.h" #include "input.h" #include "progress_bar.h" +#include "widget_core.h" #include #include #include #include #include +/* make response -1 */ #define MAKE_RESPONSE_M1(args, resp_data, response) \ if (args->resp_data != NULL) { \ *response = -1; \ @@ -62,7 +64,7 @@ void group_default_callback(callback_args_t *args) { } } } else { - widget = (widget_t *) element_ptr->element; + widget = (widget_t *)element_ptr->element; if (widget->callback != NULL) { memccpy(&new_args, args, 1, sizeof(callback_args_t)); new_args.element = element_ptr->element; @@ -104,9 +106,13 @@ group_t *init_group(WINDOW **win, widget_t *w_parent, group_el_init_t *children, w = &(((button_t *)elements[i].element)->w); break; case w_box: - case w_group: case w_end: break; + case w_group: + elements[i].element = init_group(win, &(group->w), children[i].children, + children[i].direction); + w = &(((input_t *)elements[i].element)->w); + break; case w_input: elements[i].element = init_input(win, &(group->w), children[i].label, children[i].length, @@ -130,6 +136,12 @@ group_t *init_group(WINDOW **win, widget_t *w_parent, group_el_init_t *children, group->w.x += w->x + 1; if (group->w.y < w->y) group->w.y = w->y; + } else { + w->m_x = group->w.m_x; + w->m_y = group->w.m_y + group->w.y; + group->w.y += w->y; + if (group->w.x < w->x) + group->w.x = w->x; } if (i == 0) { group->first_id = w->id; @@ -139,11 +151,13 @@ group_t *init_group(WINDOW **win, widget_t *w_parent, group_el_init_t *children, } } + if (direction == vertical) + group->w.y++; + return group; } -void draw_group(WINDOW *win, group_t *group, int32_t active_id, - widget_t *dialog_w) { +void draw_group(WINDOW *win, group_t *group, int32_t active_id) { group_el_t *children = group->elements; for (int i = 0; i < group->count; i++) { group_el_t *el = &children[i]; @@ -151,8 +165,10 @@ void draw_group(WINDOW *win, group_t *group, int32_t active_id, case w_button: draw_button((button_t *)el->element, active_id); break; - case w_box: case w_group: + draw_group(win, (group_t *)el->element, active_id); + break; + case w_box: case w_end: break; case w_input: @@ -176,8 +192,10 @@ void destroy_group(group_t *group) { case w_button: destroy_button(el->element); break; - case w_box: case w_group: + destroy_group(el->element); + break; + case w_box: case w_end: break; case w_input: diff --git a/src/widget/group.h b/src/widget/group.h index e7cf353..36e4c68 100644 --- a/src/widget/group.h +++ b/src/widget/group.h @@ -16,12 +16,14 @@ typedef struct { uint32_t is_default : 1; } group_el_t; -typedef struct { +typedef struct group_el_init_t { enum w_type type; - uint32_t is_default : 1; - uint32_t is_hidden_value : 1; + bool is_default; + bool is_hidden_value; char label[DIALOGUE_TITLE]; uint32_t length; + enum g_direction direction; + struct group_el_init_t *children; } group_el_init_t; typedef struct { @@ -38,8 +40,7 @@ typedef struct { group_t *init_group(WINDOW **win, widget_t *w_parent, group_el_init_t *children, enum g_direction dir); -void draw_group(WINDOW *win, group_t *group, int32_t active_id, - widget_t *dialog_w); +void draw_group(WINDOW *win, group_t *group, int32_t active_id); void destroy_group(group_t *group); void group_default_callback(callback_args_t *args); diff --git a/src/widget/input.c b/src/widget/input.c index 518884a..eeea55a 100644 --- a/src/widget/input.c +++ b/src/widget/input.c @@ -4,16 +4,16 @@ #include #include "app.h" -#include #include "input.h" +#include input_t *init_input(WINDOW **win, widget_t *w_parent, char *label, uint32_t length, uint32_t is_hidden_value) { input_t *input = malloc(sizeof(input_t)); init_widget(&(input->w), w_parent, win, label); input->is_disabled = 0; - input->w.y = 3; // with borders - input->w.x = length + 3; // with borders and extra space for the last element + input->w.y = 3; // with borders + input->w.x = length + 3; // with borders and extra space for the last element uint32_t t_len = strlen(input->w.title) + 4; // with borders and space if (input->w.x < t_len) input->w.x = t_len; @@ -29,8 +29,16 @@ input_t *init_input(WINDOW **win, widget_t *w_parent, char *label, int32_t draw_input(input_t *input, uint32_t active_id) { const char stars[] = "*******************"; WINDOW *win = *(input->w.parent_win); - uint32_t margin_y = input->w.m_y + input->w.w_parent->m_y; - uint32_t margin_x = input->w.m_x + input->w.w_parent->m_x; + uint32_t margin_y = input->w.m_y; + uint32_t margin_x = input->w.m_x; + + /* counts margins of the ancestors of the same window */ + widget_t *w_par = input->w.w_parent; + while (w_par->parent_win == input->w.parent_win) { + margin_y += w_par->m_y; + margin_x += w_par->m_x; + w_par = w_par->w_parent; + } wattrset(win, COLOR_PAIR(modal_color_pair)); @@ -57,13 +65,13 @@ int32_t draw_input(input_t *input, uint32_t active_id) { } if (input->is_hidden) { - mvwprintw(win, margin_y + 1, margin_x, "%.*s%*s", (int) input->value_len, stars, - input->w.x - input->value_len - 2, ""); + mvwprintw(win, margin_y + 1, margin_x, "%.*s%*s", (int)input->value_len, + stars, input->w.x - input->value_len - 2, ""); } else { mvwprintw(win, margin_y + 1, margin_x, "%s%*s", input->value, input->w.x - input->value_len - 2, ""); } - + input->w.cur.y = margin_y + 1; input->w.cur.x = margin_x; From acd03b27699da8690bbd9c53373ece99c97adb79 Mon Sep 17 00:00:00 2001 From: Alexander Zhylin Date: Fri, 20 Feb 2026 16:59:56 +0200 Subject: [PATCH 2/6] UI: nested groups: fixes first and last IDs in a parent --- TODO.md | 7 ++++++- src/widget/dialogue.h | 6 +++--- src/widget/group.c | 33 ++++++++++++++++++++++++++------- src/widget/group.h | 5 +++-- 4 files changed, 38 insertions(+), 13 deletions(-) diff --git a/TODO.md b/TODO.md index 911c38c..13f44a3 100644 --- a/TODO.md +++ b/TODO.md @@ -1,15 +1,20 @@ # TODO: +## Current: +- [ ] UI: vertical layout (needs for email in registration) +- [ ] Nested groups +- [ ] Create dynamic map of elements IDs and their pointers + ## Features: - [x] Search files - [x] Correct spacing in the search bar - [x] Finish registration -- [ ] UI: vertical layout (needs for email in registration) - [ ] Get rid of old notification mechanism - [ ] Privilege settings for files - [ ] Files removal - [ ] Encryption - [ ] UTF-8 support + ## Fixes: - [ ] Check and fix registration and login - [ ] Hotkeys for buttons diff --git a/src/widget/dialogue.h b/src/widget/dialogue.h index 895a120..f8b92c5 100644 --- a/src/widget/dialogue.h +++ b/src/widget/dialogue.h @@ -20,9 +20,9 @@ typedef struct { enum g_type type; } active; char text[DIALOGUE_TEXT]; - uint32_t is_initiated : 1; - uint32_t needs_update : 1; - uint32_t needs_destroy : 1; + bool is_initiated; + bool needs_update; + bool needs_destroy; coordinates_t *p_coordinates; enum d_color_scheme color_scheme; } dialogue_t; diff --git a/src/widget/group.c b/src/widget/group.c index 2a04bbc..71820d1 100644 --- a/src/widget/group.c +++ b/src/widget/group.c @@ -77,9 +77,17 @@ void group_default_callback(callback_args_t *args) { } } +union current_element { + group_t *group; + enum w_type type; +}; + group_t *init_group(WINDOW **win, widget_t *w_parent, group_el_init_t *children, enum g_direction direction) { group_t *group = malloc(sizeof(group_t)); + union current_element current; + group->parent_group = NULL; + current.type = w_end; init_widget(&(group->w), w_parent, win, ""); /* count elements */ group->count = 0; @@ -111,7 +119,16 @@ group_t *init_group(WINDOW **win, widget_t *w_parent, group_el_init_t *children, case w_group: elements[i].element = init_group(win, &(group->w), children[i].children, children[i].direction); - w = &(((input_t *)elements[i].element)->w); + w = &(((group_t *)elements[i].element)->w); + current.group = (group_t *)elements[i].element; + current.group->parent_group = group; + if (i == 0) { + group->first_id = current.group->first_id; + group->last_id = current.group->last_id; + } else { + group->last_id = current.group->last_id; + } + current.type = w_group; break; case w_input: elements[i].element = @@ -128,6 +145,14 @@ group_t *init_group(WINDOW **win, widget_t *w_parent, group_el_init_t *children, w = &(((ui_progress_bar_t *)elements[i].element)->w); break; } + if (current.type != w_group) { + if (i == 0) { + group->first_id = w->id; + group->last_id = w->id; + } else { + group->last_id = w->id; + } + } /* set dimensions */ elements[i].id = w->id; if (direction == horizontal) { @@ -143,12 +168,6 @@ group_t *init_group(WINDOW **win, widget_t *w_parent, group_el_init_t *children, if (group->w.x < w->x) group->w.x = w->x; } - if (i == 0) { - group->first_id = w->id; - group->last_id = w->id; - } else { - group->last_id = w->id; - } } if (direction == vertical) diff --git a/src/widget/group.h b/src/widget/group.h index 36e4c68..6f3deb5 100644 --- a/src/widget/group.h +++ b/src/widget/group.h @@ -13,7 +13,7 @@ typedef struct { uint32_t id; void *element; enum w_type type; - uint32_t is_default : 1; + bool is_default; } group_el_t; typedef struct group_el_init_t { @@ -26,12 +26,13 @@ typedef struct group_el_init_t { struct group_el_init_t *children; } group_el_init_t; -typedef struct { +typedef struct group_t{ widget_t w; group_el_t *elements; enum g_direction direction; uint32_t count; uint32_t first_id, last_id; + struct group_t *parent_group; } group_t; #define FIND_ACTIVE_ELEMENT(g, active_id, element_ptr, element_idx) \ From 57b858f98f66f9fe64356772e4d3579ea36e1db4 Mon Sep 17 00:00:00 2001 From: Alexander Zhylin Date: Mon, 23 Feb 2026 16:32:02 +0200 Subject: [PATCH 3/6] ui: dialogue: +id_map --- src/CMakeLists.txt | 2 ++ src/modals/CMakeLists.txt | 11 +++++++++-- src/modals/alert.c | 2 +- src/modals/ask_server_addr.c | 4 ++-- src/modals/download_pr.c | 4 ++-- src/modals/login_credentials.c | 4 ++-- src/modals/login_option.c | 7 +++---- src/modals/notification.c | 2 +- src/modals/register.c | 6 ++++-- src/modals/server_message.c | 2 +- src/modals/upload_dialogue.c | 4 ++-- src/modals/upload_props_dialogue.c | 4 ++-- src/utils/CMakeLists.txt | 15 +++++++++++++++ src/utils/d_array.c | 26 ++++++++++++++++++++++++++ src/utils/d_array.h | 15 +++++++++++++++ src/utils/utils.h | 4 ++++ src/widget/CMakeLists.txt | 4 +++- src/widget/dialogue.c | 1 + src/widget/dialogue.h | 4 +++- src/widget/group.c | 7 +++++-- src/widget/group.h | 5 +++-- src/widget/widget.h | 1 - 22 files changed, 106 insertions(+), 28 deletions(-) create mode 100644 src/utils/CMakeLists.txt create mode 100644 src/utils/d_array.c create mode 100644 src/utils/d_array.h create mode 100644 src/utils/utils.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 073fdc7..5ff0c89 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -44,6 +44,7 @@ add_subdirectory(widget_core) add_subdirectory(widget) add_subdirectory(modals) add_subdirectory(lib) +add_subdirectory(utils) target_link_libraries(zbbs-client-tui PRIVATE @@ -51,5 +52,6 @@ target_link_libraries(zbbs-client-tui widget modals bstring + utils ${CURSES_LIBRARIES} ) \ No newline at end of file diff --git a/src/modals/CMakeLists.txt b/src/modals/CMakeLists.txt index 5634ebe..f1d75a5 100644 --- a/src/modals/CMakeLists.txt +++ b/src/modals/CMakeLists.txt @@ -3,7 +3,13 @@ add_library(modals STATIC) file(GLOB M_HEADERS "*.h") file(GLOB M_SOURCES "*.c") -target_link_libraries(modals PRIVATE widget widget_core bstring) +target_link_libraries(modals + PRIVATE + widget + widget_core + bstring + utils +) target_sources(modals PRIVATE @@ -16,4 +22,5 @@ target_sources(modals target_include_directories(modals PRIVATE "../widget") target_include_directories(modals PRIVATE "../widget_core") -target_include_directories(modals PRIVATE "../lib/") \ No newline at end of file +target_include_directories(modals PRIVATE "../lib/") +target_include_directories(widget PRIVATE "../utils/") \ No newline at end of file diff --git a/src/modals/alert.c b/src/modals/alert.c index 7863d4f..fa5b7fd 100644 --- a/src/modals/alert.c +++ b/src/modals/alert.c @@ -33,7 +33,7 @@ void notification(const char *title, const char *message, d->w.callback = alert_cb; d->g_content = NULL; - d->g_action = init_group(&(d->win), &(d->w), actions, horizontal); + d->g_action = init_group(&(d->win), &(d->w), actions,&(d->id_map), horizontal); dialogue_init_active_id(d); d->color_scheme = color; diff --git a/src/modals/ask_server_addr.c b/src/modals/ask_server_addr.c index c9d5100..87d6416 100644 --- a/src/modals/ask_server_addr.c +++ b/src/modals/ask_server_addr.c @@ -60,8 +60,8 @@ dialogue_t *init_asa_modal(app_t *app) { dialogue_t *d = &(app->modal); d->w.callback = asa_modal_dialogue_callback; - d->g_content = init_group(&(d->win), &(d->w), content, horizontal); - d->g_action = init_group(&(d->win), &(d->w), actions, horizontal); + d->g_content = init_group(&(d->win), &(d->w), content,&(d->id_map), horizontal); + d->g_action = init_group(&(d->win), &(d->w), actions,&(d->id_map), horizontal); dialogue_init_active_id(d); diff --git a/src/modals/download_pr.c b/src/modals/download_pr.c index 214eed9..13cc980 100644 --- a/src/modals/download_pr.c +++ b/src/modals/download_pr.c @@ -24,8 +24,8 @@ dialogue_t *init_dwn_pr_modal(app_t *app, bool is_upload) { dialogue_t *d = &(app->modal); d->w.callback = dwn_pr_modal_cb; - d->g_content = init_group(&(d->win), &(d->w), content, horizontal); - d->g_action = init_group(&(d->win), &(d->w), actions, horizontal); + d->g_content = init_group(&(d->win), &(d->w), content, &(d->id_map), horizontal); + d->g_action = init_group(&(d->win), &(d->w), actions, &(d->id_map), horizontal); app->query_args->progress_bar = d->g_content->elements[0].element; app->query_args->active_dialogue = d; diff --git a/src/modals/login_credentials.c b/src/modals/login_credentials.c index e844040..c139b0d 100644 --- a/src/modals/login_credentials.c +++ b/src/modals/login_credentials.c @@ -65,8 +65,8 @@ dialogue_t *init_login_credentials_modal(app_t *app) { dialogue_t *d = &(app->modal); d->w.callback = init_login_credentials_modal_cb; - d->g_content = init_group(&(d->win), &(d->w), content, horizontal); - d->g_action = init_group(&(d->win), &(d->w), actions, horizontal); + d->g_content = init_group(&(d->win), &(d->w), content, &(d->id_map), horizontal); + d->g_action = init_group(&(d->win), &(d->w), actions, &(d->id_map), horizontal); dialogue_init_active_id(d); diff --git a/src/modals/login_option.c b/src/modals/login_option.c index 439b807..eca5975 100644 --- a/src/modals/login_option.c +++ b/src/modals/login_option.c @@ -27,7 +27,7 @@ void login_modal_option_cb(callback_args_t *args) { d->needs_destroy = true; app->params->uname = malloc(sizeof "anonymous"); strcpy(app->params->uname, "anonymous"); - write(app->params->sd, app->params->uname, sizeof "anonymous"-1); + write(app->params->sd, app->params->uname, sizeof "anonymous" - 1); app->query_args->state = S_WAIT_SERVER; print_bars(app); break; @@ -55,9 +55,8 @@ dialogue_t *init_login_option_modal(app_t *app) { "Select your login " "option", &(app->coordinates)); - app->modal.g_action = - init_group(&(app->modal.win), &(app->modal.w), children, - horizontal); + app->modal.g_action = init_group(&(app->modal.win), &(app->modal.w), children, + &(app->modal.id_map), horizontal); app->modal.w.callback = login_modal_option_cb; dialogue_init_active_id(&(app->modal)); diff --git a/src/modals/notification.c b/src/modals/notification.c index f0cdd05..aed98f1 100644 --- a/src/modals/notification.c +++ b/src/modals/notification.c @@ -43,7 +43,7 @@ dialogue_t *init_notification_modal(app_t *app) { d->w.callback = init_notification_modal_cb; d->g_content = NULL; - d->g_action = init_group(&(d->win), &(d->w), actions, horizontal); + d->g_action = init_group(&(d->win), &(d->w), actions, &(d->id_map), horizontal); dialogue_init_active_id(d); diff --git a/src/modals/register.c b/src/modals/register.c index bf6704f..894cda8 100644 --- a/src/modals/register.c +++ b/src/modals/register.c @@ -93,8 +93,10 @@ dialogue_t *init_register_modal(app_t *app) { dialogue_t *d = &(app->modal); d->w.callback = init_register_modal_cb; - d->g_content = init_group(&(d->win), &(d->w), content, horizontal); - d->g_action = init_group(&(d->win), &(d->w), actions, horizontal); + d->g_content = + init_group(&(d->win), &(d->w), content, &(d->id_map), horizontal); + d->g_action = + init_group(&(d->win), &(d->w), actions, &(d->id_map), horizontal); dialogue_init_active_id(d); diff --git a/src/modals/server_message.c b/src/modals/server_message.c index 9c312b4..fd98b49 100644 --- a/src/modals/server_message.c +++ b/src/modals/server_message.c @@ -44,7 +44,7 @@ void *init_server_message_modal(app_t *app) { init_dialogue(&(app->modal), "Server response", app->query_args->server_message.text, &(app->coordinates)); app->modal.g_action = - init_group(&(app->modal.win), &(app->modal.w), children, horizontal); + init_group(&(app->modal.win), &(app->modal.w), children, &(app->modal.id_map), horizontal); app->modal.w.callback = server_message_modal_cb; dialogue_init_active_id(&(app->modal)); diff --git a/src/modals/upload_dialogue.c b/src/modals/upload_dialogue.c index ea9fbd3..475b8a3 100644 --- a/src/modals/upload_dialogue.c +++ b/src/modals/upload_dialogue.c @@ -49,8 +49,8 @@ dialogue_t *init_upload_dialogue_modal(app_t *app) { dialogue_t *d = &(app->modal); d->w.callback = upload_dialogue_modal_cb; - d->g_content = init_group(&(d->win), &(d->w), content, horizontal); - d->g_action = init_group(&(d->win), &(d->w), actions, horizontal); + d->g_content = init_group(&(d->win), &(d->w), content, &(d->id_map), horizontal); + d->g_action = init_group(&(d->win), &(d->w), actions, &(d->id_map), horizontal); app->query_args->active_dialogue = d; diff --git a/src/modals/upload_props_dialogue.c b/src/modals/upload_props_dialogue.c index dc7be6d..b7eb3c4 100644 --- a/src/modals/upload_props_dialogue.c +++ b/src/modals/upload_props_dialogue.c @@ -64,8 +64,8 @@ dialogue_t *init_upload_props_dialogue_modal(app_t *app) { dialogue_t *d = &(app->modal); d->w.callback = upload_props_dialogue_modal_cb; - d->g_content = init_group(&(d->win), &(d->w), content, horizontal); - d->g_action = init_group(&(d->win), &(d->w), actions, horizontal); + d->g_content = init_group(&(d->win), &(d->w), content, &(d->id_map), horizontal); + d->g_action = init_group(&(d->win), &(d->w), actions, &(d->id_map), horizontal); app->query_args->active_dialogue = d; diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt new file mode 100644 index 0000000..7c04195 --- /dev/null +++ b/src/utils/CMakeLists.txt @@ -0,0 +1,15 @@ +add_library(utils STATIC) + +file(GLOB M_HEADERS "*.h") +file(GLOB M_SOURCES "*.c") + +list(REMOVE_ITEM M_HEADERS "utils.h") + +target_sources(utils + PRIVATE + ${M_SOURCES} + PUBLIC + FILE_SET HEADERS + FILES + ${M_HEADERS} +) diff --git a/src/utils/d_array.c b/src/utils/d_array.c new file mode 100644 index 0000000..8347f4c --- /dev/null +++ b/src/utils/d_array.c @@ -0,0 +1,26 @@ + +#include "d_array.h" +#include +#include + +void init_d_arr_ptr(d_array_ptr_t *arr, int64_t init_capacity) { + arr->capacity = init_capacity; + arr->length = 0; + arr->arr = malloc(sizeof(void *) * init_capacity); +} + +void add_d_arr_ptr(d_array_ptr_t *arr, void *ptr, int64_t idx) { + if (idx > arr->capacity - 1) { + arr->capacity = idx * 2; + arr->arr = realloc(arr->arr, arr->capacity); + } + arr->arr[idx] = ptr; + arr->length = idx+1; +} + +void free_d_arr(d_array_ptr_t *arr) { + arr->capacity = 0; + arr->length = 0; + free(arr->arr); + arr->arr = NULL; +} \ No newline at end of file diff --git a/src/utils/d_array.h b/src/utils/d_array.h new file mode 100644 index 0000000..b0fc803 --- /dev/null +++ b/src/utils/d_array.h @@ -0,0 +1,15 @@ +#ifndef D_ARRAY_H +#define D_ARRAY_H +/* Dynamic array of numbers */ +#include +typedef struct d_array_ptr_t { + int64_t capacity; + int64_t length; + void **arr; +} d_array_ptr_t; + +void init_d_arr_ptr(d_array_ptr_t *arr, int64_t init_capacity); +void add_d_arr_ptr(d_array_ptr_t *arr, void *ptr, int64_t idx); +void free_d_arr(d_array_ptr_t *arr); + +#endif \ No newline at end of file diff --git a/src/utils/utils.h b/src/utils/utils.h new file mode 100644 index 0000000..be68e58 --- /dev/null +++ b/src/utils/utils.h @@ -0,0 +1,4 @@ +#ifndef D_UTILS_H +#define D_UTILS_H +#include "d_array.h" +#endif \ No newline at end of file diff --git a/src/widget/CMakeLists.txt b/src/widget/CMakeLists.txt index 978176e..fcaa36f 100644 --- a/src/widget/CMakeLists.txt +++ b/src/widget/CMakeLists.txt @@ -18,7 +18,9 @@ target_link_libraries(widget PRIVATE bstring widget_core + utils ) target_include_directories(widget PRIVATE "../widget_core") -target_include_directories(widget PRIVATE "../lib/") \ No newline at end of file +target_include_directories(widget PRIVATE "../lib/") +target_include_directories(widget PRIVATE "../utils/") \ No newline at end of file diff --git a/src/widget/dialogue.c b/src/widget/dialogue.c index 31228ec..ebc9038 100644 --- a/src/widget/dialogue.c +++ b/src/widget/dialogue.c @@ -178,6 +178,7 @@ void init_dialogue(dialogue_t *dialogue, const char title[], const char text[], strcpy(dialogue->w.title, title); t_size = strlen(text); strcpy(dialogue->text, text); + init_d_arr_ptr(&(dialogue->id_map), MAX_IDS); /* trim the last new line symbol */ if (t_size && dialogue->text[t_size - 1] == '\n') dialogue->text[t_size - 1] = '\0'; diff --git a/src/widget/dialogue.h b/src/widget/dialogue.h index f8b92c5..acb7504 100644 --- a/src/widget/dialogue.h +++ b/src/widget/dialogue.h @@ -6,7 +6,8 @@ #include #include #include - +#include +#define MAX_IDS 64 enum g_type { g_content, g_action }; enum d_color_scheme { dc_normal, dc_alert }; @@ -25,6 +26,7 @@ typedef struct { bool needs_destroy; coordinates_t *p_coordinates; enum d_color_scheme color_scheme; + d_array_ptr_t id_map; } dialogue_t; void init_dialogue(dialogue_t *d, const char title[], const char text[], diff --git a/src/widget/group.c b/src/widget/group.c index 71820d1..d8159dc 100644 --- a/src/widget/group.c +++ b/src/widget/group.c @@ -1,5 +1,6 @@ #include "group.h" #include "button.h" +#include "d_array.h" #include "fs_file_list.h" #include "input.h" #include "progress_bar.h" @@ -9,6 +10,7 @@ #include #include #include +#include /* make response -1 */ #define MAKE_RESPONSE_M1(args, resp_data, response) \ @@ -83,7 +85,7 @@ union current_element { }; group_t *init_group(WINDOW **win, widget_t *w_parent, group_el_init_t *children, - enum g_direction direction) { + d_array_ptr_t *id_map, enum g_direction direction) { group_t *group = malloc(sizeof(group_t)); union current_element current; group->parent_group = NULL; @@ -118,7 +120,7 @@ group_t *init_group(WINDOW **win, widget_t *w_parent, group_el_init_t *children, break; case w_group: elements[i].element = init_group(win, &(group->w), children[i].children, - children[i].direction); + id_map, children[i].direction); w = &(((group_t *)elements[i].element)->w); current.group = (group_t *)elements[i].element; current.group->parent_group = group; @@ -155,6 +157,7 @@ group_t *init_group(WINDOW **win, widget_t *w_parent, group_el_init_t *children, } /* set dimensions */ elements[i].id = w->id; + add_d_arr_ptr(id_map, &(elements[i]), w->id); if (direction == horizontal) { w->m_x = group->w.m_x + 1 + group->w.x; w->m_y = group->w.m_y; diff --git a/src/widget/group.h b/src/widget/group.h index 6f3deb5..c776b1c 100644 --- a/src/widget/group.h +++ b/src/widget/group.h @@ -3,6 +3,7 @@ #include #include +#include enum g_direction { horizontal, @@ -26,7 +27,7 @@ typedef struct group_el_init_t { struct group_el_init_t *children; } group_el_init_t; -typedef struct group_t{ +typedef struct group_t { widget_t w; group_el_t *elements; enum g_direction direction; @@ -40,7 +41,7 @@ typedef struct group_t{ element_ptr = &(g->elements[element_idx]); group_t *init_group(WINDOW **win, widget_t *w_parent, group_el_init_t *children, - enum g_direction dir); + d_array_ptr_t *id_map, enum g_direction dir); void draw_group(WINDOW *win, group_t *group, int32_t active_id); void destroy_group(group_t *group); void group_default_callback(callback_args_t *args); diff --git a/src/widget/widget.h b/src/widget/widget.h index 2582210..2d7bb7a 100644 --- a/src/widget/widget.h +++ b/src/widget/widget.h @@ -8,5 +8,4 @@ #include "group.h" #include "input.h" #include "progress_bar.h" - #endif \ No newline at end of file From 86c11968dade269aa63fa0090467c0a21315fc76 Mon Sep 17 00:00:00 2001 From: Alexander Zhylin Date: Tue, 24 Feb 2026 16:36:00 +0200 Subject: [PATCH 4/6] ui: refactoring. changes active id mechanism --- src/modals/alert.c | 2 +- src/modals/ask_server_addr.c | 5 +- src/modals/download_pr.c | 5 +- src/modals/login_credentials.c | 5 +- src/modals/login_option.c | 3 +- src/modals/notification.c | 3 +- src/modals/register.c | 4 +- src/modals/server_message.c | 3 +- src/modals/upload_dialogue.c | 5 +- src/modals/upload_props_dialogue.c | 5 +- src/utils/d_array.c | 12 ++- src/utils/d_array.h | 2 + src/widget/dialogue.c | 141 +++++++++++------------------ src/widget/dialogue.h | 7 +- src/widget/group.c | 15 ++- src/widget/group.h | 12 +-- src/widget_core/widget_core.h | 1 + 17 files changed, 104 insertions(+), 126 deletions(-) diff --git a/src/modals/alert.c b/src/modals/alert.c index fa5b7fd..ccf176b 100644 --- a/src/modals/alert.c +++ b/src/modals/alert.c @@ -33,7 +33,7 @@ void notification(const char *title, const char *message, d->w.callback = alert_cb; d->g_content = NULL; - d->g_action = init_group(&(d->win), &(d->w), actions,&(d->id_map), horizontal); + d->g_action = init_group(&(d->win), &(d->w), actions,&(d->id_map), horizontal, g_action); dialogue_init_active_id(d); d->color_scheme = color; diff --git a/src/modals/ask_server_addr.c b/src/modals/ask_server_addr.c index 87d6416..ae1c1b3 100644 --- a/src/modals/ask_server_addr.c +++ b/src/modals/ask_server_addr.c @@ -10,6 +10,7 @@ #include #include "../connection.h" +#include "group.h" typedef struct { button_t *element; @@ -60,8 +61,8 @@ dialogue_t *init_asa_modal(app_t *app) { dialogue_t *d = &(app->modal); d->w.callback = asa_modal_dialogue_callback; - d->g_content = init_group(&(d->win), &(d->w), content,&(d->id_map), horizontal); - d->g_action = init_group(&(d->win), &(d->w), actions,&(d->id_map), horizontal); + d->g_content = init_group(&(d->win), &(d->w), content,&(d->id_map), horizontal, g_content); + d->g_action = init_group(&(d->win), &(d->w), actions,&(d->id_map), horizontal, g_action); dialogue_init_active_id(d); diff --git a/src/modals/download_pr.c b/src/modals/download_pr.c index 13cc980..13173f8 100644 --- a/src/modals/download_pr.c +++ b/src/modals/download_pr.c @@ -1,3 +1,4 @@ +#include "group.h" #include #include #include @@ -24,8 +25,8 @@ dialogue_t *init_dwn_pr_modal(app_t *app, bool is_upload) { dialogue_t *d = &(app->modal); d->w.callback = dwn_pr_modal_cb; - d->g_content = init_group(&(d->win), &(d->w), content, &(d->id_map), horizontal); - d->g_action = init_group(&(d->win), &(d->w), actions, &(d->id_map), horizontal); + d->g_content = init_group(&(d->win), &(d->w), content, &(d->id_map), horizontal, g_content); + d->g_action = init_group(&(d->win), &(d->w), actions, &(d->id_map), horizontal, g_action); app->query_args->progress_bar = d->g_content->elements[0].element; app->query_args->active_dialogue = d; diff --git a/src/modals/login_credentials.c b/src/modals/login_credentials.c index c139b0d..28806b3 100644 --- a/src/modals/login_credentials.c +++ b/src/modals/login_credentials.c @@ -1,3 +1,4 @@ +#include "group.h" #include #include #include @@ -65,8 +66,8 @@ dialogue_t *init_login_credentials_modal(app_t *app) { dialogue_t *d = &(app->modal); d->w.callback = init_login_credentials_modal_cb; - d->g_content = init_group(&(d->win), &(d->w), content, &(d->id_map), horizontal); - d->g_action = init_group(&(d->win), &(d->w), actions, &(d->id_map), horizontal); + d->g_content = init_group(&(d->win), &(d->w), content, &(d->id_map), horizontal, g_content); + d->g_action = init_group(&(d->win), &(d->w), actions, &(d->id_map), horizontal, g_action); dialogue_init_active_id(d); diff --git a/src/modals/login_option.c b/src/modals/login_option.c index eca5975..e7d414f 100644 --- a/src/modals/login_option.c +++ b/src/modals/login_option.c @@ -1,3 +1,4 @@ +#include "group.h" #include #include #include @@ -56,7 +57,7 @@ dialogue_t *init_login_option_modal(app_t *app) { "option", &(app->coordinates)); app->modal.g_action = init_group(&(app->modal.win), &(app->modal.w), children, - &(app->modal.id_map), horizontal); + &(app->modal.id_map), horizontal, g_action); app->modal.w.callback = login_modal_option_cb; dialogue_init_active_id(&(app->modal)); diff --git a/src/modals/notification.c b/src/modals/notification.c index aed98f1..f6e959c 100644 --- a/src/modals/notification.c +++ b/src/modals/notification.c @@ -1,3 +1,4 @@ +#include "group.h" #include #include #include @@ -43,7 +44,7 @@ dialogue_t *init_notification_modal(app_t *app) { d->w.callback = init_notification_modal_cb; d->g_content = NULL; - d->g_action = init_group(&(d->win), &(d->w), actions, &(d->id_map), horizontal); + d->g_action = init_group(&(d->win), &(d->w), actions, &(d->id_map), horizontal, g_action); dialogue_init_active_id(d); diff --git a/src/modals/register.c b/src/modals/register.c index 894cda8..160cc1a 100644 --- a/src/modals/register.c +++ b/src/modals/register.c @@ -94,9 +94,9 @@ dialogue_t *init_register_modal(app_t *app) { d->w.callback = init_register_modal_cb; d->g_content = - init_group(&(d->win), &(d->w), content, &(d->id_map), horizontal); + init_group(&(d->win), &(d->w), content, &(d->id_map), horizontal, g_content); d->g_action = - init_group(&(d->win), &(d->w), actions, &(d->id_map), horizontal); + init_group(&(d->win), &(d->w), actions, &(d->id_map), horizontal, g_action); dialogue_init_active_id(d); diff --git a/src/modals/server_message.c b/src/modals/server_message.c index fd98b49..87b5e77 100644 --- a/src/modals/server_message.c +++ b/src/modals/server_message.c @@ -1,3 +1,4 @@ +#include "group.h" #include #include #include @@ -44,7 +45,7 @@ void *init_server_message_modal(app_t *app) { init_dialogue(&(app->modal), "Server response", app->query_args->server_message.text, &(app->coordinates)); app->modal.g_action = - init_group(&(app->modal.win), &(app->modal.w), children, &(app->modal.id_map), horizontal); + init_group(&(app->modal.win), &(app->modal.w), children, &(app->modal.id_map), horizontal, g_action); app->modal.w.callback = server_message_modal_cb; dialogue_init_active_id(&(app->modal)); diff --git a/src/modals/upload_dialogue.c b/src/modals/upload_dialogue.c index 475b8a3..8245cab 100644 --- a/src/modals/upload_dialogue.c +++ b/src/modals/upload_dialogue.c @@ -1,4 +1,5 @@ #include "../file_processor.h" +#include "group.h" #include #include #include @@ -49,8 +50,8 @@ dialogue_t *init_upload_dialogue_modal(app_t *app) { dialogue_t *d = &(app->modal); d->w.callback = upload_dialogue_modal_cb; - d->g_content = init_group(&(d->win), &(d->w), content, &(d->id_map), horizontal); - d->g_action = init_group(&(d->win), &(d->w), actions, &(d->id_map), horizontal); + d->g_content = init_group(&(d->win), &(d->w), content, &(d->id_map), horizontal, g_content); + d->g_action = init_group(&(d->win), &(d->w), actions, &(d->id_map), horizontal, g_action); app->query_args->active_dialogue = d; diff --git a/src/modals/upload_props_dialogue.c b/src/modals/upload_props_dialogue.c index b7eb3c4..fd90b33 100644 --- a/src/modals/upload_props_dialogue.c +++ b/src/modals/upload_props_dialogue.c @@ -1,3 +1,4 @@ +#include "group.h" #include #include #include @@ -64,8 +65,8 @@ dialogue_t *init_upload_props_dialogue_modal(app_t *app) { dialogue_t *d = &(app->modal); d->w.callback = upload_props_dialogue_modal_cb; - d->g_content = init_group(&(d->win), &(d->w), content, &(d->id_map), horizontal); - d->g_action = init_group(&(d->win), &(d->w), actions, &(d->id_map), horizontal); + d->g_content = init_group(&(d->win), &(d->w), content, &(d->id_map), horizontal, g_content); + d->g_action = init_group(&(d->win), &(d->w), actions, &(d->id_map), horizontal, g_action); app->query_args->active_dialogue = d; diff --git a/src/utils/d_array.c b/src/utils/d_array.c index 8347f4c..979a6fc 100644 --- a/src/utils/d_array.c +++ b/src/utils/d_array.c @@ -2,18 +2,26 @@ #include "d_array.h" #include #include +#include void init_d_arr_ptr(d_array_ptr_t *arr, int64_t init_capacity) { arr->capacity = init_capacity; arr->length = 0; arr->arr = malloc(sizeof(void *) * init_capacity); + arr->fist_el = NULL; + arr->last_el = NULL; + memset(arr->arr, 0, init_capacity); } void add_d_arr_ptr(d_array_ptr_t *arr, void *ptr, int64_t idx) { + int64_t new_capacity = idx * 2; if (idx > arr->capacity - 1) { - arr->capacity = idx * 2; - arr->arr = realloc(arr->arr, arr->capacity); + arr->arr = realloc(arr->arr, new_capacity); + memset(arr->arr+arr->capacity, 0, arr->capacity - new_capacity); + arr->capacity = new_capacity; } + if (arr->fist_el == NULL) arr->fist_el = ptr; + arr->last_el = ptr; arr->arr[idx] = ptr; arr->length = idx+1; } diff --git a/src/utils/d_array.h b/src/utils/d_array.h index b0fc803..786896c 100644 --- a/src/utils/d_array.h +++ b/src/utils/d_array.h @@ -6,6 +6,8 @@ typedef struct d_array_ptr_t { int64_t capacity; int64_t length; void **arr; + void *fist_el; + void *last_el; } d_array_ptr_t; void init_d_arr_ptr(d_array_ptr_t *arr, int64_t init_capacity); diff --git a/src/widget/dialogue.c b/src/widget/dialogue.c index ebc9038..92bacd2 100644 --- a/src/widget/dialogue.c +++ b/src/widget/dialogue.c @@ -8,26 +8,42 @@ #include "file_list.h" #include "group.h" #include "input.h" +#include "widget_core.h" -#define INCR_ACTIVE_ID(d, current, next) \ - if (d->active.id != d->current->last_id) { \ - d->active.id = d->active.id + 1; \ - } else if (d->next != NULL) { \ - d->active.id = d->next->first_id; \ - d->active.type = next; \ - } else { \ - d->active.id = d->current->first_id; \ +void incr_active_id(dialogue_t *d) { + group_el_t *active_el = NULL; + while (true) { + for (int i = d->active_el->id + 1; + active_el == NULL && d->id_map.length > i; i++) { + active_el = d->id_map.arr[i]; + } + d->active_el = active_el; + if (d->active_el == NULL) { + d->active_el = d->id_map.fist_el; + break; + } else if (d->active_el->type != w_box && d->active_el->type != w_group) { + break; + } + active_el = NULL; } +} -#define DECR_ACTIVE_ID(d, current, next) \ - if (d->active.id != d->current->first_id) { \ - d->active.id = d->active.id - 1; \ - } else if (d->next != NULL) { \ - d->active.id = d->next->last_id; \ - d->active.type = next; \ - } else { \ - d->active.id = d->current->last_id; \ +void decr_active_id(dialogue_t *d) { + group_el_t *active_el = NULL; + while (true) { + for (int i = d->active_el->id - 1; active_el == NULL && i > 0; i--) { + active_el = d->id_map.arr[i]; + } + d->active_el = active_el; + if (d->active_el == NULL) { + d->active_el = d->id_map.last_el; + break; + } else if (d->active_el->type != w_box && d->active_el->type != w_group) { + break; + } + active_el = NULL; } +} #define CH_GROUP(d, current, next) \ { \ @@ -42,82 +58,43 @@ } \ } -widget_t *get_active_widget(dialogue_t *d) { - group_t *g; - if (d->active.type == g_action) { - g = d->g_action; - } else { - g = d->g_content; - } - widget_t *w = (widget_t *)g->elements[d->active.id - g->first_id].element; - return w; -} - void dialogue_default_callback(callback_args_t *args) { dialogue_t *d = (void *)args->element; int32_t key = *((int32_t *)args->data); callback_args_t new_args; + new_args.active_el = d->active_el; int32_t *resp_value = (int32_t *)args->resp_data; memcpy(&new_args, args, sizeof(callback_args_t)); - new_args.active_id = d->active.id; + new_args.active_id = d->active_el->id; int32_t diff; d->needs_update = true; input_t *input; widget_t *widget; + switch (key) { case '\t': - if (d->active.type == g_content) { - INCR_ACTIVE_ID(d, g_content, g_action); - } else if (d->active.type == g_action) { - INCR_ACTIVE_ID(d, g_action, g_content); - } + incr_active_id(d); *resp_value = -1; break; case '\33': /* Esc key */ *resp_value = -2; break; case KEY_RIGHT: - if (d->active.type == g_content) { - if (d->g_content->elements[d->active.id - d->g_content->first_id].type == - w_input) { - input = d->g_content->elements[d->active.id - d->g_content->first_id] - .element; - if (input->cur_pos > 0) { - input->cur_pos -= 1; - } - } else { - INCR_ACTIVE_ID(d, g_content, g_action); - } - } else if (d->active.type == g_action) { - INCR_ACTIVE_ID(d, g_action, g_content); - } + incr_active_id(d); *resp_value = -1; break; case KEY_LEFT: - if (d->active.type == g_content) { - if (d->g_content->elements[d->active.id - d->g_content->first_id].type == - w_input) { - input = d->g_content->elements[d->active.id - d->g_content->first_id] - .element; - if (input->value_len > input->cur_pos) { - input->cur_pos += 1; - } - } else { - DECR_ACTIVE_ID(d, g_content, g_action); - } - } else if (d->active.type == g_action) { - DECR_ACTIVE_ID(d, g_action, g_content); - } + decr_active_id(d); *resp_value = -1; break; case '\n': - widget = get_active_widget(d); + widget = (widget_t *)d->active_el->element; if (widget->callback != NULL) { /* Existing callback case */ new_args.element = widget; widget->callback(&new_args); break; } /* Default cases */ - if (d->active.type == g_action) { + if (d->active_el->g_type == g_action) { new_args.element = d->g_action; group_default_callback(&new_args); } else if (d->g_action != NULL) { @@ -129,31 +106,29 @@ void dialogue_default_callback(callback_args_t *args) { } } else { /* TODO: temp solution, improve */ - *resp_value = d->active.id - d->g_content->first_id; + *resp_value = d->active_el->id - d->g_content->first_id; return; } break; case KEY_UP: - if (d->g_content->elements[d->active.id - d->g_content->first_id].type == - w_fs_file_list) { + if (d->active_el->type == w_fs_file_list) { new_args.element = d->g_content; group_default_callback(&new_args); } else { - CH_GROUP(d, g_action, g_content); + /* CH_GROUP(d, g_action, g_content); */ } break; case KEY_DOWN: - if (d->g_content->elements[d->active.id - d->g_content->first_id].type == - w_fs_file_list) { + if (d->active_el->type == w_fs_file_list) { new_args.element = d->g_content; group_default_callback(&new_args); } else { - CH_GROUP(d, g_content, g_action); + /* CH_GROUP(d, g_content, g_action); */ } break; default: /* run callback function */ - if (d->active.type == g_content) { + if (d->active_el->g_type == g_content) { new_args.element = d->g_content; } else { new_args.element = d->g_action; @@ -192,9 +167,7 @@ void dialogue_init_active_id(dialogue_t *dialogue) { widget_type = dialogue->g_content->elements[i].type; if (widget_type == w_button || widget_type == w_input || widget_type == w_fs_file_list) { /* Add here new types */ - dialogue->active.type = g_content; - widget_t *w = (widget_t *)dialogue->g_content->elements[i].element; - dialogue->active.id = w->id; + dialogue->active_el = &(dialogue->g_content->elements[i]); return; } } @@ -204,9 +177,7 @@ void dialogue_init_active_id(dialogue_t *dialogue) { widget_type = dialogue->g_action->elements[i].type; if (widget_type == w_button || widget_type == w_input || widget_type == w_fs_file_list) { - dialogue->active.type = g_action; - widget_t *w = (widget_t *)dialogue->g_action->elements[i].element; - dialogue->active.id = w->id; + dialogue->active_el = &(dialogue->g_action->elements[i]); return; } } @@ -233,7 +204,6 @@ int32_t draw_dialogue(dialogue_t *d) { } group_el_t *ae_ptr = NULL; /* active element */ - uint32_t ae_idx; /* active element */ /* count dimensions */ uint32_t x = 1; /* when uses box */ uint32_t y = 1; /* when uses box */ @@ -291,22 +261,15 @@ int32_t draw_dialogue(dialogue_t *d) { wattroff(d->win, A_REVERSE); if (d->g_content != NULL) { - draw_group(d->win, d->g_content, d->active.id); + draw_group(d->win, d->g_content, d->active_el->id); } if (d->g_action != NULL) { - draw_group(d->win, d->g_action, d->active.id); + draw_group(d->win, d->g_action, d->active_el->id); } /* move cursor */ - if (d->g_content != NULL) { - FIND_ACTIVE_ELEMENT(d->g_content, d->active.id, ae_ptr, ae_idx); - } - if (ae_ptr == NULL && d->g_action != NULL) { - FIND_ACTIVE_ELEMENT(d->g_action, d->active.id, ae_ptr, ae_idx); - } - - if (ae_ptr != NULL && ae_ptr->id == d->active.id && ae_ptr->type == w_input) { - input_t *input = ae_ptr->element; + if (d->active_el != NULL && d->active_el->type == w_input) { + input_t *input = d->active_el->element; d->w.cur.y = input->w.cur.y; d->w.cur.x = input->w.cur.x + input->value_len; d->w.cur.x -= input->cur_pos; diff --git a/src/widget/dialogue.h b/src/widget/dialogue.h index acb7504..3d7f017 100644 --- a/src/widget/dialogue.h +++ b/src/widget/dialogue.h @@ -8,7 +8,7 @@ #include #include #define MAX_IDS 64 -enum g_type { g_content, g_action }; + enum d_color_scheme { dc_normal, dc_alert }; typedef struct { @@ -16,10 +16,7 @@ typedef struct { WINDOW *win; group_t *g_content; group_t *g_action; - struct { - int32_t id; - enum g_type type; - } active; + group_el_t *active_el; char text[DIALOGUE_TEXT]; bool is_initiated; bool needs_update; diff --git a/src/widget/group.c b/src/widget/group.c index d8159dc..5534d02 100644 --- a/src/widget/group.c +++ b/src/widget/group.c @@ -18,24 +18,23 @@ *response = -1; \ } +/* TODO: adapt to a new indexation method */ void group_default_callback(callback_args_t *args) { group_t *g = (group_t *)args->element; int32_t key = *((int32_t *)args->data); int32_t *response = (int32_t *)args->resp_data; input_t *input; widget_t *widget; - group_el_t *element_ptr; - int32_t element_idx = -1; + group_el_t *element_ptr = args->active_el; + int32_t element_idx = element_ptr->id - g->first_id; ; u_int32_t start_pos = 0; callback_args_t new_args; switch (key) { case '\n': /* Enter */ - FIND_ACTIVE_ELEMENT(g, args->active_id, element_ptr, element_idx); *response = element_idx; break; case KEY_BACKSPACE: case KEY_DL: - FIND_ACTIVE_ELEMENT(g, args->active_id, element_ptr, element_idx); if (element_ptr->type == w_input) { input = (input_t *)element_ptr->element; if (input->value_len) { @@ -51,7 +50,6 @@ void group_default_callback(callback_args_t *args) { MAKE_RESPONSE_M1(args, resp_data, response); break; default: - FIND_ACTIVE_ELEMENT(g, args->active_id, element_ptr, element_idx); if (element_ptr->type == w_input) { input = (input_t *)element_ptr->element; if (input->max_len > input->value_len) { @@ -85,7 +83,7 @@ union current_element { }; group_t *init_group(WINDOW **win, widget_t *w_parent, group_el_init_t *children, - d_array_ptr_t *id_map, enum g_direction direction) { + d_array_ptr_t *id_map, enum g_direction direction, enum g_type g_type) { group_t *group = malloc(sizeof(group_t)); union current_element current; group->parent_group = NULL; @@ -105,6 +103,7 @@ group_t *init_group(WINDOW **win, widget_t *w_parent, group_el_init_t *children, for (int32_t i = 0; i < group->count; i++) { elements[i].type = children[i].type; elements[i].is_default = children[i].is_default; + elements[i].g_type = g_type; } /* init child elements */ @@ -120,7 +119,7 @@ group_t *init_group(WINDOW **win, widget_t *w_parent, group_el_init_t *children, break; case w_group: elements[i].element = init_group(win, &(group->w), children[i].children, - id_map, children[i].direction); + id_map, children[i].direction, g_type); w = &(((group_t *)elements[i].element)->w); current.group = (group_t *)elements[i].element; current.group->parent_group = group; @@ -157,7 +156,7 @@ group_t *init_group(WINDOW **win, widget_t *w_parent, group_el_init_t *children, } /* set dimensions */ elements[i].id = w->id; - add_d_arr_ptr(id_map, &(elements[i]), w->id); + add_d_arr_ptr(id_map, elements + i, w->id); if (direction == horizontal) { w->m_x = group->w.m_x + 1 + group->w.x; w->m_y = group->w.m_y; diff --git a/src/widget/group.h b/src/widget/group.h index c776b1c..d3ea051 100644 --- a/src/widget/group.h +++ b/src/widget/group.h @@ -2,8 +2,10 @@ #define GROUP_H #include -#include #include +#include + +enum g_type { g_content, g_action }; enum g_direction { horizontal, @@ -14,6 +16,7 @@ typedef struct { uint32_t id; void *element; enum w_type type; + enum g_type g_type; bool is_default; } group_el_t; @@ -36,12 +39,9 @@ typedef struct group_t { struct group_t *parent_group; } group_t; -#define FIND_ACTIVE_ELEMENT(g, active_id, element_ptr, element_idx) \ - element_idx = active_id - g->first_id; \ - element_ptr = &(g->elements[element_idx]); - group_t *init_group(WINDOW **win, widget_t *w_parent, group_el_init_t *children, - d_array_ptr_t *id_map, enum g_direction dir); + d_array_ptr_t *id_map, enum g_direction dir, + enum g_type g_type); void draw_group(WINDOW *win, group_t *group, int32_t active_id); void destroy_group(group_t *group); void group_default_callback(callback_args_t *args); diff --git a/src/widget_core/widget_core.h b/src/widget_core/widget_core.h index 3a07e09..83473c9 100644 --- a/src/widget_core/widget_core.h +++ b/src/widget_core/widget_core.h @@ -21,6 +21,7 @@ typedef struct { void *data; void *resp_data; uint32_t active_id; + void *active_el; } callback_args_t; enum rsize { From b10766f6e3e68f826e0349bea049150bbfae683d Mon Sep 17 00:00:00 2001 From: Alexander Zhylin Date: Wed, 25 Feb 2026 16:15:28 +0200 Subject: [PATCH 5/6] ui: new value-forwarding method --- TODO.md | 3 +- src/modals/ask_server_addr.c | 7 ++-- src/modals/login_credentials.c | 6 +-- src/modals/login_option.c | 6 +-- src/modals/notification.c | 10 ++--- src/modals/register.c | 15 ++++---- src/modals/server_message.c | 10 ++--- src/modals/upload_dialogue.c | 38 +++++++++--------- src/modals/upload_props_dialogue.c | 6 +-- src/query.c | 8 ++-- src/widget/button.h | 1 - src/widget/dialogue.c | 25 ++++++------ src/widget/fs_file_list.c | 2 +- src/widget/group.c | 22 +++++------ src/widget/group.h | 18 ++------- src/widget/input.h | 10 ++--- src/widget_core/widget_core.h | 62 ++++++++++++++++++++++++------ 17 files changed, 134 insertions(+), 115 deletions(-) diff --git a/TODO.md b/TODO.md index 13f44a3..cdeedc7 100644 --- a/TODO.md +++ b/TODO.md @@ -3,7 +3,8 @@ ## Current: - [ ] UI: vertical layout (needs for email in registration) - [ ] Nested groups -- [ ] Create dynamic map of elements IDs and their pointers +- [x] Create dynamic map of elements IDs and their pointers +- [ ] Adapt modals to new value-forwarding method ## Features: - [x] Search files diff --git a/src/modals/ask_server_addr.c b/src/modals/ask_server_addr.c index ae1c1b3..dfe2a13 100644 --- a/src/modals/ask_server_addr.c +++ b/src/modals/ask_server_addr.c @@ -11,6 +11,7 @@ #include #include "../connection.h" #include "group.h" +#include "widget_core.h" typedef struct { button_t *element; @@ -18,19 +19,17 @@ typedef struct { } group_bt_t; void asa_modal_dialogue_callback(callback_args_t *args) { - int32_t response; callback_args_t d_args; app_t *app = args->app; dialogue_t *d = (dialogue_t *)app->active_widget; memcpy(&d_args, args, sizeof(callback_args_t)); d_args.app = NULL; - d_args.resp_data = &response; d_args.element = app->active_widget; input_t *in_ip = d->g_content->elements[0].element; input_t *in_port = d->g_content->elements[1].element; dialogue_default_callback(&d_args); - if (response > -1) { - switch (response) { + if (d_args.resp_data.code == cbrp_val) { + switch (d_args.resp_data.val.val.num) { case 0: get_ip_port(app->params, in_ip->value, in_port->value); connect_to_server(app); diff --git a/src/modals/login_credentials.c b/src/modals/login_credentials.c index 28806b3..d6de53e 100644 --- a/src/modals/login_credentials.c +++ b/src/modals/login_credentials.c @@ -13,19 +13,17 @@ typedef struct { } group_bt_t; void init_login_credentials_modal_cb(callback_args_t *args) { - int32_t response; callback_args_t d_args; app_t *app = args->app; dialogue_t *d = (dialogue_t *)app->active_widget; memcpy(&d_args, args, sizeof(callback_args_t)); d_args.app = NULL; - d_args.resp_data = &response; d_args.element = app->active_widget; input_t *in_name = d->g_content->elements[0].element; input_t *in_pass = d->g_content->elements[1].element; dialogue_default_callback(&d_args); - if (response > -1) { - switch (response) { + if (d_args.resp_data.code == cbrp_val) { + switch (d_args.resp_data.val.val.num) { case 0: app->params->uname = malloc(in_name->value_len + 1); app->params->pass = malloc(in_pass->value_len + 1); diff --git a/src/modals/login_option.c b/src/modals/login_option.c index e7d414f..e5d7e1b 100644 --- a/src/modals/login_option.c +++ b/src/modals/login_option.c @@ -13,17 +13,15 @@ typedef struct { } group_bt_t; void login_modal_option_cb(callback_args_t *args) { - int32_t response; callback_args_t d_args; app_t *app = args->app; dialogue_t *d = (dialogue_t *)app->active_widget; memcpy(&d_args, args, sizeof(callback_args_t)); d_args.app = NULL; - d_args.resp_data = &response; d_args.element = app->active_widget; dialogue_default_callback(&d_args); - if (response > -1) { - switch (response) { + if (d_args.resp_data.code == cbrp_val) { + switch (d_args.resp_data.val.val.num) { case 0: d->needs_destroy = true; app->params->uname = malloc(sizeof "anonymous"); diff --git a/src/modals/notification.c b/src/modals/notification.c index f6e959c..f85ca1e 100644 --- a/src/modals/notification.c +++ b/src/modals/notification.c @@ -6,21 +6,17 @@ #include -/* TODO: get rid of this mechanism */ - typedef struct { button_t *element; enum w_type type; } group_bt_t; void init_notification_modal_cb(callback_args_t *args) { - int32_t response; callback_args_t d_args; app_t *app = args->app; dialogue_t *d = (dialogue_t *)app->active_widget; memcpy(&d_args, args, sizeof(callback_args_t)); d_args.app = NULL; - d_args.resp_data = &response; d_args.element = app->active_widget; dialogue_default_callback(&d_args); @@ -33,7 +29,8 @@ dialogue_t *init_notification_modal(app_t *app) { return NULL; group_el_init_t actions[] = { - {.type = w_button, .label = "OK", .is_default = 1}, {.type = w_end}}; + {.type = w_button, .label = "OK", .is_default = 1, .val.num = 1}, + {.type = w_end}}; init_dialogue(&(app->modal), "Notification", app->query_args->notification, &(app->coordinates)); @@ -44,7 +41,8 @@ dialogue_t *init_notification_modal(app_t *app) { d->w.callback = init_notification_modal_cb; d->g_content = NULL; - d->g_action = init_group(&(d->win), &(d->w), actions, &(d->id_map), horizontal, g_action); + d->g_action = init_group(&(d->win), &(d->w), actions, &(d->id_map), + horizontal, g_action); dialogue_init_active_id(d); diff --git a/src/modals/register.c b/src/modals/register.c index 160cc1a..a0e570c 100644 --- a/src/modals/register.c +++ b/src/modals/register.c @@ -19,20 +19,19 @@ typedef struct { void init_register_modal_cb(callback_args_t *args) { char query[256]; int32_t qlen = 0; - int32_t response; callback_args_t d_args; app_t *app = args->app; dialogue_t *d = (dialogue_t *)app->active_widget; memcpy(&d_args, args, sizeof(callback_args_t)); d_args.app = NULL; - d_args.resp_data = &response; d_args.element = app->active_widget; input_t *in_name = d->g_content->elements[0].element; input_t *in_pass = d->g_content->elements[1].element; input_t *in_pass_r = d->g_content->elements[2].element; dialogue_default_callback(&d_args); - if (response > -1) { - switch (response) { + if (d_args.resp_data.code == cbrp_val && + d_args.resp_data.val.type == val_num) { + switch (d_args.resp_data.val.val.num) { case 0: if (strcmp(in_pass->value, in_pass_r->value)) { alert("Your passwords do not match"); @@ -93,10 +92,10 @@ dialogue_t *init_register_modal(app_t *app) { dialogue_t *d = &(app->modal); d->w.callback = init_register_modal_cb; - d->g_content = - init_group(&(d->win), &(d->w), content, &(d->id_map), horizontal, g_content); - d->g_action = - init_group(&(d->win), &(d->w), actions, &(d->id_map), horizontal, g_action); + d->g_content = init_group(&(d->win), &(d->w), content, &(d->id_map), + horizontal, g_content); + d->g_action = init_group(&(d->win), &(d->w), actions, &(d->id_map), + horizontal, g_action); dialogue_init_active_id(d); diff --git a/src/modals/server_message.c b/src/modals/server_message.c index 87b5e77..3b6278f 100644 --- a/src/modals/server_message.c +++ b/src/modals/server_message.c @@ -12,18 +12,16 @@ typedef struct { } group_bt_t; void server_message_modal_cb(callback_args_t *args) { - int32_t response; callback_args_t d_args; app_t *app = args->app; dialogue_t *d = (dialogue_t *)app->active_widget; memcpy(&d_args, args, sizeof(callback_args_t)); d_args.app = NULL; - d_args.resp_data = &response; d_args.element = app->active_widget; dialogue_default_callback(&d_args); - if (response > -1) { - switch (response) { - case 0: + if (d_args.resp_data.code == cbrp_val) { + switch (d_args.resp_data.val.val.num) { + case 1: d->needs_destroy = true; /* TODO: macros */ free(app->query_args->server_message.text); @@ -40,7 +38,7 @@ void *init_server_message_modal(app_t *app) { if (app == NULL) return NULL; group_el_init_t children[] = { - {.type = w_button, .label = "OK", .is_default = 1}, {.type = w_end}}; + {.type = w_button, .label = "OK", .is_default = 1, .val.num = 1}, {.type = w_end}}; init_dialogue(&(app->modal), "Server response", app->query_args->server_message.text, &(app->coordinates)); diff --git a/src/modals/upload_dialogue.c b/src/modals/upload_dialogue.c index 8245cab..78440c8 100644 --- a/src/modals/upload_dialogue.c +++ b/src/modals/upload_dialogue.c @@ -1,35 +1,35 @@ #include "../file_processor.h" #include "group.h" -#include #include #include #include +#include void upload_dialogue_modal_cb(callback_args_t *args) { - int32_t response = -1; callback_args_t d_args; app_t *app = args->app; dialogue_t *d = (dialogue_t *)app->active_widget; memcpy(&d_args, args, sizeof(callback_args_t)); d_args.app = NULL; - d_args.resp_data = &response; d_args.element = app->active_widget; dialogue_default_callback(&d_args); ui_fs_file_list_t *fui = (ui_fs_file_list_t *)d->g_content->elements[0].element; - switch (response) { - case 0: - app->query_args->state = S_WAIT_SERVER; - d->needs_destroy = true; - break; - case 1: - file_upload_open(fui->d_path, fui->current->name, app->query_args); - d->needs_destroy = true; - app->query_args->state = S_UPLOAD_PARAMS; - break; - case -2: - d->needs_destroy = true; - app->query_args->state = WAIT_CLIENT; + if (d_args.resp_data.code == cbrp_val) { + switch (d_args.resp_data.val.val.num) { + case 0: + app->query_args->state = S_WAIT_SERVER; + d->needs_destroy = true; + break; + case 1: + file_upload_open(fui->d_path, fui->current->name, app->query_args); + d->needs_destroy = true; + app->query_args->state = S_UPLOAD_PARAMS; + break; + case -2: + d->needs_destroy = true; + app->query_args->state = WAIT_CLIENT; + } } } @@ -50,8 +50,10 @@ dialogue_t *init_upload_dialogue_modal(app_t *app) { dialogue_t *d = &(app->modal); d->w.callback = upload_dialogue_modal_cb; - d->g_content = init_group(&(d->win), &(d->w), content, &(d->id_map), horizontal, g_content); - d->g_action = init_group(&(d->win), &(d->w), actions, &(d->id_map), horizontal, g_action); + d->g_content = init_group(&(d->win), &(d->w), content, &(d->id_map), + horizontal, g_content); + d->g_action = init_group(&(d->win), &(d->w), actions, &(d->id_map), + horizontal, g_action); app->query_args->active_dialogue = d; diff --git a/src/modals/upload_props_dialogue.c b/src/modals/upload_props_dialogue.c index fd90b33..3857e68 100644 --- a/src/modals/upload_props_dialogue.c +++ b/src/modals/upload_props_dialogue.c @@ -8,20 +8,18 @@ #include void upload_props_dialogue_modal_cb(callback_args_t *args) { - int32_t response = -1; callback_args_t d_args; app_t *app = args->app; dialogue_t *d = (dialogue_t *)app->active_widget; memcpy(&d_args, args, sizeof(callback_args_t)); d_args.app = NULL; - d_args.resp_data = &response; d_args.element = app->active_widget; dialogue_default_callback(&d_args); char query[256]; int32_t query_len = 0; input_t *desc_input = (input_t *)d->g_content->elements[0].element; - if (response > -1) { - switch (response) { + if (d_args.resp_data.code == cbrp_val) { + switch (d_args.resp_data.val.val.num) { case 0: query_len = sprintf(query, "file upload \"%s\" %zu 1\n", diff --git a/src/query.c b/src/query.c index 6c6fd15..58e5558 100644 --- a/src/query.c +++ b/src/query.c @@ -16,7 +16,6 @@ #include #include -#include "app.h" #include "connection.h" #include "draw_modal.h" #include "file_processor.h" @@ -25,6 +24,7 @@ #include "server.h" #include "types.h" #include "widget/app.h" +#include "widget_core/widget_core.h" static void wait_side(app_t *app); void user_request_description(query_args_t *q_args); @@ -45,8 +45,10 @@ void query_loop(app_t *app) { size_t qlen; int sr; static file_args_t file_args; - callback_args_t d_args = { - .app = app, .element = NULL, .data = NULL, .resp_data = NULL}; + callback_args_t d_args = {.app = app, + .element = NULL, + .data = NULL, + .resp_data.code = cbrc_none}; init_file_args(&file_args); query_args->sd = sd; diff --git a/src/widget/button.h b/src/widget/button.h index 57f88dc..aacb19d 100644 --- a/src/widget/button.h +++ b/src/widget/button.h @@ -1,7 +1,6 @@ #ifndef BUTTON_H #define BUTTON_H -#include "../main.h" #include #include diff --git a/src/widget/dialogue.c b/src/widget/dialogue.c index 92bacd2..63fae47 100644 --- a/src/widget/dialogue.c +++ b/src/widget/dialogue.c @@ -62,30 +62,28 @@ void dialogue_default_callback(callback_args_t *args) { dialogue_t *d = (void *)args->element; int32_t key = *((int32_t *)args->data); callback_args_t new_args; - new_args.active_el = d->active_el; - int32_t *resp_value = (int32_t *)args->resp_data; memcpy(&new_args, args, sizeof(callback_args_t)); - new_args.active_id = d->active_el->id; + new_args.active_el = d->active_el; int32_t diff; d->needs_update = true; input_t *input; widget_t *widget; - switch (key) { case '\t': incr_active_id(d); - *resp_value = -1; + args->resp_data.code = cbrc_none; break; case '\33': /* Esc key */ - *resp_value = -2; + args->resp_data.code = cbrp_val; + args->resp_data.val.val.num = -2; break; case KEY_RIGHT: incr_active_id(d); - *resp_value = -1; + args->resp_data.code = cbrc_none; break; case KEY_LEFT: decr_active_id(d); - *resp_value = -1; + args->resp_data.code = cbrc_none; break; case '\n': widget = (widget_t *)d->active_el->element; @@ -97,16 +95,19 @@ void dialogue_default_callback(callback_args_t *args) { if (d->active_el->g_type == g_action) { new_args.element = d->g_action; group_default_callback(&new_args); + mempcpy(&(args->resp_data), &(new_args.resp_data), sizeof(new_args.resp_data)); } else if (d->g_action != NULL) { for (int i = 0; i < d->g_action->count; i++) { if (d->g_action->elements[i].is_default) { - *resp_value = i; + args->resp_data.code = cbrp_val; + args->resp_data.val.val.num = i; return; } } } else { /* TODO: temp solution, improve */ - *resp_value = d->active_el->id - d->g_content->first_id; + args->resp_data.code = cbrp_val; + args->resp_data.val.val.num = d->active_el->idx; return; } break; @@ -167,7 +168,7 @@ void dialogue_init_active_id(dialogue_t *dialogue) { widget_type = dialogue->g_content->elements[i].type; if (widget_type == w_button || widget_type == w_input || widget_type == w_fs_file_list) { /* Add here new types */ - dialogue->active_el = &(dialogue->g_content->elements[i]); + dialogue->active_el = dialogue->g_content->elements + i; return; } } @@ -177,7 +178,7 @@ void dialogue_init_active_id(dialogue_t *dialogue) { widget_type = dialogue->g_action->elements[i].type; if (widget_type == w_button || widget_type == w_input || widget_type == w_fs_file_list) { - dialogue->active_el = &(dialogue->g_action->elements[i]); + dialogue->active_el = dialogue->g_action->elements + i; return; } } diff --git a/src/widget/fs_file_list.c b/src/widget/fs_file_list.c index 5983045..c68924e 100644 --- a/src/widget/fs_file_list.c +++ b/src/widget/fs_file_list.c @@ -133,7 +133,7 @@ void fs_file_list_cb(callback_args_t *args) { } break; case '\n': - select_item(fui, (int32_t *) args->resp_data); + select_item(fui, (int32_t *) args->resp_data.val.val.num); break; } } diff --git a/src/widget/group.c b/src/widget/group.c index 5534d02..ba29455 100644 --- a/src/widget/group.c +++ b/src/widget/group.c @@ -12,26 +12,19 @@ #include #include -/* make response -1 */ -#define MAKE_RESPONSE_M1(args, resp_data, response) \ - if (args->resp_data != NULL) { \ - *response = -1; \ - } - -/* TODO: adapt to a new indexation method */ void group_default_callback(callback_args_t *args) { group_t *g = (group_t *)args->element; int32_t key = *((int32_t *)args->data); - int32_t *response = (int32_t *)args->resp_data; + int64_t response = args->resp_data.val.val.num; input_t *input; widget_t *widget; group_el_t *element_ptr = args->active_el; - int32_t element_idx = element_ptr->id - g->first_id; ; u_int32_t start_pos = 0; callback_args_t new_args; switch (key) { case '\n': /* Enter */ - *response = element_idx; + args->resp_data.code = cbrp_val; + mempcpy(&(args->resp_data.val), &(element_ptr->val), sizeof(struct val_t)); break; case KEY_BACKSPACE: case KEY_DL: @@ -47,7 +40,7 @@ void group_default_callback(callback_args_t *args) { } } } - MAKE_RESPONSE_M1(args, resp_data, response); + args->resp_data.code = cbrc_none; break; default: if (element_ptr->type == w_input) { @@ -64,6 +57,7 @@ void group_default_callback(callback_args_t *args) { } } } else { + /* callback case */ widget = (widget_t *)element_ptr->element; if (widget->callback != NULL) { memccpy(&new_args, args, 1, sizeof(callback_args_t)); @@ -72,7 +66,7 @@ void group_default_callback(callback_args_t *args) { break; } } - MAKE_RESPONSE_M1(args, resp_data, response); + args->resp_data.code = cbrc_none; break; } } @@ -104,6 +98,8 @@ group_t *init_group(WINDOW **win, widget_t *w_parent, group_el_init_t *children, elements[i].type = children[i].type; elements[i].is_default = children[i].is_default; elements[i].g_type = g_type; + elements[i].idx = i; + elements[i].val.type = val_nul; } /* init child elements */ @@ -112,6 +108,8 @@ group_t *init_group(WINDOW **win, widget_t *w_parent, group_el_init_t *children, switch (elements[i].type) { case w_button: elements[i].element = init_button(win, &(group->w), children[i].label); + elements[i].val.type = val_num; + elements[i].val.val.num = children[i].val.num; w = &(((button_t *)elements[i].element)->w); break; case w_box: diff --git a/src/widget/group.h b/src/widget/group.h index d3ea051..fb7cc40 100644 --- a/src/widget/group.h +++ b/src/widget/group.h @@ -5,21 +5,6 @@ #include #include -enum g_type { g_content, g_action }; - -enum g_direction { - horizontal, - vertical, -}; - -typedef struct { - uint32_t id; - void *element; - enum w_type type; - enum g_type g_type; - bool is_default; -} group_el_t; - typedef struct group_el_init_t { enum w_type type; bool is_default; @@ -27,6 +12,9 @@ typedef struct group_el_init_t { char label[DIALOGUE_TITLE]; uint32_t length; enum g_direction direction; + union { + int64_t num; + } val; struct group_el_init_t *children; } group_el_init_t; diff --git a/src/widget/input.h b/src/widget/input.h index 2409eec..54b2707 100644 --- a/src/widget/input.h +++ b/src/widget/input.h @@ -8,11 +8,11 @@ typedef struct { widget_t w; char value[INPUT_TEXT]; - uint32_t cur_pos; /* is relative to the end of a string */ - uint32_t value_len; - uint32_t max_len; - uint32_t is_disabled : 1; - uint32_t is_hidden : 1; + int64_t cur_pos; /* is relative to the end of a string */ + int64_t value_len; + int64_t max_len; + bool is_disabled; + bool is_hidden; } input_t; input_t *init_input(WINDOW **win, widget_t *w_parent, char *label, diff --git a/src/widget_core/widget_core.h b/src/widget_core/widget_core.h index 83473c9..d058c83 100644 --- a/src/widget_core/widget_core.h +++ b/src/widget_core/widget_core.h @@ -5,6 +5,13 @@ #include #include +enum g_type { g_content, g_action }; + +enum g_direction { + horizontal, + vertical, +}; + enum w_type { w_end, w_button, @@ -15,12 +22,45 @@ enum w_type { w_fs_file_list, }; +enum val_type { + val_num, + val_ptr, + val_nul, +}; + +struct val_t { + enum val_type type; + union { + void *ptr; + int64_t num; + } val; +}; + +typedef struct { + int64_t id; + int64_t idx; /* element index in the group */ + void *element; + enum w_type type; + enum g_type g_type; + bool is_default; + struct val_t val; +} group_el_t; + +enum cbrp_code { + cbrc_none, /* default */ + cbrp_val, + cbrp_g_el, /* element of type group_el_t */ + cbrp_err +}; + typedef struct { void *app; void *element; void *data; - void *resp_data; - uint32_t active_id; + struct { + enum cbrp_code code; + struct val_t val; + } resp_data; void *active_el; } callback_args_t; @@ -35,19 +75,18 @@ enum rsize { }; typedef struct widget_t { - uint32_t id; + int64_t id; char title[DIALOGUE_TITLE]; - uint32_t x; - uint32_t y; - uint32_t m_x; - uint32_t m_y; + int64_t x; + int64_t y; + int64_t m_x; + int64_t m_y; struct { - uint32_t y; - uint32_t x; + int64_t y; + int64_t x; } cur; WINDOW *const *parent_win; struct widget_t *w_parent; - // enum rsize rsize; void (*callback)(callback_args_t *args); } widget_t; @@ -60,7 +99,8 @@ enum pmt_attrs { void init_widget(widget_t *w, widget_t *w_parent, WINDOW **win, char *title); int32_t get_max_line_len(const char *text, uint32_t *line_count); -uint32_t print_multiline_text(WINDOW *win, const char *text, const uint32_t win_width, const uint32_t y, +uint32_t print_multiline_text(WINDOW *win, const char *text, + const uint32_t win_width, const uint32_t y, const uint32_t x, const uint16_t attrs); #endif \ No newline at end of file From 8f6ec355e7e872715ca57063e6c31acf9712a406 Mon Sep 17 00:00:00 2001 From: Alexander Zhylin Date: Thu, 26 Feb 2026 15:07:04 +0200 Subject: [PATCH 6/6] ui: finishes nested groups; +email field in registration modal --- TODO.md | 8 +++--- src/modals/alert.c | 2 +- src/modals/ask_server_addr.c | 13 +++------- src/modals/download_pr.c | 2 +- src/modals/login_credentials.c | 8 +++--- src/modals/login_option.c | 12 ++++----- src/modals/notification.c | 2 +- src/modals/register.c | 21 ++++++++------- src/modals/server_message.c | 2 +- src/modals/upload_dialogue.c | 2 +- src/modals/upload_props_dialogue.c | 4 +-- src/widget/app.h | 1 - src/widget/dialogue.c | 41 +++++++++++++++++------------- src/widget/input.c | 17 ++++++++----- 14 files changed, 71 insertions(+), 64 deletions(-) diff --git a/TODO.md b/TODO.md index cdeedc7..f0e19c3 100644 --- a/TODO.md +++ b/TODO.md @@ -1,10 +1,12 @@ # TODO: ## Current: -- [ ] UI: vertical layout (needs for email in registration) -- [ ] Nested groups +- [x] UI: vertical layout (needs for email in registration) +- [x] Nested groups - [x] Create dynamic map of elements IDs and their pointers -- [ ] Adapt modals to new value-forwarding method +- [x] Adapt modals to new value-forwarding method +- [ ] Move text to the end if text in the input is larger, than field +- [ ] More intuitive navigation between nested groups (by pressing left and right buttons) ## Features: - [x] Search files diff --git a/src/modals/alert.c b/src/modals/alert.c index ccf176b..8b23e04 100644 --- a/src/modals/alert.c +++ b/src/modals/alert.c @@ -25,7 +25,7 @@ void notification(const char *title, const char *message, return; group_el_init_t actions[] = { - {.type = w_button, .label = "OK", .is_default = 1}, {.type = w_end}}; + {.type = w_button, .label = "OK", .is_default = true}, {.type = w_end}}; dialogue_t *d = malloc(sizeof(dialogue_t)); diff --git a/src/modals/ask_server_addr.c b/src/modals/ask_server_addr.c index dfe2a13..27f3451 100644 --- a/src/modals/ask_server_addr.c +++ b/src/modals/ask_server_addr.c @@ -1,17 +1,10 @@ #include #include #include - -// #include "../app.h" -// #include "../widget/dialogue.h" -// #include "../widget/group.h" -// #include "../widget/input.h" -// #include "../widget/button.h" - #include +#include #include "../connection.h" #include "group.h" -#include "widget_core.h" typedef struct { button_t *element; @@ -51,8 +44,8 @@ dialogue_t *init_asa_modal(app_t *app) { {.type = w_end}}; group_el_init_t actions[] = { - {.type = w_button, .label = "Connect", .is_default = 1}, - {.type = w_button, .label = "Cancel", .is_default = 0}, + {.type = w_button, .label = "Connect", .is_default = true, .val.num = 1}, + {.type = w_button, .label = "Cancel", .is_default = false, .val.num = 2}, {.type = w_end}}; init_dialogue(&(app->modal), "Connect to server", diff --git a/src/modals/download_pr.c b/src/modals/download_pr.c index 13173f8..7d453b1 100644 --- a/src/modals/download_pr.c +++ b/src/modals/download_pr.c @@ -17,7 +17,7 @@ dialogue_t *init_dwn_pr_modal(app_t *app, bool is_upload) { sprintf(text, "%s file: %s", is_upload ? "Uploading" : "Downloading", is_upload ? app->query_args->file->name : app->file_args->f_selected.name); group_el_init_t actions[] = { - {.type = w_button, .label = "Cancel", .is_default = 1}, {.type = w_end}}; + {.type = w_button, .label = "Cancel", .is_default = true, .val.num = 1}, {.type = w_end}}; app->modal.w.parent_win = &app->win; init_dialogue(&(app->modal), is_upload ? "Upload" : "Download", text, diff --git a/src/modals/login_credentials.c b/src/modals/login_credentials.c index d6de53e..13f282f 100644 --- a/src/modals/login_credentials.c +++ b/src/modals/login_credentials.c @@ -24,7 +24,7 @@ void init_login_credentials_modal_cb(callback_args_t *args) { dialogue_default_callback(&d_args); if (d_args.resp_data.code == cbrp_val) { switch (d_args.resp_data.val.val.num) { - case 0: + case 1: app->params->uname = malloc(in_name->value_len + 1); app->params->pass = malloc(in_pass->value_len + 1); strncpy(app->params->uname, in_name->value, in_name->value_len); @@ -36,7 +36,7 @@ void init_login_credentials_modal_cb(callback_args_t *args) { d->needs_destroy = true; print_bars(app); break; - case 1: + case 2: app->query_args->state = S_ASK_LOGIN_TYPE; app->modal.needs_destroy = true; } @@ -55,8 +55,8 @@ dialogue_t *init_login_credentials_modal(app_t *app) { {.type = w_end}}; group_el_init_t actions[] = { - {.type = w_button, .label = "Login", .is_default = true}, - {.type = w_button, .label = "Cancel", .is_default = false}, + {.type = w_button, .label = "Login", .is_default = true, .val.num = 1}, + {.type = w_button, .label = "Cancel", .is_default = false, .val.num = 2}, {.type = w_end}}; init_dialogue(&(app->modal), "Login", "Your login credentials", diff --git a/src/modals/login_option.c b/src/modals/login_option.c index e5d7e1b..09d4f69 100644 --- a/src/modals/login_option.c +++ b/src/modals/login_option.c @@ -22,7 +22,7 @@ void login_modal_option_cb(callback_args_t *args) { dialogue_default_callback(&d_args); if (d_args.resp_data.code == cbrp_val) { switch (d_args.resp_data.val.val.num) { - case 0: + case 1: d->needs_destroy = true; app->params->uname = malloc(sizeof "anonymous"); strcpy(app->params->uname, "anonymous"); @@ -30,11 +30,11 @@ void login_modal_option_cb(callback_args_t *args) { app->query_args->state = S_WAIT_SERVER; print_bars(app); break; - case 1: + case 2: d->needs_destroy = true; app->query_args->state = S_ASK_LOGIN_USER; break; - case 2: + case 3: d->needs_destroy = true; app->query_args->state = S_ASK_REGISTER; break; @@ -45,9 +45,9 @@ void login_modal_option_cb(callback_args_t *args) { dialogue_t *init_login_option_modal(app_t *app) { if (app == NULL) return NULL; - group_el_init_t children[] = {{.type = w_button, .label = "Anonymous"}, - {.type = w_button, .label = "User"}, - {.type = w_button, .label = "Register"}, + group_el_init_t children[] = {{.type = w_button, .label = "Anonymous", .val.num = 1}, + {.type = w_button, .label = "User", .val.num = 2}, + {.type = w_button, .label = "Register", .val.num = 3}, {.type = w_end}}; init_dialogue(&(app->modal), "Login", diff --git a/src/modals/notification.c b/src/modals/notification.c index f85ca1e..a4988b3 100644 --- a/src/modals/notification.c +++ b/src/modals/notification.c @@ -29,7 +29,7 @@ dialogue_t *init_notification_modal(app_t *app) { return NULL; group_el_init_t actions[] = { - {.type = w_button, .label = "OK", .is_default = 1, .val.num = 1}, + {.type = w_button, .label = "OK", .is_default = true, .val.num = 1}, {.type = w_end}}; init_dialogue(&(app->modal), "Notification", app->query_args->notification, diff --git a/src/modals/register.c b/src/modals/register.c index a0e570c..0fd619b 100644 --- a/src/modals/register.c +++ b/src/modals/register.c @@ -25,14 +25,17 @@ void init_register_modal_cb(callback_args_t *args) { memcpy(&d_args, args, sizeof(callback_args_t)); d_args.app = NULL; d_args.element = app->active_widget; - input_t *in_name = d->g_content->elements[0].element; - input_t *in_pass = d->g_content->elements[1].element; - input_t *in_pass_r = d->g_content->elements[2].element; + group_t *g_email_user = d->g_content->elements[0].element; + group_t *g_passwords = d->g_content->elements[1].element; + input_t *in_name = g_email_user->elements[0].element; + input_t *in_email = g_email_user->elements[1].element; + input_t *in_pass = g_passwords->elements[0].element; + input_t *in_pass_r = g_passwords->elements[1].element; dialogue_default_callback(&d_args); if (d_args.resp_data.code == cbrp_val && d_args.resp_data.val.type == val_num) { switch (d_args.resp_data.val.val.num) { - case 0: + case 1: if (strcmp(in_pass->value, in_pass_r->value)) { alert("Your passwords do not match"); in_pass->value[0] = '\0'; @@ -47,12 +50,12 @@ void init_register_modal_cb(callback_args_t *args) { app->params->uname[in_name->value_len] = 0; strncpy(app->params->pass, in_pass->value, in_pass->value_len); app->params->pass[in_pass->value_len] = 0; - sprintf(query, "register %s %s\n%n", app->params->uname, - app->params->pass, &qlen); + sprintf(query, "register %s %s %*s\n%n", app->params->uname, + app->params->pass, (int) in_email->value_len, in_email->value, &qlen); write(app->params->sd, query, qlen); app->query_args->state = S_WAIT_REGISTER_CONFIRMATION; break; - case 1: + case 2: app->query_args->state = S_ASK_LOGIN_TYPE; app->modal.needs_destroy = true; } @@ -83,8 +86,8 @@ dialogue_t *init_register_modal(app_t *app) { {.type = w_end}}; group_el_init_t actions[] = { - {.type = w_button, .label = "Register", .is_default = true}, - {.type = w_button, .label = "Cancel", .is_default = false}, + {.type = w_button, .label = "Register", .is_default = true, .val.num = 1}, + {.type = w_button, .label = "Cancel", .is_default = false, .val.num = 2}, {.type = w_end}}; init_dialogue(&(app->modal), "Credentials", "There is information needed", diff --git a/src/modals/server_message.c b/src/modals/server_message.c index 3b6278f..e7fb528 100644 --- a/src/modals/server_message.c +++ b/src/modals/server_message.c @@ -38,7 +38,7 @@ void *init_server_message_modal(app_t *app) { if (app == NULL) return NULL; group_el_init_t children[] = { - {.type = w_button, .label = "OK", .is_default = 1, .val.num = 1}, {.type = w_end}}; + {.type = w_button, .label = "OK", .is_default = true, .val.num = 1}, {.type = w_end}}; init_dialogue(&(app->modal), "Server response", app->query_args->server_message.text, &(app->coordinates)); diff --git a/src/modals/upload_dialogue.c b/src/modals/upload_dialogue.c index 78440c8..f1b33df 100644 --- a/src/modals/upload_dialogue.c +++ b/src/modals/upload_dialogue.c @@ -42,7 +42,7 @@ dialogue_t *init_upload_dialogue_modal(app_t *app) { {.type = w_end}}; group_el_init_t actions[] = { - {.type = w_button, .label = "Cancel", .is_default = 0}, {.type = w_end}}; + {.type = w_button, .label = "Cancel", .is_default = false, .val.num = 0}, {.type = w_end}}; app->modal.w.parent_win = &app->win; init_dialogue(&(app->modal), "Upload new file", "Enter essential data", diff --git a/src/modals/upload_props_dialogue.c b/src/modals/upload_props_dialogue.c index 3857e68..def81ec 100644 --- a/src/modals/upload_props_dialogue.c +++ b/src/modals/upload_props_dialogue.c @@ -52,8 +52,8 @@ dialogue_t *init_upload_props_dialogue_modal(app_t *app) { {.type = w_end}}; group_el_init_t actions[] = { - {.type = w_button, .label = "Upload", .is_default = 1}, - {.type = w_button, .label = "Cancel", .is_default = 0}, + {.type = w_button, .label = "Upload", .is_default = true}, + {.type = w_button, .label = "Cancel", .is_default = false}, {.type = w_end}, }; diff --git a/src/widget/app.h b/src/widget/app.h index 79d84c3..1714e0b 100644 --- a/src/widget/app.h +++ b/src/widget/app.h @@ -10,7 +10,6 @@ typedef enum { modal_color_pair = 3, - modal_border_color_pair = 4, } color; typedef enum { diff --git a/src/widget/dialogue.c b/src/widget/dialogue.c index 63fae47..7cdb10a 100644 --- a/src/widget/dialogue.c +++ b/src/widget/dialogue.c @@ -95,7 +95,8 @@ void dialogue_default_callback(callback_args_t *args) { if (d->active_el->g_type == g_action) { new_args.element = d->g_action; group_default_callback(&new_args); - mempcpy(&(args->resp_data), &(new_args.resp_data), sizeof(new_args.resp_data)); + mempcpy(&(args->resp_data), &(new_args.resp_data), + sizeof(new_args.resp_data)); } else if (d->g_action != NULL) { for (int i = 0; i < d->g_action->count; i++) { if (d->g_action->elements[i].is_default) { @@ -105,7 +106,6 @@ void dialogue_default_callback(callback_args_t *args) { } } } else { - /* TODO: temp solution, improve */ args->resp_data.code = cbrp_val; args->resp_data.val.val.num = d->active_el->idx; return; @@ -160,27 +160,32 @@ void init_dialogue(dialogue_t *dialogue, const char title[], const char text[], dialogue->text[t_size - 1] = '\0'; } -void dialogue_init_active_id(dialogue_t *dialogue) { - enum w_type widget_type; +int group_init_active_id(group_t *g, dialogue_t *d) { + enum w_type wt; + for (int i = 0; i < g->count; i++) { + wt = g->elements[i].type; + if (wt == w_button || wt == w_input || + wt == w_fs_file_list) { /* Add here new types */ + d->active_el = g->elements + i; + return 1; + } else if (wt == w_group) { + group_t *child_g = g->elements[i].element; + return group_init_active_id(child_g, d); + } + } + return 0; +} + +void dialogue_init_active_id(dialogue_t *dialogue) { if (dialogue->g_content != NULL) { - for (int i = 0; i < dialogue->g_content->count; i++) { - widget_type = dialogue->g_content->elements[i].type; - if (widget_type == w_button || widget_type == w_input || - widget_type == w_fs_file_list) { /* Add here new types */ - dialogue->active_el = dialogue->g_content->elements + i; - return; - } + if (group_init_active_id(dialogue->g_content, dialogue)) { + return; } } if (dialogue->g_action != NULL) { - for (int i = 0; i < dialogue->g_action->count; i++) { - widget_type = dialogue->g_action->elements[i].type; - if (widget_type == w_button || widget_type == w_input || - widget_type == w_fs_file_list) { - dialogue->active_el = dialogue->g_action->elements + i; - return; - } + if (group_init_active_id(dialogue->g_action, dialogue)) { + return; } } } diff --git a/src/widget/input.c b/src/widget/input.c index eeea55a..75c7d11 100644 --- a/src/widget/input.c +++ b/src/widget/input.c @@ -31,7 +31,7 @@ int32_t draw_input(input_t *input, uint32_t active_id) { WINDOW *win = *(input->w.parent_win); uint32_t margin_y = input->w.m_y; uint32_t margin_x = input->w.m_x; - + /* counts margins of the ancestors of the same window */ widget_t *w_par = input->w.w_parent; while (w_par->parent_win == input->w.parent_win) { @@ -40,13 +40,15 @@ int32_t draw_input(input_t *input, uint32_t active_id) { w_par = w_par->w_parent; } - wattrset(win, COLOR_PAIR(modal_color_pair)); + if (input->w.id == active_id) { + wattrset(win, COLOR_PAIR(3)); + } else { + wattrset(win, COLOR_PAIR(0) | A_REVERSE | A_BOLD); + } mvwhline(win, margin_y, margin_x, 0, input->w.x); mvwhline(win, margin_y + 2, margin_x, 0, input->w.x); - mvwprintw(win, margin_y, margin_x + 1, " %s ", input->w.title); - // left mvwvline(win, margin_y, margin_x, ACS_ULCORNER, 1); mvwvline(win, margin_y + 1, margin_x, 0, 1); @@ -56,6 +58,9 @@ int32_t draw_input(input_t *input, uint32_t active_id) { mvwvline(win, margin_y + 1, margin_x + input->w.x - 1, 0, 1); mvwvline(win, margin_y + 2, margin_x + input->w.x - 1, ACS_LRCORNER, 1); + wattrset(win, COLOR_PAIR(0) | A_REVERSE); + mvwprintw(win, margin_y, margin_x + 1, " %s ", input->w.title); + margin_x++; if (input->w.id == active_id) { @@ -66,10 +71,10 @@ int32_t draw_input(input_t *input, uint32_t active_id) { if (input->is_hidden) { mvwprintw(win, margin_y + 1, margin_x, "%.*s%*s", (int)input->value_len, - stars, input->w.x - input->value_len - 2, ""); + stars, (int)(input->w.x - input->value_len - 2), ""); } else { mvwprintw(win, margin_y + 1, margin_x, "%s%*s", input->value, - input->w.x - input->value_len - 2, ""); + (int)(input->w.x - input->value_len - 2), ""); } input->w.cur.y = margin_y + 1;