diff --git a/game/res/ui/debug_window.scm b/game/res/ui/debug_window.scm new file mode 100644 index 0000000..fa5c39e --- /dev/null +++ b/game/res/ui/debug_window.scm @@ -0,0 +1,78 @@ +(define COLOR_TRANSPARENT '(0.0 0.0 0.0 0.0)) +(define COLOR_WHITE '(1.0 1.0 1.0 1.0)) +(define COLOR_BLACK '(0.0 0.0 0.0 1.0)) + +(define style-default + (style-new #:text-color COLOR_WHITE + #:text-color-hovered COLOR_WHITE + #:text-color-clicked COLOR_WHITE + #:ghost-text-color '(1.0 1.0 1.0 0.5) + #:icon-color COLOR_WHITE + #:icon-color-hovered COLOR_WHITE + #:icon-color-clicked COLOR_WHITE + #:scrollbar-color COLOR_WHITE + #:scrollbar-color-hovered COLOR_WHITE + #:scrollbar-color-clicked COLOR_WHITE + #:border-color COLOR_WHITE + #:progress-color COLOR_WHITE + #:resize-handle-color COLOR_WHITE)) + +(define style-window + (style-new #:background-color '(0.2 0.2 0.2 1.0) + #:background-color-hovered '(0.2 0.2 0.2 1.0) + #:background-color-clicked '(0.3 0.3 0.3 1.0) + #:text-color COLOR_WHITE + #:text-color-hovered COLOR_WHITE + #:text-color-clicked COLOR_WHITE + #:border-color '(0.5 0.5 0.5 1.0) + #:resize-handle-color COLOR_WHITE + #:border-width 3)) + +(define style-container + (style-new #:background-color '(0.3 0.3 0.3 1.0) + #:background-color-hovered '(0.3 0.3 0.3 1.0) + #:background-color-clicked '(0.3 0.3 0.3 1.0) + #:border-color '(0.4 0.4 0.4 1.0) + #:border-width 2)) + +(define style-icon-button + (style-new #:icon-color COLOR_WHITE + #:icon-color-hovered '(1.0 1.0 0.0 1.0) + #:icon-color-clicked '(1.0 0.0 0.0 1.0))) + + +(define (widget-checkbox-icon-switch widget event) + (if (= (widget-get-icon widget) 1) + (widget-set-icon! widget 2) + (widget-set-icon! widget 1))) + +;; The function is evaulated and returns a pointer, the game eats it +;; and adds it to the UI root. Boom. +(define (widget-checkbox text flag . args) + (apply + widget-new + (append + (list 'container #:horizontal #t #:layout-spacing 8) + args + (list + (widget-new 'plain #:style style-icon-button #:icon 1 #:aspect-ratio 1 + #:on-click (lambda (w e) (widget-checkbox-icon-switch w e) (flags-switch flag))) + (widget-new 'label #:style style-default #:text text #:layout-weight 1))))) + +(widget-new + 'window + #:style style-window + #:text "Debug window" + #:w 350 + #:layout-padding 8 + #:vertical #f + #:horizontal #t + ;; The rest is children. + (widget-new + 'flowbox + #:style style-container + #: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))) diff --git a/game/src/flags.c b/game/src/flags.c index c21f466..d2121f9 100644 --- a/game/src/flags.c +++ b/game/src/flags.c @@ -8,20 +8,53 @@ flags_init () flags = g_hash_table_new (g_str_hash, g_str_equal); } +static SCM +_scm_flags_set (SCM flag_scm, SCM val_scm) +{ + const char *flag = scm_to_utf8_string (flag_scm); + gboolean val = scm_is_true (val_scm); + flags_set (flag, val); + return SCM_UNSPECIFIED; +} + +static SCM +_scm_flags_get (SCM flag_scm) +{ + const char *flag = scm_to_utf8_string (flag_scm); + gboolean val = flags_get (flag); + return val ? SCM_BOOL_T : SCM_BOOL_F; +} + +static SCM +_scm_flags_switch (SCM flag_scm) +{ + const char *flag = scm_to_utf8_string (flag_scm); + flags_switch (flag); + return SCM_UNSPECIFIED; +} + void -flags_switch (char *flag) +flags_guile_register () +{ + scm_c_define_gsubr("flags-set", 2, 0, 0, _scm_flags_set); + scm_c_define_gsubr("flags-get", 1, 0, 0, _scm_flags_get); + scm_c_define_gsubr("flags-switch", 1, 0, 0, _scm_flags_switch); +} + +void +flags_switch (const char *flag) { flags_set (flag, !flags_get (flag)); } void -flags_set (char *flag, gboolean value) +flags_set (const char *flag, gboolean value) { - g_hash_table_replace (flags, flag, GINT_TO_POINTER (value)); + g_hash_table_replace (flags, g_strdup (flag), GINT_TO_POINTER (value)); } gboolean -flags_get (char *flag) +flags_get (const char *flag) { return GPOINTER_TO_INT (g_hash_table_lookup (flags, flag)); } diff --git a/game/src/flags.h b/game/src/flags.h index 7b86b94..c721b1f 100644 --- a/game/src/flags.h +++ b/game/src/flags.h @@ -1,6 +1,7 @@ #pragma once #include +#include /* @@ -23,6 +24,7 @@ */ void flags_init (); -void flags_switch (char *flag); -void flags_set (char *flag, gboolean value); -gboolean flags_get (char *flag); +void flags_guile_register (); +void flags_switch (const char *flag); +void flags_set (const char *flag, gboolean value); +gboolean flags_get (const char *flag); diff --git a/game/src/main.c b/game/src/main.c index 949d81e..634a307 100644 --- a/game/src/main.c +++ b/game/src/main.c @@ -20,6 +20,7 @@ SDL_AppInit (void **appstate, int argc, char **argv) SDL_SetAppMetadata (GAME_NAME, GAME_VERSION, "eur.chopininteractive.wschod-game"); scm_init_guile (); + flags_guile_register (); widget_guile_register (); graphics_init (); diff --git a/game/src/ui/uiman.c b/game/src/ui/uiman.c index 318aac0..e6c6dd7 100644 --- a/game/src/ui/uiman.c +++ b/game/src/ui/uiman.c @@ -3,14 +3,6 @@ extern SDL_Window *sdl_window; extern int sdl_window_w, sdl_window_h; -/* Not really required for the functioning of the UI system. */ - -extern float delta_time; -extern int n_frame_ents; -extern int n_frame_chunks; -extern int render_distance; -extern vec3 camera_position; - /* UI global state variables. */ static Widget *ui_root; @@ -29,8 +21,13 @@ uiman_init () ui_widget_mesh_init (); glm_ortho (0.0, sdl_window_w, sdl_window_h, 0.0, -1.0, 1.0, projection); - ui_root = Widget_plain_new (); + 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"); + Widget *debug_window = (Widget *) scm_to_pointer (scm_debug); + Widget_add_child (ui_root, debug_window); + Widget_dump (ui_root, 0); } void diff --git a/game/src/ui/uiman.h b/game/src/ui/uiman.h index 996b006..07c91d9 100644 --- a/game/src/ui/uiman.h +++ b/game/src/ui/uiman.h @@ -1,7 +1,8 @@ #pragma once +#include + #include "widget_core.h" -#include "widget_composites.h" #include "../flags.h" #include "../state.h" #include "../camera.h" diff --git a/game/src/ui/widget_composites.c b/game/src/ui/widget_composites.c deleted file mode 100644 index e835c57..0000000 --- a/game/src/ui/widget_composites.c +++ /dev/null @@ -1,63 +0,0 @@ -#include "widget_composites.h" - -Widget * -Widget_label_make_int (const char *fmt, int *ptr, TextAlignment alignment) -{ - Widget *w = Widget_label_new (NULL, alignment); - Widget_set_on_event (w, EVENT_TICK, uicb_widget_op, WidgetOpData_new (OP_LABEL_SHOW_INT, ptr, fmt)); - return w; -} - -Widget * -Widget_label_make_float (const char *fmt, float *ptr, TextAlignment alignment) -{ - Widget *w = Widget_label_new (NULL, alignment); - Widget_set_on_event (w, EVENT_TICK, uicb_widget_op, WidgetOpData_new (OP_LABEL_SHOW_FLOAT, ptr, fmt)); - return w; -} - -Widget * -Widget_composite_spinner_make_int (const char *fmt, int *ptr) -{ - Widget *cont = Widget_container_new (TRUE); - Widget *left = Widget_plain_new (); - Widget *label = Widget_label_make_int (fmt, ptr, TEXT_ALIGN_CENTER); - Widget *right = Widget_plain_new (); - - left->style = WSTYLE (STYLE_ICON_BUTTON); - right->style = WSTYLE (STYLE_ICON_BUTTON); - - 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_UP, uicb_widget_op, WidgetOpData_new (OP_MODIFY_INT, ptr, 1.0)); - right->icon = ICON_PLUS; - right->aspect_ratio = 1.0; - - cont->layout_spacing = 8; - Widget_add_child_weighed (cont, left, 1); - Widget_add_child_weighed (cont, label, 1); - Widget_add_child_weighed (cont, right, 1); - - return cont; -} - -Widget * -Widget_composite_checkbox_make (const char *label, char *flag) -{ - Widget *cont = Widget_container_new (TRUE); - Widget *box = Widget_plain_new (); - - box->style = WSTYLE (STYLE_ICON_BUTTON); - - box->icon = ICON_CHECKBOX_UNCHECKED; - box->aspect_ratio = 1.0; - 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)); - - cont->layout_spacing = 8; - Widget_add_child_weighed (cont, box, 1); - Widget_add_child_weighed (cont, Widget_label_new (label, TEXT_ALIGN_LEFT), 1); - - return cont; -} diff --git a/game/src/ui/widget_composites.h b/game/src/ui/widget_composites.h deleted file mode 100644 index f1c680a..0000000 --- a/game/src/ui/widget_composites.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "widget_fw.h" -#include "widget_core.h" -#include "widget_ops.h" - -Widget *Widget_label_make_int (const char *fmt, int *ptr, TextAlignment alignment); -Widget *Widget_label_make_float (const char *fmt, float *ptr, TextAlignment alignment); - -Widget *Widget_composite_spinner_make_int (const char *format, int *ptr); -Widget *Widget_composite_checkbox_make (const char *label, char *flag); - -Widget *Widget_composite_window_make (const char *title); diff --git a/game/src/ui/widget_core.c b/game/src/ui/widget_core.c index ced3586..297a113 100644 --- a/game/src/ui/widget_core.c +++ b/game/src/ui/widget_core.c @@ -1,145 +1,14 @@ #include "widget_core.h" -static const Widget DEFAULT_WIDGET = - { - .kind = WIDGET_PLAIN, - .do_layout = NULL, - .do_render = Widget_render_base, - .visible = true, - .layout_dirty = true, - .children = NULL, - .icon = ICON_NONE - }; - -static inline Widget * -_uiel_new (WidgetKind kind) -{ - /* new0 instead of new is important, otherwise there's trash memory - in the function pointers */ - Widget *e = g_new0 (Widget, 1); - *e = DEFAULT_WIDGET; - e->kind = kind; - e->children = g_array_new (false, false, sizeof (Widget *)); - e->style = WSTYLE (STYLE_DEFAULT); - return e; -} - Widget * -Widget_plain_new () -{ - return _uiel_new (WIDGET_PLAIN); -} - -Widget * -Widget_label_new (const char *text, TextAlignment alignment) +Widget_new () { - Widget *e = _uiel_new (WIDGET_LABEL); - e->do_render = Widget_render_label; - e->label.text = g_string_new (text); - e->label.text_alignment = alignment; - return e; -} - -Widget * -Widget_scrollbar_new (Widget *target, gboolean horizontal) -{ - Widget *e = _uiel_new (WIDGET_SCROLLBAR); - e->do_render = Widget_render_scrollbar; - e->scrollbar.target = target; - e->scrollbar.horizontal = 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; - Widget_set_on_event (e, EVENT_SCROLL_UP, uicb_widget_op, WidgetOpData_new (OP_SCROLLABLE_SCROLL, target, SCROLL_UP)); - Widget_set_on_event (e, EVENT_SCROLL_DOWN, uicb_widget_op, WidgetOpData_new (OP_SCROLLABLE_SCROLL, target, SCROLL_DOWN)); - return e; -} - -Widget * -Widget_progress_new (gboolean horizontal, float min, float max, float *ptr) -{ - Widget *e = _uiel_new (WIDGET_SCROLLBAR); - e->do_render = Widget_render_progress; - e->progress.horizontal = horizontal; - e->progress.min = min; - e->progress.max = max; - e->progress.ptr = ptr; - return e; -} - -Widget * -Widget_multiline_text_new (const char *text, int line_height, TextAlignment alignment) -{ - Widget *e = _uiel_new (WIDGET_MULTILINE_TEXT); - e->do_layout = Widget_layout_multiline_text; - e->do_render = Widget_render_multiline_text; - e->multiline_text.text = g_string_new (text); - e->multiline_text.lines = g_array_new (FALSE, FALSE, sizeof (GString)); - e->multiline_text.line_height = line_height; - e->multiline_text.text_alignment = alignment; - return e; -} - -Widget * -Widget_entry_new (const char *ghost_text) -{ - Widget *e = _uiel_new (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_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; -} - -Widget * -Widget_container_new (gboolean horizontal) -{ - Widget *e = _uiel_new (WIDGET_CONTAINER); - e->do_layout = Widget_layout_container; - e->container.horizontal = horizontal; - return e; -} - -Widget * -Widget_scrollable_new () -{ - Widget *e = _uiel_new (WIDGET_SCROLLABLE); - e->do_layout = Widget_layout_scrollable; - e->scrollable.scroll_amount = 10; - Widget_set_on_event (e, EVENT_SCROLL_UP, uicb_widget_op, WidgetOpData_new (OP_SCROLLABLE_SCROLL, e, SCROLL_UP)); - Widget_set_on_event (e, EVENT_SCROLL_DOWN, uicb_widget_op, WidgetOpData_new (OP_SCROLLABLE_SCROLL, e, SCROLL_DOWN)); - return e; -} - -Widget * -Widget_flowbox_new (gboolean horizontal) -{ - Widget *e = _uiel_new (WIDGET_FLOWBOX); - e->do_layout = Widget_layout_flowbox; - e->flowbox.horizontal = horizontal; - return e; -} - -Widget * -Widget_window_new (const char *title, int titlebar_height) -{ - Widget *e = _uiel_new (WIDGET_WINDOW); - e->window.title = g_string_new (title); - e->window.titlebar_height = titlebar_height; - e->window.resizable_h = TRUE; - e->window.resizable_v = TRUE; - e->do_layout = Widget_layout_window; - e->do_render = Widget_render_window; - e->on_event[EVENT_LEFT_DOWN] = uicb_window_start_drag; - e->on_event[EVENT_MOUSE_MOVE] = uicb_window_drag; - e->on_event[EVENT_LEFT_UP] = uicb_window_stop_drag; - return e; + Widget *w = g_new0 (Widget, 1); + w->children = g_array_new (false, false, sizeof (Widget *)); + w->style = g_new0 (WidgetStyle, 1); + w->visible = TRUE; + w->do_render = Widget_render_base; + return w; } void @@ -155,20 +24,6 @@ Widget_add_child (Widget *parent, Widget *child) g_array_append_val (parent->children, child); } -void -Widget_add_child_weighed (Widget *parent, Widget *child, float layout_weight) -{ - child->layout_weight = layout_weight; - Widget_add_child (parent, child); -} - -void Widget_add_child_flowbox (Widget *parent, Widget *child, - float layout_weight, int cross_axis_size) -{ - child->size[parent->flowbox.horizontal ? 1 : 0] = cross_axis_size; - Widget_add_child_weighed (parent, child, layout_weight); -} - void Widget_set_on_event (Widget *widget, WidgetEvent event, void (*on_event) (Widget *, SDL_Event *, void *), @@ -186,12 +41,6 @@ Widget_set_on_key (Widget *widget, SDL_Scancode key, widget->key_data[key] = userdata; } -void -Widget_label_set_text (Widget *widget, const char *text) -{ - g_string_overwrite (widget->label.text, 0, text); -} - void Widget_label_printf (Widget *widget, const char *format, ...) { @@ -201,13 +50,6 @@ 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) { @@ -379,7 +221,9 @@ Widget_free (Widget *widget) { if (widget->on_event[ev] && widget->event_data[ev]) { - g_free (widget->event_data[ev]); + /* TODO: add ownership data to the Widget to determine + whether we allocated that, or Guile did. */ + /* g_free (widget->event_data[ev]); */ } } diff --git a/game/src/ui/widget_core.h b/game/src/ui/widget_core.h index 341ccb4..1377eef 100644 --- a/game/src/ui/widget_core.h +++ b/game/src/ui/widget_core.h @@ -189,31 +189,15 @@ typedef struct Widget void *key_data[SDL_SCANCODE_COUNT]; } Widget; -Widget *Widget_plain_new (); -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, int 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); -Widget *Widget_window_new (const char *title, int titlebar_height); +Widget *Widget_new (); void Widget_add_child (Widget *parent, Widget *child); -void Widget_add_child_weighed (Widget *parent, Widget *child, - float layout_weight); -void Widget_add_child_flowbox (Widget *parent, Widget *child, - float layout_weight, int cross_axis_size); 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_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, ivec2 direction); diff --git a/game/src/ui/widget_guile.c b/game/src/ui/widget_guile.c index 455312b..6589107 100644 --- a/game/src/ui/widget_guile.c +++ b/game/src/ui/widget_guile.c @@ -1,69 +1,369 @@ #include "widget_guile.h" +/* Widget styles */ + +void +scm_vec4_to_float_array(SCM val, float out[4]) +{ + if (scm_is_pair (val)) { + for (int i = 0; i < 4; ++i) + { + if (!scm_is_pair (val)) + { + scm_misc_error ("scm_vec4_to_float_array", "Expected list of 4 values", SCM_EOL); + } + + out[i] = (float) scm_to_double (scm_car(val)); + val = scm_cdr (val); + } + + if (!scm_is_null (val)) + { + scm_misc_error ("scm_vec4_to_float_array", "List must have exactly 4 values", SCM_EOL); + } + } + else + { + scm_wrong_type_arg ("scm_vec4_to_float_array", 0, val); + } +} + +static SCM +_wg_style_new (SCM args) +{ + WidgetStyle *s = g_new0 (WidgetStyle, 1); + + while (scm_is_pair (args)) + { + SCM key = scm_car (args); + args = scm_cdr (args); + SCM val = scm_car (args); + args = scm_cdr (args); + + const char *k = scm_to_utf8_string (scm_symbol_to_string (scm_keyword_to_symbol (key))); + + if (!strcmp (k, "background-color")) + { + scm_vec4_to_float_array (val, s->background_color); + } + + if (!strcmp (k, "background-color-hovered")) + { + scm_vec4_to_float_array (val, s->background_color_hovered); + } + + if (!strcmp (k, "background-color-clicked")) + { + scm_vec4_to_float_array (val, s->background_color_clicked); + } + + if (!strcmp (k, "text-color")) + { + scm_vec4_to_float_array (val, s->text_color); + } + + if (!strcmp (k, "text-color-hovered")) + { + scm_vec4_to_float_array (val, s->text_color_hovered); + } + + if (!strcmp (k, "text-color-clicked")) + { + scm_vec4_to_float_array (val, s->text_color_clicked); + } + + if (!strcmp (k, "ghost-text-color")) + { + scm_vec4_to_float_array (val, s->ghost_text_color); + } + + if (!strcmp (k, "icon-color")) + { + scm_vec4_to_float_array (val, s->icon_color); + } + + if (!strcmp (k, "icon-color-hovered")) + { + scm_vec4_to_float_array (val, s->icon_color_hovered); + } + + if (!strcmp (k, "icon-color-clicked")) + { + scm_vec4_to_float_array (val, s->icon_color_clicked); + } + + if (!strcmp (k, "scrollbar-color")) + { + scm_vec4_to_float_array (val, s->scrollbar_color); + } + + if (!strcmp (k, "scrollbar-color-hovered")) + { + scm_vec4_to_float_array (val, s->scrollbar_color_hovered); + } + + if (!strcmp (k, "scrollbar-color-clicked")) + { + scm_vec4_to_float_array (val, s->scrollbar_color_clicked); + } + + if (!strcmp (k, "border-color")) + { + scm_vec4_to_float_array (val, s->border_color); + } + + if (!strcmp (k, "progress-color")) + { + scm_vec4_to_float_array (val, s->progress_color); + } + + if (!strcmp (k, "resize-handle-color")) + { + scm_vec4_to_float_array (val, s->resize_handle_color); + } + + if (!strcmp (k, "border-width")) + { + s->border_width = scm_to_int (val); + } + } + + return scm_from_pointer (s, NULL); +} + +/* Widgets */ + static Widget * _wgg_build (const char *type) { - Widget *e = g_new0 (Widget, 1); - e->children = g_array_new (false, false, sizeof (Widget *)); - e->style = WSTYLE (STYLE_DEFAULT); + Widget *w = Widget_new (); - if (g_strcmp0 (type, "plain") == 0) + if (!g_strcmp0 (type, "plain")) { - e->kind = WIDGET_PLAIN; + w->kind = WIDGET_PLAIN; } - if (g_strcmp0 (type, "label") == 0) + if (!g_strcmp0 (type, "label")) { - e->kind = WIDGET_LABEL; + w->kind = WIDGET_LABEL; + w->do_render = Widget_render_label; + w->label.text = g_string_new (NULL); } - if (g_strcmp0 (type, "scrollbar") == 0) + if (!g_strcmp0 (type, "scrollbar")) { - e->kind = WIDGET_SCROLLBAR; + w->kind = WIDGET_SCROLLBAR; + w->do_render = Widget_render_scrollbar; } - if (g_strcmp0 (type, "progress") == 0) + if (!g_strcmp0 (type, "progress")) { - e->kind = WIDGET_PROGRESS; + w->kind = WIDGET_PROGRESS; + w->do_render = Widget_render_progress; } - if (g_strcmp0 (type, "entry") == 0) + if (!g_strcmp0 (type, "entry")) { - e->kind = WIDGET_ENTRY; + w->kind = WIDGET_ENTRY; + w->do_render = Widget_render_entry; } - if (g_strcmp0 (type, "multiline") == 0) + if (!g_strcmp0 (type, "multiline")) { - e->kind = WIDGET_MULTILINE_TEXT; + w->kind = WIDGET_MULTILINE_TEXT; + w->do_render = Widget_render_multiline_text; + w->multiline_text.text = g_string_new (NULL); + w->multiline_text.lines = g_array_new (FALSE, FALSE, sizeof (GString)); } - if (g_strcmp0 (type, "container") == 0) + if (!g_strcmp0 (type, "container")) { - e->kind = WIDGET_CONTAINER; + w->kind = WIDGET_CONTAINER; + w->do_layout = Widget_layout_container; } - if (g_strcmp0 (type, "scrollable") == 0) + if (!g_strcmp0 (type, "scrollable")) { - e->kind = WIDGET_SCROLLABLE; + w->kind = WIDGET_SCROLLABLE; + w->do_layout = Widget_layout_scrollable; } - if (g_strcmp0 (type, "flowbox") == 0) + if (!g_strcmp0 (type, "flowbox")) { - e->kind = WIDGET_FLOWBOX; + w->kind = WIDGET_FLOWBOX; + w->do_layout = Widget_layout_flowbox; } - if (g_strcmp0 (type, "window") == 0) + if (!g_strcmp0 (type, "window")) { - e->kind = WIDGET_WINDOW; + w->kind = WIDGET_WINDOW; + w->do_render = Widget_render_window; + 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; } - return NULL; + return w; +} + +void +_wgcb_event (Widget *w, SDL_Event *event, void *data) +{ + SCM proc = (SCM) data; + if (scm_is_true (scm_procedure_p (proc))) + { + scm_call_2 (proc, scm_from_pointer (w, NULL), scm_from_pointer (event, NULL)); + } } static void _wgg_set_property (Widget *w, const char *key, SCM val) { UNUSED (w, key, val); + + if (!g_strcmp0 (key, "visible")) + { + w->visible = scm_is_true (val); + } + + if (!g_strcmp0 (key, "x")) + { + w->position[0] = scm_to_int (val); + } + + if (!g_strcmp0 (key, "y")) + { + w->position[1] = scm_to_int (val); + } + + if (!g_strcmp0 (key, "w")) + { + w->size[0] = scm_to_int (val); + } + + if (!g_strcmp0 (key, "h")) + { + w->size[1] = scm_to_int (val); + } + + if (!g_strcmp0 (key, "layout-padding")) + { + w->layout_padding = scm_to_int (val); + } + + if (!g_strcmp0 (key, "layout-spacing")) + { + w->layout_spacing = scm_to_int (val); + } + + if (!g_strcmp0 (key, "layout-weight")) + { + w->layout_weight = scm_to_double (val); + } + + if (!g_strcmp0 (key, "horizontal")) + { + switch (w->kind) + { + case WIDGET_SCROLLBAR: + w->scrollbar.horizontal = scm_is_true (val); + break; + case WIDGET_PROGRESS: + w->progress.horizontal = scm_is_true (val); + break; + case WIDGET_CONTAINER: + w->container.horizontal = scm_is_true (val); + break; + case WIDGET_SCROLLABLE: + w->scrollable.layout_horizontal = scm_is_true (val); + break; + case WIDGET_FLOWBOX: + w->flowbox.horizontal = scm_is_true (val); + break; + case WIDGET_WINDOW: + w->window.resizable_h = scm_is_true (val); + default: + break; + } + } + + if (!g_strcmp0 (key, "vertical") && w->kind == WIDGET_WINDOW) + { + switch (w->kind) + { + case WIDGET_WINDOW: + w->window.resizable_v = scm_is_true (val); + break; + case WIDGET_SCROLLABLE: + w->window.resizable_v = scm_is_true (val); + default: + break; + } + } + + if (!g_strcmp0 (key, "aspect-ratio")) + { + w->aspect_ratio = scm_to_double (val); + } + + if (!g_strcmp0 (key, "line-height") && w->kind == WIDGET_MULTILINE_TEXT) + { + w->multiline_text.line_height = scm_to_int (val); + } + + if (!g_strcmp0 (key, "style")) + { + w->style = (const WidgetStyle *) scm_to_pointer (val); + } + + if (!g_strcmp0 (key, "icon")) + { + w->icon = scm_to_int (val); + } + + if (!g_strcmp0 (key, "text")) + { + switch (w->kind) + { + case WIDGET_LABEL: + g_string_overwrite (w->label.text, 0, scm_to_utf8_string (val)); + break; + case WIDGET_MULTILINE_TEXT: + g_string_overwrite (w->multiline_text.text, 0, scm_to_utf8_string (val)); + break; + case WIDGET_WINDOW: + g_string_overwrite (w->window.title, 0, scm_to_utf8_string (val)); + default: + break; + } + } + + if (!g_strcmp0 (key, "on-tick")) + { + if (!scm_is_true (scm_procedure_p (val))) + { + scm_misc_error ("widget-set-property", "Expected a procedure for on-tick", scm_list_1 (val)); + } + + w->on_event[EVENT_TICK] = _wgcb_event; + w->event_data[EVENT_TICK] = (void *) val; + scm_gc_protect_object (val); + } + + if (!g_strcmp0 (key, "on-click")) + { + if (!scm_is_true (scm_procedure_p (val))) + { + scm_misc_error ("widget-set-property", "Expected a procedure for on-tick", scm_list_1 (val)); + } + + w->on_event[EVENT_LEFT_UP] = _wgcb_event; + w->event_data[EVENT_LEFT_UP] = (void *) val; + scm_gc_protect_object (val); + } } // (widget-new TYPE #:PROP1 VALUE1 ... #:PROPN VALUEN child1 ... childn) @@ -79,6 +379,7 @@ _wg_widget_new (SCM args) args = scm_cdr (args); const char *type = scm_symbol_to_string (type_sym) ? scm_to_utf8_string (scm_symbol_to_string (type_sym)) : NULL; + if (!type) { scm_misc_error ("widget-new", "Invalid widget type symbol", SCM_EOL); @@ -91,28 +392,33 @@ _wg_widget_new (SCM args) scm_misc_error ("widget-new", "Unknown widget type", scm_list_1 (type_sym)); } - // Parse keyword args + /* Parse props */ while (scm_is_pair (args)) { SCM key = scm_car (args); - args = scm_cdr (args); if (!scm_is_keyword (key)) { break; } + + args = scm_cdr (args); if (!scm_is_pair (args)) { scm_misc_error ("widget-new", "Expected value after keyword", SCM_EOL); } - + SCM val = scm_car (args); args = scm_cdr (args); - _wgg_set_property (w, scm_to_utf8_string (scm_keyword_to_symbol (key)), val); + SCM sym = scm_keyword_to_symbol (key); + SCM str_obj = scm_symbol_to_string (sym); + const char *key_cstr = scm_to_utf8_string (str_obj); + + _wgg_set_property (w, key_cstr, val); } - // Remaining args = children + /* Parse the children of the widget. */ while (scm_is_pair (args)) { SCM child = scm_car (args); @@ -124,8 +430,28 @@ _wg_widget_new (SCM args) return scm_from_pointer (w, NULL); } +static SCM +_wg_get_icon (SCM widget_ptr) +{ + Widget *w = (Widget *) scm_to_pointer (widget_ptr); + return scm_from_int (w->icon); +} + +static SCM +_wg_set_icon (SCM widget_ptr, SCM icon_val) +{ + Widget *w = (Widget *) scm_to_pointer (widget_ptr); + int icon = scm_to_int (icon_val); + w->icon = icon; + return SCM_UNSPECIFIED; +} + void widget_guile_register () { scm_c_define_gsubr ("widget-new", 0, 0, 1, _wg_widget_new); + scm_c_define_gsubr ("style-new", 0, 0, 1, _wg_style_new); + + scm_c_define_gsubr ("widget-get-icon", 1, 0, 0, _wg_get_icon); + scm_c_define_gsubr ("widget-set-icon!", 2, 0, 0, _wg_set_icon); } diff --git a/game/src/ui/widget_ops.c b/game/src/ui/widget_ops.c index f667e94..1144f7c 100644 --- a/game/src/ui/widget_ops.c +++ b/game/src/ui/widget_ops.c @@ -14,13 +14,6 @@ WidgetOpData_new (WidgetOp op, void *ptr, ...) switch (data->op) { - case OP_LABEL_SHOW_INT: - case OP_LABEL_SHOW_FLOAT: - data->fmt = va_arg (ap, const char *); - break; - case OP_MODIFY_INT: - data->delta = (float) va_arg (ap, double); - break; case OP_SCROLLABLE_SCROLL: glm_ivec2_copy (va_arg (ap, int *), data->scroll_dir); break; @@ -40,21 +33,6 @@ uicb_widget_op (Widget *self, SDL_Event *sdl_event, void *userdata) switch (data->op) { - case OP_SWITCH_FLAG: - flags_switch ((char *) data->ptr); - break; - case OP_LABEL_SHOW_INT: - Widget_label_printf (self, data->fmt, * (int *)data->ptr); - break; - case OP_LABEL_SHOW_FLOAT: - Widget_label_printf (self, data->fmt, * (float *)data->ptr); - break; - case OP_CHECKBOX_SHOW_FLAG: - self->icon = flags_get ((char *) data->ptr) ? ICON_CHECKBOX_CHECKED : ICON_CHECKBOX_UNCHECKED; - break; - case OP_MODIFY_INT: - *(int*) data->ptr += (int) data->delta; - break; case OP_SCROLLABLE_SCROLL: Widget_scrollable_scroll ((Widget *) data->ptr, data->scroll_dir); break; diff --git a/game/src/ui/widget_ops.h b/game/src/ui/widget_ops.h index a0ac4b7..a03faa3 100644 --- a/game/src/ui/widget_ops.h +++ b/game/src/ui/widget_ops.h @@ -7,14 +7,6 @@ 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_START_TEXT_INPUT, @@ -31,9 +23,6 @@ typedef struct WidgetOpData WidgetOp op; void *ptr; - /* These are used dependant on what you wanna do. */ - const char *fmt; - float delta; ivec2 scroll_dir; } WidgetOpData; diff --git a/game/src/ui/widget_style.h b/game/src/ui/widget_style.h index cf48ba4..63cb171 100644 --- a/game/src/ui/widget_style.h +++ b/game/src/ui/widget_style.h @@ -2,10 +2,6 @@ #include -#define COLOR_TRANSPARENT { 0.0, 0.0, 0.0, 0.0 } -#define COLOR_BLACK { 0.0, 0.0, 0.0, 1.0 } -#define COLOR_WHITE { 1.0, 1.0, 1.0, 1.0 } - typedef struct WidgetStyle { vec4 background_color; @@ -33,78 +29,3 @@ typedef struct WidgetStyle unsigned int background_image; } WidgetStyle; - -typedef enum WidgetStyleId - { - STYLE_DEFAULT, - STYLE_TOOLTIP, - STYLE_ICON_BUTTON, - STYLE_WINDOW, - STYLE_CONTAINER, - NSTYLES - } WidgetStyleId; - -static const WidgetStyle widget_styles[NSTYLES] = { - [STYLE_DEFAULT] = { - .text_color = COLOR_WHITE, - .text_color_hovered = COLOR_WHITE, - .text_color_clicked = COLOR_WHITE, - .ghost_text_color = { 1.0, 1.0, 1.0, 0.5 }, - - .icon_color = COLOR_WHITE, - .icon_color_hovered = COLOR_WHITE, - .icon_color_clicked = COLOR_WHITE, - - .scrollbar_color = COLOR_WHITE, - .scrollbar_color_hovered = COLOR_WHITE, - .scrollbar_color_clicked = COLOR_WHITE, - - .border_color = COLOR_WHITE, - .progress_color = COLOR_WHITE, - .resize_handle_color = { 0.6, 0.6, 0.6, 1.0 }, - - .border_width = 0, - }, - - [STYLE_TOOLTIP] = { - .background_color = { 0.1, 0.1, 0.1, 1.0 }, - .background_color_hovered = { 0.1, 0.1, 0.1, 1.0 }, - .background_color_clicked = { 0.1, 0.1, 0.1, 1.0 }, - - .text_color = COLOR_WHITE, - .text_color_hovered = COLOR_WHITE, - .text_color_clicked = COLOR_WHITE, - }, - - [STYLE_ICON_BUTTON] = { - .icon_color = COLOR_WHITE, - .icon_color_hovered = { 1.0, 1.0, 0.0, 1.0 }, - .icon_color_clicked = { 1.0, 0.0, 0.0, 1.0 }, - }, - - [STYLE_WINDOW] = { - .background_color = { 0.2, 0.2, 0.2, 1.0 }, - .background_color_hovered = { 0.2, 0.2, 0.2, 1.0 }, - .background_color_clicked = { 0.3, 0.3, 0.3, 1.0 }, - - .text_color = COLOR_WHITE, - .text_color_hovered = COLOR_WHITE, - .text_color_clicked = COLOR_WHITE, - - .border_color = { 0.5, 0.5, 0.5, 1.0 }, - .resize_handle_color = COLOR_WHITE, - - .border_width = 3, - }, - - [STYLE_CONTAINER] = { - .background_color = { 0.3, 0.3, 0.3, 1.0 }, - .background_color_hovered = { 0.3, 0.3, 0.3, 1.0 }, - .background_color_clicked = { 0.3, 0.3, 0.3, 1.0 }, - - .border_color = { 0.4, 0.4, 0.4, 1.0 }, - .border_width = 2, - }, -}; - -#define WSTYLE(id) (&widget_styles[id])