pane

Termbox2 based terminal UI library
git clone git://git.dimitrijedobrota.com/pane.git
Log | Files | Refs

commit 724f53a36a61bc30c84d90a9d69babcd775bea4b
parent beac70e094573a3313fbdadef2f7738c3e3eca87
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date:   Wed, 31 Aug 2022 20:06:30 +0200

Basic responsive widget and payload interface

Diffstat:
Minclude/pane.h | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Minclude/utils.h | 5-----
Msrc/main.c | 126++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msrc/pane.c | 299+++++++++++++++++++++++++++++++++++++++++--------------------------------------
4 files changed, 305 insertions(+), 192 deletions(-)

diff --git a/include/pane.h b/include/pane.h @@ -1,6 +1,8 @@ #ifndef PANE_H #define PANE_H +#include <termbox.h> + #define T Pane_T typedef struct T *T; @@ -8,34 +10,83 @@ int pane_start(void); int pane_stop(void); int pane_handle_resize(void); int pane_resize(T self); + T pane_child(T self, int index); T *pane_split(T self, size_t count, ...); T *pane_vsplit(T self, size_t count, ...); void pane_unsplit(T self); +void setActive(int direction); void pane_setTitle(T self, char *title); +/* WIDGET */ + +typedef struct data_T *data_T; +typedef struct widget_T *widget_T; + +struct data_T { + void *payload; + int (*handle_input)(data_T, struct tb_event); + + int widget_num; + widget_T *widgets; +}; + +struct widget_T { + int resized; + int inited; + + char *title; + Pane_T pane; + data_T data; + void *style; + void *info; + void (*callback)(widget_T); +}; + +int widget_activate(widget_T self, Pane_T pane); +widget_T pane_widget(T self); + +/* MENU */ + +typedef struct menu_T *menu_T; +typedef struct menuItem_T *menuItem_T; +typedef struct menuStyle_T *menuStyle_T; +typedef struct menuInfo_T *menuInfo_T; + struct menuItem_T { void (*callback)(char *, int); char *name; }; -typedef struct menu_T *menu_T; -struct menu_T { - T pane; - char *title; +struct menuStyle_T { char *separator; - int padding; int spacing; +}; + +struct menuInfo_T { + int start; + int end; + + int y; + int x; + int x_sep; + + int max_item_len; + int sep_len; + int items_num; +}; + +struct menu_T { + int current; int items_size; struct menuItem_T items[]; }; -void pane_menu(menu_T self); -void setActive(int direction); -void printDiagram(T self); +void pane_menu(widget_T widget); +int menu_hadleInput(data_T data, struct tb_event ev); #undef T #endif diff --git a/include/utils.h b/include/utils.h @@ -17,9 +17,4 @@ #define UNUSED(x) (void)(x) -#define HANDLE_RESIZE \ - { \ - pane_handle_resize(); \ - } - #endif diff --git a/src/main.c b/src/main.c @@ -1,7 +1,6 @@ #define TB_IMPL #define TB_OPT_TRUECOLOR -#include <termbox.h> #include <stdio.h> #include "pane.h" @@ -16,76 +15,131 @@ void quit(char *pass, int index) } struct menu_T mainMenu = { - .pane=NULL, - .title = "Main Menu", - .separator="<------------->", - .padding = 5, - .spacing = 2, - .items = { { quit, "Start" }, { quit, "Help" }, { quit, "Exit" }, + { quit, "Start" }, + { quit, "Help" }, + { quit, "Exit" }, + { quit, "Start" }, + { quit, "Help" }, + { quit, "Exit" }, }, - .items_size = 3 + .items_size = 9, + .current = 0, +}; + +struct menuStyle_T mainMenuStyle = { + .separator = "<------------->", + .padding = 5, + .spacing = 2, +}; + +struct menuStyle_T mainMenuStyle2 = { + .separator = "***************", + .padding = 0, + .spacing = 0, +}; + +struct data_T mainMenu_payload = { + .payload = (void *)&mainMenu, + .handle_input = menu_hadleInput, +}; + +struct menuInfo_T mainMenuInfo; + +struct widget_T mainMenu_widget = { + .pane = NULL, + .data = &mainMenu_payload, + .callback = pane_menu, + .title = "Main Menu", + .style = &mainMenuStyle, + .info = &mainMenuInfo, }; -void movement(void) +struct menuInfo_T mainMenuInfo2; + +struct widget_T mainMenu_widget2 = { + .pane = NULL, + .inited = 0, + .resized = 0, + .data = &mainMenu_payload, + .callback = pane_menu, + .title = "Main Menu 2", + .style = &mainMenuStyle2, + .info = &mainMenuInfo2, +}; + +void input_loop(void) { struct tb_event ev; + size_t i; while (1) { tb_poll_event(&ev); switch (ev.type) { case TB_EVENT_RESIZE: - HANDLE_RESIZE; - break; - case TB_EVENT_KEY: - switch (ev.ch) { - case 'q': - return; - } + pane_handle_resize(); + continue; + case TB_EVENT_KEY: { + widget_T widget; + data_T payload; + switch (ev.key) { case TB_KEY_ARROW_UP: setActive(0); - break; + continue; case TB_KEY_ARROW_RIGHT: setActive(1); - break; + continue; case TB_KEY_ARROW_DOWN: setActive(2); - break; + continue; case TB_KEY_ARROW_LEFT: setActive(3); - break; - case TB_KEY_ESC: - quit(0, 0); + continue; + } + if (ev.ch == 'q' || ev.key == TB_KEY_ESC) { + return; } + + if (!(widget = pane_widget(ACTIVE))) + continue; + + if (!(payload = widget->data)) { + continue; + } + + if (payload->handle_input(payload, ev)) { + for (i = 0; i < payload->widget_num; i++) { + widget = payload->widgets[i]; + widget->callback(widget); + tb_present(); + } + } + break; + } } } } int main(int argc, char **argv) { - struct tb_event ev; - int y = 0; - pane_start(); { - Pane_T *sections, *mids, *bottoms; + Pane_T *sections, *mids, *bots; sections = pane_vsplit(MAIN, 3, 1, 1, 1); - bottoms = pane_split(sections[2], 2, 1, 1); - - while (1) { - mids = pane_split(sections[1], 2, 1, 2); - pane_vsplit(mids[0], 2, 1, 1); - movement(); - pane_unsplit(sections[1]); - movement(); - } + mids = pane_split(sections[1], 2, 1, 2); + bots = pane_split(sections[2], 2, 1, 1); + + pane_vsplit(mids[0], 2, 1, 1); + + widget_activate(&mainMenu_widget, mids[1]); + widget_activate(&mainMenu_widget2, bots[1]); + input_loop(); } pane_stop(); - printDiagram(MAIN); return 0; } diff --git a/src/pane.c b/src/pane.c @@ -1,28 +1,30 @@ #include <termbox.h> #include <stdarg.h> #include <mem.h> +#include <stdlib.h> #include "pane.h" #include "utils.h" #define T Pane_T struct T { + int horizontal; int children_num; T *children; - int horizontal; int *rules; - char *title; + int x; + int y; int width; int height; - int x; - int y; - int active; T direction[4]; + widget_T widget; + + char *title; int index; int child_no; T parent; @@ -74,22 +76,20 @@ void draw_border(T self) tb_print(self->x, y_max, color, 0, BORDER_CORNER_3); tb_print(x_max, y_max, color, 0, BORDER_CORNER_4); - /* tb_printf(self->x + 1, self->y + 1, TB_RED, 0, "%d", self->index); */ - draw_title(self); + + tb_printf(self->x, self->y, TB_RED, 0, "%dx%d", self->height, + self->width); } void pane_clear(T self, int clear_border) { - size_t i; - /* int border = self->children_num != 0; */ + size_t i, j; int border = !clear_border; - int width = self->width - 3 * border; - char *line = malloc(width * sizeof(char)); - memset(line, ' ', width); - for (i = self->y + border; i < self->y + self->height - border; i++) - tb_print(self->x + border, i, TB_BLACK, 0, line); + for (j = border; j < self->height - border; j++) + for (i = border; i < self->width - border; i++) + tb_printf(self->x + i, self->y + j, TB_BLACK, 0, " "); } #define SPLIT_FORMULA(ac, ad, sc, sd) \ @@ -173,10 +173,13 @@ int pane_resize(T self) { size_t i; + if (self->widget) { + self->widget->resized = 1; + self->widget->callback(self->widget); + } + if (self->children_num == 0) { draw_border(self); - tb_present(); - /* usleep(200000); */ return 1; } @@ -196,6 +199,7 @@ int pane_handle_resize(void) tb_clear(); pane_resize(MAIN); + tb_present(); return 1; } @@ -224,10 +228,14 @@ T pane_child(T self, int index) return self->children[index]; } +widget_T pane_widget(T self) +{ + return self->widget; +} + void split(T self, size_t count, va_list ap) { static int index = 1; - int current; size_t i; self->children_num = count; @@ -316,123 +324,6 @@ void pane_setTitle(T self, char *title) draw_title(self); } -int centerVertical(T self, int len) -{ - return (self->height - len) / 2; -} - -int centerHorisontal(T self, int len) -{ - return (self->width - len - 2) / 2 + 1; -} - -void pane_menu(menu_T menu) -{ - struct tb_event ev; - int rl_crnt = 0; - int i, len, maxi = 0; - int sep_len = strlen(menu->separator); - - for (i = 0; i < menu->items_size; i++) - if ((len = strlen(menu->items[i].name)) > maxi) - maxi = len; - - pane_setTitle(menu->pane, menu->title); - - int ab_crnt, items_num, y_start; - ab_crnt = 0; -redraw:; - - // optimal number of elements to display on the screen - int max_items = - (menu->pane->height - menu->padding * 2 + menu->spacing) / - (menu->spacing + 1); - // number of items displayed cant exceed actual number of items in the menu - items_num = ACLAMP(max_items, 0, menu->items_size); - // staring point for displaying these items for them to be centered - y_start = centerVertical(menu->pane, items_num * (menu->spacing + 1) - - menu->spacing); - - while (1) { - pane_clear(menu->pane, 0); - int y = menu->pane->y + y_start; - int x = menu->pane->x + centerHorisontal(menu->pane, maxi); - int x_separator = - menu->pane->x + centerHorisontal(menu->pane, sep_len); - - if (rl_crnt == -1) - ab_crnt--; - - if (rl_crnt == items_num) - ab_crnt++; - - CLAMP(ab_crnt, 0, menu->items_size - items_num); - CLAMP(rl_crnt, 0, items_num - 1); - - if (!menu->items_size) { - char *message = "NOTHING TO DISPLAY HERE!"; - int x = centerHorisontal(menu->pane, strlen(message)); - tb_print(x, y, TB_BLUE, 0, message); - tb_present(); - tb_poll_event(&ev); - return; - } - - if (items_num < menu->items_size && ab_crnt == 0) { - tb_print(x_separator, y - 1, TB_BLUE, 0, - menu->separator); - } - - int index = ab_crnt; - for (i = 0; i < items_num; i++) { - int color = (i == rl_crnt) ? TB_RED : TB_BLUE; - tb_printf(x, y, color, 0, "%s", - menu->items[ab_crnt + i].name); - y += menu->spacing + 1; - } - - if (items_num < menu->items_size && - ab_crnt == menu->items_size - items_num) { - tb_print(x_separator, y - menu->spacing, TB_BLUE, 0, - menu->separator); - } - - tb_present(); - - while (1) { - tb_poll_event(&ev); - - if (ev.type == TB_EVENT_RESIZE) { - HANDLE_RESIZE; - goto redraw; - } else if (ev.type == TB_EVENT_KEY) { - if (ev.ch == 'k' || ev.ch == 'w' || - ev.key == TB_KEY_ARROW_UP) { - rl_crnt--; - break; - } - if (ev.ch == 'j' || ev.ch == 's' || - ev.key == TB_KEY_ARROW_DOWN) { - rl_crnt++; - break; - } - if (ev.ch == 'q' || ev.key == TB_KEY_ESC) { - pane_clear(menu->pane, 1); - tb_present(); - return; - } - if (ev.key == TB_KEY_ENTER) { - int index = ab_crnt + rl_crnt; - struct menuItem_T *item = - menu->items + index; - item->callback(item->name, index); - return; - } - } - } - } -} - void setActive(int dir) { T old = ACTIVE, tmp; @@ -466,17 +357,139 @@ void setActive(int dir) tb_present(); } -void printDiagram(T self) +int centerVertical(T self, int len) +{ + return self->y + (self->height - len) / 2; +} + +int centerHorisontal(T self, int len) +{ + return self->x + (self->width - len - 2) / 2 + 1; +} + +/* WIDGET */ + +int widget_activate(widget_T self, Pane_T pane) +{ + self->pane = pane; + pane->widget = self; + pane_setTitle(pane, self->title); + + data_T data = self->data; + data->widgets = realloc(data->widgets, data->widget_num + 1); + data->widgets[data->widget_num++] = self; + + self->callback(self); + tb_present(); + + return 1; +} + +/* MENU DISPLAY */ + +int menu_hadleInput(data_T data, struct tb_event ev) +{ + menu_T menu = (menu_T)data->payload; + + if (ev.type == TB_EVENT_KEY) { + if (ev.ch == 'k' || ev.ch == 'w') { + menu->current--; + CLAMP(menu->current, 0, menu->items_size - 1); + return 1; + } + if (ev.ch == 'j' || ev.ch == 's') { + menu->current++; + CLAMP(menu->current, 0, menu->items_size - 1); + return 1; + } + if (ev.key == TB_KEY_ENTER) { + struct menuItem_T *item = menu->items + menu->current; + item->callback(item->name, menu->current); + return 1; + } + } + + return 0; +} + +void pane_menu(widget_T widget) { - static int index = 0; size_t i; + Pane_T pane = widget->pane; + menuInfo_T info = (menuInfo_T)(widget->info); + menuStyle_T style = (menuStyle_T)(widget->style); + menu_T menu = (menu_T)(widget->data->payload); - if (self != MAIN) - printf("%p:%d: parent:%d up:%d;down:%d;left:%d;right:%d;\n", - self, self->index, self->parent->index, UP(self)->index, - DOWN(self)->index, LEFT(self)->index, - RIGHT(self)->index); + if (!widget->inited) { + widget->inited = 1; + widget->resized = 1; - for (i = 0; i < self->children_num; i++) - printDiagram(self->children[i]); + int len, maxi = 0; + + for (i = 0; i < menu->items_size; i++) + if ((len = strlen(menu->items[i].name)) > maxi) + maxi = len; + + info->max_item_len = maxi; + info->sep_len = strlen(style->separator); + info->start = 0; + info->end = info->items_num - 1; + } + + if (widget->resized) { + widget->resized = 0; + + int padding = (style->padding * 2 + 1 > pane->height) ? + 0 : + style->padding; + // optimal number of elements to display on the screen + int max_items = (pane->height - padding * 2 + style->spacing) / + (style->spacing + 1); + // number of items displayed cant exceed actual number of items in the menu + info->items_num = ACLAMP(max_items, 0, menu->items_size); + + info->x_sep = centerHorisontal(pane, info->sep_len); + info->x = centerHorisontal(pane, info->max_item_len); + info->y = centerVertical( + pane, info->items_num * (style->spacing + 1) - + style->spacing); + } + + /* while (1) { */ + pane_clear(pane, 0); + + int relative = menu->current - info->start; + if (relative < 0) { + info->start--; + info->end--; + } else if (relative > 0 && relative > info->end - info->start - 1) { + info->start++; + info->end++; + } + + CLAMP(info->start, 0, menu->items_size - info->items_num); + CLAMP(info->end, info->items_num, menu->items_size); + + if (!menu->items_size) { + char *message = "NOTHING TO DISPLAY HERE!"; + int x = centerHorisontal(pane, strlen(message)); + tb_print(x, info->y, TB_BLUE, 0, message); + return; + } + + if (info->items_num != menu->items_size && info->start == 0) + tb_print(info->x_sep, info->y - 1, TB_BLUE, 0, + style->separator); + + int y = info->y; + for (i = info->start; i < info->end; i++) { + int color = (i == menu->current) ? TB_RED : TB_BLUE; + tb_printf(info->x, y, color, 0, "%s", menu->items[i].name); + y += style->spacing + 1; + } + + if (info->items_num != menu->items_size && + info->end == menu->items_size) + tb_print(info->x_sep, y - style->spacing, TB_BLUE, 0, + style->separator); }