FxLibcTest/src/widgets/fbrowser.c

301 lines
8.2 KiB
C

#include <ft/widgets/fbrowser.h>
#include <justui/jwidget-api.h>
#include <justui/jscene.h>
#include <justui/jlabel.h>
#include <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 = _(0,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, _(7,13));
jwidget_set_fixed_width(b->headers, _(39,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(
#ifdef FX9860G
"fxlibc unit tests!\n"
"Run with F6:\n"
"- Here: all\n"
"- On header\n"
"- On single test",
#else
"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",
#endif
summary);
jlabel_set_block_alignment(l, J_ALIGN_LEFT, J_ALIGN_TOP);
jlabel_set_line_spacing(l, _(1,3));
jlabel_set_font(l, _(&font_mini, dfont_default()));
jwidget_set_stretch(l, 1, 1, false);
jwidget_set_margin(l, 0, 0, 0, _(1,0));
// ... or a label for headers with no tests yet...
l = jlabel_create(_("No test", "No test for this header."), b->rstack);
jlabel_set_block_alignment(l, J_ALIGN_LEFT, J_ALIGN_TOP);
jlabel_set_font(l, _(&font_mini, dfont_default()));
jwidget_set_margin(l, 2, 0, 0, 0);
jwidget_set_stretch(l, 1, 1, false);
jwidget_set_margin(l, 0, 0, 0, _(1,0));
// 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 = _(1,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, _(7,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;
#ifdef FXCG50
if(selected && !focused) {
drect(l->x, l->y, l->x + w - 1, l->y + h - 1, C_RGB(24, 24, 24));
}
#endif
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;
}
font_t const *old_font = dfont(_(&font_mini, dfont_default()));
if(is_headers && l->row == 0)
dtext(l->x+_(1,2), l->y+_(1,2), C_BLACK, "Summary");
else if(is_headers)
dprint(l->x+_(1,16), l->y+_(1,2), C_BLACK, "%s", name);
else
dprint(l->x+_(7,16), l->y+_(1,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) {
#ifdef FX9860G
extern bopti_image_t img_status, img_status2;
bopti_image_t *i = (selected && focused) ? &img_status2 : &img_status;
if(!is_headers)
dsubimage(l->x+1, l->y+1, i, 6*(color-1), 0, 5, 5, DIMAGE_NONE);
#else
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);
}
#endif
}
dfont(old_font);
}
//---
// 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==JWIDGET_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();
}