Hotpot-proto/btk/btk-cell.c

713 lines
17 KiB
C
Raw Normal View History

2022-07-06 22:10:46 -04:00
#include <cairo/cairo.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "btk-cell.h"
#include "btk-log.h"
#include "btk-cell-render.c"
void
btk_cell_set_button(btk_cell_t *c,
unsigned int cx,
unsigned int cy,
unsigned int cw,
char *label,
void (*func)(void))
{
btk_cell_set_empty(c, cx, cy, cw, 1);
c->group = BTK_CELL_GROUP_DYNAMIC;
c->type = BTK_CELL_TYPE_BUTTON;
c->lh = 1;
c->label = label;
NEW_PP(btk_pp_button_t, c->pp);
PP_BUTTON(c)->func = func;
}
void
btk_cell_set_editor(btk_cell_t *c,
unsigned int cx,
unsigned int cy,
unsigned int cw,
unsigned int ch,
char *text,
unsigned int text_w)
{
btk_cell_set_empty(c, cx, cy, cw, ch);
c->group = BTK_CELL_GROUP_FIELD;
c->type = BTK_CELL_TYPE_EDITOR;
c->lh = 0;
c->label = "no data";
NEW_PP(btk_pp_editor_t, c->pp);
PP_EDITOR(c)->text = text;
PP_EDITOR(c)->text_w = text_w;
PP_EDITOR(c)->caret = 0;
PP_EDITOR(c)->caret_2d.x = 0;
PP_EDITOR(c)->caret_2d.y = 0;
PP_EDITOR(c)->scroll = 0;
PP_EDITOR(c)->par = NULL;
}
void
btk_cell_set_empty(btk_cell_t *c,
unsigned int cx,
unsigned int cy,
unsigned int cw,
unsigned int ch)
{
c->group = BTK_CELL_GROUP_PASSIVE;
c->type = BTK_CELL_TYPE_EMPTY;
c->state = BTK_CELL_STATE_INITIAL;
c->ca.x = cx;
c->ca.y = cy;
c->ca.w = cw;
c->ca.h = ch;
c->lh = 0;
c->pp = NULL;
c->fd = NULL;
c->label = NULL;
}
void
btk_cell_set_mark(btk_cell_t *c,
unsigned int cx,
unsigned int cy,
unsigned int cw,
int justify,
char *label)
{
btk_cell_set_empty(c, cx, cy, cw, 1);
c->type = BTK_CELL_TYPE_MARK;
c->lh = 1;
c->label = label;
NEW_PP(btk_pp_mark_t, c->pp);
PP_MARK(c)->justify = justify;
}
void
btk_cell_set_input(btk_cell_t *c,
unsigned int cx,
unsigned int cy,
unsigned int cw,
char *text,
unsigned int text_w)
{
btk_cell_set_empty(c, cx, cy, cw, 1);
c->group = BTK_CELL_GROUP_DYNAMIC;
c->type = BTK_CELL_TYPE_INPUT;
c->lh = 1;
c->label = "-/-";
NEW_PP(btk_pp_input_t, c->pp);
PP_INPUT(c)->text = text;
PP_INPUT(c)->text_w = text_w;
PP_INPUT(c)->caret = 0;
PP_INPUT(c)->scroll = 0;
}
void
btk_cell_set_list(btk_cell_t *c,
unsigned int cx,
unsigned int cy,
unsigned int cw,
unsigned int ch,
char **items,
unsigned int *items_n,
unsigned int items_w,
int **filter,
int **order,
int **spot,
void (*func_trigger)(int),
void (*func_sel)(int))
{
btk_cell_set_empty(c, cx, cy, cw, ch);
c->group = BTK_CELL_GROUP_FIELD;
c->type = BTK_CELL_TYPE_LIST;
c->label = "no data";
NEW_PP(btk_pp_list_t, c->pp);
PP_LIST(c)->items = items;
PP_LIST(c)->items_n = items_n;
PP_LIST(c)->items_w = items_w;
PP_LIST(c)->item_sel = -1;
PP_LIST(c)->filter = filter;
PP_LIST(c)->order = order;
PP_LIST(c)->spot = spot;
PP_LIST(c)->scroll = 0;
PP_LIST(c)->func_trigger = func_trigger;
PP_LIST(c)->func_sel = func_sel;
}
void
btk_cell_set_switch(btk_cell_t *c,
unsigned int cx,
unsigned int cy,
unsigned int cw,
char *label,
void (*func)(int, btk_arg_t),
btk_arg_t func_args)
{
btk_cell_set_empty(c, cx, cy, cw, 1);
c->group = BTK_CELL_GROUP_DYNAMIC;
c->type = BTK_CELL_TYPE_SWITCH;
c->lh = 1;
c->label = label;
NEW_PP(btk_pp_switch_t, c->pp);
PP_SWITCH(c)->func = func;
PP_SWITCH(c)->func_args = func_args;
}
void
btk_cell_set_table(btk_cell_t *c,
unsigned int cx,
unsigned int cy,
unsigned int cw,
unsigned int ch,
char *header, /* must be same item_w as items */
char **items,
unsigned int items_w,
unsigned int cols_n,
unsigned int *rows_n,
int *cols_ena,
unsigned int *cols_cw,
unsigned int sc,
int **filter,
int **order,
int **spot,
void (*func_trigger)(int),
void (*func_sel)(int))
{
btk_cell_set_empty(c, cx, cy, cw, ch);
c->group = BTK_CELL_GROUP_FIELD;
c->type = BTK_CELL_TYPE_TABLE;
c->label = "no data";
NEW_PP(btk_pp_table_t, c->pp);
PP_TABLE(c)->header = header;
PP_TABLE(c)->items = items;
PP_TABLE(c)->items_w = items_w;
PP_TABLE(c)->rows_n = rows_n;
PP_TABLE(c)->cols_n = cols_n;
PP_TABLE(c)->row_sel = -1;
PP_TABLE(c)->cols_ena = cols_ena;
PP_TABLE(c)->cols_cw = cols_cw;
PP_TABLE(c)->sc = sc;
PP_TABLE(c)->tw = 0;
PP_TABLE(c)->filter = filter;
PP_TABLE(c)->order = order;
PP_TABLE(c)->spot = spot;
PP_TABLE(c)->scroll.x = 0;
PP_TABLE(c)->scroll.y = 0;
PP_TABLE(c)->func_trigger = func_trigger;
PP_TABLE(c)->func_sel = func_sel;
PP_TABLE(c)->cols_pw = (unsigned int*)malloc(cols_n * sizeof(int));
PP_TABLE(c)->cols_px = (unsigned int*)malloc(cols_n * sizeof(int));
}
/****************************************************************************************/
void
btk_cell_button_trigger(btk_cell_t *c, int button)
{
if (button == 1 && PP_BUTTON(c)->func)
PP_BUTTON(c)->func();
}
void
btk_cell_destroy(btk_cell_t *c)
{
if (c->pp) {
switch (c->type) {
case BTK_CELL_TYPE_TABLE:
free(PP_TABLE(c)->cols_pw);
free(PP_TABLE(c)->cols_px);
break;
case BTK_CELL_TYPE_EDITOR:
btk_text_destroy_par(PP_EDITOR(c)->par);
break;
default:
break;
}
free(c->pp);
}
free(c);
}
void
btk_cell_draw(btk_cell_t *c, cairo_t *c_ctx)
{
if (!c)
return;
if ((c->state & BTK_CELL_STATE_HIDDEN))
return;
switch(c->type) {
case BTK_CELL_TYPE_MARK:
render_mark(c, c_ctx);
break;
case BTK_CELL_TYPE_BUTTON:
render_button(c, c_ctx);
break;
case BTK_CELL_TYPE_SWITCH:
render_switch(c, c_ctx);
break;
case BTK_CELL_TYPE_LIST:
render_list(c);
break;
case BTK_CELL_TYPE_TABLE:
render_table(c);
break;
case BTK_CELL_TYPE_INPUT:
render_input(c, c_ctx);
break;
case BTK_CELL_TYPE_EDITOR:
render_editor(c);
break;
default:
fill_cell(c, c_ctx, btk_cl_empty);
break;
};
}
void
btk_cell_editor_input_button(btk_cell_t *c, int button, int combo, btk_pos_t pt)
{
/* scrolling if appropriate button is clicked */
switch (button) {
case 1:
break;
case 4:
PP_EDITOR(c)->scroll -= btk_scroll_speed;
return;
case 5:
PP_EDITOR(c)->scroll += btk_scroll_speed;
return;
default:
return;
}
/* caret positioning */
if (pt.x < label_offset.x || pt.x >= c->pa.w - label_offset.x ||
pt.y < label_offset.y - glyph.h)
return;
PP_EDITOR(c)->caret_2d.x = (pt.x - label_offset.x) / glyph.w;
PP_EDITOR(c)->caret_2d.y = (pt.y - label_offset.y + PP_EDITOR(c)->scroll + glyph.h) / (glyph.h + btk_text_spacing);
PP_EDITOR(c)->caret_2d = btk_text_guard_2d_caret(PP_EDITOR(c)->par, PP_EDITOR(c)->caret_2d);
PP_EDITOR(c)->caret = btk_text_caret_2dto1d(PP_EDITOR(c)->par, PP_EDITOR(c)->caret_2d);
//btk_text_caret_1dto2d(PP_EDITOR(c)->par, PP_EDITOR(c)->caret);
}
void
btk_cell_editor_input_key(btk_cell_t *c, uint32_t key)
{
btk_pp_editor_t *pp = PP_EDITOR(c);
int caret_type, modif = 0;
switch (key) {
case 0xff08:
pp->caret -= btk_text_del_char(pp->text, pp->caret, 0);
caret_type = 0;
modif = 1;
break;
case 0xff50:
pp->caret_2d.x = 0;
caret_type = 1;
break;
case 0xff51:
pp->caret--;
caret_type = 0;
break;
case 0xff52:
pp->caret_2d.y--;
caret_type = 1;
break;
case 0xff53:
pp->caret++;
caret_type = 0;
break;
case 0xff54:
pp->caret_2d.y++;
caret_type = 1;
break;
case 0xff57:
pp->caret_2d.x = pp->text_w;
caret_type = 1;
break;
case 0xffff:
btk_text_del_char(pp->text, pp->caret, 1);
caret_type = 0;
modif = 1;
break;
case 0xff0d:
pp->caret += btk_text_insert_char(pp->text, '\n', pp->caret, pp->text_w);
caret_type = 0;
modif = 1;
break;
case 0xff09:
pp->caret += btk_text_insert_char(pp->text, '\t', pp->caret, pp->text_w);
caret_type = 0;
modif = 1;
break;
default :
/* only allow ascii printable chars */
if (key < 32 || key >= 127)
return;
pp->caret += btk_text_insert_char(pp->text, key, pp->caret, pp->text_w);
caret_type = 0;
modif = 1;
break;
}
if (modif) {
btk_cell_editor_update_text(c);
}
if (caret_type) {
pp->caret_2d = btk_text_guard_2d_caret(pp->par, pp->caret_2d);
pp->caret = btk_text_caret_2dto1d(pp->par, pp->caret_2d);
} else {
pp->caret = btk_text_guard_1d_caret(pp->text, pp->caret);
pp->caret_2d = btk_text_caret_1dto2d(pp->par, pp->caret);
}
/* modify scroll to show caret if needed */
if (pp->caret_2d.y * (glyph.h + btk_text_spacing) < pp->scroll)
pp->scroll = pp->caret_2d.y * (glyph.h + btk_text_spacing);
if (pp->caret_2d.y * (glyph.h + btk_text_spacing) >= pp->scroll + c->pa.h - 2 * label_offset.y) {
pp->scroll = (pp->caret_2d.y + 1) * (glyph.h + btk_text_spacing) - c->pa.h + label_offset.y;
}
}
void
btk_cell_editor_reset_caret(btk_cell_t*c)
{
PP_EDITOR(c)->caret = 0;
PP_EDITOR(c)->caret_2d.x = 0;
PP_EDITOR(c)->caret_2d.y = 0;
PP_EDITOR(c)->scroll = 0;
}
void
btk_cell_editor_update_text(btk_cell_t *c)
{
btk_pp_editor_t *pp = PP_EDITOR(c);
int max_n = (c->pa.w - 2 * label_offset.x) / glyph.w + 1;
if (!pp->text)
return;
if (pp->par)
btk_text_destroy_par(pp->par);
pp->par = btk_text_get_par(pp->text, max_n);
pp->caret_2d = btk_text_caret_1dto2d(pp->par, pp->caret);
/* modify scroll to show caret if needed */
if (pp->caret_2d.y * (glyph.h + btk_text_spacing) < pp->scroll)
pp->scroll = pp->caret_2d.y * (glyph.h + btk_text_spacing);
if (pp->caret_2d.y * (glyph.h + btk_text_spacing) >= pp->scroll + c->pa.h - label_offset.y) {
pp->scroll = (pp->caret_2d.y + 1) * (glyph.h + btk_text_spacing) - c->pa.h + label_offset.y;
}
}
btk_field_t*
btk_cell_field_create(cairo_surface_t *c_srf, btk_area_t a)
{
btk_field_t *f = (btk_field_t *)malloc(sizeof(btk_field_t));
f->c_srf = cairo_surface_create_for_rectangle(c_srf, a.x, a.y, a.w, a.h);
f->c_ctx = cairo_create(f->c_srf);
cairo_set_font_options(f->c_ctx, font_opt);
cairo_set_font_size(f->c_ctx, btk_font_size);
cairo_select_font_face(f->c_ctx,
btk_font_name,
CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
return f;
}
void
btk_cell_field_destroy(btk_field_t *f)
{
if (!f)
return;
cairo_destroy(f->c_ctx);
cairo_surface_destroy(f->c_srf);
free(f);
}
void
btk_cell_input_button(btk_cell_t *c, int button, int combo, btk_pos_t pt)
{
if (button != 1)
return;
if (pt.x >= 2 * label_offset.x + glyph.w && pt.x < c->pa.w - label_offset.x)
PP_INPUT(c)->caret = (pt.x - 2 * label_offset.x - glyph.w) /
glyph.w + PP_INPUT(c)->scroll;
int text_n = strlen(PP_INPUT(c)->text);
if (PP_INPUT(c)->caret < 0)
PP_INPUT(c)->caret = 0;
if (PP_INPUT(c)->caret > text_n)
PP_INPUT(c)->caret = text_n;
/* TODO combo clicks selections */
}
void
btk_cell_input_input_key(btk_cell_t *c, uint32_t key)
{
btk_pp_input_t *pp = PP_INPUT(c);
switch (key) {
case 0xff08:
pp->caret -= btk_text_del_char(pp->text, pp->caret, 0);
break;
case 0xff50:
pp->caret = 0;
break;
case 0xff51:
pp->caret--;
break;
case 0xff53:
pp->caret++;
break;
case 0xff57:
pp->caret = pp->text_w;
break;
case 0xffff:
btk_text_del_char(pp->text, pp->caret, 1);
break;
default :
/* only allow ascii printable chars */
if (key < 32 || key >= 127)
return;
pp->caret += btk_text_insert_char(pp->text, key, pp->caret, pp->text_w);
return;
}
int text_n = strlen(pp->text);
if (pp->caret < 0)
pp->caret = 0;
if (pp->caret > text_n)
pp->caret = text_n;
}
void
btk_cell_input_reset_caret(btk_cell_t *c)
{
PP_INPUT(c)->caret = 0;
}
void
btk_cell_list_deselect(btk_cell_t *c)
{
PP_LIST(c)->item_sel = -1;
}
void
btk_cell_list_input_button(btk_cell_t *c, int button, int combo, btk_pos_t pt)
{
btk_pp_list_t *pp = PP_LIST(c);
int prev_sel = pp->item_sel;
/* scrolling if appropriate button is clicked */
switch (button) {
case 4:
pp->scroll -= btk_scroll_speed;
return;
case 5:
pp->scroll += btk_scroll_speed;
return;
}
/* filter out remaining unwanted inputs */
if (button != 1)
return;
/* find item under pointer */
pp->item_sel = (pt.y + pp->scroll) / (btk_cp.h + btk_frame);
/* deselect if out of bounds */
if (pp->item_sel < 0 || pp->item_sel >= *(pp->items_n))
pp->item_sel = -1;
/* adjust scrolling so that select item is displayed in full */
// TODO
/* on selection change, exec sel func */
if (prev_sel != pp->item_sel && pp->func_sel)
pp->func_sel(pp->item_sel);
/* if double click, exec trigger func */
if (combo == 2 && pp->func_trigger)
pp->func_trigger(pp->item_sel);
}
int
btk_cell_list_get_sel(btk_cell_t *c)
{
return PP_LIST(c)->item_sel;
}
void
btk_cell_setup_font(cairo_t *c_ctx)
{
font_opt = cairo_font_options_create();
cairo_font_options_set_subpixel_order(font_opt, CAIRO_SUBPIXEL_ORDER_DEFAULT);
cairo_font_options_set_antialias(font_opt, CAIRO_ANTIALIAS_NONE);
cairo_font_options_set_hint_style(font_opt, CAIRO_HINT_STYLE_SLIGHT);
cairo_font_options_set_hint_metrics(font_opt, CAIRO_HINT_METRICS_ON);
cairo_set_font_options(c_ctx, font_opt);
cairo_set_font_size(c_ctx, btk_font_size);
cairo_select_font_face(c_ctx,
btk_font_name,
CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
cairo_font_extents_t fe;
cairo_font_extents(c_ctx, &fe);
glyph.h = fe.height;
glyph.w = fe.max_x_advance;
label_offset.x = (btk_cp.h - fe.ascent) / 2 + btk_text_offset_tweak.x;
label_offset.y = (btk_cp.h - fe.ascent) / 2 + btk_text_offset_tweak.y + fe.ascent;
}
void
btk_cell_switch_toggle(btk_cell_t *c, int button)
{
if (button != 1)
return;
c->state ^= BTK_CELL_STATE_ON;
if (PP_SWITCH(c)->func)
PP_SWITCH(c)->func(((c->state & BTK_CELL_STATE_ON) >> 4), PP_SWITCH(c)->func_args);
}
void
btk_cell_table_deselect(btk_cell_t *c)
{
PP_TABLE(c)->row_sel = -1;
}
char*
btk_cell_table_get_item(btk_cell_t *c, int col, int row)
{
if (col < 0 || row < 0 || col >= PP_TABLE(c)->cols_n || row >= *(PP_TABLE(c)->rows_n))
return NULL;
return *(PP_TABLE(c)->items) +
PP_TABLE(c)->items_w * (col + row * PP_TABLE(c)->cols_n);
}
void
btk_cell_table_input_button(btk_cell_t *c, int button, int combo, btk_pos_t pt)
{
btk_pp_table_t *pp = PP_TABLE(c);
int prev_sel = pp->row_sel;
/* scrolling if appropriate button is clicked */
switch (button) {
case 4:
pp->scroll.y -= btk_scroll_speed;
return;
case 5:
pp->scroll.y += btk_scroll_speed;
return;
case 6:
pp->scroll.x -= btk_scroll_speed;
return;
case 7:
pp->scroll.x += btk_scroll_speed;
return;
}
/* TODO ugly af block, to redo */
/* if click on header */
if (pt.y < btk_cp.h) {
int ena;
/* find clicked column */
for (int i = 0; i < pp->cols_n; i++) {
ena = pp->cols_ena ? pp->cols_ena[i] : 1;
if (ena && pt.x > pp->cols_px[i] && pt.x <= pp->cols_px[i] + pp->cols_pw[i]) {
switch (button) {
case 1:
pp->cols_cw[i]++;
break;
case 2:
/* TODO sorting ? */
break;
case 3:
pp->cols_cw[i] -= pp->cols_cw[i] > 1 ? 1 : 0;
}
btk_cell_table_update_geometry(c);
break;
}
}
return;
}
/* remaining item list actions */
/* filter out unwanted inputs */
if (button != 1)
return;
/* find item under pointer */
pp->row_sel = (pt.y + pp->scroll.y) / (btk_cp.h + btk_frame) -1;
/* deselect if out of bounds */
if (pp->row_sel < 0 || pp->row_sel >= *(pp->rows_n))
pp->row_sel = -1;
/* on selection change, exec sel func */
if (prev_sel != pp->row_sel && pp->func_sel)
pp->func_sel(pp->row_sel);
/* if double click, exec trigger func */
if (combo == 2 && pp->func_trigger)
pp->func_trigger(pp->row_sel);
}
void
btk_cell_table_update_geometry(btk_cell_t *c)
{
btk_pp_table_t *pp = PP_TABLE(c);
unsigned int cw = 0; /* width in cell units */
unsigned int pw = 0; /* total minimum pixel width */
unsigned int ew = 0; /* extra width if tw > minimum width */
int ena;
/* calculate extra width in pixels */
for (int i = 0; i < pp->cols_n; i++) {
ena = pp->cols_ena ? pp->cols_ena[i] : 1;
cw += ena ? pp->cols_cw[i] : 0;
}
pw = cw * (btk_cp.w + btk_frame) - btk_frame;
ew = pw < c->pa.w ? c->pa.w - pw : 0;
pp->tw = pw + ew;
/* calculate each columns widths in pixels */
for (int i = 0; i < pp->cols_n; i++) {
pp->cols_pw[i] = pp->cols_cw[i] * (btk_cp.w + btk_frame) - btk_frame;
pp->cols_pw[i] += pp->sc == i ? ew : 0;
}
/* calculate columns positions */
int pos = 0;
for (int i = 0; i < pp->cols_n; i++) {
pp->cols_px[i] = pos;
ena = pp->cols_ena ? pp->cols_ena[i] : 1;
pos += ena ? pp->cols_pw[i] + btk_frame : 0;
}
}