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:
M | include/pane.h | | | 67 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------- |
M | include/utils.h | | | 5 | ----- |
M | src/main.c | | | 126 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------- |
M | src/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);
}