FxLibcTest/src/widgets/fbrowser.c

274 lines
7.3 KiB
C

#include <ft/widgets/fbrowser.h>
#include <justui/jwidget-api.h>
#include <justui/jscene.h>
#include <justui/jlabel.h>
#include <gint/std/stdlib.h>
/* Type identifier for fbrowser */
static int fbrowser_type_id = -1;
/* Events */
uint16_t FBROWSER_VALIDATED;
/* Header list and test list renderers */
static void render_entries(flist *l, void *data, int is_headers);
/* Update whatever is displayed on the right panel */
static void update_right_panel(fbrowser *b);
fbrowser *fbrowser_create(void *parent)
{
if(fbrowser_type_id < 0) return NULL;
jlabel *l;
fbrowser *b = malloc(sizeof *b);
if(!b) return NULL;
b->data_headers = NULL;
b->data_tests = NULL;
jwidget_init(&b->widget, fbrowser_type_id, parent);
jlayout_set_hbox(b)->spacing = 4;
// On the left, list of headers
b->headers = flist_create(GINT_CALL_NULL, b);
flist_set_rows(b->headers, 1);
flist_select(b->headers, 0);
flist_set_row_height(b->headers, 13);
jwidget_set_fixed_width(b->headers, 120);
jwidget_set_stretch(b->headers, 0, 1, false);
// On the right, either a summary of the whole application...
b->rstack = jwidget_create(b);
jwidget_set_stretch(b->rstack, 1, 1, false);
jlayout_set_stack(b->rstack);
jwidget *summary = jwidget_create(b->rstack);
jwidget_set_stretch(summary, 1, 1, false);
jlayout_set_vbox(summary)->spacing = 4;
b->bar_top = fbar_create(summary);
jwidget_set_stretch(b->bar_top, 1, 0, false);
l = jlabel_create(
"Summary here!\n\n"
"Green: Passed\n"
"Orange: Skipped\n"
"Red: Failed\n"
"Blue: Not tested\n"
"Gray: Empty\n\n"
"Run tests with F6:\n"
"- Here: runs all tests\n"
"- On a header: all tests in header\n"
"- On a test: just that test",
summary);
jlabel_set_block_alignment(l, J_ALIGN_LEFT, J_ALIGN_TOP);
jlabel_set_line_spacing(l, 3);
jwidget_set_stretch(l, 1, 1, false);
// ... or a label for headers with no tests yet...
l = jlabel_create("No test for this header.", b->rstack);
jlabel_set_block_alignment(l, J_ALIGN_LEFT, J_ALIGN_TOP);
jwidget_set_margin(l, 2, 0, 0, 0);
jwidget_set_stretch(l, 1, 1, false);
// Or a header summary and a test list for the selected header
jwidget *rvbox = jwidget_create(b->rstack);
jwidget_set_stretch(rvbox, 1, 1, false);
jlayout_set_vbox(rvbox)->spacing = 4;
b->bar_right = fbar_create(rvbox);
jwidget_set_stretch(b->bar_right, 1, 0, false);
b->tests = flist_create(GINT_CALL_NULL, rvbox);
flist_set_row_height(b->tests, 13);
jwidget_set_stretch(b->tests, 1, 1, false);
return b;
}
void fbrowser_set_headers(fbrowser *b, ft_list *headers, bool select)
{
int rows = 0;
while(headers[rows].name) rows++;
flist_set_renderer(b->headers,
GINT_CALL(render_entries, 0 /* Overridden */, (void *)headers, 1));
flist_set_rows(b->headers, rows + 1);
flist_select(b->headers, select ? 1 : 0);
jscene *scene = jscene_owning(b);
if(scene) jscene_set_focused_widget(scene, b->headers);
b->data_headers = headers;
fbar_set_lists(b->bar_top, headers);
update_right_panel(b);
b->widget.update = 1;
}
void fbrowser_set_tests(fbrowser *b, ft_test **tests)
{
int rows = 0;
while(tests[rows]) rows++;
flist_set_renderer(b->tests,
GINT_CALL(render_entries, 0 /* Overridden */, (void *)tests, 0));
flist_set_rows(b->tests, rows);
flist_select(b->tests, -1);
b->data_tests = tests;
fbar_set_tests(b->bar_right, tests);
b->widget.update = 1;
}
ft_list *fbrowser_current_header(fbrowser *b)
{
if(!b->data_headers || b->headers->selected < 1) return NULL;
return &b->data_headers[b->headers->selected - 1];
}
ft_test *fbrowser_current_test(fbrowser *b)
{
if(!b->data_tests || b->tests->selected < 0) return NULL;
return b->data_tests[b->tests->selected];
}
//---
// Rendering
//---
static void render_entries(flist *l, void *data, int is_headers)
{
jscene *scene = jscene_owning(l);
bool selected = (l->row == l->selected);
bool focused = (scene && jscene_focused_widget(scene) == l);
int w = jwidget_content_width(l);
int h = l->row_height;
if(selected && !focused) {
drect(l->x, l->y, l->x + w - 1, l->y + h - 1, C_RGB(24, 24, 24));
}
char const *name = "(null)";
int color = C_BLACK;
if(is_headers && l->row != 0) {
ft_list const *headers = data;
ft_list const *header = &headers[l->row - 1];
color = ft_list_color(header);
name = header->name;
}
else {
ft_test const **tests = data;
ft_test const *test = tests[l->row];
color = ft_test_color(test);
name = test->name;
}
if(is_headers && l->row == 0)
dtext(l->x + 2, l->y + 2, C_BLACK, "Summary");
else
dprint(l->x + 16, l->y + 2, C_BLACK, "%s", name);
if(selected && focused) {
drect(l->x, l->y, l->x + w - 1, l->y + h - 1, C_INVERT);
}
if(is_headers && l->row == 0) return;
for(int y = 0; y < 9; y++)
for(int x = 0; x < 9; x++)
{
if((x == 0 || x == 8) && (y == 0 || y == 8)) continue;
dpixel(l->x + 3 + x, l->y + 2 + y, color);
}
}
//---
// Polymorphic widget operations
//---
void update_right_panel(fbrowser *b)
{
jlayout_stack *ls = jlayout_get_stack(b->rstack);
ft_list *header = fbrowser_current_header(b);
ft_test **tests = header ? header->children : NULL;
if(!header) {
ls->active = 0;
}
else if(!tests) {
ls->active = 1;
}
else {
fbrowser_set_tests(b, tests);
ls->active = 2;
}
b->widget.update = 1;
}
static bool fbrowser_poly_event(void *b0, jevent e)
{
fbrowser *b = b0;
jscene *scene = jscene_owning(b);
int key = (e.type==JSCENE_KEY || e.key.type==KEYEV_DOWN) ? e.key.key : 0;
void *f = scene ? jscene_focused_widget(scene) : NULL;
/* Update right panel when moving in the header list */
if(e.type == FLIST_SELECTED && e.source == b->headers) {
update_right_panel(b);
return true;
}
/* Move focus to the test list when validating a header */
if(e.type == FLIST_VALIDATED && e.source == b->headers) {
ft_list *header = fbrowser_current_header(b);
if(header && header->children) {
if(scene) jscene_set_focused_widget(scene, b->tests);
flist_select(b->tests, 0);
b->widget.update = 1;
}
return true;
}
/* Move focus back when pressing EXIT */
if(key == KEY_EXIT && f == b->tests) {
jscene_set_focused_widget(scene, b->headers);
flist_select(b->tests, -1);
b->widget.update = 1;
return true;
}
/* Emit validation when pressing EXE on a test */
if(e.type == FLIST_VALIDATED && e.source == b->tests) {
ft_test *test = fbrowser_current_test(b);
if(test) {
jevent e = { .source = b, .type = FBROWSER_VALIDATED };
jwidget_emit(b, e);
}
return true;
}
return false;
}
/* fbrowser type definition */
static jwidget_poly type_fbrowser = {
.name = "fbrowser",
.event = fbrowser_poly_event,
};
/* Type registration */
__attribute__((constructor(2000)))
static void j_register_fbrowser(void)
{
fbrowser_type_id = j_register_widget(&type_fbrowser, "jwidget");
FBROWSER_VALIDATED = j_register_event();
}