diff --git a/game/res/ui/debug_window.scm b/game/res/scripts/ui/debug_window.scm similarity index 97% rename from game/res/ui/debug_window.scm rename to game/res/scripts/ui/debug_window.scm index fa5c39e..cf1a767 100644 --- a/game/res/ui/debug_window.scm +++ b/game/res/scripts/ui/debug_window.scm @@ -74,5 +74,5 @@ #:layout-padding 8 #:layout-spacing 8 #:horizontal #t - (widget-checkbox "Flight" "flight_enabled" #:layout-weight 0.5 #:h 20) - (widget-checkbox "Wireframe" "wireframe_enabled" #:layout-weight 0.5 #:h 20))) + (widget-checkbox "Flight" "flight_enabled" #:layout-weight 0.5 #:h 20 #:tooltip "Testt") + (widget-checkbox "Wireframe" "wireframe_enabled" #:layout-weight 0.5 #:h 20 #:tooltip "test222"))) diff --git a/game/res/scripts/ui/tooltip.scm b/game/res/scripts/ui/tooltip.scm new file mode 100644 index 0000000..6bfbf37 --- /dev/null +++ b/game/res/scripts/ui/tooltip.scm @@ -0,0 +1,22 @@ +(define COLOR_WHITE '(1.0 1.0 1.0 1.0)) +(define COLOR_BLACK '(0.0 0.0 0.0 1.0)) + +(define style-tooltip + (style-new + #:background-color COLOR_BLACK)) + +(define style-tooltip-text + (style-new + #:text-color COLOR_WHITE + #:text-color-hovered COLOR_WHITE + #:text-color-clicked COLOR_WHITE)) + +(widget-new + 'flowbox + #:style style-tooltip + #:w 200 + #:visible #f + #:horizontal #t + #:layout-padding 4 + #:layout-spacing 8 + (widget-new 'multiline #:line-height 20 #:layout-weight 1 #:style style-tooltip-text)) diff --git a/game/src/ui/uiman.c b/game/src/ui/uiman.c index e6c6dd7..65b98f6 100644 --- a/game/src/ui/uiman.c +++ b/game/src/ui/uiman.c @@ -12,8 +12,8 @@ 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; */ +static Widget *tooltip_widget = NULL; +static Widget *tooltip_text = NULL; void uiman_init () @@ -24,10 +24,13 @@ uiman_init () ui_root = Widget_new (); IVEC2_SETI (ui_root->size, sdl_window_w, sdl_window_h); - SCM scm_debug = scm_c_primitive_load ("res/ui/debug_window.scm"); + SCM scm_debug = scm_c_primitive_load ("res/scripts/ui/debug_window.scm"); Widget *debug_window = (Widget *) scm_to_pointer (scm_debug); Widget_add_child (ui_root, debug_window); - Widget_dump (ui_root, 0); + + SCM scm_tooltip = scm_c_primitive_load ("res/scripts/ui/tooltip.scm"); + tooltip_widget = (Widget *) scm_to_pointer (scm_tooltip); + tooltip_text = g_array_index (tooltip_widget->children, Widget *, 0); } void @@ -40,7 +43,7 @@ void uiman_render () { Widget_render (ui_root, projection, sdl_window_h); - /* Widget_render (tooltip_widget, projection, sdl_window_h); */ + Widget_render (tooltip_widget, projection, sdl_window_h); } void @@ -95,7 +98,8 @@ uiman_cb_switch_focus (SDL_Event *event, void *data) flags_switch ("ui_has_focus"); SDL_SetWindowRelativeMouseMode (sdl_window, !flags_get ("ui_has_focus")); - /* We reset the global states of the UI. */ + /* We reset the global states of the UI. TODO: also launch the + callbacks on mouse leave? */ SDL_StopTextInput (sdl_window); _uiman_set_hovered (hovered_widget, FALSE); hovered_widget = NULL; @@ -104,7 +108,7 @@ uiman_cb_switch_focus (SDL_Event *event, void *data) _uiman_set_clicked (clicked_widget, FALSE); clicked_widget = NULL; - /* tooltip_widget->visible = FALSE; */ + tooltip_widget->visible = FALSE; return TRUE; } @@ -171,13 +175,13 @@ uiman_cb_mouse_motion (SDL_Event *event, void *data) 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; */ - /* IVEC2_SETI (tooltip_widget->position, pos[0] + 12, pos[1] + 12); */ + g_string_overwrite (tooltip_text->multiline_text.text, 0, tooltip_source->tooltip->str); + tooltip_widget->visible = TRUE; + IVEC2_SETI (tooltip_widget->position, pos[0] + 12, pos[1] + 12); } else { - /* tooltip_widget->visible = FALSE; */ + tooltip_widget->visible = FALSE; } /* This callback always eats the event, because why would the game diff --git a/game/src/ui/widget_ops.c b/game/src/ui/widget_boilerplate.c similarity index 52% rename from game/src/ui/widget_ops.c rename to game/src/ui/widget_boilerplate.c index 1144f7c..9b125e2 100644 --- a/game/src/ui/widget_ops.c +++ b/game/src/ui/widget_boilerplate.c @@ -1,97 +1,105 @@ -#include "widget_ops.h" +#include "widget_boilerplate.h" extern SDL_Window *sdl_window; -WidgetOpData * -WidgetOpData_new (WidgetOp op, void *ptr, ...) +/* Entry boilerplate */ +void +ui_boilerplate_entry_move_left (Widget *self, SDL_Event *sdl_event, void *userdata) { - WidgetOpData *data = g_new0 (WidgetOpData, 1); - data->op = op; - data->ptr = ptr; - - va_list ap; - va_start (ap, ptr); - - switch (data->op) - { - case OP_SCROLLABLE_SCROLL: - glm_ivec2_copy (va_arg (ap, int *), data->scroll_dir); - break; - default: - break; - } + UNUSED (sdl_event, userdata); - va_end (ap); - return data; + if (self->entry.cursor_position > 0) + { + self->entry.cursor_position--; + } } void -uicb_widget_op (Widget *self, SDL_Event *sdl_event, void *userdata) +ui_boilerplate_entry_move_right (Widget *self, SDL_Event *sdl_event, void *userdata) { - UNUSED (sdl_event); - WidgetOpData *data = userdata; - - switch (data->op) + UNUSED (sdl_event, userdata); + + if (self->entry.cursor_position < g_utf8_strlen (self->entry.content->str, -1)) { - case OP_SCROLLABLE_SCROLL: - Widget_scrollable_scroll ((Widget *) data->ptr, data->scroll_dir); - break; - case OP_START_TEXT_INPUT: - SDL_StartTextInput (sdl_window); - break; - case OP_STOP_TEXT_INPUT: - SDL_StopTextInput (sdl_window); - break; - case OP_ENTRY_INPUT_TEXT: - Widget_entry_input_text (self, sdl_event->text.text); - break; - case OP_ENTRY_DELETE_BACKWARD: - if (self->entry.cursor_position > 0 - && self->entry.content->len > 0) - { - gchar *start = g_utf8_offset_to_pointer (self->entry.content->str, self->entry.cursor_position - 1); - gchar *end = g_utf8_next_char (start); - gint byte_offset = start - self->entry.content->str; - gint byte_len = end - start; - - g_string_erase (self->entry.content, byte_offset, byte_len); - self->entry.cursor_position--; - } - break; - case OP_ENTRY_DELETE_FORWARD: - int total_chars = g_utf8_strlen (self->entry.content->str, -1); - - if (self->entry.cursor_position < total_chars) - { - gchar *start = g_utf8_offset_to_pointer (self->entry.content->str, self->entry.cursor_position); - gchar *end = g_utf8_next_char (start); - gint byte_offset = start - self->entry.content->str; - gint byte_len = end - start; - - g_string_erase (self->entry.content, byte_offset, byte_len); - } - break; - case OP_ENTRY_MOVE_LEFT: - if (self->entry.cursor_position > 0) - { - self->entry.cursor_position--; - } - break; - case OP_ENTRY_MOVE_RIGHT: - if (self->entry.cursor_position < g_utf8_strlen (self->entry.content->str, -1)) - { - self->entry.cursor_position++; - } - break; - default: - break; + self->entry.cursor_position++; } } +void +ui_boilerplate_entry_delete_backward (Widget *self, SDL_Event *sdl_event, void *userdata) +{ + UNUSED (sdl_event, userdata); + + if (self->entry.cursor_position > 0 + && self->entry.content->len > 0) + { + gchar *start = g_utf8_offset_to_pointer (self->entry.content->str, self->entry.cursor_position - 1); + gchar *end = g_utf8_next_char (start); + gint byte_offset = start - self->entry.content->str; + gint byte_len = end - start; + + g_string_erase (self->entry.content, byte_offset, byte_len); + self->entry.cursor_position--; + } +} + +void +ui_boilerplate_entry_delete_forward (Widget *self, SDL_Event *sdl_event, void *userdata) +{ + UNUSED (sdl_event, userdata); + + int total_chars = g_utf8_strlen (self->entry.content->str, -1); + + if (self->entry.cursor_position < total_chars) + { + gchar *start = g_utf8_offset_to_pointer (self->entry.content->str, self->entry.cursor_position); + gchar *end = g_utf8_next_char (start); + gint byte_offset = start - self->entry.content->str; + gint byte_len = end - start; + + g_string_erase (self->entry.content, byte_offset, byte_len); + } +} + +void +ui_boilerplate_entry_input_text (Widget *self, SDL_Event *sdl_event, void *userdata) +{ + UNUSED (userdata); + Widget_entry_input_text (self, sdl_event->text.text); +} + +void +ui_boilerplate_entry_start_input (Widget *self, SDL_Event *sdl_event, void *userdata) +{ + UNUSED (self, sdl_event, userdata); + SDL_StartTextInput (sdl_window); +} + +void +ui_boilerplate_entry_stop_input (Widget *self, SDL_Event *sdl_event, void *userdata) +{ + UNUSED (self, sdl_event, userdata); + SDL_StopTextInput (sdl_window); +} + /* Scrollbar boilerplate. */ void -uicb_scrollbar_start_scroll (Widget *self, SDL_Event *sdl_event, void *userdata) +ui_boilerplate_scrollbar_scroll_up (Widget *self, SDL_Event *sdl_event, void *userdata) +{ + UNUSED (sdl_event, userdata); + Widget_scrollable_scroll (self->scrollbar.target, SCROLL_UP); +} + +void +ui_boilerplate_scrollbar_scroll_down (Widget *self, SDL_Event *sdl_event, void *userdata) +{ + UNUSED (sdl_event, userdata); + Widget_scrollable_scroll (self->scrollbar.target, SCROLL_DOWN); +} + +void +ui_boilerplate_scrollbar_start_scroll (Widget *self, SDL_Event *sdl_event, void *userdata) { UNUSED (userdata); @@ -123,7 +131,7 @@ uicb_scrollbar_start_scroll (Widget *self, SDL_Event *sdl_event, void *userdata) } void -uicb_scrollbar_scroll (Widget *self, SDL_Event *sdl_event, void *userdata) +ui_boilerplate_scrollbar_scroll (Widget *self, SDL_Event *sdl_event, void *userdata) { UNUSED (userdata); @@ -154,7 +162,7 @@ uicb_scrollbar_scroll (Widget *self, SDL_Event *sdl_event, void *userdata) } void -uicb_scrollbar_stop_scroll (Widget *self, SDL_Event *sdl_event, void *userdata) +ui_boilerplate_scrollbar_stop_scroll (Widget *self, SDL_Event *sdl_event, void *userdata) { UNUSED (sdl_event, userdata); self->scrollbar.dragging = FALSE; @@ -174,7 +182,7 @@ ui_window_is_in_resize_area (Widget *self, vec2 mouse_pos) } void -uicb_window_start_drag (Widget *self, SDL_Event *sdl_event, void *userdata) +ui_boilerplate_window_start_drag (Widget *self, SDL_Event *sdl_event, void *userdata) { UNUSED (userdata); @@ -195,21 +203,21 @@ uicb_window_start_drag (Widget *self, SDL_Event *sdl_event, void *userdata) } void -_uicb_window_move (Widget *self, ivec2 mouse_pos) +_ui_boilerplate_window_move (Widget *self, ivec2 mouse_pos) { self->position[0] = mouse_pos[0] - self->window.drag_offset[0]; self->position[1] = mouse_pos[1] - self->window.drag_offset[1]; } void -_uicb_window_resize (Widget *self, ivec2 mouse_pos) +_ui_boilerplate_window_resize (Widget *self, ivec2 mouse_pos) { self->size[0] = mouse_pos[0] - self->position[0]; self->size[1] = mouse_pos[1] - self->position[1]; } void -uicb_window_drag (Widget *self, SDL_Event *sdl_event, void *userdata) +ui_boilerplate_window_drag (Widget *self, SDL_Event *sdl_event, void *userdata) { UNUSED (userdata); @@ -217,18 +225,18 @@ uicb_window_drag (Widget *self, SDL_Event *sdl_event, void *userdata) if (self->window.moving) { - _uicb_window_move (self, mouse_pos); + _ui_boilerplate_window_move (self, mouse_pos); self->layout_dirty = TRUE; } else if (self->window.resizing) { - _uicb_window_resize (self, mouse_pos); + _ui_boilerplate_window_resize (self, mouse_pos); self->layout_dirty = TRUE; } } void -uicb_window_stop_drag (Widget *self, SDL_Event *sdl_event, void *userdata) +ui_boilerplate_window_stop_drag (Widget *self, SDL_Event *sdl_event, void *userdata) { UNUSED (sdl_event, userdata); self->window.moving = FALSE; diff --git a/game/src/ui/widget_boilerplate.h b/game/src/ui/widget_boilerplate.h new file mode 100644 index 0000000..c9c1b92 --- /dev/null +++ b/game/src/ui/widget_boilerplate.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include "widget_fw.h" +#include "widget_core.h" + +void ui_boilerplate_entry_start_input (Widget *self, SDL_Event *sdl_event, void *userdata); +void ui_boilerplate_entry_stop_input (Widget *self, SDL_Event *sdl_event, void *userdata); + +void ui_boilerplate_scrollbar_scroll_up (Widget *self, SDL_Event *sdl_event, void *userdata); +void ui_boilerplate_scrollbar_scroll_down (Widget *self, SDL_Event *sdl_event, void *userdata); + +void ui_boilerplate_scrollbar_start_scroll (Widget *self, SDL_Event *sdl_event, void *userdata); +void ui_boilerplate_scrollbar_scroll (Widget *self, SDL_Event *sdl_event, void *userdata); +void ui_boilerplate_scrollbar_stop_scroll (Widget *self, SDL_Event *sdl_event, void *userdata); + +void ui_boilerplate_window_start_drag (Widget *self, SDL_Event *sdl_event, void *userdata); +void ui_boilerplate_window_drag (Widget *self, SDL_Event *sdl_event, void *userdata); +void ui_boilerplate_window_stop_drag (Widget *self, SDL_Event *sdl_event, void *userdata); diff --git a/game/src/ui/widget_core.c b/game/src/ui/widget_core.c index 297a113..5a2d261 100644 --- a/game/src/ui/widget_core.c +++ b/game/src/ui/widget_core.c @@ -5,6 +5,7 @@ Widget_new () { Widget *w = g_new0 (Widget, 1); w->children = g_array_new (false, false, sizeof (Widget *)); + w->tooltip = g_string_new (NULL); w->style = g_new0 (WidgetStyle, 1); w->visible = TRUE; w->do_render = Widget_render_base; @@ -24,32 +25,6 @@ Widget_add_child (Widget *parent, Widget *child) g_array_append_val (parent->children, child); } -void -Widget_set_on_event (Widget *widget, WidgetEvent event, - void (*on_event) (Widget *, SDL_Event *, void *), - void *userdata) -{ - widget->on_event[event] = on_event; - widget->event_data[event] = userdata; -} - -void -Widget_set_on_key (Widget *widget, SDL_Scancode key, - void (*on_key) (Widget *, SDL_Event *, void *), void *userdata) -{ - widget->on_key[key] = on_key; - widget->key_data[key] = userdata; -} - -void -Widget_label_printf (Widget *widget, const char *format, ...) -{ - va_list args; - va_start (args, format); - g_string_vprintf (widget->label.text, format, args); - va_end (args); -} - void Widget_entry_input_text (Widget *widget, const char *text) { @@ -92,27 +67,6 @@ Widget_hit_test (Widget *widget, ivec2 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_cond (Widget *widget, ivec2 pos, gboolean (*cond_func) (Widget *, void *), void *userdata) { @@ -230,29 +184,3 @@ 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); - } - } - - WFOREACH (widget, child) - { - Widget_dump (child, depth + 1); - } -} diff --git a/game/src/ui/widget_core.h b/game/src/ui/widget_core.h index 1377eef..ba7abd3 100644 --- a/game/src/ui/widget_core.h +++ b/game/src/ui/widget_core.h @@ -7,7 +7,7 @@ #include "widget_render.h" #include "widget_layout.h" #include "widget_style.h" -#include "widget_ops.h" +#include "widget_boilerplate.h" #include "../util.h" #include "../resources.h" @@ -19,8 +19,8 @@ for (unsigned int _i = 0; _i < (widget)->children->len; ++_i) \ for (Widget *child = g_array_index ((widget)->children, Widget *, _i); child; child = NULL) -#define SCROLL_UP (vec2) { 0.0, -1.0 } -#define SCROLL_DOWN (vec2) { 0.0, 1.0 } +#define SCROLL_UP (ivec2) { 0, -1 } +#define SCROLL_DOWN (ivec2) { 0, 1 } typedef enum WidgetKind { @@ -190,25 +190,17 @@ typedef struct Widget } Widget; Widget *Widget_new (); - void Widget_add_child (Widget *parent, Widget *child); -void Widget_set_on_event (Widget *widget, WidgetEvent event, void (*on_event) (Widget *, SDL_Event *, void *), void *userdata); -void Widget_set_on_key (Widget *widget, SDL_Scancode key, void (*on_key) (Widget *, SDL_Event *, void *), void *userdata); - -void Widget_label_printf (Widget *widget, const char *format, ...); - void Widget_entry_input_text (Widget *widget, const char *text); - void Widget_scrollable_scroll (Widget *widget, ivec2 direction); -gboolean wcond_tooltip (Widget *widget, void *userdata); -gboolean wcond_event (Widget *widget, void *event); -gboolean wcond_any (Widget *widget, void *userdata); +static inline gboolean wcond_tooltip (Widget *widget, void *userdata) { UNUSED (userdata); return widget->tooltip != NULL; } +static inline gboolean wcond_event (Widget *widget, void *event) { WidgetEvent e = GPOINTER_TO_INT (event); return widget->on_event[e] != NULL; } +static inline gboolean wcond_any (Widget *widget, void *userdata) { UNUSED (widget, userdata); return TRUE; } + Widget *Widget_find_xy_cond (Widget *widget, ivec2 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); diff --git a/game/src/ui/widget_guile.c b/game/src/ui/widget_guile.c index 6589107..f29f700 100644 --- a/game/src/ui/widget_guile.c +++ b/game/src/ui/widget_guile.c @@ -154,6 +154,11 @@ _wgg_build (const char *type) { w->kind = WIDGET_SCROLLBAR; w->do_render = Widget_render_scrollbar; + w->on_event[EVENT_LEFT_DOWN] = ui_boilerplate_scrollbar_start_scroll; + w->on_event[EVENT_MOUSE_MOVE] = ui_boilerplate_scrollbar_scroll; + w->on_event[EVENT_LEFT_UP] = ui_boilerplate_scrollbar_stop_scroll; + w->on_event[EVENT_SCROLL_UP] = ui_boilerplate_scrollbar_scroll_up; + w->on_event[EVENT_SCROLL_DOWN] = ui_boilerplate_scrollbar_scroll_down; } if (!g_strcmp0 (type, "progress")) @@ -172,6 +177,7 @@ _wgg_build (const char *type) { w->kind = WIDGET_MULTILINE_TEXT; w->do_render = Widget_render_multiline_text; + w->do_layout = Widget_layout_multiline_text; w->multiline_text.text = g_string_new (NULL); w->multiline_text.lines = g_array_new (FALSE, FALSE, sizeof (GString)); } @@ -201,9 +207,9 @@ _wgg_build (const char *type) w->do_layout = Widget_layout_window; w->window.title = g_string_new ("Window"); w->window.titlebar_height = 20; - w->on_event[EVENT_LEFT_DOWN] = uicb_window_start_drag; - w->on_event[EVENT_MOUSE_MOVE] = uicb_window_drag; - w->on_event[EVENT_LEFT_UP] = uicb_window_stop_drag; + w->on_event[EVENT_LEFT_DOWN] = ui_boilerplate_window_start_drag; + w->on_event[EVENT_MOUSE_MOVE] = ui_boilerplate_window_drag; + w->on_event[EVENT_LEFT_UP] = ui_boilerplate_window_stop_drag; } return w; @@ -341,6 +347,11 @@ _wgg_set_property (Widget *w, const char *key, SCM val) } } + if (!g_strcmp0 (key, "tooltip")) + { + g_string_overwrite (w->tooltip, 0, scm_to_utf8_string (val)); + } + if (!g_strcmp0 (key, "on-tick")) { if (!scm_is_true (scm_procedure_p (val))) diff --git a/game/src/ui/widget_ops.h b/game/src/ui/widget_ops.h deleted file mode 100644 index a03faa3..0000000 --- a/game/src/ui/widget_ops.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include - -#include "widget_fw.h" -#include "widget_core.h" - -typedef enum WidgetOp - { - OP_SCROLLABLE_SCROLL, - - OP_START_TEXT_INPUT, - OP_STOP_TEXT_INPUT, - OP_ENTRY_INPUT_TEXT, - OP_ENTRY_DELETE_BACKWARD, - OP_ENTRY_DELETE_FORWARD, - OP_ENTRY_MOVE_LEFT, - OP_ENTRY_MOVE_RIGHT - } WidgetOp; - -typedef struct WidgetOpData -{ - WidgetOp op; - void *ptr; - - ivec2 scroll_dir; -} WidgetOpData; - -WidgetOpData *WidgetOpData_new (WidgetOp op, void *ptr, ...); -void uicb_widget_op (Widget *self, SDL_Event *sdl_event, void *userdata); - -void uicb_scrollbar_start_scroll (Widget *self, SDL_Event *sdl_event, void *userdata); -void uicb_scrollbar_scroll (Widget *self, SDL_Event *sdl_event, void *userdata); -void uicb_scrollbar_stop_scroll (Widget *self, SDL_Event *sdl_event, void *userdata); - -void uicb_window_start_drag (Widget *self, SDL_Event *sdl_event, void *userdata); -void uicb_window_drag (Widget *self, SDL_Event *sdl_event, void *userdata); -void uicb_window_stop_drag (Widget *self, SDL_Event *sdl_event, void *userdata);