eco2d/code/foundation/src/gui/tooltip.c

171 lines
4.2 KiB
C

// Tooltip system with multilevel modal support
typedef struct _tooltip {
const char *name;
const char *content;
const char **links;
} tooltip;
static tooltip *tooltips = 0;
//~ registration
void tooltip_register(tooltip desc) {
if (!tooltips) {
zpl_array_init(tooltips, zpl_heap());
}
desc.links = 0;
zpl_array_append(tooltips, desc);
}
void tooltip_destroy_all(void) {
if (!tooltips) return;
for (zpl_isize i = 0; i < zpl_array_count(tooltips); ++i) {
tooltip *tp = (tooltips + i);
if (tp->links) {
zpl_array_free(tp->links);
}
}
zpl_array_free(tooltips);
}
void tooltip_build_links(void) {
for (zpl_isize i = 0; i < zpl_array_count(tooltips); ++i) {
tooltip *tp = (tooltips + i);
for (zpl_isize j = 0; j < zpl_array_count(tooltips); ++j) {
tooltip *linked_tp = (tooltips + j);
if (tp == linked_tp)
continue;
if (strstr(tp->content, linked_tp->name)) {
if (!tp->links) {
zpl_array_init(tp->links, zpl_heap());
}
zpl_array_append(tp->links, linked_tp->name);
}
}
}
}
void tooltip_register_defaults(void) {
// test
tooltip_register( (tooltip) { .name = "ASSET_WOOD", .content = "Used as a building material or fuel for the ASSET_FURNACE." } );
tooltip_register( (tooltip) { .name = "ASSET_FURNACE", .content = "Producer used to smelt ASSET_IRON_ORE into ASSET_IRON_INGOT." } );
tooltip_register( (tooltip) { .name = "ASSET_IRON_ORE", .content = "Natural resource that can be smelted in ASSET_FURNACE." } );
tooltip_register( (tooltip) { .name = "ASSET_IRON_INGOT", .content = "Used as a building material. It is smelted from ASSET_IRON_ORE" } );
}
//~ rendering
#define TOOLTIP_MOUSE_DIST 400.0f
typedef struct _tooltip_node {
float xpos, ypos;
tooltip *desc;
struct _tooltip_node *next;
} tooltip_node;
static tooltip_node main_tooltip = { 0 };
static bool tooltip__should_stay_open = false;
tooltip *tooltip__find_desc(const char *name) {
for (zpl_isize i = 0; i < zpl_array_count(tooltips); ++i) {
tooltip *tp = (tooltips + i);
if (!strcmp(tp->name, name))
return tp;
}
return 0;
}
void tooltip_show(const char* name, float xpos, float ypos) {
if (!tooltips) return;
tooltip *desc = tooltip__find_desc(name);
if (!name) return;
main_tooltip = (tooltip_node) {
.xpos = xpos,
.ypos = ypos,
.desc = desc,
.next = 0
};
}
void tooltip__clear_node(tooltip_node *node) {
if (node->next) {
tooltip__clear_node(node->next);
zpl_mfree(node->next);
}
}
void tooltip_clear(void) {
tooltip__clear_node(&main_tooltip);
main_tooltip = (tooltip_node) {0};
}
void tooltip__draw_node(tooltip_node *node) {
if (!node) return;
tooltip *desc = node->desc;
Vector2 mpos = GetMousePosition();
if (nk_begin_titled(game_ui, zpl_bprintf("%d%s", (int)node->xpos, desc->name), desc->name, nk_rect(node->xpos, node->ypos, 300, 1200),
NK_WINDOW_BORDER | NK_WINDOW_NO_SCROLLBAR | NK_WINDOW_DYNAMIC | NK_WINDOW_TITLE | NK_WINDOW_MOVABLE)) {
nk_layout_row_dynamic(game_ui, 30, 1);
nk_label_wrap(game_ui, desc->content);
if (desc->links) {
nk_label(game_ui, "See Also:", NK_TEXT_LEFT);
nk_layout_row_dynamic(game_ui, 10, 2);
for (zpl_isize i = 0; i < zpl_array_count(desc->links); ++i) {
// todo styling
if (nk_button_label(game_ui, desc->links[i])) {
if (node->next) tooltip__clear_node(node->next);
if (!node->next) node->next = zpl_malloc(sizeof(tooltip_node));
*node->next = (tooltip_node) {
.xpos = mpos.x+5,
.ypos = mpos.y+5,
.desc = tooltip__find_desc(desc->links[i]),
.next = 0
};
}
}
}
// suggest closing tooltip
struct nk_vec2 wpos = nk_window_get_position(game_ui);
Vector2 tp_pos = (Vector2) { .x = wpos.x, .y = wpos.y };
if (Vector2Distance(mpos, tp_pos) <= TOOLTIP_MOUSE_DIST) {
tooltip__should_stay_open = true;
}
nk_end(game_ui);
// draw nested tooltip
if (node->next) {
tooltip__draw_node(node->next);
}
}
}
void tooltip_draw(void) {
if (!main_tooltip.desc) return;
tooltip__draw_node(&main_tooltip);
if (!tooltip__should_stay_open) {
tooltip_clear();
}
tooltip__should_stay_open = false;
}