Hotpot-proto/btk/btk-cell-render.c
2022-07-06 22:10:46 -04:00

442 lines
12 KiB
C

void fill_cell (btk_cell_t *, cairo_t *, btk_rgb_t);
void guard_scroll (int *, int, int, unsigned int, int);
void render_button (btk_cell_t *, cairo_t *);
void render_editor (btk_cell_t *);
void render_gauge (btk_cell_t *, cairo_t *);
void render_input (btk_cell_t *, cairo_t *);
void render_list (btk_cell_t *);
void render_mark (btk_cell_t *, cairo_t *);
void render_prompt (btk_cell_t *, cairo_t *);
void render_table (btk_cell_t *);
void render_table_row (btk_cell_t *, char *, int, btk_rgb_t, btk_rgb_t);
void render_text (btk_cell_t *);
void render_switch (btk_cell_t *, cairo_t *);
void render_wheel (btk_cell_t *, cairo_t *);
void reset_field (btk_cell_t *);
void write_label (cairo_t *, int, int, unsigned int, int, char *, btk_rgb_t);
static cairo_font_options_t *font_opt;
static btk_pos_t label_offset;
static btk_size_t glyph;
/* ch is a modifier to force the heigth of a rendered cell to 1 cell unit */
void
fill_cell(btk_cell_t *c, cairo_t *c_ctx, btk_rgb_t cl)
{
cairo_set_source_rgb(c_ctx, cl.r, cl.g, cl.b);
cairo_rectangle(c_ctx, c->pa.x, c->pa.y, c->pa.w, c->pa.h);
cairo_fill(c_ctx);
}
/* pi = pixel indent, of each scrollable element in the considered axis */
/* cl = cell length, in the considered axis */
void
guard_scroll(int *scroll_var, int pi, int cl, unsigned int items_n, int extra)
{
if (*scroll_var < 0 || !items_n) { /* no negative scroll position */
*scroll_var = 0;
} else if (cl >= pi * items_n + extra) { /* reset scroll pos if everything fits in */
*scroll_var = 0;
} else if (*scroll_var + cl > pi * items_n + extra) {
*scroll_var = pi * items_n - cl + extra;
}
}
void
render_button(btk_cell_t *c, cairo_t *c_ctx)
{
btk_rgb_t clt, cl;
if ((c->state & BTK_CELL_STATE_DISABLED)) {
cl = btk_cl_dyn_disabled;
clt = btk_cl_text_disabled;
} else if ((c->state & BTK_CELL_STATE_PRESSED)) {
cl = btk_cl_dyn_pressed;
clt = btk_cl_text_hilight;
} else {
cl = btk_cl_dyn_idle;
clt = btk_cl_text_normal;
}
fill_cell(c, c_ctx, cl);
write_label(c_ctx, c->pa.x, c->pa.y, c->pa.w, BTK_JUSTIFY_LEFT, c->label, clt);
}
void
render_editor(btk_cell_t *c)
{
btk_pp_editor_t *pp = PP_EDITOR(c);
cairo_set_source_rgb(c->fd->c_ctx, btk_cl_field.r, btk_cl_field.g, btk_cl_field.b);
cairo_paint(c->fd->c_ctx);
int text_n = 0;
if (*pp->text)
text_n = strlen(pp->text);
guard_scroll(&pp->scroll, glyph.h + btk_text_spacing, c->pa.h, pp->par->rows_n, label_offset.y);
/* render caret */
if (!(c->state & BTK_CELL_STATE_DISABLED)) {
cairo_set_source_rgb(c->fd->c_ctx,
btk_cl_bell.r,
btk_cl_bell.g,
btk_cl_bell.b);
cairo_move_to(c->fd->c_ctx,
label_offset.x + pp->caret_2d.x * glyph.w,
pp->caret_2d.y * (glyph.h + btk_text_spacing) +
btk_caret_offset + label_offset.y - pp->scroll);
cairo_show_text(c->fd->c_ctx, "_");
}
/* just render label if there are no rows to draw */
if (!text_n || !pp->par) {
write_label(c->fd->c_ctx,
0,
0,
c->pa.w,
BTK_JUSTIFY_LEFT,
c->label,
btk_cl_text_lolight);
return;
}
btk_rgb_t cl;
/* else render row by row */
for (int i = 0; i < pp->par->rows_n; i++) {
cl = btk_cl[pp->par->rows_spot[i]];
cairo_set_source_rgb(c->fd->c_ctx, cl.r, cl.g, cl.b);
cairo_move_to(c->fd->c_ctx,
label_offset.x,
label_offset.y - pp->scroll + i * (glyph.h + btk_text_spacing));
cairo_show_text(c->fd->c_ctx, pp->par->rows + i * pp->par->rows_max_w);
}
}
void
render_input(btk_cell_t *c, cairo_t *c_ctx)
{
btk_pp_input_t *pp = PP_INPUT(c);
int text_n = strlen(pp->text);
btk_rgb_t clt, clt2;
/* passive components */
clt = (c->state & BTK_CELL_STATE_DISABLED) ? btk_cl_dyn_disabled : btk_cl_pass_on;
fill_cell(c, c_ctx, btk_cl_field);
cairo_set_source_rgb(c_ctx, clt.r, clt.g, clt.b);
cairo_move_to(c_ctx, c->pa.x + label_offset.x, c->pa.y + label_offset.y);
cairo_show_text(c_ctx, "");
/* limits */
int max_n = (c->pa.w - 3 * label_offset.x ) / glyph.w - 1;
if (pp->caret > pp->scroll + max_n - 1)
pp->scroll = pp->caret - max_n + 1;
if (pp->caret < pp->scroll)
pp->scroll = pp->caret;
if (text_n > max_n && pp->scroll + max_n > text_n)
pp->scroll -= pp->scroll + max_n - text_n;
/* render caret */
if (!(c->state & BTK_CELL_STATE_DISABLED)) {
cairo_set_source_rgb(c_ctx, btk_cl_bell.r, btk_cl_bell.g, btk_cl_bell.b);
cairo_move_to(c_ctx,
c->pa.x + 2 * label_offset.x + (1 + pp->caret - pp->scroll) * glyph.w,
c->pa.y + label_offset.y + btk_caret_offset);
cairo_show_text(c_ctx, "_");
}
/* prepare text to render */
char cut_text[max_n + 1];
if (text_n) {
strncpy(cut_text, pp->text + pp->scroll, max_n);
cut_text[max_n] = '\0';
clt2 = btk_cl_text_disabled;
} else {
strcpy(cut_text, c->label);
clt2 = btk_cl_text_lolight;
}
/* render text */
cairo_set_source_rgb(c_ctx, clt2.r, clt2.g, clt2.b);
cairo_move_to(c_ctx,
c->pa.x + 2 * label_offset.x + glyph.w,
c->pa.y + label_offset.y);
cairo_show_text(c_ctx, cut_text);
}
void
render_list(btk_cell_t *c)
{
reset_field(c);
guard_scroll(&(PP_LIST(c)->scroll),
btk_cp.h + btk_frame,
c->pa.h,
*(PP_LIST(c)->items_n),
0);
/* compute item range to render */
int item_min = PP_LIST(c)->scroll / (btk_cp.h + btk_frame);
int item_max = (PP_LIST(c)->scroll + c->pa.h) / (btk_cp.h + btk_frame) + 1;
item_max = BTK_MIN(item_max, *(PP_LIST(c)->items_n));
/* just render label if there are no rows to draw */
if (!*(PP_LIST(c)->items_n)) {
write_label(c->fd->c_ctx,
0,
0,
c->pa.w,
BTK_JUSTIFY_LEFT,
c->label,
btk_cl_text_lolight);
return;
}
/* render sub cells */
btk_rgb_t cl, clt;
int row_pos;
for (int i = item_min; i < item_max; i++) {
if (i == PP_LIST(c)->item_sel) {
if ((c->state & BTK_CELL_STATE_PRESSED)) {
cl = btk_cl_dyn_pressed;
clt = btk_cl_text_hilight;
} else {
clt = btk_cl_text_select;
cl = btk_cl_select;
}
} else {
cl = btk_cl_field;
clt = btk_cl_text_disabled;
if (PP_LIST(c)->spot)
clt = btk_cl[*(*(PP_LIST(c)->spot) + i)];
}
row_pos = (btk_cp.h + btk_frame) * i - PP_LIST(c)->scroll;
cairo_set_source_rgb(c->fd->c_ctx, cl.r, cl.g, cl.b);
cairo_rectangle(c->fd->c_ctx, 0, row_pos, c->pa.w, btk_cp.h);
cairo_fill(c->fd->c_ctx);
write_label(c->fd->c_ctx,
0,
row_pos,
c->pa.w,
BTK_JUSTIFY_LEFT,
*(PP_LIST(c)->items) + i * PP_LIST(c)->items_w,
clt);
}
}
void
render_mark(btk_cell_t *c, cairo_t *c_ctx)
{
fill_cell(c, c_ctx, btk_cl_pass_off);
btk_rgb_t clt, cl = {0.0f, 0.0f, 0.0f};
if ((c->state & BTK_CELL_STATE_BELL)) {
cl = btk_cl_bell;
clt = btk_cl_text_bell;
} else if ((c->state & BTK_CELL_STATE_ON)) {
cl = btk_cl_pass_on;
clt = btk_cl_text_hilight;
} else {
clt = c->state & BTK_CELL_STATE_DISABLED ?
btk_cl_text_disabled : btk_cl_text_normal;
}
if ((c->state & (BTK_CELL_STATE_BELL | BTK_CELL_STATE_ON))) {
cairo_set_source_rgb(c_ctx, cl.r, cl.g, cl.b);
cairo_rectangle(c_ctx,
c->pa.x + btk_frame,
c->pa.y + btk_frame,
c->pa.w - 2 * btk_frame,
c->pa.h - 2 * btk_frame);
cairo_fill(c_ctx);
}
write_label(c_ctx, c->pa.x, c->pa.y, c->pa.w, PP_MARK(c)->justify, c->label, clt);
}
void
render_switch(btk_cell_t *c, cairo_t *c_ctx)
{
btk_rgb_t clt, cl;
if ((c->state & BTK_CELL_STATE_DISABLED)) {
cl = btk_cl_dyn_disabled;
clt = btk_cl_text_disabled;
} else if ((c->state & BTK_CELL_STATE_PRESSED)) {
cl = btk_cl_dyn_pressed;
clt = btk_cl_text_hilight;
} else {
cl = btk_cl_dyn_idle;
clt = btk_cl_text_normal;
}
fill_cell(c, c_ctx, cl);
write_label(c_ctx,
c->pa.x,
c->pa.y,
c->pa.w - btk_cp.h,
BTK_JUSTIFY_LEFT,
c->label,
clt);
/* state indicator */
cl = (c->state & BTK_CELL_STATE_ON) ? btk_cl_pass_on : btk_cl_pass_off;
cairo_set_source_rgb(c_ctx, btk_cl_frame.r, btk_cl_frame.g, btk_cl_frame.b);
cairo_rectangle(c_ctx,
c->pa.x + c->pa.w - btk_cp.h + btk_padding,
c->pa.y + btk_padding,
btk_cp.h - 2 * btk_padding,
btk_cp.h - 2 * btk_padding);
cairo_fill(c_ctx);
cairo_set_source_rgb(c_ctx, cl.r, cl.g, cl.b);
cairo_rectangle(c_ctx,
c->pa.x + c->pa.w - btk_cp.h + btk_padding + btk_frame,
c->pa.y + btk_padding + btk_frame,
btk_cp.h - 2 * (btk_padding + btk_frame),
btk_cp.h - 2 * (btk_padding + btk_frame));
cairo_fill(c_ctx);
}
void
render_table(btk_cell_t *c)
{
reset_field(c);
btk_pp_table_t *pp = PP_TABLE(c);
/* horizontal then vertical scroll */
guard_scroll(&pp->scroll.x, pp->tw, c->pa.w, 1, 0);
guard_scroll(&pp->scroll.y, btk_cp.h + btk_frame, c->pa.h, *pp->rows_n + 1, 0);
int header_indent = pp->header ? 1 : 0;
/* compute item range to render */
int item_min = pp->scroll.y / (btk_cp.h + btk_frame);
int item_max = (pp->scroll.y + c->pa.h) / (btk_cp.h + btk_frame);
item_max = BTK_MIN(item_max, *pp->rows_n);
/* just render label if there are no rows to draw */
if (!*pp->rows_n) {
write_label(c->fd->c_ctx,
0,
header_indent * (btk_cp.h + btk_frame),
c->pa.w,
BTK_JUSTIFY_LEFT,
c->label,
btk_cl_text_lolight);
goto header;
}
/* render rows */
btk_rgb_t cl, clt;
int row_pos;
for (int i = item_min; i < item_max; i++) {
if (i == pp->row_sel) {
if ((c->state & BTK_CELL_STATE_PRESSED)) {
cl = btk_cl_dyn_pressed;
clt = btk_cl_text_hilight;
} else {
clt = btk_cl_text_select;
cl = btk_cl_select;
}
} else {
cl = btk_cl_field;
clt = btk_cl_text_disabled;
if (pp->spot)
clt = btk_cl[*(*(PP_TABLE(c)->spot) + i)];
}
row_pos = (btk_cp.h + btk_frame) * (i + header_indent) - pp->scroll.y;
render_table_row(c, btk_cell_table_get_item(c, 0, i), row_pos, cl, clt);
}
/* render header */
header:
if (pp->header)
render_table_row(c, pp->header, 0, btk_cl_pass_off, btk_cl_text_normal);
}
void
render_table_row(btk_cell_t *c,
char *row_data_start,
int row_pos,
btk_rgb_t cl,
btk_rgb_t clt)
{
btk_pp_table_t *pp = PP_TABLE(c);
char label[pp->items_w];
btk_rgb_t clt2;
for (int i = 0; i < pp->cols_n; i++) {
cairo_set_source_rgb(c->fd->c_ctx, cl.r, cl.g, cl.b);
cairo_rectangle(c->fd->c_ctx,
pp->cols_px[i] - pp->scroll.x,
row_pos, pp->cols_pw[i],
btk_cp.h);
cairo_fill(c->fd->c_ctx);
clt2 = clt;
strcpy(label, row_data_start + i * pp->items_w);
if (label[0] == '\n' || label[0] == '\0') {
clt2 = btk_cl_text_lolight;
strncpy(label, "-/-", pp->items_w);
}
write_label(c->fd->c_ctx,
pp->cols_px[i] - pp->scroll.x,
row_pos,
pp->cols_pw[i],
BTK_JUSTIFY_LEFT,
label,
clt2);
}
}
void
reset_field(btk_cell_t *c)
{
if (!c->fd) {
btk_log_warning(BTK_WARNING_DRAW_NO_FIELD);
return;
}
cairo_set_source_rgb(c->fd->c_ctx, btk_cl_empty.r, btk_cl_empty.g, btk_cl_empty.b);
cairo_paint(c->fd->c_ctx);
}
void
write_label(cairo_t *c_ctx,
int px,
int py,
unsigned int pw,
int justify,
char *label,
btk_rgb_t clt)
{
int n = strlen(label);
int max_n = (pw - 2 * label_offset.x) / glyph.w;
char cut_label[max_n];
/* make label fit in cell if needed */
if (n > max_n) {
int caret;
if (justify == BTK_JUSTIFY_LEFT) {
caret = max_n - 3;
strncpy(cut_label, label, max_n);
} else {
caret = 0;
strncpy(cut_label, label + n - max_n, max_n);
}
cut_label[caret] = '.';
cut_label[++caret] = '.';
cut_label[++caret] = '.';
cut_label[max_n] = '\0';
}
/* draw label */
cairo_set_source_rgb(c_ctx, clt.r, clt.g, clt.b);
if (justify == BTK_JUSTIFY_LEFT) {
cairo_move_to(c_ctx, px + label_offset.x, py + label_offset.y);
} else {
int a = n > max_n ? max_n : n;
cairo_move_to(c_ctx, px + pw - label_offset.x - glyph.w * a, py + label_offset.y);
}
char *l = n > max_n ? cut_label : label;
cairo_show_text(c_ctx, l);
}