From ee2bccc5c87a77cd7a60e07eb89ab2fa7cbd7f73 Mon Sep 17 00:00:00 2001 From: Alexander Zhylin Date: Mon, 20 Apr 2026 15:23:12 +0300 Subject: [PATCH] UI: finishes callback processing in main_window, key input callbacks and draw bottom bar --- src/connection.c | 4 +-- src/main.h | 1 + src/main_window.c | 9 +++--- src/modals/login_credentials.c | 2 +- src/modals/login_option.c | 2 +- src/query.c | 19 +++++++---- src/server.c | 2 +- src/widget/app.c | 54 +++++++++++++++++-------------- src/widget/app.h | 3 +- src/widget/dialogue.c | 2 +- src/widget/file_list.c | 13 ++++---- src/widget/file_list.h | 3 +- src/widget/fs_file_list.c | 59 +++++++++++++++++++++++++++------- src/widget/fs_file_list.h | 3 +- src/widget_core/widget_core.h | 1 + 15 files changed, 115 insertions(+), 62 deletions(-) diff --git a/src/connection.c b/src/connection.c index e7071a6..e51ed21 100644 --- a/src/connection.c +++ b/src/connection.c @@ -26,7 +26,7 @@ void connect_to_server(w_app_t *app) { w_app_destroy(app, 2); } app->params->is_connected = TRUE; - w_app_print_bars(app); + w_app_draw_bars(app); } void init_params(params_t *params) { @@ -59,7 +59,7 @@ void get_ip_port(params_t *params, char *ip, char *port) { void close_session(int sd) { close(sd); } -int init_client() { +int init_client(void) { int res; int cs = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in client; diff --git a/src/main.h b/src/main.h index 1c07d3d..1a5fa0b 100644 --- a/src/main.h +++ b/src/main.h @@ -95,6 +95,7 @@ typedef struct main_window { int32_t (*cb_b_press) (void *app, void *args); /* callback on button press */ void (*cb_refresh) (void *app); /* refresh callback */ struct action_key *b_keys; + int32_t b_keys_len; } main_window_t; typedef struct query_args { diff --git a/src/main_window.c b/src/main_window.c index 370aa03..c18197f 100644 --- a/src/main_window.c +++ b/src/main_window.c @@ -1,5 +1,6 @@ #include "main_window.h" #include "fs_file_list.h" +#include "server.h" void main_window_set(w_app_t *app, enum main_window_type type) { if (app->main_ui.ui != NULL) { @@ -8,12 +9,11 @@ void main_window_set(w_app_t *app, enum main_window_type type) { app->main_ui.type = type; switch (type) { case mw_fl_server: - app->main_ui.ui = w_fl_init(app); - app->active_callback = w_fl_cb; + w_fl_init(app); + app->query_args->state = S_FILE_LIST; break; case mw_fl_local: - app->main_ui.ui = w_lfl_init_win(app); - app->active_callback = w_lfl_cb; + w_lfl_init_win(app); break; case mw_f_desc: break; @@ -33,6 +33,7 @@ void main_window_draw(w_app_t *app) { case mw_f_desc: break; } + w_app_draw_bbar(app); } void main_window_destroy(w_app_t *app) { diff --git a/src/modals/login_credentials.c b/src/modals/login_credentials.c index a610761..8bec29d 100644 --- a/src/modals/login_credentials.c +++ b/src/modals/login_credentials.c @@ -35,7 +35,7 @@ void init_login_credentials_modal_cb(w_cb_args_t *args) { server_send_string(app->query_args, app->params->uname); app->query_args->state = S_WAIT_SERVER; d->needs_destroy = true; - w_app_print_bars(app); + w_app_draw_bars(app); break; case 2: app->query_args->state = S_ASK_LOGIN_TYPE; diff --git a/src/modals/login_option.c b/src/modals/login_option.c index edc4f73..1019d4f 100644 --- a/src/modals/login_option.c +++ b/src/modals/login_option.c @@ -29,7 +29,7 @@ void login_modal_option_cb(w_cb_args_t *args) { strcpy(app->params->uname, "anonymous"); server_send_string(app->query_args, app->params->uname); app->query_args->state = S_WAIT_SERVER; - w_app_print_bars(app); + w_app_draw_bars(app); break; case 2: d->needs_destroy = true; diff --git a/src/query.c b/src/query.c index 9d5be8f..38a0b6d 100644 --- a/src/query.c +++ b/src/query.c @@ -39,9 +39,9 @@ int32_t process_user_input(w_app_t *app, w_cb_args_t *d_args); #define EXIT_APP(message, app, exit_code) \ { \ - w_alert(message); \ + w_alert(message); \ close_session(app->params->sd); \ - w_app_destroy(app, exit_code); \ + w_app_destroy(app, exit_code); \ } void query_loop(w_app_t *app) { @@ -112,8 +112,14 @@ void query_loop(w_app_t *app) { } if (FD_ISSET(STDIN_FILENO, &readfds)) { - if (ERR == process_user_input(app, &d_args)) { - EXIT_APP("*** error while processing user input ***", app, 4) + if (app->main_ui.cb_b_press != NULL) { + if (ERR == app->main_ui.cb_b_press(app, &d_args)) { + EXIT_APP("*** error while processing user input ***", app, 4) + } + } else { + if (ERR == process_user_input(app, &d_args)) { + EXIT_APP("*** error while processing user input ***", app, 4) + } } } } @@ -131,8 +137,9 @@ void wait_register(query_args_t *q_args) { int upload_confirm_cb(w_app_t *app, char *query, int q_len) { if (!strncmp("finished\n", query, sizeof("finished\n") - 1)) { w_ui_file_list_t *fui = (w_ui_file_list_t *)app->query_args->main_ui->ui; - w_notification("File upload", dc_normal, "File %s is uploaded to the server!", - app->query_args->file->name); + w_notification("File upload", dc_normal, + "File %s is uploaded to the server!", + app->query_args->file->name); clear_file_in_query(app->query_args); app->query_args->state = S_FILE_LIST; app->modal.needs_destroy = true; diff --git a/src/server.c b/src/server.c index 15c801a..8cd227c 100644 --- a/src/server.c +++ b/src/server.c @@ -47,7 +47,7 @@ int server_wait_reg_confirm_cb(w_app_t *app, char *line, int l_len) { "Welcome, %s!", app->params->uname); w_notification("Registration", dc_normal, query); - w_app_print_bars(app); + w_app_draw_bars(app); return 0; } else { q_args->state = S_ASK_REGISTER; diff --git a/src/widget/app.c b/src/widget/app.c index 1b73dab..d58dc22 100644 --- a/src/widget/app.c +++ b/src/widget/app.c @@ -33,11 +33,9 @@ w_app_t *w_app_init() { _app->modal.win = NULL; _app->modal.is_initiated = 0; - /* default callback */ - _app->active_callback = w_fl_cb; - - /* print decorative bars */ - w_app_print_bars(_app); + /* print top and bottom bars */ + _app->main_ui.b_keys_len = 0; + w_app_draw_bars(_app); /* here goes box borders */ w_app_draw_borders(_app); @@ -96,7 +94,9 @@ int64_t print_bottom_menu_option(WINDOW *win, char *key, char *title, int64_t y, return key_len + title_len; } -void w_app_print_bars(w_app_t *app) { +void w_app_draw_bbar(w_app_t *app); + +void w_app_draw_bars(w_app_t *app) { char top_text[64] = "Hello!"; wattrset(app->win, A_REVERSE); @@ -116,32 +116,38 @@ void w_app_print_bars(w_app_t *app) { ip_addr[1], ip_addr[2], ip_addr[3], app->params->uname); } mvwprintw(app->win, 1, 2, "%s", top_text); + wattroff(app->win, A_REVERSE); /* add content to the bottom bar */ + if (app->main_ui.b_keys_len > 0) { + w_app_draw_bbar(app); + } +} - struct action_key action_keys[] = { - {.key = "F1", .title = "Help"}, - {.key = "U", .title = "Upload"}, - {.key = "S", .title = "Search"}, - {.key = "F9", .title = "Quit"}, - }; - - int t_margin = 0; /* tab margin */ - int c_size = app->coordinates.max_x / 4; /* column size */ +void w_app_draw_bbar(w_app_t *app) { + wattrset(app->win, A_REVERSE); + int t_margin = 0; /* tab margin */ + int c_size = + app->coordinates.max_x / app->main_ui.b_keys_len; /* column size */ int incr_last = app->coordinates.max_x % - 4; /* increment size fo the last element (fill empty space) */ + app->main_ui.b_keys_len; /* increment size fo the last element + (fill empty space) */ - for (int i = 0; i < 3; i++) { - struct action_key *k = &action_keys[i]; - t_margin += - print_bottom_menu_option(app->win, k->key, k->title, - app->coordinates.cur_y - 2, t_margin, c_size); + struct action_key *k = NULL; + if (app->main_ui.b_keys_len > 1) { + for (int i = 0; i < app->main_ui.b_keys_len - 1; i++) { + k = &(app->main_ui.b_keys)[i]; + t_margin += print_bottom_menu_option(app->win, k->key, k->title, + app->coordinates.cur_y - 2, t_margin, + c_size); + } } - t_margin += print_bottom_menu_option( - app->win, action_keys[3].key, action_keys[3].title, - app->coordinates.cur_y - 2, t_margin, c_size + incr_last); + k = &(app->main_ui.b_keys)[app->main_ui.b_keys_len - 1]; + t_margin += print_bottom_menu_option(app->win, k->key, k->title, + app->coordinates.cur_y - 2, t_margin, + c_size + incr_last); wattroff(app->win, A_REVERSE); } diff --git a/src/widget/app.h b/src/widget/app.h index f4e73ad..0ba7ed4 100644 --- a/src/widget/app.h +++ b/src/widget/app.h @@ -31,8 +31,9 @@ w_app_t *w_app_init(void); void w_app_init_nc(void); void w_app_destroy(w_app_t *app, int32_t exit_code); void w_app_draw_borders(w_app_t *app); -void w_app_print_bars(w_app_t *app); +void w_app_draw_bars(w_app_t *app); void w_app_refresh(w_app_t *app); void w_app_draw_modal(w_app_t *app); +void w_app_draw_bbar(w_app_t *app); #endif // N_APP_H \ No newline at end of file diff --git a/src/widget/dialogue.c b/src/widget/dialogue.c index 1467311..e35a147 100644 --- a/src/widget/dialogue.c +++ b/src/widget/dialogue.c @@ -319,7 +319,7 @@ void w_dialogue_destroy(w_dialogue_t *d, void *_app) { d->win = NULL; d->is_initiated = 0; d->needs_destroy = false; - app->active_callback = w_fl_cb; + app->active_callback = (w_cb_t) app->main_ui.cb_b_press; app->query_args->active_dialogue = NULL; u_d_arr_ptr_free(&(d->id_map)); w_app_refresh(app); diff --git a/src/widget/file_list.c b/src/widget/file_list.c index 53ccfa7..3cbc7a2 100644 --- a/src/widget/file_list.c +++ b/src/widget/file_list.c @@ -19,7 +19,7 @@ void w_fl_reset(w_ui_file_list_t *fl_ui); -void w_fl_cb(w_cb_args_t *args) { +static void w_fl_cb(w_cb_args_t *args) { w_app_t *app = args->app; w_ui_file_list_t *fui = app->query_args->main_ui->ui; int32_t key = *((int32_t *)args->data); @@ -133,7 +133,7 @@ static struct action_key action_keys[] = { {.key = "F9", .title = "Quit", .code = KEY_F(9)}, }; -int32_t process_user_input(w_app_t *app, w_cb_args_t *d_args) { +static int32_t process_user_input(w_app_t *app, w_cb_args_t *d_args) { int32_t c; w_ui_file_list_t *fui = (w_ui_file_list_t *)app->query_args->main_ui->ui; c = wgetch(app->win); @@ -143,8 +143,7 @@ int32_t process_user_input(w_app_t *app, w_cb_args_t *d_args) { return OK; case 'U': case 'u': - if (!app->modal.is_initiated && !fui->active_search && - app->main_ui.type != mw_fl_local) { + if (!app->modal.is_initiated && !fui->active_search) { /* app->query_args->state = S_UPLOAD_FILE_SELECT; */ main_window_set(app, mw_fl_local); main_window_draw(app); @@ -166,13 +165,15 @@ int32_t process_user_input(w_app_t *app, w_cb_args_t *d_args) { return OK; } -w_ui_file_list_t *w_fl_init(w_app_t *app) { +void *w_fl_init(w_app_t *app) { w_ui_file_list_t *fui = malloc(sizeof(w_ui_file_list_t)); app->main_ui.ui = fui; app->main_ui.type = mw_fl_server; app->main_ui.cb_refresh = w_fl_cb_refresh; app->main_ui.b_keys = action_keys; - app->main_ui.cb_b_press = process_user_input; + app->main_ui.b_keys_len = 4; + app->main_ui.cb_b_press = (w_cb_press_t) process_user_input; + app->active_callback = w_fl_cb; w_init(&(fui->w), NULL, &(app->win), ""); WINDOW *win_parent = app->win; fui->current_idx = 0; diff --git a/src/widget/file_list.h b/src/widget/file_list.h index 9c91564..10680f2 100644 --- a/src/widget/file_list.h +++ b/src/widget/file_list.h @@ -27,8 +27,7 @@ typedef struct { } w_ui_file_list_t; void w_fl_draw(w_ui_file_list_t *fl_ui); -w_ui_file_list_t *w_fl_init(w_app_t *app); -void w_fl_cb(w_cb_args_t *args); +void *w_fl_init(w_app_t *app); void w_fl_reset(w_ui_file_list_t *fl_ui); void w_fl_destroy(w_ui_file_list_t **fui); diff --git a/src/widget/fs_file_list.c b/src/widget/fs_file_list.c index 07db581..a3b5e83 100644 --- a/src/widget/fs_file_list.c +++ b/src/widget/fs_file_list.c @@ -14,12 +14,13 @@ #include #include +#include "../main_window.h" +#include "../server.h" #include "alert.h" - #include "fs_file_list.h" #include "widget_core.h" -void w_fl_reset(w_lfl_ui_t *fl_ui); +// void w_fl_reset(w_lfl_ui_t *fl_ui); struct fl_args { char *name; @@ -186,7 +187,7 @@ void open_selected_item(w_lfl_ui_t *fui, w_cbrp_data *resp_data) { /* next page */ void page_next(w_lfl_ui_t *fui) { - int count = fui->rows_num; + int count = fui->max_lines; w_lfl_item_t *n_page_item = fui->page_start; if (fui->cur_page < fui->pages_num) { for (; count; count--) { @@ -202,7 +203,7 @@ void page_next(w_lfl_ui_t *fui) { /* previous page */ void page_previous(w_lfl_ui_t *fui) { - int count = fui->rows_num; + int count = fui->max_lines; w_lfl_item_t *n_page_item = fui->page_start; if (fui->cur_page > 1) { for (; count; count--) { @@ -216,13 +217,13 @@ void page_previous(w_lfl_ui_t *fui) { w_lfl_draw(fui); } -void w_lfl_cb(w_cb_args_t *args) { +static void w_lfl_cb(w_cb_args_t *args) { w_lfl_ui_t *fui = args->element; int32_t key = *((int32_t *)args->data); switch (key) { case KEY_DOWN: if (fui->current->next != NULL) { - if (fui->current_idx < fui->rows_num - 1) { + if (fui->current_idx < fui->max_lines - 1) { fui->current = fui->current->next; fui->current_idx++; } else { @@ -269,7 +270,7 @@ w_lfl_ui_t *w_lfl_init(WINDOW **win, w_t *w_parent) { fl_ui->w.callback = w_lfl_cb; fl_ui->w.sz.x = getmaxx(win_par) / 10 * 8; fl_ui->w.sz.y = getmaxy(win_par) / 10 * 8; - fl_ui->rows_num = 0; /* detects on the first draw */ + fl_ui->max_lines = 0; /* detects on the first draw */ fl_ui->cur_page = 1; return fl_ui; } @@ -284,11 +285,47 @@ static void w_lfl_cb_refresh(void *data) { wnoutrefresh(fui->win_info); } +static struct action_key action_keys[] = { + {.key = "Esc", .title = "Back", .code = '\33'}, + {.key = "F9", .title = "Quit", .code = KEY_F(9)}, +}; + +static int32_t process_user_input(w_app_t *app, w_cb_args_t *d_args) { + int32_t c; + w_lfl_ui_t *fui = (w_lfl_ui_t *)app->query_args->main_ui->ui; + c = wgetch(app->win); + switch (c) { + case KEY_F(9): + w_app_destroy(app, 0); + return OK; + case 'D': + case 'd': + case '\33': /* ESC key */ + if (!app->modal.is_initiated) { + main_window_set(app, mw_fl_server); + main_window_draw(app); + server_send_string(app->query_args, "file list %u %u\n", fui->max_lines, + 1); + d_args->element = app->main_ui.ui; + break; + } + default: + d_args->data = (void *)&c; + app->active_callback(d_args); + break; + } + return OK; +} + w_lfl_ui_t *w_lfl_init_win(w_app_t *app) { w_lfl_ui_t *fui = malloc(sizeof(w_lfl_ui_t)); app->main_ui.ui = fui; app->main_ui.type = mw_fl_local; app->main_ui.cb_refresh = w_lfl_cb_refresh; + app->main_ui.b_keys = action_keys; + app->main_ui.b_keys_len = 2; + app->main_ui.cb_b_press = (w_cb_press_t)process_user_input; + app->active_callback = w_lfl_cb; w_init(&(fui->w), NULL, &(app->win), ""); fui->current_idx = 0; fui->win_info = NULL; @@ -299,7 +336,7 @@ w_lfl_ui_t *w_lfl_init_win(w_app_t *app) { fui->w.callback = w_lfl_cb; fui->w.sz.x = getmaxx(app->win); fui->w.sz.y = getmaxy(app->win); - fui->rows_num = 0; /* detects on the first draw */ + fui->max_lines = 0; /* detects on the first draw */ fui->cur_page = 1; fui->w.parent_win = &(app->win); /* * INIT UI * */ @@ -326,9 +363,9 @@ void w_lfl_draw(w_lfl_ui_t *fui) { getmaxyx(win, sz_y, sz_x); int32_t sz_y_f = sz_y - 2; // actual size (without box) int32_t sz_x_f = sz_x - 2; // actual size (without box) - fui->rows_num = sz_y_f; - fui->pages_num = fui->files_num / fui->rows_num + - (fui->files_num % fui->rows_num > 0 ? 1 : 0); + fui->max_lines = sz_y_f; + fui->pages_num = fui->files_num / fui->max_lines + + (fui->files_num % fui->max_lines > 0 ? 1 : 0); p_y = 1; p_x = 1; w_lfl_item_t *el = fui->page_start; diff --git a/src/widget/fs_file_list.h b/src/widget/fs_file_list.h index cd97109..88a5055 100644 --- a/src/widget/fs_file_list.h +++ b/src/widget/fs_file_list.h @@ -24,7 +24,7 @@ typedef struct { w_lfl_item_t *start; w_lfl_item_t *page_start; int32_t current_idx; - int32_t rows_num; + int32_t max_lines; int32_t cur_page; int32_t pages_num; size_t files_num; @@ -35,7 +35,6 @@ typedef struct { void w_lfl_draw(w_lfl_ui_t *fl_ui); void w_lfl_destroy(w_lfl_ui_t *fl_ui); -void w_lfl_cb(w_cb_args_t *args); void w_lfl_reset(w_lfl_ui_t *fl_ui); w_lfl_ui_t *w_lfl_init(WINDOW **win, w_t *w_parent); w_lfl_ui_t *w_lfl_init_win(w_app_t *app); diff --git a/src/widget_core/widget_core.h b/src/widget_core/widget_core.h index 77ee704..69d59f2 100644 --- a/src/widget_core/widget_core.h +++ b/src/widget_core/widget_core.h @@ -73,6 +73,7 @@ typedef struct { } w_cb_args_t; typedef void (*w_cb_t)(w_cb_args_t *args); +typedef int32_t (*w_cb_press_t)(void *app, void *data); typedef struct w_t { int64_t id;