panel and scroll_view

This commit is contained in:
2026-01-16 19:35:11 +05:00
parent b9a622c9d1
commit db861cd698
6 changed files with 157 additions and 72 deletions

View File

@@ -18,9 +18,9 @@ void ClientCLI_construct(ClientCLI* self){
zeroStruct(self); zeroStruct(self);
self->style.common = (TimStyle){ self->style.common = (TimStyle){
.brd = Color256_MidGray, .brd = Color256_LightGray,
.bg = Color256_NavyBlue, .bg = Color256_NavyBlue,
.fg = Color256_MidGray .fg = Color256_LightGray
}; };
self->style.focused = (TimStyle){ self->style.focused = (TimStyle){
.brd = Color256_White, .brd = Color256_White,
@@ -28,7 +28,7 @@ void ClientCLI_construct(ClientCLI* self){
.fg = Color256_White .fg = Color256_White
}; };
self->style.error = (TimStyle){ self->style.error = (TimStyle){
.brd = Color256_MidGray, .brd = Color256_LightGray,
.bg = Color256_DarkRed, .bg = Color256_DarkRed,
.fg = Color256_White .fg = Color256_White
}; };

View File

@@ -35,8 +35,10 @@ void ClientCLI_run(ClientCLI* self);
enum { enum {
Color256_White = 0xe7, Color256_Black = 0x10,
Color256_MidGray = 0xf8, Color256_MidGray = 0xf8,
Color256_LightGray = 0xfb,
Color256_White = 0xe7,
Color256_NavyBlue = 0x11, Color256_NavyBlue = 0x11,
Color256_DeepSkyBlue = 0x18, Color256_DeepSkyBlue = 0x18,
Color256_DarkRed = 0x58, Color256_DarkRed = 0x58,
@@ -59,22 +61,22 @@ void TextInputState_construct(TextInputState* ctx, cstr label,
/// tim_edit with tim_label over its upper border. /// tim_edit with tim_label over its upper border.
void text_input(TextInputState* ctx, i32 x, i32 y, i32 w, TimStyle style); void text_input(TextInputState* ctx, i32 x, i32 y, i32 w, TimStyle style);
void TimScrollItem_fromTextInputState(TimScrollItem* item, TextInputState* input); void TimPanelItem_fromTextInputState(TimPanelItem* item, TextInputState* input);
/// Intended to use in TimScrollItem /// Intended to use in TimPanelItem
/// @param data TextInputState* /// @param data TextInputState*
void draw_item_text_input(bool is_selected, TimRect place, void* data); void draw_item_text_input(void* data, TimRect place, bool is_selected);
List_declare(TimScrollItem); List_declare(TimPanelItem);
typedef struct StartScreenContext { typedef struct StartScreenContext {
ClientCLI* client; ClientCLI* client;
char* err_msg; // heap only char* err_msg; // heap only
TextInputState input_username; TextInputState input_username;
TextInputState input_password; TextInputState input_password;
List(TimScrollItem) scroll_list; TimPanel central_panel;
TimScrollState scroll_state; TimPanel central_buttons_panel;
} StartScreenContext; } StartScreenContext;
void StartScreenContext_construct(StartScreenContext* ctx, ClientCLI* client); void StartScreenContext_construct(StartScreenContext* ctx, ClientCLI* client);
@@ -84,6 +86,8 @@ void start_screen(StartScreenContext* ctx);
typedef struct MainScreenContext { typedef struct MainScreenContext {
ClientCLI* client; ClientCLI* client;
TimPanel central_panel;
TimScrollView central_scroll_view;
} MainScreenContext; } MainScreenContext;
void MainScreenContext_construct(MainScreenContext* ctx, ClientCLI* client); void MainScreenContext_construct(MainScreenContext* ctx, ClientCLI* client);

View File

@@ -19,6 +19,8 @@ static const str farewell_art = STR(
); );
static void draw_test_label(void* data, TimRect place, bool is_selected);
static void draw_central_panel(void* data, TimRect place);
static Result(SavedServer*) joinNewServer(ClientCLI* self); static Result(SavedServer*) joinNewServer(ClientCLI* self);
static Result(SavedServer*) selectServerFromCache(ClientCLI* self); static Result(SavedServer*) selectServerFromCache(ClientCLI* self);
static Result(void) showSavedServer(ClientCLI* self, SavedServer* server); static Result(void) showSavedServer(ClientCLI* self, SavedServer* server);
@@ -29,23 +31,64 @@ static Result(void) loginAtServer(ClientCLI* self);
void MainScreenContext_construct(MainScreenContext* ctx, ClientCLI* client){ void MainScreenContext_construct(MainScreenContext* ctx, ClientCLI* client){
zeroStruct(ctx); zeroStruct(ctx);
ctx->client = client; ctx->client = client;
///////////////////////////////////////////////////////////////////////////
// central_panel //
///////////////////////////////////////////////////////////////////////////
{
List(TimPanelItem) items = List_TimPanelItem_alloc(4);
TimPanelItem item_test_label = {
.w = A, .h = 12, .data = ctx, .draw = draw_test_label
};
List_TimPanelItem_push(&items, item_test_label);
List_TimPanelItem_push(&items, item_test_label);
List_TimPanelItem_push(&items, item_test_label);
List_TimPanelItem_push(&items, item_test_label);
ctx->central_panel.items = items.data;
ctx->central_panel.len = items.len;
}
///////////////////////////////////////////////////////////////////////////
// central_scroll_view //
///////////////////////////////////////////////////////////////////////////
{
ctx->central_scroll_view.content_h = 50;
ctx->central_scroll_view.data = ctx;
ctx->central_scroll_view.draw = draw_central_panel;
}
} }
void MainScreenContext_destroy(MainScreenContext* ctx){ void MainScreenContext_destroy(MainScreenContext* ctx){
free(ctx->central_panel.items);
} }
void main_screen(MainScreenContext* ctx){ void main_screen(MainScreenContext* ctx){
tim_frame(0, 0, ~0, ~0, ctx->client->style.common); if (tim->event.type == TimEvent_Draw) {
tim_fill(tim_cell(" ", ctx->client->style.common.fg, ctx->client->style.common.bg), 0, 0, A, A);
}
if(tim_button("[Esc/Q] Quit", A, A, A, A, ctx->client->style.common) if(tim_button_noborder("[Esc/Q] Exit", 1, 0, 14, 1, ctx->client->style.common)
|| tim_is_key_press('q') || tim_is_key_press('q')
|| tim_is_key_press(TimKey_Escape)) || tim_is_key_press(TimKey_Escape))
{ {
ctx->client->state = ClientCLIState_Exit; ctx->client->state = ClientCLIState_Exit;
} }
tim_scroll_view(&ctx->central_scroll_view, 0, 1, ~0, ~0, ctx->client->style.common);
} }
static void draw_test_label(void* data, TimRect place, bool is_selected){
MainScreenContext* ctx = data;
TimStyle style = is_selected ? ctx->client->style.focused : ctx->client->style.common;
tim_frame(place.x, place.y, place.w, place.h, style);
tim_label("0\n1\n2\n3\n4\n5\n6\n7\n8\n9",
place.x + 1, place.y + 1, place.w - 2, place.h - 2, style);
}
static void draw_central_panel(void* data, TimRect place){
MainScreenContext* ctx = data;
tim_panel(&ctx->central_panel, false, place.x, place.y, place.w, place.h);
}
/* /*
static Result(void) ClientCLI_execCommand(ClientCLI* self, str command, bool* stop){ static Result(void) ClientCLI_execCommand(ClientCLI* self, str command, bool* stop){

View File

@@ -2,7 +2,9 @@
#include "network/tcp-chat-protocol/v1.h" #include "network/tcp-chat-protocol/v1.h"
#include "tlibc/filesystem.h" #include "tlibc/filesystem.h"
static void draw_buttons(bool is_selected, TimRect place, void* data); static void draw_central_buttons_panel(void* data, TimRect place, bool is_selected);
static void draw_start_button(void* data, TimRect place, bool is_selected);
static void draw_exit_button(void* data, TimRect place, bool is_selected);
static Result(void) openUserDB(StartScreenContext* ctx); static Result(void) openUserDB(StartScreenContext* ctx);
@@ -21,77 +23,101 @@ static void clearError(StartScreenContext* ctx){
void StartScreenContext_construct(StartScreenContext* ctx, ClientCLI* client){ void StartScreenContext_construct(StartScreenContext* ctx, ClientCLI* client){
zeroStruct(ctx); zeroStruct(ctx);
ctx->client = client; ctx->client = client;
///////////////////////////////////////////////////////////////////////////
// input_username //
///////////////////////////////////////////////////////////////////////////
{
Array(char) username_buf = Array_char_alloc(USERNAME_SIZE_MAX + 1); Array(char) username_buf = Array_char_alloc(USERNAME_SIZE_MAX + 1);
TextInputState_construct(&ctx->input_username, "[username]", TextInputState_construct(&ctx->input_username, "[username]",
username_buf, NULL, false, username_buf, NULL, false,
ctx->client->style.common, ctx->client->style.focused); ctx->client->style.common, ctx->client->style.focused);
}
///////////////////////////////////////////////////////////////////////////
// input_password //
///////////////////////////////////////////////////////////////////////////
{
Array(char) password_buf = Array_char_alloc(PASSWORD_SIZE_MAX + 1); Array(char) password_buf = Array_char_alloc(PASSWORD_SIZE_MAX + 1);
TextInputState_construct(&ctx->input_password, "[password]", TextInputState_construct(&ctx->input_password, "[password]",
password_buf, NULL, true, password_buf, NULL, true,
ctx->client->style.common, ctx->client->style.focused); ctx->client->style.common, ctx->client->style.focused);
}
///////////////////////////////////////////////////////////////////////////
// central_panel //
///////////////////////////////////////////////////////////////////////////
{
List(TimPanelItem) items = List_TimPanelItem_alloc(4);
ctx->scroll_list = List_TimScrollItem_alloc(8); TimPanelItem item_username_input;
TimPanelItem_fromTextInputState(&item_username_input, &ctx->input_username);
List_TimPanelItem_push(&items, item_username_input);
TimScrollItem item_input_username; TimPanelItem item_password_input;
TimScrollItem_fromTextInputState(&item_input_username, &ctx->input_username); TimPanelItem_fromTextInputState(&item_password_input, &ctx->input_password);
List_TimScrollItem_push(&ctx->scroll_list, item_input_username); List_TimPanelItem_push(&items, item_password_input);
TimScrollItem item_input_password; TimPanelItem item_central_buttons_panel = {
TimScrollItem_fromTextInputState(&item_input_password, &ctx->input_password); .w = A, .h = 3, .data = ctx, .draw = draw_central_buttons_panel
List_TimScrollItem_push(&ctx->scroll_list, item_input_password);
TimScrollItem item_buttons = {
.data = ctx,
.focus_target = NULL,
.h = 3,
.draw = draw_buttons
}; };
List_TimScrollItem_push(&ctx->scroll_list, item_buttons); List_TimPanelItem_push(&items, item_central_buttons_panel);
ctx->scroll_state = (TimScrollState){ .items = ctx->scroll_list.data, .len = ctx->scroll_list.len }; ctx->central_panel.items = items.data;
ctx->central_panel.len = items.len;
}
///////////////////////////////////////////////////////////////////////////
// central_buttons_panel //
///////////////////////////////////////////////////////////////////////////
{
List(TimPanelItem) items = List_TimPanelItem_alloc(4);
TimPanelItem item_start_button = {
.w = A, .h = 3, .data = ctx, .draw = draw_start_button
};
List_TimPanelItem_push(&items, item_start_button);
TimPanelItem item_exit_button = {
.w = A, .h = 3, .data = ctx, .draw = draw_exit_button
};
List_TimPanelItem_push(&items, item_exit_button);
ctx->central_buttons_panel.items = items.data;
ctx->central_buttons_panel.len = items.len;
ctx->central_buttons_panel.is_horizontal = true;
}
} }
void StartScreenContext_destroy(StartScreenContext* ctx){ void StartScreenContext_destroy(StartScreenContext* ctx){
free(ctx->input_username.edit.s); free(ctx->input_username.edit.s);
free(ctx->input_password.edit.s); free(ctx->input_password.edit.s);
free(ctx->err_msg); free(ctx->err_msg);
List_TimScrollItem_destroy(&ctx->scroll_list); free(ctx->central_panel.items);
free(ctx->central_buttons_panel.items);
} }
void start_screen(StartScreenContext* ctx) void start_screen(StartScreenContext* ctx)
{ {
if (tim->event.type == TimEvent_Draw) {
tim_fill(tim_cell(" ", ctx->client->style.common.fg, ctx->client->style.common.bg), 0, 0, A, A); tim_fill(tim_cell(" ", ctx->client->style.common.fg, ctx->client->style.common.bg), 0, 0, A, A);
tim_scope(A, A, 40, 13) {
tim_scroll(&ctx->scroll_state, 1, 1, ~1, ~1, ctx->client->style.common);
} }
tim_frame(A, A, 40, 11, ctx->client->style.common);
tim_panel(&ctx->central_panel, true, A, A, 38, 9);
if(ctx->err_msg){ if(ctx->err_msg){
i32 below_scroll = tim->scopes[tim->scope].h/2 + 6; i32 below_list = tim->scopes[tim->scope].h/2 + 6;
tim_label("ERROR: ", A, below_scroll, A, A, ctx->client->style.error); tim_label("ERROR: ", A, below_list, A, A, ctx->client->style.error);
tim_label(ctx->err_msg, A, below_scroll + 1, A, A, ctx->client->style.error); tim_label(ctx->err_msg, A, below_list + 1, A, A, ctx->client->style.error);
} }
} }
static void draw_central_buttons_panel(void* data, TimRect place, bool is_selected){
StartScreenContext* ctx = data;
tim_panel(&ctx->central_buttons_panel, is_selected, place.x, place.y, place.w, place.h);
}
static void draw_start_button(void* data, TimRect place, bool is_selected){
static void draw_buttons(bool is_selected, TimRect place, void* data){
StartScreenContext* ctx = data; StartScreenContext* ctx = data;
TimStyle style = is_selected ? ctx->client->style.focused : ctx->client->style.common; TimStyle style = is_selected ? ctx->client->style.focused : ctx->client->style.common;
i32 start_w = place.w / 2; if(tim_button("[Enter] Start", place.x, place.y, place.w, A, style)
i32 dist = 1;
i32 quit_x = place.x + start_w + dist;
i32 quit_w = place.w - start_w - dist;
if(tim_button("[Esc/Q] Quit", quit_x, place.y, quit_w, A, ctx->client->style.common)
|| tim_is_key_press('q')
|| ctx->input_username.result_key == TimKey_Escape
|| ctx->input_password.result_key == TimKey_Escape)
{
ctx->client->state = ClientCLIState_Exit;
return;
}
if(tim_button("[Enter] Start", place.x, place.y, start_w, A, style)
|| tim_is_key_press(TimKey_Enter)) || tim_is_key_press(TimKey_Enter))
{ {
clearError(ctx); clearError(ctx);
@@ -116,21 +142,32 @@ static void draw_buttons(bool is_selected, TimRect place, void* data){
return; return;
} }
// create client // create client
try_handle(ctx->client->client, p, Client_create(username, password), handleError); try_handle(ctx->client->client, p, Client_create(username, password), handleError);
// init user DB // init user DB
try_handle_void(openUserDB(ctx), handleError); try_handle_void(openUserDB(ctx), handleError);
// erase password from memory // erase password from memory
memset(ctx->input_password.edit.s, 0, ctx->input_password.edit.capacity); memset(ctx->input_password.edit.s, 0, ctx->input_password.edit.capacity);
// switch to next screen
ctx->client->state = ClientCLIState_MainScreen; ctx->client->state = ClientCLIState_MainScreen;
return; return;
} }
} }
static void draw_exit_button(void* data, TimRect place, bool is_selected){
StartScreenContext* ctx = data;
TimStyle style = is_selected ? ctx->client->style.focused : ctx->client->style.common;
if(tim_button("[Esc/Q] Exit", place.x, place.y, place.w, A, style)
|| tim_is_key_press('q')
|| tim_is_key_press(TimKey_Escape)
|| ctx->input_username.result_key == TimKey_Escape
|| ctx->input_password.result_key == TimKey_Escape)
{
ctx->client->state = ClientCLIState_Exit;
return;
}
}
static Result(void) openUserDB(StartScreenContext* ctx){ static Result(void) openUserDB(StartScreenContext* ctx){
Deferral(8); Deferral(8);

View File

@@ -16,15 +16,16 @@ void text_input(TextInputState* ctx, i32 x, i32 y, i32 w, TimStyle style){
tim_label(ctx->label, x + 3, y, A, 1, style); tim_label(ctx->label, x + 3, y, A, 1, style);
} }
void TimScrollItem_fromTextInputState(TimScrollItem* item, TextInputState* input){ void TimPanelItem_fromTextInputState(TimPanelItem* item, TextInputState* input){
zeroStruct(item); zeroStruct(item);
item->w = A;
item->h = 3;
item->data = input; item->data = input;
item->focus_target = &input->edit; item->focus_target = &input->edit;
item->h = 3;
item->draw = draw_item_text_input; item->draw = draw_item_text_input;
} }
void draw_item_text_input(bool is_selected, TimRect place, void* data){ void draw_item_text_input(void* data, TimRect place, bool is_selected){
TextInputState* ctx = data; TextInputState* ctx = data;
TimStyle style = is_selected ? ctx->style.focused : ctx->style.common; TimStyle style = is_selected ? ctx->style.focused : ctx->style.common;
text_input(ctx, place.x, place.y, place.w, style); text_input(ctx, place.x, place.y, place.w, style);