diff --git a/game/src/ui/uiman.c b/game/src/ui/uiman.c index 7ac477d..d47ca34 100644 --- a/game/src/ui/uiman.c +++ b/game/src/ui/uiman.c @@ -15,6 +15,7 @@ static Widget *ui_root; static mat4 projection; static Widget *hovered_widget = NULL; +static Widget *clicked_widget = NULL; static Widget *focused_widget = NULL; const char *lorem_ipsum = @@ -58,27 +59,20 @@ uiman_init () Widget_add_child_weighed (chkboxes, Widget_composite_checkbox_make ("Flight", "flight_enabled"), 1); Widget_add_child_weighed (debug_menu, chkboxes, 1); - Widget *window2 = Widget_flowbox_new (FALSE); - window2->style->layout_padding = 8; - window2->style->background_image = RESTEX ("terrain0"); - VEC2_SETI (window2->position, 414, 32); - VEC2_SETI (window2->size, 400, 300); - - Widget_add_child_weighed (window2, Widget_test_new (100), 1); - Widget_add_child_weighed (window2, Widget_test_new (100), 1); - Widget_add_child_weighed (window2, Widget_test_new (100), 1); - Widget_add_child_weighed (window2, Widget_test_new (100), 1); - Widget_add_child_weighed (window2, Widget_test_new (100), 1); - Widget_add_child_weighed (window2, Widget_test_new (100), 1); - - Widget *entry = Widget_entry_new ("Input here"); - entry->style->background_image = RESTEX ("terrain4"); - entry->size[0] = 150; + Widget *console = Widget_container_new (FALSE); + VEC2_SETI (console->position, 418, 32); + VEC2_SETI (console->size, 350, 200); + console->style->layout_padding = 8; + console->style->background_image = RESTEX ("terrain3"); + Widget *scrollable = Widget_scrollable_new (); + Widget *entry = Widget_entry_new ("Command..."); + entry->style->background_image = RESTEX ("terrain2"); entry->size[1] = 20; - Widget_add_child_weighed (window2, entry, 1); - Widget_add_child_weighed (window2, Widget_test_new (100), 1); - Widget_add_child (ui_root, window2); + Widget_add_child_weighed (console, scrollable, 1); + Widget_add_child (console, entry); + + Widget_add_child (ui_root, console); } void @@ -202,7 +196,6 @@ uiman_cb_mouse_motion (SDL_Event *event, void *data) { _uiman_call_on_event (hovered_widget, event, EVENT_MOUSE_LEAVE); _uiman_set_hovered (hovered_widget, FALSE); - _uiman_set_clicked (hovered_widget, FALSE); _uiman_call_on_event (new_hover, event, EVENT_MOUSE_ENTER); _uiman_set_hovered (new_hover, TRUE); hovered_widget = new_hover; @@ -222,7 +215,9 @@ uiman_cb_button_down (SDL_Event *event, void *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 }); + _uiman_set_clicked (widget, TRUE); + clicked_widget = widget; if (widget != focused_widget) { @@ -242,9 +237,10 @@ uiman_cb_button_up (SDL_Event *event, void *data) UNUSED (data); WidgetEvent e = event->button.button == SDL_BUTTON_LEFT ? EVENT_LEFT_UP : EVENT_RIGHT_UP; - Widget *widget = Widget_find_xy (ui_root, (vec2) { event->button.x, event->button.y }); - _uiman_set_clicked (widget, FALSE); - return _uiman_call_on_event (widget, event, e); + gboolean val = _uiman_call_on_event (clicked_widget, event, e); + _uiman_set_clicked (clicked_widget, FALSE); + clicked_widget = NULL; + return val; } gboolean diff --git a/game/src/ui/widget_composites.c b/game/src/ui/widget_composites.c index 29f2801..1c039d1 100644 --- a/game/src/ui/widget_composites.c +++ b/game/src/ui/widget_composites.c @@ -52,11 +52,11 @@ Widget_composite_spinner_make_int (const char *fmt, int *ptr) Widget_set_style (left, widget_style_icon_button); Widget_set_style (right, widget_style_icon_button); - Widget_set_on_event (left, EVENT_LEFT_DOWN, uicb_widget_op, WidgetOpData_new (OP_MODIFY_INT, ptr, -1.0)); - left->icon = ICON_SPINNER_LEFT; + Widget_set_on_event (left, EVENT_LEFT_UP, uicb_widget_op, WidgetOpData_new (OP_MODIFY_INT, ptr, -1.0)); + left->icon = ICON_MINUS; left->aspect_ratio = 1.0; - Widget_set_on_event (right, EVENT_LEFT_DOWN, uicb_widget_op, WidgetOpData_new (OP_MODIFY_INT, ptr, 1.0)); - right->icon = ICON_SPINNER_RIGHT; + Widget_set_on_event (right, EVENT_LEFT_UP, uicb_widget_op, WidgetOpData_new (OP_MODIFY_INT, ptr, 1.0)); + right->icon = ICON_PLUS; right->aspect_ratio = 1.0; Widget_add_child_weighed (cont, left, 1); diff --git a/game/src/ui/widget_core.c b/game/src/ui/widget_core.c index 5c2c10c..09f0bf2 100644 --- a/game/src/ui/widget_core.c +++ b/game/src/ui/widget_core.c @@ -91,7 +91,6 @@ Widget_scrollbar_new (Widget *target, gboolean horizontal) e->on_event[EVENT_LEFT_DOWN] = uicb_scrollbar_start_scroll; e->on_event[EVENT_MOUSE_MOVE] = uicb_scrollbar_scroll; e->on_event[EVENT_LEFT_UP] = uicb_scrollbar_stop_scroll; - e->on_event[EVENT_MOUSE_LEAVE] = uicb_scrollbar_stop_scroll; Widget_set_on_event (e, EVENT_SCROLL_UP, uicb_widget_op, WidgetOpData_new (OP_SCROLLABLE_SCROLL, target, (vec2) { 0.0, -1.0 })); Widget_set_on_event (e, EVENT_SCROLL_DOWN, uicb_widget_op, @@ -134,7 +133,8 @@ Widget_entry_new (const char *ghost_text) Widget_set_on_event (e, EVENT_FOCUS_GAINED, uicb_widget_op, WidgetOpData_new (OP_START_TEXT_INPUT, NULL)); Widget_set_on_event (e, EVENT_FOCUS_LOST, uicb_widget_op, WidgetOpData_new (OP_STOP_TEXT_INPUT, NULL)); Widget_set_on_event (e, EVENT_TEXT_INPUT, uicb_widget_op, WidgetOpData_new (OP_ENTRY_INPUT_TEXT, NULL)); - Widget_set_on_key (e, SDL_SCANCODE_BACKSPACE, uicb_widget_op, WidgetOpData_new (OP_ENTRY_DELETE_CHAR, NULL)); + Widget_set_on_key (e, SDL_SCANCODE_BACKSPACE, uicb_widget_op, WidgetOpData_new (OP_ENTRY_DELETE_BACKWARD, NULL)); + Widget_set_on_key (e, SDL_SCANCODE_DELETE, uicb_widget_op, WidgetOpData_new (OP_ENTRY_DELETE_FORWARD, NULL)); Widget_set_on_key (e, SDL_SCANCODE_LEFT, uicb_widget_op, WidgetOpData_new (OP_ENTRY_MOVE_LEFT, NULL)); Widget_set_on_key (e, SDL_SCANCODE_RIGHT, uicb_widget_op, WidgetOpData_new (OP_ENTRY_MOVE_RIGHT, NULL)); return e; @@ -232,8 +232,11 @@ Widget_label_printf (Widget *widget, const char *format, ...) void Widget_entry_input_text (Widget *widget, const char *text) { - g_string_insert (widget->entry.content, widget->entry.cursor_position, text); - widget->entry.cursor_position += strlen (text); + gchar *insert_point = g_utf8_offset_to_pointer (widget->entry.content->str, widget->entry.cursor_position); + gint byte_offset = insert_point - widget->entry.content->str; + + g_string_insert (widget->entry.content, byte_offset, text); + widget->entry.cursor_position += g_utf8_strlen (text, -1); } void diff --git a/game/src/ui/widget_core.h b/game/src/ui/widget_core.h index 0dfe346..c57b102 100644 --- a/game/src/ui/widget_core.h +++ b/game/src/ui/widget_core.h @@ -39,8 +39,8 @@ typedef enum WidgetIcon ICON_NONE = -1, ICON_CHECKBOX_UNCHECKED = 0, ICON_CHECKBOX_CHECKED, - ICON_SPINNER_LEFT, - ICON_SPINNER_RIGHT, + ICON_MINUS, + ICON_PLUS, ICON_ARROW_LEFT, ICON_ARROW_RIGHT, ICON_ARROW_UP, @@ -107,7 +107,8 @@ typedef struct EntryWidget { GString *ghost_text; GString *content; - float cursor_position; + /* cursor_position is in UTF-8 characters. */ + int cursor_position; } EntryWidget; typedef struct ContainerWidget @@ -136,7 +137,8 @@ typedef struct Widget GArray *children; WidgetKind kind; - union { + union + { LabelWidget label; ScrollbarWidget scrollbar; ProgressWidget progress; diff --git a/game/src/ui/widget_layout.c b/game/src/ui/widget_layout.c index 3e48e52..2ffb4ba 100644 --- a/game/src/ui/widget_layout.c +++ b/game/src/ui/widget_layout.c @@ -21,9 +21,13 @@ Widget_layout_container (Widget *self) if (child->layout_weight <= 0.0 || child->aspect_ratio > 0.0) { - child->size[main_axis] = horizontal - ? container_cross * child->aspect_ratio - : container_cross / child->aspect_ratio; + if (child->aspect_ratio > 0.0) + { + child->size[main_axis] = horizontal + ? container_cross * child->aspect_ratio + : container_cross / child->aspect_ratio; + } + total_fixed += child->size[main_axis]; } else diff --git a/game/src/ui/widget_ops.c b/game/src/ui/widget_ops.c index 03d2bb9..c914e11 100644 --- a/game/src/ui/widget_ops.c +++ b/game/src/ui/widget_ops.c @@ -67,12 +67,31 @@ uicb_widget_op (Widget *self, SDL_Event *sdl_event, void *userdata) case OP_ENTRY_INPUT_TEXT: Widget_entry_input_text (self, sdl_event->text.text); break; - case OP_ENTRY_DELETE_CHAR: + case OP_ENTRY_DELETE_BACKWARD: if (self->entry.cursor_position > 0 - && self->entry.content->len > 0) { - g_string_erase (self->entry.content, self->entry.cursor_position - 1, 1); - self->entry.cursor_position--; - } + && 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) @@ -81,7 +100,7 @@ uicb_widget_op (Widget *self, SDL_Event *sdl_event, void *userdata) } break; case OP_ENTRY_MOVE_RIGHT: - if (self->entry.cursor_position < self->entry.content->len) + if (self->entry.cursor_position < g_utf8_strlen (self->entry.content->str, -1)) { self->entry.cursor_position++; } diff --git a/game/src/ui/widget_ops.h b/game/src/ui/widget_ops.h index 7e041ff..ea990c0 100644 --- a/game/src/ui/widget_ops.h +++ b/game/src/ui/widget_ops.h @@ -20,7 +20,8 @@ typedef enum WidgetOp OP_START_TEXT_INPUT, OP_STOP_TEXT_INPUT, OP_ENTRY_INPUT_TEXT, - OP_ENTRY_DELETE_CHAR, + OP_ENTRY_DELETE_BACKWARD, + OP_ENTRY_DELETE_FORWARD, OP_ENTRY_MOVE_LEFT, OP_ENTRY_MOVE_RIGHT } WidgetOp;