442 lines
12 KiB
C
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);
|
||
|
}
|