FxLibcTest/src/widgets/flist.c

221 lines
4.8 KiB
C

#include <ft/widgets/flist.h>
#include <ft/util.h>
#include <justui/jwidget-api.h>
#include <stdlib.h>
/* 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();
}