Entry cursor
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
#version 100
|
||||
|
||||
precision mediump float;
|
||||
|
||||
uniform vec4 cursor_color;
|
||||
|
||||
varying vec2 f_uv;
|
||||
|
||||
void main ()
|
||||
{
|
||||
gl_FragColor = cursor_color;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#version 100
|
||||
|
||||
attribute vec3 position;
|
||||
attribute vec2 uv;
|
||||
|
||||
uniform mat3 model;
|
||||
uniform mat4 projection;
|
||||
|
||||
varying vec2 f_uv;
|
||||
|
||||
void
|
||||
main ()
|
||||
{
|
||||
f_uv = uv;
|
||||
gl_Position = projection * vec4 (model * vec3 (position.xy, 1.0), 1.0);
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
@@ -73,6 +73,7 @@ evtman_init ()
|
||||
evtman_add_handler (2, SDL_EVENT_WINDOW_RESIZED, NULL, NULL, uiman_cb_window_resize, NULL);
|
||||
/* The UI effectively eats most SDL events, as it needs it for it's
|
||||
own calculations. */
|
||||
evtman_add_handler (2, SDL_EVENT_KEY_DOWN, evt_pred_key, KEY (SDLK_TAB), uiman_cb_switch_focus, NULL);
|
||||
evtman_add_handler (1, SDL_EVENT_KEY_DOWN, evt_pred_flag, "ui_has_focus", uiman_cb_key_down, NULL);
|
||||
evtman_add_handler (1, SDL_EVENT_KEY_UP, evt_pred_flag, "ui_has_focus", uiman_cb_key_up, NULL);
|
||||
evtman_add_handler (1, SDL_EVENT_MOUSE_MOTION, evt_pred_flag, "ui_has_focus", uiman_cb_mouse_motion, NULL);
|
||||
@@ -89,7 +90,6 @@ evtman_init ()
|
||||
evtman_add_handler (0, SDL_EVENT_KEY_UP, evt_pred_key, KEY (SDLK_A), evt_cb_flag_off, "moving_left");
|
||||
evtman_add_handler (0, SDL_EVENT_KEY_DOWN, evt_pred_key, KEY (SDLK_D), evt_cb_flag_on, "moving_right");
|
||||
evtman_add_handler (0, SDL_EVENT_KEY_UP, evt_pred_key, KEY (SDLK_D), evt_cb_flag_off, "moving_right");
|
||||
evtman_add_handler (0, SDL_EVENT_KEY_DOWN, evt_pred_key, KEY (SDLK_TAB), uiman_cb_switch_focus, NULL);
|
||||
evtman_add_handler (0, SDL_EVENT_MOUSE_MOTION, NULL, NULL, evt_cb_rotate_camera, NULL);
|
||||
|
||||
evtman_add_handler (0, SDL_EVENT_KEY_DOWN, evt_pred_key, KEY (SDLK_SPACE), evt_cb_flag_on, "flying_up");
|
||||
|
||||
@@ -151,6 +151,7 @@ resources_init ()
|
||||
RAP ("clouds", RLP ("res/shaders/clouds.vert", "res/shaders/clouds.frag"));
|
||||
RAP ("uiwidget", RLP ("res/shaders/uiwidget.vert", "res/shaders/uiwidget.frag"));
|
||||
RAP ("uitext", RLP ("res/shaders/uitext.vert", "res/shaders/uitext.frag"));
|
||||
RAP ("uicursor", RLP ("res/shaders/uicursor.vert", "res/shaders/uicursor.frag"));
|
||||
|
||||
RAT ("terrain0", RLT ("res/textures/dirt.png"));
|
||||
RAT ("terrain1", RLT ("res/textures/drygrass.png"));
|
||||
|
||||
+63
-8
@@ -15,6 +15,7 @@ static Widget *ui_root;
|
||||
static mat4 projection;
|
||||
|
||||
static Widget *hovered_widget = NULL;
|
||||
static Widget *focused_widget = NULL;
|
||||
|
||||
const char *lorem_ipsum =
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut "
|
||||
@@ -70,6 +71,13 @@ uiman_init ()
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -92,11 +100,11 @@ uiman_free ()
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
_uiman_call_on_event (Widget *widget, WidgetEvent event)
|
||||
_uiman_call_on_event (Widget *widget, SDL_Event *sdl_event, WidgetEvent event)
|
||||
{
|
||||
if (widget && widget->on_event[event])
|
||||
{
|
||||
widget->on_event[event] (widget, widget->event_data[event]);
|
||||
widget->on_event[event] (widget, sdl_event, widget->event_data[event]);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -121,12 +129,29 @@ _uiman_set_hovered (Widget *widget, gboolean state)
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
_uiman_set_focused (Widget *widget, gboolean state)
|
||||
{
|
||||
if (widget)
|
||||
{
|
||||
widget->focused = state;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
uiman_cb_switch_focus (SDL_Event *event, void *data)
|
||||
{
|
||||
UNUSED (event, data);
|
||||
flags_switch ("ui_has_focus");
|
||||
SDL_SetWindowRelativeMouseMode (sdl_window, !flags_get ("ui_has_focus"));
|
||||
|
||||
/* We reset the global states of the UI. */
|
||||
SDL_StopTextInput (sdl_window);
|
||||
_uiman_set_hovered (hovered_widget, FALSE);
|
||||
hovered_widget = NULL;
|
||||
_uiman_set_focused (focused_widget, FALSE);
|
||||
focused_widget = NULL;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -143,6 +168,19 @@ gboolean
|
||||
uiman_cb_key_down (SDL_Event *event, void *data)
|
||||
{
|
||||
UNUSED (event, data);
|
||||
|
||||
SDL_Scancode sc = event->key.scancode;
|
||||
if (focused_widget && focused_widget->on_key[sc])
|
||||
{
|
||||
focused_widget->on_key[sc] (focused_widget, event, focused_widget->key_data[sc]);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (focused_widget && SDL_TextInputActive (sdl_window))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -162,15 +200,15 @@ uiman_cb_mouse_motion (SDL_Event *event, void *data)
|
||||
|
||||
if (new_hover != hovered_widget)
|
||||
{
|
||||
_uiman_call_on_event (hovered_widget, EVENT_MOUSE_LEAVE);
|
||||
_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_MOUSE_ENTER);
|
||||
_uiman_call_on_event (new_hover, event, EVENT_MOUSE_ENTER);
|
||||
_uiman_set_hovered (new_hover, TRUE);
|
||||
hovered_widget = new_hover;
|
||||
}
|
||||
|
||||
_uiman_call_on_event (hovered_widget, EVENT_MOUSE_MOVE);
|
||||
_uiman_call_on_event (hovered_widget, event, EVENT_MOUSE_MOVE);
|
||||
|
||||
/* This callback always eats the event, because why would the game
|
||||
need mouse motion if the UI has stolen the focus? */
|
||||
@@ -185,7 +223,17 @@ 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);
|
||||
return _uiman_call_on_event (widget, e);
|
||||
|
||||
if (widget != focused_widget)
|
||||
{
|
||||
_uiman_set_focused (focused_widget, FALSE);
|
||||
_uiman_call_on_event (focused_widget, event, EVENT_FOCUS_LOST);
|
||||
_uiman_set_focused (widget, TRUE);
|
||||
_uiman_call_on_event (widget, event, EVENT_FOCUS_GAINED);
|
||||
focused_widget = widget;
|
||||
}
|
||||
|
||||
return _uiman_call_on_event (widget, event, e);
|
||||
}
|
||||
|
||||
gboolean
|
||||
@@ -196,7 +244,7 @@ uiman_cb_button_up (SDL_Event *event, void *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, e);
|
||||
return _uiman_call_on_event (widget, event, e);
|
||||
}
|
||||
|
||||
gboolean
|
||||
@@ -206,12 +254,19 @@ uiman_cb_mouse_wheel (SDL_Event *event, void *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);
|
||||
return _uiman_call_on_event (widget, e);
|
||||
return _uiman_call_on_event (widget, event, e);
|
||||
}
|
||||
|
||||
gboolean
|
||||
uiman_cb_text_input (SDL_Event *event, void *data)
|
||||
{
|
||||
UNUSED (event, data);
|
||||
|
||||
if (focused_widget)
|
||||
{
|
||||
_uiman_call_on_event (focused_widget, event, EVENT_TEXT_INPUT);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ Widget *
|
||||
Widget_label_make_int (const char *fmt, int *ptr, TextAlignment alignment)
|
||||
{
|
||||
Widget *w = Widget_label_new (NULL, alignment);
|
||||
Widget_set_on_tick (w, uicb_widget_op, WidgetOpData_new (OP_LABEL_SHOW_INT, ptr, fmt));
|
||||
Widget_set_on_event (w, EVENT_TICK, uicb_widget_op, WidgetOpData_new (OP_LABEL_SHOW_INT, ptr, fmt));
|
||||
return w;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ Widget *
|
||||
Widget_label_make_float (const char *fmt, float *ptr, TextAlignment alignment)
|
||||
{
|
||||
Widget *w = Widget_label_new (NULL, alignment);
|
||||
Widget_set_on_tick (w, uicb_widget_op, WidgetOpData_new (OP_LABEL_SHOW_FLOAT, ptr, fmt));
|
||||
Widget_set_on_event (w, EVENT_TICK, uicb_widget_op, WidgetOpData_new (OP_LABEL_SHOW_FLOAT, ptr, fmt));
|
||||
return w;
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ Widget_composite_checkbox_make (const char *label, char *flag)
|
||||
|
||||
box->icon = ICON_CHECKBOX_UNCHECKED;
|
||||
box->aspect_ratio = 1.0;
|
||||
Widget_set_on_tick (box, uicb_widget_op, WidgetOpData_new (OP_CHECKBOX_SHOW_FLAG, flag));
|
||||
Widget_set_on_event (box, EVENT_TICK, uicb_widget_op, WidgetOpData_new (OP_CHECKBOX_SHOW_FLAG, flag));
|
||||
Widget_set_on_event (box, EVENT_LEFT_UP, uicb_widget_op, WidgetOpData_new (OP_SWITCH_FLAG, flag));
|
||||
|
||||
Widget_add_child_weighed (cont, box, 1);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
/* #include "widget_fw.h" */
|
||||
#include "widget_fw.h"
|
||||
#include "widget_core.h"
|
||||
#include "widget_ops.h"
|
||||
|
||||
|
||||
+36
-16
@@ -20,6 +20,7 @@ WidgetStyle widget_style_default =
|
||||
.text_color = { 1.0, 1.0, 1.0, 1.0 },
|
||||
.text_color_hovered = { 1.0, 1.0, 1.0, 1.0 },
|
||||
.text_color_clicked = { 1.0, 1.0, 1.0, 1.0 },
|
||||
.ghost_text_color = { 1.0, 1.0, 1.0, 0.5 },
|
||||
|
||||
.icon_color = { 1.0, 1.0, 1.0, 1.0 },
|
||||
.icon_color_hovered = { 1.0, 1.0, 1.0, 1.0 },
|
||||
@@ -123,6 +124,22 @@ Widget_multiline_text_new (const char *text, float line_height, TextAlignment al
|
||||
return e;
|
||||
}
|
||||
|
||||
Widget *
|
||||
Widget_entry_new (const char *ghost_text)
|
||||
{
|
||||
Widget *e = ui_el_new_internal (WIDGET_ENTRY);
|
||||
e->do_render = Widget_render_entry;
|
||||
e->entry.ghost_text = g_string_new (ghost_text);
|
||||
e->entry.content = g_string_new (NULL);
|
||||
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_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;
|
||||
}
|
||||
|
||||
Widget *
|
||||
Widget_container_new (gboolean horizontal)
|
||||
{
|
||||
@@ -174,22 +191,23 @@ Widget_add_child_weighed (Widget *parent, Widget *child, float layout_weight)
|
||||
Widget_add_child (parent, child);
|
||||
}
|
||||
|
||||
void
|
||||
Widget_set_on_tick (Widget *widget,
|
||||
void (*on_tick) (Widget *, void *), void *userdata)
|
||||
{
|
||||
widget->on_tick = on_tick;
|
||||
widget->tick_data = userdata;
|
||||
}
|
||||
|
||||
void
|
||||
Widget_set_on_event (Widget *widget, WidgetEvent event,
|
||||
void (*on_event) (Widget *, void *), void *userdata)
|
||||
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_set_style (Widget *widget, WidgetStyle style)
|
||||
{
|
||||
@@ -211,6 +229,13 @@ Widget_label_printf (Widget *widget, const char *format, ...)
|
||||
va_end (args);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void
|
||||
Widget_scrollable_scroll (Widget *widget, vec2 direction)
|
||||
{
|
||||
@@ -288,9 +313,9 @@ Widget_find_xy (Widget *widget, vec2 pos)
|
||||
void
|
||||
Widget_tick (Widget *widget)
|
||||
{
|
||||
if (widget->on_tick)
|
||||
if (widget->on_event[EVENT_TICK])
|
||||
{
|
||||
widget->on_tick (widget, widget->tick_data);
|
||||
widget->on_event[EVENT_TICK] (widget, NULL, widget->event_data[EVENT_TICK]);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < widget->children->len; ++i)
|
||||
@@ -341,11 +366,6 @@ Widget_free (Widget *widget)
|
||||
g_string_free (widget->label.text, TRUE);
|
||||
}
|
||||
|
||||
if (widget->on_tick && widget->tick_data)
|
||||
{
|
||||
g_free (widget->tick_data);
|
||||
}
|
||||
|
||||
for (int ev = 0; ev < EVENT_COUNT; ++ev)
|
||||
{
|
||||
if (widget->on_event[ev] && widget->event_data[ev])
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <glib-2.0/glib.h>
|
||||
#include <cglm/cglm.h>
|
||||
|
||||
#include "widget_fw.h"
|
||||
#include "widget_render.h"
|
||||
#include "widget_layout.h"
|
||||
#include "widget_style.h"
|
||||
@@ -24,6 +25,7 @@ typedef enum WidgetKind
|
||||
WIDGET_LABEL,
|
||||
WIDGET_SCROLLBAR,
|
||||
WIDGET_PROGRESS,
|
||||
WIDGET_ENTRY,
|
||||
|
||||
WIDGET_MULTILINE_TEXT,
|
||||
|
||||
@@ -48,7 +50,8 @@ typedef enum WidgetIcon
|
||||
typedef enum WidgetEvent
|
||||
{
|
||||
EVENT_NONE = -1, /* Don't use. */
|
||||
EVENT_LEFT_DOWN = 0,
|
||||
EVENT_TICK = 0,
|
||||
EVENT_LEFT_DOWN,
|
||||
EVENT_LEFT_UP,
|
||||
EVENT_RIGHT_DOWN,
|
||||
EVENT_RIGHT_UP,
|
||||
@@ -57,6 +60,9 @@ typedef enum WidgetEvent
|
||||
EVENT_MOUSE_MOVE,
|
||||
EVENT_SCROLL_UP,
|
||||
EVENT_SCROLL_DOWN,
|
||||
EVENT_FOCUS_GAINED,
|
||||
EVENT_FOCUS_LOST,
|
||||
EVENT_TEXT_INPUT,
|
||||
|
||||
EVENT_COUNT
|
||||
} WidgetEvent;
|
||||
@@ -97,6 +103,13 @@ typedef struct MultilineTextWidget
|
||||
TextAlignment text_alignment;
|
||||
} MultilineTextWidget;
|
||||
|
||||
typedef struct EntryWidget
|
||||
{
|
||||
GString *ghost_text;
|
||||
GString *content;
|
||||
float cursor_position;
|
||||
} EntryWidget;
|
||||
|
||||
typedef struct ContainerWidget
|
||||
{
|
||||
gboolean horizontal;
|
||||
@@ -128,6 +141,7 @@ typedef struct Widget
|
||||
ScrollbarWidget scrollbar;
|
||||
ProgressWidget progress;
|
||||
MultilineTextWidget multiline_text;
|
||||
EntryWidget entry;
|
||||
|
||||
ContainerWidget container;
|
||||
ScrollableWidget scrollable;
|
||||
@@ -136,7 +150,7 @@ typedef struct Widget
|
||||
|
||||
gboolean visible;
|
||||
gboolean layout_dirty;
|
||||
gboolean hovered, clicked;
|
||||
gboolean hovered, clicked, focused;
|
||||
|
||||
vec2 position;
|
||||
vec2 size;
|
||||
@@ -149,11 +163,11 @@ typedef struct Widget
|
||||
void (*do_layout) (struct Widget *);
|
||||
void (*do_render) (struct Widget *, mat4, int);
|
||||
|
||||
void (*on_tick) (struct Widget *, void *);
|
||||
void *tick_data;
|
||||
|
||||
void (*on_event[EVENT_COUNT]) (struct Widget *, void *);
|
||||
void (*on_event[EVENT_COUNT]) (struct Widget *, SDL_Event *sdl_event, void *);
|
||||
void *event_data[EVENT_COUNT];
|
||||
|
||||
void (*on_key[SDL_SCANCODE_COUNT]) (struct Widget *, SDL_Event *null, void *);
|
||||
void *key_data[SDL_SCANCODE_COUNT];
|
||||
} Widget;
|
||||
|
||||
Widget *Widget_plain_new ();
|
||||
@@ -162,6 +176,7 @@ Widget *Widget_label_new (const char *text, TextAlignment alignment);
|
||||
Widget *Widget_scrollbar_new (Widget *target, gboolean horizontal);
|
||||
Widget *Widget_progress_new (gboolean horizontal, float min, float max, float *ptr);
|
||||
Widget *Widget_multiline_text_new (const char *text, float line_height, TextAlignment alignment);
|
||||
Widget *Widget_entry_new (const char *ghost_text);
|
||||
Widget *Widget_container_new (gboolean horizontal);
|
||||
Widget *Widget_scrollable_new ();
|
||||
Widget *Widget_flowbox_new (gboolean horizontal);
|
||||
@@ -170,13 +185,15 @@ void Widget_add_child (Widget *parent, Widget *child);
|
||||
void Widget_add_child_weighed (Widget *parent, Widget *child,
|
||||
float layout_weight);
|
||||
|
||||
void Widget_set_on_tick (Widget *widget, void (*on_tick) (Widget *, void *), void *userdata);
|
||||
void Widget_set_on_event (Widget *widget, WidgetEvent event, void (*on_event) (Widget *, void *), void *userdata);
|
||||
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_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_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);
|
||||
|
||||
+38
-10
@@ -1,5 +1,7 @@
|
||||
#include "widget_ops.h"
|
||||
|
||||
extern SDL_Window *sdl_window;
|
||||
|
||||
WidgetOpData *
|
||||
WidgetOpData_new (WidgetOp op, void *ptr, ...)
|
||||
{
|
||||
@@ -31,8 +33,9 @@ WidgetOpData_new (WidgetOp op, void *ptr, ...)
|
||||
}
|
||||
|
||||
void
|
||||
uicb_widget_op (Widget *self, void *userdata)
|
||||
uicb_widget_op (Widget *self, SDL_Event *sdl_event, void *userdata)
|
||||
{
|
||||
UNUSED (sdl_event);
|
||||
WidgetOpData *data = userdata;
|
||||
|
||||
switch (data->op)
|
||||
@@ -55,6 +58,34 @@ uicb_widget_op (Widget *self, void *userdata)
|
||||
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_CHAR:
|
||||
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--;
|
||||
}
|
||||
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 < self->entry.content->len)
|
||||
{
|
||||
self->entry.cursor_position++;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -63,14 +94,12 @@ uicb_widget_op (Widget *self, void *userdata)
|
||||
/* Scrollbar boilerplate. */
|
||||
|
||||
void
|
||||
uicb_scrollbar_start_scroll (Widget *self, void *userdata)
|
||||
uicb_scrollbar_start_scroll (Widget *self, SDL_Event *sdl_event, void *userdata)
|
||||
{
|
||||
UNUSED (userdata);
|
||||
|
||||
Widget *scr = self->scrollbar.target;
|
||||
vec2 mouse_pos;
|
||||
SDL_GetMouseState ((float*) &mouse_pos[0], (float*) &mouse_pos[1]);
|
||||
|
||||
vec2 mouse_pos = { sdl_event->button.x, sdl_event->button.y };
|
||||
unsigned int idx = self->scrollbar.horizontal ? 0 : 1;
|
||||
|
||||
float content_size = scr->scrollable.content_size[idx];
|
||||
@@ -97,7 +126,7 @@ uicb_scrollbar_start_scroll (Widget *self, void *userdata)
|
||||
}
|
||||
|
||||
void
|
||||
uicb_scrollbar_scroll (Widget *self, void *userdata)
|
||||
uicb_scrollbar_scroll (Widget *self, SDL_Event *sdl_event, void *userdata)
|
||||
{
|
||||
UNUSED (userdata);
|
||||
|
||||
@@ -107,8 +136,7 @@ uicb_scrollbar_scroll (Widget *self, void *userdata)
|
||||
}
|
||||
|
||||
Widget *scr = self->scrollbar.target;
|
||||
vec2 mouse_pos;
|
||||
SDL_GetMouseState((float*) &mouse_pos[0], (float*) &mouse_pos[1]);
|
||||
vec2 mouse_pos = { sdl_event->motion.x, sdl_event->motion.y };
|
||||
|
||||
unsigned int idx = self->scrollbar.horizontal ? 0 : 1;
|
||||
float content_size = scr->scrollable.content_size[idx];
|
||||
@@ -129,8 +157,8 @@ uicb_scrollbar_scroll (Widget *self, void *userdata)
|
||||
}
|
||||
|
||||
void
|
||||
uicb_scrollbar_stop_scroll (Widget *self, void *userdata)
|
||||
uicb_scrollbar_stop_scroll (Widget *self, SDL_Event *sdl_event, void *userdata)
|
||||
{
|
||||
UNUSED (userdata);
|
||||
UNUSED (sdl_event, userdata);
|
||||
self->scrollbar.dragging = FALSE;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include "widget_fw.h"
|
||||
#include "widget_core.h"
|
||||
|
||||
@@ -7,10 +9,20 @@ typedef enum WidgetOp
|
||||
{
|
||||
OP_SWITCH_FLAG,
|
||||
OP_MODIFY_INT,
|
||||
|
||||
OP_LABEL_SHOW_INT,
|
||||
OP_LABEL_SHOW_FLOAT,
|
||||
|
||||
OP_CHECKBOX_SHOW_FLAG,
|
||||
OP_SCROLLABLE_SCROLL
|
||||
|
||||
OP_SCROLLABLE_SCROLL,
|
||||
|
||||
OP_START_TEXT_INPUT,
|
||||
OP_STOP_TEXT_INPUT,
|
||||
OP_ENTRY_INPUT_TEXT,
|
||||
OP_ENTRY_DELETE_CHAR,
|
||||
OP_ENTRY_MOVE_LEFT,
|
||||
OP_ENTRY_MOVE_RIGHT
|
||||
} WidgetOp;
|
||||
|
||||
typedef struct WidgetOpData
|
||||
@@ -25,8 +37,8 @@ typedef struct WidgetOpData
|
||||
} WidgetOpData;
|
||||
|
||||
WidgetOpData *WidgetOpData_new (WidgetOp op, void *ptr, ...);
|
||||
void uicb_widget_op (Widget *self, void *userdata);
|
||||
void uicb_widget_op (Widget *self, SDL_Event *sdl_event, void *userdata);
|
||||
|
||||
void uicb_scrollbar_start_scroll (Widget *self, void *userdata);
|
||||
void uicb_scrollbar_scroll (Widget *self, void *userdata);
|
||||
void uicb_scrollbar_stop_scroll (Widget *self, 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);
|
||||
|
||||
+103
-99
@@ -15,6 +15,50 @@ _Widget_get_aabb (Widget *self, vec2 aabb[2])
|
||||
glm_vec2_copy ((vec2) { self->position[0] + self->size[0], self->position[1] + self->size[1] }, aabb[1]);
|
||||
}
|
||||
|
||||
static inline void
|
||||
_Widget_render_text_line(const char *text, TextAlignment alignment, vec2 position,
|
||||
vec2 size, float max_width, vec4 color, mat4 projection)
|
||||
{
|
||||
program_use (RESPRG ("uitext"));
|
||||
program_set_mat4(RESPRG ("uitext"), "projection", projection);
|
||||
program_set_vec4(RESPRG ("uitext"), "color", color);
|
||||
|
||||
int nchars = g_utf8_strlen (text, -1);
|
||||
float total_w = nchars * size[0];
|
||||
|
||||
float xoff = 0.0;
|
||||
|
||||
switch (alignment)
|
||||
{
|
||||
case TEXT_ALIGN_CENTER:
|
||||
xoff = (max_width - total_w) * 0.5;
|
||||
break;
|
||||
case TEXT_ALIGN_RIGHT:
|
||||
xoff = max_width - total_w;
|
||||
break;
|
||||
case TEXT_ALIGN_LEFT:
|
||||
default:
|
||||
xoff = 0.0;
|
||||
}
|
||||
|
||||
const char *p = text;
|
||||
while (*p)
|
||||
{
|
||||
gunichar ch = g_utf8_get_char (p);
|
||||
program_set_float (RESPRG ("uitext"), "character", ch);
|
||||
|
||||
mat3 model;
|
||||
glm_translate2d_make (model, (vec2) { position[0] + xoff, position[1] });
|
||||
glm_scale2d (model, size);
|
||||
program_set_mat3 (RESPRG ("uitext"), "model", model);
|
||||
|
||||
SimpleMesh_render (&quad, FALSE);
|
||||
|
||||
xoff += size[0];
|
||||
p = g_utf8_next_char (p);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Widget_render_base (Widget *self, mat4 projection, int window_height)
|
||||
{
|
||||
@@ -94,58 +138,15 @@ Widget_render_label (Widget *self, mat4 projection, int window_height)
|
||||
{
|
||||
Widget_render_base (self, projection, window_height);
|
||||
|
||||
float char_w = self->size[1] * (9.0f / 20.0f);
|
||||
vec2 pos = { self->position[0], self->position[1] };
|
||||
|
||||
WidgetStyle *style = self->style;
|
||||
|
||||
program_use (RESPRG ("uitext"));
|
||||
program_set_mat4 (RESPRG ("uitext"), "projection", projection);
|
||||
texture_use (style->font_bitmap, 0);
|
||||
|
||||
program_set_vec4 (RESPRG ("uitext"), "color", style->text_color);
|
||||
|
||||
if (self->clicked)
|
||||
{
|
||||
program_set_vec4 (RESPRG ("uitext"), "color", style->text_color_clicked);
|
||||
}
|
||||
else if (self->hovered)
|
||||
{
|
||||
program_set_vec4 (RESPRG ("uitext"), "color", style->text_color_hovered);
|
||||
}
|
||||
|
||||
float char_width = self->size[1] * (9.0 / 20.0);
|
||||
float total_width = g_utf8_strlen (self->label.text->str, -1) * char_width;
|
||||
float offset = 0.0;
|
||||
|
||||
switch (self->label.text_alignment)
|
||||
{
|
||||
case TEXT_ALIGN_LEFT:
|
||||
offset = -char_width;
|
||||
break;
|
||||
case TEXT_ALIGN_CENTER:
|
||||
offset = (self->size[0] / 2) - (total_width / 2.0) -char_width;
|
||||
break;
|
||||
case TEXT_ALIGN_RIGHT:
|
||||
offset = self->size[0] - total_width - char_width;
|
||||
break;
|
||||
}
|
||||
|
||||
const char *p = self->label.text->str;
|
||||
while (*p)
|
||||
{
|
||||
gunichar ch = g_utf8_get_char (p);
|
||||
|
||||
program_set_float (RESPRG ("uitext"), "character", ch);
|
||||
|
||||
mat3 model;
|
||||
glm_translate2d_make (model, (vec2) { self->position[0] + offset + char_width, self->position[1] });
|
||||
glm_scale2d (model, (vec2) { char_width, self->size[1] });
|
||||
offset += char_width;
|
||||
|
||||
program_set_mat3 (RESPRG ("uitext"), "model", model);
|
||||
|
||||
SimpleMesh_render (&quad, FALSE);
|
||||
|
||||
p = g_utf8_next_char (p);
|
||||
}
|
||||
_Widget_render_text_line (self->label.text->str, self->label.text_alignment,
|
||||
pos, (vec2) { char_w, self->size[1] }, self->size[0],
|
||||
self->clicked ? style->text_color_clicked : self->hovered
|
||||
? style->text_color_hovered : style->text_color, projection);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -212,67 +213,70 @@ Widget_render_progress (Widget *self, mat4 projection, int window_height)
|
||||
void
|
||||
Widget_render_multiline_text (Widget *self, mat4 projection, int window_height)
|
||||
{
|
||||
Widget_render_base(self, projection, window_height);
|
||||
Widget_render_base (self, projection, window_height);
|
||||
|
||||
WidgetStyle *style = self->style;
|
||||
|
||||
program_use (RESPRG ("uitext"));
|
||||
program_set_mat4 (RESPRG ("uitext"), "projection", projection);
|
||||
texture_use (style->font_bitmap, 0);
|
||||
program_set_vec4 (RESPRG ("uitext"), "color", style->text_color);
|
||||
|
||||
float pad = style->layout_padding;
|
||||
guint lines = self->multiline_text.lines->len;
|
||||
|
||||
if (lines == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float row_h = self->multiline_text.line_height;
|
||||
float char_w = row_h * (9.0 / 20.0);
|
||||
float x0 = self->position[0] + pad;
|
||||
float y0 = self->position[1] + pad;
|
||||
float usable = self->size[0] - 2 * pad;
|
||||
vec2 pos0 = { self->position[0] + pad, self->position[1] + pad };
|
||||
|
||||
for (guint i = 0; i < lines; ++i)
|
||||
for (unsigned int i = 0; i < self->multiline_text.lines->len; ++i)
|
||||
{
|
||||
GString *line = g_array_index (self->multiline_text.lines, GString *, i);
|
||||
guint nchars = g_utf8_strlen (line->str, -1);
|
||||
float line_w = nchars * char_w;
|
||||
vec2 pos = { pos0[0], pos0[1] + i * row_h };
|
||||
_Widget_render_text_line(line->str, self->multiline_text.text_alignment, pos,
|
||||
(vec2) { char_w, row_h }, self->size[0] - 2 * pad,
|
||||
style->text_color, projection);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Widget_render_entry (Widget *self, mat4 projection, int window_height)
|
||||
{
|
||||
Widget_render_base (self, projection, window_height);
|
||||
|
||||
WidgetStyle *style = self->style;
|
||||
texture_use (style->font_bitmap, 0);
|
||||
|
||||
float char_w = self->size[1] * (9.0f / 20.0f);
|
||||
float cursor_in_px = self->entry.cursor_position * char_w;
|
||||
/* -char_w because we leave space for the cursor at the end of the
|
||||
text. */
|
||||
float offset = glm_max (0, cursor_in_px - (self->size[0] - char_w));
|
||||
|
||||
if (!self->focused && self->entry.content->len == 0)
|
||||
{
|
||||
_Widget_render_text_line (self->entry.ghost_text->str, TEXT_ALIGN_LEFT,
|
||||
self->position, (vec2) { char_w, self->size[1] }, self->size[0],
|
||||
style->ghost_text_color, projection);
|
||||
}
|
||||
else
|
||||
{
|
||||
vec2 pos = { self->position[0] - offset, self->position[1] };
|
||||
_Widget_render_text_line (self->entry.content->str, TEXT_ALIGN_LEFT,
|
||||
pos, (vec2) { char_w, self->size[1] }, self->size[0],
|
||||
style->text_color, projection);
|
||||
}
|
||||
|
||||
if (self->focused)
|
||||
{
|
||||
float x = self->position[0] + cursor_in_px - offset;
|
||||
float y = self->position[1];
|
||||
float w = 2.0;
|
||||
float h = self->size[1];
|
||||
|
||||
float y = y0 + i * row_h;
|
||||
float x;
|
||||
mat3 model;
|
||||
glm_translate2d_make(model, (vec2) { x, y });
|
||||
glm_scale2d(model, (vec2) { w, h });
|
||||
|
||||
switch (self->multiline_text.text_alignment)
|
||||
{
|
||||
case TEXT_ALIGN_CENTER:
|
||||
x = x0 + (usable - line_w) * 0.5;
|
||||
break;
|
||||
case TEXT_ALIGN_RIGHT:
|
||||
x = x0 + (usable - line_w);
|
||||
break;
|
||||
case TEXT_ALIGN_LEFT:
|
||||
default:
|
||||
x = x0;
|
||||
break;
|
||||
}
|
||||
program_use (RESPRG ("uicursor"));
|
||||
program_set_mat3 (RESPRG ("uicursor"), "model", model);
|
||||
program_set_mat4 (RESPRG ("uicursor"), "projection", projection);
|
||||
program_set_vec4 (RESPRG ("uicursor"), "cursor_color", style->text_color);
|
||||
|
||||
const char *p = line->str;
|
||||
while (*p)
|
||||
{
|
||||
gunichar ch = g_utf8_get_char (p);
|
||||
program_set_float (RESPRG ("uitext"), "character", ch);
|
||||
|
||||
mat3 model;
|
||||
glm_translate2d_make (model, (vec2) { x, y });
|
||||
glm_scale2d (model, (vec2) { char_w, row_h });
|
||||
program_set_mat3 (RESPRG ("uitext"), "model", model);
|
||||
|
||||
SimpleMesh_render (&quad, FALSE);
|
||||
|
||||
x += char_w;
|
||||
p = g_utf8_next_char (p);
|
||||
}
|
||||
SimpleMesh_render(&quad, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,3 +12,4 @@ void Widget_render_label (Widget *self, mat4 projection, int window_height);
|
||||
void Widget_render_scrollbar (Widget *self, mat4 projection, int window_height);
|
||||
void Widget_render_progress (Widget *self, mat4 projection, int window_height);
|
||||
void Widget_render_multiline_text (Widget *self, mat4 projection, int window_height);
|
||||
void Widget_render_entry (Widget *self, mat4 projection, int window_height);
|
||||
|
||||
@@ -12,6 +12,7 @@ typedef struct WidgetStyle
|
||||
vec4 text_color;
|
||||
vec4 text_color_hovered;
|
||||
vec4 text_color_clicked;
|
||||
vec4 ghost_text_color;
|
||||
|
||||
vec4 icon_color;
|
||||
vec4 icon_color_hovered;
|
||||
|
||||
Reference in New Issue
Block a user