+ 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:
## Current:
- [ ] Login and registration error
- [ ] PageUp/PageDown key functionality in lists
- [ ] File upload multiline description
- [ ] File upload multiline description (window)
- [ ] Permission checkboxes for file upload/edit
- [ ] 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:
init_upload_props_dialogue_modal(app);
break;
case WAIT_SERVER_INIT:
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:
default:
if (app->query_args->notification != NULL) {
init_notification_modal(app);
} else {
+15 -8
View File
@@ -1,11 +1,12 @@
#include "group.h"
#include <widget.h>
#include "widget_core.h"
#include <ncursesw/ncurses.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <widget.h>
void upload_props_dialogue_modal_cb(callback_args_t *args) {
callback_args_t d_args;
@@ -18,9 +19,10 @@ void upload_props_dialogue_modal_cb(callback_args_t *args) {
char query[256];
int32_t query_len = 0;
input_t *desc_input = (input_t *)d->g_content->elements[0].element;
/* TODO: permissions */
if (d_args.resp_data.code == cbrp_val) {
switch (d_args.resp_data.val.val.num) {
case 0:
case 1:
query_len =
sprintf(query, "file upload \"%s\" %zu 1\n",
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;
write(app->query_args->sd, query, query_len);
break;
case 1:
case 2:
if (app->query_args->file->name != NULL)
free(app->query_args->file->name);
if (app->query_args->file->path != NULL)
@@ -48,12 +50,15 @@ dialogue_t *init_upload_props_dialogue_modal(app_t *app) {
if (app == NULL)
return NULL;
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}};
group_el_init_t actions[] = {
{.type = w_button, .label = "Upload", .is_default = true},
{.type = w_button, .label = "Cancel", .is_default = false},
{.type = w_button, .label = "Upload", .is_default = false, .val.num = 1},
{.type = w_button, .label = "Cancel", .is_default = false, .val.num = 2},
{.type = w_end},
};
@@ -63,8 +68,10 @@ 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, 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;
+3 -10
View File
@@ -45,10 +45,8 @@ 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.code = cbrc_none};
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;
@@ -183,12 +181,7 @@ int process_query(app_t *app) {
case WAIT_REGISTER:
wait_register(query_args);
break;
case S_ASK_SEVER_IP:
case S_ASK_LOGIN_TYPE:
case S_N_D:
case S_ASK_LOGIN_USER:
case S_PRINT_SERVER_MESSAGE:
case S_UPLOAD_FILE_SELECT:
default:
break;
}
return 0;
+3 -3
View File
@@ -62,13 +62,13 @@ int process_server_command(char *line, int l_len, app_t *app) {
}
/* 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->pass);
params->uname = NULL;
params->pass = NULL;
// get_missing_params(params);
write(q_args->sd, params->uname, strlen(params->uname));
q_args->state = S_ASK_LOGIN_USER;
return 0;
}
+1 -1
View File
@@ -6,7 +6,7 @@
typedef struct {
widget_t w;
char hotkey;
int32_t hotkey;
uint32_t is_disabled : 1;
} 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
+13 -11
View File
@@ -4,6 +4,8 @@
#include <sys/types.h>
#include "app.h"
#include "button.h"
#include "checkbox.h"
#include "dialogue.h"
#include "file_list.h"
#include "group.h"
@@ -193,14 +195,14 @@ void dialogue_init_active_id(dialogue_t *dialogue) {
#define DETECT_GROUP_SIZE(group, line_max_len, y, x) \
if (group) { \
group->w.ps.y = y; \
if (line_max_len < group->w.sz.x) { \
line_max_len = group->w.sz.x; \
group->w.ps.x = 0; \
group->w.ps.y = y; \
if (line_max_len < group->w.sz.x) { \
line_max_len = group->w.sz.x; \
group->w.ps.x = 0; \
} else { \
group->w.ps.x = (line_max_len - group->w.sz.x) / 2; \
group->w.ps.x = (line_max_len - group->w.sz.x) / 2; \
} \
y += group->w.sz.y; \
y += group->w.sz.y; \
}
int32_t draw_dialogue(dialogue_t *d) {
@@ -275,11 +277,11 @@ int32_t draw_dialogue(dialogue_t *d) {
}
/* move cursor */
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;
if (d->active_el != NULL &&
(d->active_el->type == w_input || d->active_el->type == w_checkbox)) {
widget_t *w = d->active_el->element;
d->w.cur.y = w->cur.y;
d->w.cur.x = w->cur.x;
if (d->w.cur.y || d->w.cur.x) {
wmove(d->win, d->w.cur.y, d->w.cur.x);
curs_set(true);
+25 -34
View File
@@ -1,5 +1,6 @@
#include "group.h"
#include "button.h"
#include "checkbox.h"
#include "d_array.h"
#include "fs_file_list.h"
#include "input.h"
@@ -12,51 +13,31 @@
#include <sys/types.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) {
group_t *g = (group_t *)args->element;
int32_t key = *((int32_t *)args->data);
int64_t response = args->resp_data.val.val.num;
input_t *input;
widget_t *widget;
group_el_t *element_ptr = args->active_el;
u_int32_t start_pos = 0;
callback_args_t new_args;
switch (key) {
case '\n': /* Enter */
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:
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;
default:
if (element_ptr->type == w_input) {
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 {
if (!group_default_key_action(args)) {
/* callback case */
widget = (widget_t *)element_ptr->element;
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));
w = &(((ui_progress_bar_t *)elements[i].element)->w);
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 (i == 0) {
@@ -202,6 +187,9 @@ void draw_group(WINDOW *win, group_t *group, int32_t active_id) {
case w_fs_file_list:
draw_fs_file_list((ui_fs_file_list_t *)el->element);
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:
destroy_fs_file_list(el->element);
break;
case w_checkbox:
destroy_checkbox(el->element);
break;
}
}
if (group->elements != NULL) {
+41 -5
View File
@@ -7,13 +7,49 @@
#include "input.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,
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.sz.y = 3; // with borders
input->w.sz.x = length + 3; // with borders and extra space for the last element
input->w.sz.y = 3; // with borders
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
if (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) {
mvwprintw(win, pos_y + 1, pos_x, "%.*s%*s", (int)input->value_len,
stars, (int)(input->w.sz.x - input->value_len - 2), "");
mvwprintw(win, pos_y + 1, pos_x, "%.*s%*s", (int)input->value_len, stars,
(int)(input->w.sz.x - input->value_len - 2), "");
} else {
mvwprintw(win, pos_y + 1, pos_x, "%s%*s", input->value,
(int)(input->w.sz.x - input->value_len - 2), "");
}
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);
return 0;
+1
View File
@@ -15,6 +15,7 @@ typedef struct {
bool is_hidden;
} input_t;
bool input_default_key_action(callback_args_t *args);
input_t *init_input(WINDOW **win, widget_t *w_parent, char *label,
uint32_t size, uint32_t is_hidden_value);
int32_t draw_input(input_t *input, uint32_t active_id);
+1
View File
@@ -8,4 +8,5 @@
#include "group.h"
#include "input.h"
#include "progress_bar.h"
#include "checkbox.h"
#endif
+1
View File
@@ -18,6 +18,7 @@ enum w_type {
w_box,
w_group,
w_input,
w_checkbox,
w_progress,
w_fs_file_list,
};