#include #include #include #include #include /* 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(); }