#include #include #include #include /* Type identifier for flist */ static int flist_type_id = -1; /* Events */ uint16_t FLIST_SELECTED; uint16_t FLIST_VALIDATED; /* Update the count of visible items after a layout change */ static void update_visible(flist *l); flist *flist_create(gint_call_t renderer, void *parent) { if(flist_type_id < 0) return NULL; flist *l = malloc(sizeof *l); if(!l) return NULL; jwidget_init(&l->widget, flist_type_id, parent); l->renderer = renderer; l->rows = 0; l->visible = 0; l->top = 0; l->selected = -1; l->row_height = 3; return l; } void flist_set_renderer(flist *l, gint_call_t renderer) { l->renderer = renderer; } //--- // Configuration of rows //--- void flist_set_rows(flist *l, int rows) { if(l->rows == rows) return; l->rows = max(rows, 0); if(l->selected < 0 && l->rows > 0) l->selected = 0; if(l->selected >= l->rows) l->selected = l->rows - 1; l->widget.dirty = 1; } void flist_set_row_height(flist *l, int row_height) { if(l->row_height == row_height) return; l->row_height = row_height; l->widget.dirty = 1; } //--- // Movement //--- /* Guarantee that the positioning is valid */ static int bound(flist *l, int pos) { pos = max(pos, 0); pos = min(pos, flist_end(l)); return pos; } void flist_scroll_to(flist *l, int offset) { l->top = bound(l, offset); } void flist_select(flist *l, int entry) { if(entry < -1 || entry >= l->rows) return; l->selected = entry; l->widget.update = 1; /* TODO: flist_select(): Scroll to show selected entry */ } int flist_end(flist *l) { update_visible(l); return max((int)l->rows - (int)l->visible, 0); } //--- // Polymorphic widget operations //--- /* Recompute (visible) based on the current size */ static void update_visible(flist *l) { if(l->visible != 0) return; l->visible = max(0, jwidget_content_height(l) / l->row_height); } static void flist_poly_csize(void *l0) { flist *l = l0; l->widget.w = 64; l->widget.h = 32; } static void flist_poly_layout(void *l0) { flist *l = l0; update_visible(l); } static void flist_poly_render(void *l0, int x, int base_y) { flist *l = l0; int cw = jwidget_content_width(l); int y = base_y; for(uint i = 0; l->top + i < l->rows && i < l->visible; i++) { l->x = x; l->y = y; l->row = l->top + i; l->renderer.args[0] = (gint_call_arg_t)(void *)l; gint_call(l->renderer); y += l->row_height; } /* Scrollbar */ if(l->visible < l->rows) { /* Area where the scroll bar lives */ int area_w = _(1,2); int area_x = x + cw - area_w; int area_y = base_y; int area_h = jwidget_content_height(l); /* Position and size of scroll bar within the area */ int bar_y = (l->top * area_h + l->rows/2) / l->rows; int bar_h = (l->visible * area_h + l->rows/2) / l->rows; #ifdef FXCG50 drect(area_x, area_y, area_x+area_w-1, area_y+area_h-1, C_RGB(24,24,24)); #endif drect(area_x, area_y + bar_y, area_x + area_w - 1, area_y + bar_y + bar_h, C_BLACK); } } static bool flist_poly_event(void *l0, jevent e) { flist *l = l0; update_visible(l); if(e.type != JWIDGET_KEY) return false; if(e.key.type == KEYEV_UP) return false; if(e.key.key == KEY_DOWN && l->selected < l->rows - 1) { if(e.key.shift) l->selected = l->rows - 1; else l->selected++; /* Make sure the selected item remains visible */ if(l->selected - l->top > l->visible - 1) l->top = bound(l, l->selected - l->visible + 1); l->widget.update = 1; jevent e = { .source = l, .type = FLIST_SELECTED }; jwidget_emit(l, e); return true; } if(e.key.key == KEY_UP && l->selected > 0) { if(e.key.shift) l->selected = 0; else l->selected--; /* Same thing as before */ if(l->selected < l->top) l->top = bound(l, l->selected); jevent e = { .source = l, .type = FLIST_SELECTED }; jwidget_emit(l, e); l->widget.update = 1; return true; } if(e.key.key == KEY_EXE) { jevent e = { .source = l, .type = FLIST_VALIDATED }; jwidget_emit(l, e); return true; } return false; } /* flist type definition */ static jwidget_poly type_flist = { .name = "flist", .csize = flist_poly_csize, .layout = flist_poly_layout, .render = flist_poly_render, .event = flist_poly_event, }; /* Type registration */ __attribute__((constructor(2000))) static void j_register_flist(void) { flist_type_id = j_register_widget(&type_flist, "jwidget"); FLIST_SELECTED = j_register_event(); FLIST_VALIDATED = j_register_event(); }