+ UI: checkboxes

F Login: Fixes scenario when credentials are incorrect
R UI: group -> refactors key processing
This commit is contained in:
2026-03-11 14:21:25 +02:00
parent e20a239ac6
commit 0bf2446664
14 changed files with 192 additions and 86 deletions
+2 -1
View File
@@ -1,8 +1,9 @@
# TODO: # TODO:
## Current: ## Current:
- [ ] Login and registration error
- [ ] PageUp/PageDown key functionality in lists - [ ] PageUp/PageDown key functionality in lists
- [ ] File upload multiline description - [ ] File upload multiline description (window)
- [ ] Permission checkboxes for file upload/edit - [ ] Permission checkboxes for file upload/edit
- [ ] More intuitive navigation between nested groups (by pressing left and right buttons) - [ ] More intuitive navigation between nested groups (by pressing left and right buttons)
+1 -13
View File
@@ -36,19 +36,7 @@ void app_draw_modal(app_t *app) {
case S_UPLOAD_PARAMS: case S_UPLOAD_PARAMS:
init_upload_props_dialogue_modal(app); init_upload_props_dialogue_modal(app);
break; break;
case WAIT_SERVER_INIT: default:
case WAIT_SERVER:
case WAIT_REGISTER:
case WAIT_REGISTER_CONFIRMATION:
case WAIT_CLIENT:
case S_FILE_LIST:
case S_FILE_SELECT:
case S_UPLOAD_REQUESTED:
case S_ASK_USER_BEFORE_LOGIN:
case S_ERR:
case S_WAIT_SERVER:
case S_N_D:
case S_NEXT_ACTION:
if (app->query_args->notification != NULL) { if (app->query_args->notification != NULL) {
init_notification_modal(app); init_notification_modal(app);
} else { } else {
+15 -8
View File
@@ -1,11 +1,12 @@
#include "group.h" #include "group.h"
#include <widget.h> #include "widget_core.h"
#include <ncursesw/ncurses.h> #include <ncursesw/ncurses.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <widget.h>
void upload_props_dialogue_modal_cb(callback_args_t *args) { void upload_props_dialogue_modal_cb(callback_args_t *args) {
callback_args_t d_args; callback_args_t d_args;
@@ -18,9 +19,10 @@ void upload_props_dialogue_modal_cb(callback_args_t *args) {
char query[256]; char query[256];
int32_t query_len = 0; int32_t query_len = 0;
input_t *desc_input = (input_t *)d->g_content->elements[0].element; input_t *desc_input = (input_t *)d->g_content->elements[0].element;
/* TODO: permissions */
if (d_args.resp_data.code == cbrp_val) { if (d_args.resp_data.code == cbrp_val) {
switch (d_args.resp_data.val.val.num) { switch (d_args.resp_data.val.val.num) {
case 0: case 1:
query_len = query_len =
sprintf(query, "file upload \"%s\" %zu 1\n", sprintf(query, "file upload \"%s\" %zu 1\n",
app->query_args->file->name, app->query_args->file->size); app->query_args->file->name, app->query_args->file->size);
@@ -31,7 +33,7 @@ void upload_props_dialogue_modal_cb(callback_args_t *args) {
app->query_args->state = S_UPLOAD_REQUESTED; app->query_args->state = S_UPLOAD_REQUESTED;
write(app->query_args->sd, query, query_len); write(app->query_args->sd, query, query_len);
break; break;
case 1: case 2:
if (app->query_args->file->name != NULL) if (app->query_args->file->name != NULL)
free(app->query_args->file->name); free(app->query_args->file->name);
if (app->query_args->file->path != NULL) if (app->query_args->file->path != NULL)
@@ -48,12 +50,15 @@ dialogue_t *init_upload_props_dialogue_modal(app_t *app) {
if (app == NULL) if (app == NULL)
return NULL; return NULL;
group_el_init_t content[] = { group_el_init_t content[] = {
{.type = w_input, .label = "File description", .length = 30}, {.type = w_checkbox, .label = "Visible for all", .is_default = true},
{.type = w_checkbox,
.label = "Visible for registered",
.is_default = false},
{.type = w_end}}; {.type = w_end}};
group_el_init_t actions[] = { group_el_init_t actions[] = {
{.type = w_button, .label = "Upload", .is_default = true}, {.type = w_button, .label = "Upload", .is_default = false, .val.num = 1},
{.type = w_button, .label = "Cancel", .is_default = false}, {.type = w_button, .label = "Cancel", .is_default = false, .val.num = 2},
{.type = w_end}, {.type = w_end},
}; };
@@ -63,8 +68,10 @@ dialogue_t *init_upload_props_dialogue_modal(app_t *app) {
dialogue_t *d = &(app->modal); dialogue_t *d = &(app->modal);
d->w.callback = upload_props_dialogue_modal_cb; d->w.callback = upload_props_dialogue_modal_cb;
d->g_content = init_group(&(d->win), &(d->w), content, &(d->id_map), horizontal, g_content); d->g_content = init_group(&(d->win), &(d->w), content, &(d->id_map),
d->g_action = init_group(&(d->win), &(d->w), actions, &(d->id_map), horizontal, g_action); 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; app->query_args->active_dialogue = d;
+3 -10
View File
@@ -45,10 +45,8 @@ void query_loop(app_t *app) {
size_t qlen; size_t qlen;
int sr; int sr;
static file_args_t file_args; static file_args_t file_args;
callback_args_t d_args = {.app = app, callback_args_t d_args = {
.element = NULL, .app = app, .element = NULL, .data = NULL, .resp_data.code = cbrc_none};
.data = NULL,
.resp_data.code = cbrc_none};
init_file_args(&file_args); init_file_args(&file_args);
query_args->sd = sd; query_args->sd = sd;
@@ -183,12 +181,7 @@ int process_query(app_t *app) {
case WAIT_REGISTER: case WAIT_REGISTER:
wait_register(query_args); wait_register(query_args);
break; break;
case S_ASK_SEVER_IP: default:
case S_ASK_LOGIN_TYPE:
case S_N_D:
case S_ASK_LOGIN_USER:
case S_PRINT_SERVER_MESSAGE:
case S_UPLOAD_FILE_SELECT:
break; break;
} }
return 0; return 0;
+3 -3
View File
@@ -62,13 +62,13 @@ int process_server_command(char *line, int l_len, app_t *app) {
} }
/* LOGIN AGAIN */ /* LOGIN AGAIN */
if (!strncmp(line, "login_again>", ws_pos)) { if (!strncmp(line, "login_again>\n", ws_pos)) {
alert("Incorrect credentials! Try again");
free(params->uname); free(params->uname);
free(params->pass); free(params->pass);
params->uname = NULL; params->uname = NULL;
params->pass = NULL; params->pass = NULL;
// get_missing_params(params); q_args->state = S_ASK_LOGIN_USER;
write(q_args->sd, params->uname, strlen(params->uname));
return 0; return 0;
} }
+1 -1
View File
@@ -6,7 +6,7 @@
typedef struct { typedef struct {
widget_t w; widget_t w;
char hotkey; int32_t hotkey;
uint32_t is_disabled : 1; uint32_t is_disabled : 1;
} button_t; } button_t;
+68
View File
@@ -0,0 +1,68 @@
#include <ncursesw/ncurses.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "app.h"
#include "checkbox.h"
#include <widget_core.h>
bool checkbox_default_key_action(callback_args_t *args) {
int32_t key = *((int32_t *)args->data);
group_el_t *g_el = args->active_el;
checkbox_t *cbx = g_el->element;
switch (key) {
case '\40':
cbx->value = !cbx->value;
break;
default:
break;
}
return true;
}
checkbox_t *init_checkbox(WINDOW **win, widget_t *w_parent, char *label) {
checkbox_t *cbx = malloc(sizeof(checkbox_t));
init_widget(&(cbx->w), w_parent, win, label);
cbx->is_disabled = false;
cbx->value = false;
cbx->w.sz.x = strlen(cbx->w.title) + 4; // + "[ ] "
cbx->w.sz.y = 1;
cbx->w.cur.y = 0;
cbx->w.cur.x = 1;
return cbx;
}
int32_t draw_checkbox(checkbox_t *cbx, uint32_t active_id) {
char title[DIALOGUE_TITLE + 6];
WINDOW *win = *(cbx->w.parent_win);
uint32_t margin_y = cbx->w.ps.y + cbx->w.m.y;
uint32_t margin_x = cbx->w.ps.x + cbx->w.m.x;
widget_t *w_par = cbx->w.w_parent;
while (w_par->parent_win == cbx->w.parent_win) {
margin_y += w_par->ps.y;
margin_x += w_par->ps.x;
w_par = w_par->w_parent;
}
char val = cbx->value ? 'x' : ' ';
if (cbx->w.id == active_id) {
sprintf(title, "[%c] %s", val, cbx->w.title);
wattrset(win, COLOR_PAIR(modal_color_pair) | A_BOLD | A_REVERSE);
} else {
sprintf(title, "[%c] %s", val, cbx->w.title);
wattrset(win, COLOR_PAIR(0) | A_REVERSE);
}
mvwprintw(win, margin_y, margin_x, "%s", title);
cbx->w.cur.y = margin_y;
cbx->w.cur.x = margin_x + 1;
wattroff(win, A_BOLD | A_REVERSE);
return strlen(title);
}
void destroy_checkbox(checkbox_t *btn) { free(btn); }
+17
View File
@@ -0,0 +1,17 @@
#ifndef CHECKBOX_H
#define CHECKBOX_H
#include <widget_core.h>
#include <stdint.h>
typedef struct {
widget_t w;
bool is_disabled;
bool value;
} checkbox_t;
bool checkbox_default_key_action(callback_args_t *args);
checkbox_t *init_checkbox(WINDOW **win, widget_t *w_parent, char *label);
int32_t draw_checkbox(checkbox_t *cbx, uint32_t active_id);
void destroy_checkbox(checkbox_t *cbx);
#endif
+7 -5
View File
@@ -4,6 +4,8 @@
#include <sys/types.h> #include <sys/types.h>
#include "app.h" #include "app.h"
#include "button.h"
#include "checkbox.h"
#include "dialogue.h" #include "dialogue.h"
#include "file_list.h" #include "file_list.h"
#include "group.h" #include "group.h"
@@ -275,11 +277,11 @@ int32_t draw_dialogue(dialogue_t *d) {
} }
/* move cursor */ /* move cursor */
if (d->active_el != NULL && d->active_el->type == w_input) { if (d->active_el != NULL &&
input_t *input = d->active_el->element; (d->active_el->type == w_input || d->active_el->type == w_checkbox)) {
d->w.cur.y = input->w.cur.y; widget_t *w = d->active_el->element;
d->w.cur.x = input->w.cur.x + input->value_len; d->w.cur.y = w->cur.y;
d->w.cur.x -= input->cur_pos; d->w.cur.x = w->cur.x;
if (d->w.cur.y || d->w.cur.x) { if (d->w.cur.y || d->w.cur.x) {
wmove(d->win, d->w.cur.y, d->w.cur.x); wmove(d->win, d->w.cur.y, d->w.cur.x);
curs_set(true); curs_set(true);
+25 -34
View File
@@ -1,5 +1,6 @@
#include "group.h" #include "group.h"
#include "button.h" #include "button.h"
#include "checkbox.h"
#include "d_array.h" #include "d_array.h"
#include "fs_file_list.h" #include "fs_file_list.h"
#include "input.h" #include "input.h"
@@ -12,51 +13,31 @@
#include <sys/types.h> #include <sys/types.h>
#include <utils.h> #include <utils.h>
bool group_default_key_action(callback_args_t *args) {
group_el_t *element = args->active_el;
switch (element->type) {
case w_input:
return input_default_key_action(args);
case w_checkbox:
return checkbox_default_key_action(args);
default:
return false;
}
}
void group_default_callback(callback_args_t *args) { void group_default_callback(callback_args_t *args) {
group_t *g = (group_t *)args->element;
int32_t key = *((int32_t *)args->data); int32_t key = *((int32_t *)args->data);
int64_t response = args->resp_data.val.val.num;
input_t *input;
widget_t *widget; widget_t *widget;
group_el_t *element_ptr = args->active_el; group_el_t *element_ptr = args->active_el;
u_int32_t start_pos = 0;
callback_args_t new_args; callback_args_t new_args;
switch (key) { switch (key) {
case '\n': /* Enter */ case '\n': /* Enter */
args->resp_data.code = cbrp_val; args->resp_data.code = cbrp_val;
mempcpy(&(args->resp_data.val), &(element_ptr->val), sizeof(struct val_t)); mempcpy(&(args->resp_data.val), &(element_ptr->val), sizeof(struct val_t));
break;
case KEY_BACKSPACE:
case KEY_DL:
if (element_ptr->type == w_input) {
input = (input_t *)element_ptr->element;
if (input->value_len) {
if (input->cur_pos > 0) {
start_pos = input->value_len-- - input->cur_pos;
memmove(input->value + start_pos - 1, input->value + start_pos,
input->value_len - start_pos + 2);
} else {
input->value[--input->value_len] = '\0';
}
}
}
args->resp_data.code = cbrc_none;
break; break;
default: default:
if (element_ptr->type == w_input) { if (!group_default_key_action(args)) {
input = (input_t *)element_ptr->element;
if (input->max_len > input->value_len) {
if (input->cur_pos > 0) {
start_pos = input->value_len++ - input->cur_pos;
memmove(input->value + start_pos + 1, input->value + start_pos,
input->value_len - start_pos);
input->value[start_pos] = key;
} else {
input->value[input->value_len++] = key;
input->value[input->value_len] = '\0';
}
}
} else {
/* callback case */ /* callback case */
widget = (widget_t *)element_ptr->element; widget = (widget_t *)element_ptr->element;
if (widget->callback != NULL) { if (widget->callback != NULL) {
@@ -145,6 +126,10 @@ group_t *init_group(WINDOW **win, widget_t *w_parent, group_el_init_t *children,
elements[i].element = init_fs_file_list(win, &(group->w)); elements[i].element = init_fs_file_list(win, &(group->w));
w = &(((ui_progress_bar_t *)elements[i].element)->w); w = &(((ui_progress_bar_t *)elements[i].element)->w);
break; break;
case w_checkbox:
elements[i].element = init_checkbox(win, &(group->w), children[i].label);
w = &(((checkbox_t *)elements[i].element)->w);
break;
} }
if (current.type != w_group) { if (current.type != w_group) {
if (i == 0) { if (i == 0) {
@@ -202,6 +187,9 @@ void draw_group(WINDOW *win, group_t *group, int32_t active_id) {
case w_fs_file_list: case w_fs_file_list:
draw_fs_file_list((ui_fs_file_list_t *)el->element); draw_fs_file_list((ui_fs_file_list_t *)el->element);
break; break;
case w_checkbox:
draw_checkbox((checkbox_t *)el->element, active_id);
break;
} }
} }
} }
@@ -229,6 +217,9 @@ void destroy_group(group_t *group) {
case w_fs_file_list: case w_fs_file_list:
destroy_fs_file_list(el->element); destroy_fs_file_list(el->element);
break; break;
case w_checkbox:
destroy_checkbox(el->element);
break;
} }
} }
if (group->elements != NULL) { if (group->elements != NULL) {
+40 -4
View File
@@ -7,13 +7,49 @@
#include "input.h" #include "input.h"
#include <widget_core.h> #include <widget_core.h>
bool input_default_key_action(callback_args_t *args) {
int32_t key = *((int32_t *)args->data);
group_el_t *g_el = args->active_el;
input_t *input = g_el->element;
int64_t start_pos = 0;
switch (key) {
case KEY_BACKSPACE:
case KEY_DL:
if (input->value_len) {
if (input->cur_pos > 0) {
start_pos = input->value_len-- - input->cur_pos;
memmove(input->value + start_pos - 1, input->value + start_pos,
input->value_len - start_pos + 2);
} else {
input->value[--input->value_len] = '\0';
}
}
break;
default:
if (input->cur_pos > 0) {
start_pos = input->value_len++ - input->cur_pos;
memmove(input->value + start_pos + 1, input->value + start_pos,
input->value_len - start_pos);
input->value[start_pos] = key;
} else {
input->value[input->value_len++] = key;
input->value[input->value_len] = '\0';
}
break;
}
args->resp_data.code = cbrc_none;
return true;
}
input_t *init_input(WINDOW **win, widget_t *w_parent, char *label, input_t *init_input(WINDOW **win, widget_t *w_parent, char *label,
uint32_t length, uint32_t is_hidden_value) { uint32_t length, uint32_t is_hidden_value) {
input_t *input = malloc(sizeof(input_t)); input_t *input = malloc(sizeof(input_t));
init_widget(&(input->w), w_parent, win, label); init_widget(&(input->w), w_parent, win, label);
input->is_disabled = 0; input->is_disabled = 0;
input->w.sz.y = 3; // with borders input->w.sz.y = 3; // with borders
input->w.sz.x = length + 3; // with borders and extra space for the last element input->w.sz.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 uint32_t t_len = strlen(input->w.title) + 4; // with borders and space
if (input->w.sz.x < t_len) if (input->w.sz.x < t_len)
input->w.sz.x = t_len; input->w.sz.x = t_len;
@@ -70,15 +106,15 @@ int32_t draw_input(input_t *input, uint32_t active_id) {
} }
if (input->is_hidden) { if (input->is_hidden) {
mvwprintw(win, pos_y + 1, pos_x, "%.*s%*s", (int)input->value_len, mvwprintw(win, pos_y + 1, pos_x, "%.*s%*s", (int)input->value_len, stars,
stars, (int)(input->w.sz.x - input->value_len - 2), ""); (int)(input->w.sz.x - input->value_len - 2), "");
} else { } else {
mvwprintw(win, pos_y + 1, pos_x, "%s%*s", input->value, mvwprintw(win, pos_y + 1, pos_x, "%s%*s", input->value,
(int)(input->w.sz.x - input->value_len - 2), ""); (int)(input->w.sz.x - input->value_len - 2), "");
} }
input->w.cur.y = pos_y + 1; input->w.cur.y = pos_y + 1;
input->w.cur.x = pos_x; input->w.cur.x = pos_x + input->value_len - input->cur_pos;
wattroff(win, A_BOLD | A_REVERSE); wattroff(win, A_BOLD | A_REVERSE);
return 0; return 0;
+1
View File
@@ -15,6 +15,7 @@ typedef struct {
bool is_hidden; bool is_hidden;
} input_t; } input_t;
bool input_default_key_action(callback_args_t *args);
input_t *init_input(WINDOW **win, widget_t *w_parent, char *label, input_t *init_input(WINDOW **win, widget_t *w_parent, char *label,
uint32_t size, uint32_t is_hidden_value); uint32_t size, uint32_t is_hidden_value);
int32_t draw_input(input_t *input, uint32_t active_id); int32_t draw_input(input_t *input, uint32_t active_id);
+1
View File
@@ -8,4 +8,5 @@
#include "group.h" #include "group.h"
#include "input.h" #include "input.h"
#include "progress_bar.h" #include "progress_bar.h"
#include "checkbox.h"
#endif #endif
+1
View File
@@ -18,6 +18,7 @@ enum w_type {
w_box, w_box,
w_group, w_group,
w_input, w_input,
w_checkbox,
w_progress, w_progress,
w_fs_file_list, w_fs_file_list,
}; };