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); }