Tooltip rendering
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
# To launch with debug messages: G_MESSAGES_DEBUG=all ./wschod
|
||||
|
||||
SRC := $(shell find src -name '*.c')
|
||||
OBJ := $(patsubst src/%.c, build/%.o, $(SRC))
|
||||
DEP := $(OBJ:.o=.d)
|
||||
|
||||
+42
-7
@@ -18,14 +18,28 @@ static Widget *hovered_widget = NULL;
|
||||
static Widget *clicked_widget = NULL;
|
||||
static Widget *focused_widget = NULL;
|
||||
|
||||
static Widget *tooltip_widget = NULL;
|
||||
static Widget *tooltip_text = NULL;
|
||||
|
||||
const char *lorem_ipsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
|
||||
|
||||
void
|
||||
uiman_init ()
|
||||
{
|
||||
ui_root = Widget_plain_new ();
|
||||
VEC2_SETI (ui_root->size, sdl_window_w, sdl_window_h);
|
||||
ui_widget_mesh_init ();
|
||||
glm_ortho (0.0, (float) sdl_window_w, (float) sdl_window_h, 0.0, -1.0, 1.0, projection);
|
||||
|
||||
tooltip_widget = Widget_flowbox_new (TRUE);
|
||||
tooltip_widget->visible = FALSE;
|
||||
tooltip_widget->size[0] = 200;
|
||||
tooltip_widget->style->layout_padding = 4;
|
||||
tooltip_widget->style->background_image = RESTEX ("terrain4");
|
||||
tooltip_text = Widget_multiline_text_new (NULL, 20, TEXT_ALIGN_CENTER);
|
||||
Widget_add_child_flowbox (tooltip_widget, tooltip_text, 1, 0);
|
||||
|
||||
ui_root = Widget_plain_new ();
|
||||
VEC2_SETI (ui_root->size, sdl_window_w, sdl_window_h);
|
||||
|
||||
Widget *debug_menu = Widget_flowbox_new (TRUE);
|
||||
debug_menu->style->layout_padding = 8;
|
||||
debug_menu->style->background_image = RESTEX ("terrain4");
|
||||
@@ -40,12 +54,16 @@ uiman_init ()
|
||||
Widget_add_child_flowbox (debug_menu, Widget_label_make_float ("Y: %.1f", &camera_position[1], TEXT_ALIGN_CENTER), 1./3., 20);
|
||||
Widget_add_child_flowbox (debug_menu, Widget_label_make_float ("Z: %.1f", &camera_position[2], TEXT_ALIGN_RIGHT), 1./3., 20);
|
||||
|
||||
Widget_add_child_flowbox (debug_menu, Widget_composite_spinner_make_int ("Render distance: %d", &render_distance), 1, 20);
|
||||
Widget *spinner = Widget_composite_spinner_make_int ("Render distance: %d", &render_distance);
|
||||
spinner->tooltip = g_string_new (lorem_ipsum);
|
||||
Widget_add_child_flowbox (debug_menu, spinner, 1, 20);
|
||||
|
||||
Widget_add_child_flowbox (debug_menu, Widget_composite_checkbox_make ("Wireframe", "wireframe_enabled"), 1./2., 20);
|
||||
Widget_add_child_flowbox (debug_menu, Widget_composite_checkbox_make ("Flight", "flight_enabled"), 1./2., 20);
|
||||
|
||||
Widget_add_child (ui_root, debug_menu);
|
||||
|
||||
Widget_dump (ui_root, 0);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -58,6 +76,7 @@ void
|
||||
uiman_render ()
|
||||
{
|
||||
Widget_render (ui_root, projection, sdl_window_h);
|
||||
Widget_render (tooltip_widget, projection, sdl_window_h);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -121,6 +140,8 @@ uiman_cb_switch_focus (SDL_Event *event, void *data)
|
||||
_uiman_set_clicked (clicked_widget, FALSE);
|
||||
clicked_widget = NULL;
|
||||
|
||||
tooltip_widget->visible = FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -164,8 +185,9 @@ gboolean
|
||||
uiman_cb_mouse_motion (SDL_Event *event, void *data)
|
||||
{
|
||||
UNUSED (event, data);
|
||||
|
||||
Widget *new_hover = Widget_find_xy (ui_root, (vec2) { event->motion.x, event->motion.y });
|
||||
vec2 pos = { event->motion.x, event->motion.y };
|
||||
|
||||
Widget *new_hover = Widget_find_xy_cond (ui_root, pos, wcond_any, NULL);
|
||||
|
||||
if (new_hover != hovered_widget)
|
||||
{
|
||||
@@ -180,6 +202,19 @@ uiman_cb_mouse_motion (SDL_Event *event, void *data)
|
||||
/* Even if it isn't the hovered widget, this is useful for stuff
|
||||
like scrollbars. */
|
||||
_uiman_call_on_event (clicked_widget, event, EVENT_MOUSE_MOVE);
|
||||
|
||||
Widget *tooltip_source = Widget_find_xy_cond (ui_root, pos, wcond_tooltip, NULL);
|
||||
|
||||
if (tooltip_source && tooltip_source->tooltip && tooltip_source->tooltip->len > 0)
|
||||
{
|
||||
Widget_multiline_text_set_text (tooltip_text, tooltip_source->tooltip->str);
|
||||
tooltip_widget->visible = TRUE;
|
||||
VEC2_SETI (tooltip_widget->position, pos[0] + 12, pos[1] + 12);
|
||||
}
|
||||
else
|
||||
{
|
||||
tooltip_widget->visible = FALSE;
|
||||
}
|
||||
|
||||
/* This callback always eats the event, because why would the game
|
||||
need mouse motion if the UI has stolen the focus? */
|
||||
@@ -192,7 +227,7 @@ uiman_cb_button_down (SDL_Event *event, void *data)
|
||||
UNUSED (data);
|
||||
|
||||
WidgetEvent e = event->button.button == SDL_BUTTON_LEFT ? EVENT_LEFT_DOWN : EVENT_RIGHT_DOWN;
|
||||
Widget *widget = Widget_find_xy (ui_root, (vec2) { event->button.x, event->button.y });
|
||||
Widget *widget = Widget_find_xy_cond (ui_root, (vec2) { event->button.x, event->button.y }, wcond_any, NULL);
|
||||
|
||||
_uiman_set_clicked (widget, TRUE);
|
||||
clicked_widget = widget;
|
||||
@@ -227,7 +262,7 @@ uiman_cb_mouse_wheel (SDL_Event *event, void *data)
|
||||
UNUSED (event, data);
|
||||
|
||||
WidgetEvent e = event->wheel.y > 0 ? EVENT_SCROLL_UP : EVENT_SCROLL_DOWN;
|
||||
Widget *widget = Widget_find_xy_event (ui_root, (vec2) { event->wheel.mouse_x, event->wheel.mouse_y }, e);
|
||||
Widget *widget = Widget_find_xy_cond (ui_root, (vec2) { event->wheel.mouse_x, event->wheel.mouse_y }, wcond_event, GINT_TO_POINTER (e));
|
||||
return _uiman_call_on_event (widget, event, e);
|
||||
}
|
||||
|
||||
|
||||
+68
-10
@@ -236,6 +236,13 @@ Widget_label_printf (Widget *widget, const char *format, ...)
|
||||
va_end (args);
|
||||
}
|
||||
|
||||
void
|
||||
Widget_multiline_text_set_text (Widget *widget, const char *text)
|
||||
{
|
||||
g_string_overwrite (widget->multiline_text.text, 0, text);
|
||||
widget->layout_dirty = TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
Widget_entry_input_text (Widget *widget, const char *text)
|
||||
{
|
||||
@@ -274,8 +281,29 @@ Widget_hit_test (Widget *widget, vec2 point)
|
||||
&& y >= top && y <= bottom);
|
||||
}
|
||||
|
||||
gboolean
|
||||
wcond_tooltip (Widget *widget, void *userdata)
|
||||
{
|
||||
UNUSED (userdata);
|
||||
return widget->tooltip != NULL;
|
||||
}
|
||||
|
||||
gboolean
|
||||
wcond_event (Widget *widget, void *event)
|
||||
{
|
||||
WidgetEvent e = GPOINTER_TO_INT (event);
|
||||
return widget->on_event[e] != NULL;
|
||||
}
|
||||
|
||||
gboolean
|
||||
wcond_any (Widget *widget, void *userdata)
|
||||
{
|
||||
UNUSED (widget, userdata);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Widget *
|
||||
Widget_find_xy_event (Widget *widget, vec2 pos, WidgetEvent event)
|
||||
Widget_find_xy_cond (Widget *widget, vec2 pos, gboolean (*cond_func) (Widget *, void *), void *userdata)
|
||||
{
|
||||
if (!widget->visible)
|
||||
{
|
||||
@@ -298,7 +326,7 @@ Widget_find_xy_event (Widget *widget, vec2 pos, WidgetEvent event)
|
||||
for (int i = widget->children->len - 1; i >= 0; --i)
|
||||
{
|
||||
Widget *child = g_array_index (widget->children, Widget *, i);
|
||||
Widget *found = Widget_find_xy_event (child, pos_scrolled, event);
|
||||
Widget *found = Widget_find_xy_cond (child, pos_scrolled, cond_func, userdata);
|
||||
|
||||
if (found != NULL)
|
||||
{
|
||||
@@ -306,18 +334,12 @@ Widget_find_xy_event (Widget *widget, vec2 pos, WidgetEvent event)
|
||||
}
|
||||
}
|
||||
|
||||
if (event == EVENT_NONE || widget->on_event[event])
|
||||
if (cond_func (widget, userdata))
|
||||
{
|
||||
return widget;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Widget *
|
||||
Widget_find_xy (Widget *widget, vec2 pos)
|
||||
{
|
||||
return Widget_find_xy_event (widget, pos, EVENT_NONE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -343,6 +365,14 @@ Widget_render (Widget *widget, mat4 projection, int window_height)
|
||||
widget->do_layout (widget);
|
||||
}
|
||||
|
||||
if (!widget->visible)
|
||||
{
|
||||
/* If the children need layouting, it doesn't matter, as we're
|
||||
not visible anyway. They'll layout when the widget becomes
|
||||
visible again. */
|
||||
return;
|
||||
}
|
||||
|
||||
if (widget->do_render)
|
||||
{
|
||||
widget->do_render (widget, projection, window_height);
|
||||
@@ -386,3 +416,31 @@ Widget_free (Widget *widget)
|
||||
|
||||
g_free (widget);
|
||||
}
|
||||
|
||||
void
|
||||
Widget_dump (Widget *widget, int depth)
|
||||
{
|
||||
if (!widget) return;
|
||||
|
||||
GString *prefix = g_string_new (NULL);
|
||||
for (int i = 0; i < depth; ++i)
|
||||
{
|
||||
g_string_append (prefix, " ");
|
||||
}
|
||||
|
||||
g_debug ("%sWidget %p kind=%d visible=%d", prefix->str, widget, widget->kind, widget->visible);
|
||||
|
||||
for (int i = 0; i < EVENT_COUNT; ++i)
|
||||
{
|
||||
if (widget->on_event[i])
|
||||
{
|
||||
g_debug ("%s Has handler for event %d", prefix->str, i);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < widget->children->len; ++i)
|
||||
{
|
||||
Widget *child = g_array_index (widget->children, Widget *, i);
|
||||
Widget_dump (child, depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,6 @@ typedef enum WidgetIcon
|
||||
|
||||
typedef enum WidgetEvent
|
||||
{
|
||||
EVENT_NONE = -1, /* Don't use. */
|
||||
EVENT_TICK = 0,
|
||||
EVENT_LEFT_DOWN,
|
||||
EVENT_LEFT_UP,
|
||||
@@ -162,6 +161,8 @@ typedef struct Widget
|
||||
WidgetStyle *style;
|
||||
WidgetIcon icon;
|
||||
|
||||
GString *tooltip;
|
||||
|
||||
void (*do_layout) (struct Widget *);
|
||||
void (*do_render) (struct Widget *, mat4, int);
|
||||
|
||||
@@ -196,13 +197,19 @@ void Widget_set_style (Widget *widget, WidgetStyle style);
|
||||
void Widget_label_set_text (Widget *widget, const char *text);
|
||||
void Widget_label_printf (Widget *widget, const char *format, ...);
|
||||
|
||||
void Widget_multiline_text_set_text (Widget *widget, const char *text);
|
||||
|
||||
void Widget_entry_input_text (Widget *widget, const char *text);
|
||||
|
||||
void Widget_scrollable_scroll (Widget *widget, vec2 direction);
|
||||
|
||||
Widget *Widget_find_xy_event (Widget *widget, vec2 pos, WidgetEvent event);
|
||||
Widget *Widget_find_xy (Widget *widget, vec2 point);
|
||||
gboolean wcond_tooltip (Widget *widget, void *userdata);
|
||||
gboolean wcond_event (Widget *widget, void *event);
|
||||
gboolean wcond_any (Widget *widget, void *userdata);
|
||||
Widget *Widget_find_xy_cond (Widget *widget, vec2 point, gboolean (*cond_func) (Widget *, void *), void *userdata);
|
||||
|
||||
void Widget_tick (Widget *widget);
|
||||
void Widget_render (Widget *widget, mat4 projection, int window_height);
|
||||
void Widget_free (Widget *widget);
|
||||
|
||||
void Widget_dump (Widget *widget, int depth);
|
||||
|
||||
Reference in New Issue
Block a user