forked from Lephenixnoir/FxLibcTest
274 lines
7.3 KiB
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();
|
|
}
|