Commit 6c3e2755 by beoran

ZORI GUI now has the beginning of vertical menus, but the joykey focus still needs fixing..

parent 7d875c99
......@@ -80,6 +80,7 @@ set(ERUTA_SRC_FILES
src/zori/zori_button.c
src/zori/zori.c
src/zori/zori_console.c
src/zori/zori_caption.c
src/zori/zori_longtext.c
src/zori/zori_menu.c
src/zori/zori_page.c
......
#ifndef ui_H_INCLUDED
#define ui_H_INCLUDED
typedef struct UI_ UI;
/* Data struct for the particular GUI state */
struct ui_state {
zori_id screen;
zori_id console;
};
UI * ui_alloc(void);
void vva_list_scan (va_list args , char * format , va_list data );
void va_list_scan (va_list args , char * format , ... );
void ui_setup(void);
......
......@@ -3,6 +3,8 @@
#include <zori.h>
#define ZORI_WIDGET_TYPE_BUTTON ZORI_WIDGET_TYPE('z','b','u','t')
struct zori_button {
struct zori_widget widget;
zori_string * text;
......@@ -10,6 +12,17 @@ struct zori_button {
};
struct zori_button *zori_widget_to_button(struct zori_widget *widget);
int zori_button_on_mouse_axes(union zori_event *event);
int zori_button_on_mouse_click(union zori_event *event);
void zori_draw_button(struct zori_button *button);
int zori_button_on_draw(union zori_event *event);
struct zori_button *zori_button_text_(struct zori_button *button, zori_string *text);
struct zori_button *zori_button_init(struct zori_button *button, zori_string *text);
struct zori_button *zori_button_new(zori_id id, zori_id parent, zori_box *box, zori_string *text);
zori_id zori_new_button(zori_id id, zori_id parent, zori_box *box, zori_string *text);
#endif
......
#ifndef zori_caption_H_INCLUDED
#define zori_caption_H_INCLUDED
#endif
#ifndef zori_console_H_INCLUDED
#define zori_console_H_INCLUDED
#define ZORI_WIDGET_TYPE_CONSOLE ZORI_WIDGET_TYPE('z','c','o','n')
typedef int (zori_console_command)(struct zori_widget *, const char * command, void * data);
/* A console is a console for command-line interaction and error display. When it's active it captures all input (as long as it's active) */
......
#ifndef zori_menu_H_INCLUDED
#define zori_menu_H_INCLUDED
#define ZORI_WIDGET_TYPE_MENU ZORI_WIDGET_TYPE('z','m','e','n')
struct zori_menu {
struct zori_widget widget;
/* The index of the menu item that currently is selected. Negative means none. */
int selected_index;
};
#endif
......
......@@ -3,6 +3,8 @@
#include "zori.h"
#define ZORI_WIDGET_TYPE_PAGE ZORI_WIDGET_TYPE('z','p','a','g')
/* In Zori, the GUI is paginated. This means that on any
* screen, only a single GUI page can be active. The intent is to
* support different GUI modes such as startup screen, status view,
......
#ifndef zori_root_H_INCLUDED
#define zori_root_H_INCLUDED
#define ZORI_WIDGET_TYPE_ROOT ZORI_WIDGET_TYPE('z','r','o','o')
#endif
......
......@@ -3,6 +3,8 @@
#include "zori.h"
#define ZORI_WIDGET_TYPE_SCREEN ZORI_WIDGET_TYPE('z','s','c','r')
/* The top level widget for a single display. */
struct zori_screen {
/* A screen is a widget. */
......@@ -18,6 +20,7 @@ struct zori_screen {
zori_id zori_new_screen(zori_id id, zori_display * display);
struct zori_screen * zori_widget_to_screen(struct zori_widget * widget);
#endif
......
......@@ -21,6 +21,8 @@
#include "store.h"
#include "scegra.h"
#include "callrb.h"
#include "zori.h"
#include "zori_console.h"
......
......@@ -22,12 +22,9 @@
#include "zori.h"
#include "zori_screen.h"
#include "zori_console.h"
#include "zori_button.h"
#include "ui.h"
/* Sub data struct for the particular GUI state */
struct GuiState {
zori_id screen;
zori_id console;
};
/* The data struct contains all global state and other data of the application.
*/
......@@ -100,7 +97,7 @@ struct State_ {
struct zori_console * console;
/* GUI data. */
struct GuiState ui;
struct ui_state ui;
/* The current actor, controlled by the player. */
......@@ -502,7 +499,9 @@ int state_initjoystick(State * self) {
/* Initialize the GUI for the Eruta engine. */
State * state_init_gui(State * self, BOOL fullscreen) {
/* Set up Zori GUI. */
zori_id main_page, main_menu;
struct zori_style style;
Rebox box;
memset(&style, 0, sizeof(style));
style.text.font = self->font;
style.text.color = color_rgb(255,255,255);
......@@ -517,6 +516,29 @@ State * state_init_gui(State * self, BOOL fullscreen) {
return state_errmsg_(self, "Could not init GUI screen.\n");
}
main_page = zori_new_page(-1, self->ui.screen);
LOG_NOTE("Main page: %d\n", main_page);
box = rebox_make(280, 80, 140, 240);
main_menu = zori_new_menu(-1, main_page, &box);
LOG_NOTE("Main menu: %d\n", main_menu);
{
USTR_INFO si;
USTR * bs = ustr_refcstr(&si, "Button 1");
Rebox box = rebox_make(300, 100, 100, 60);
zori_id button = zori_new_button(-1, main_menu, &box, bs);
LOG_NOTE("Button: %d\n", button);
}
{
USTR_INFO si;
USTR * bs = ustr_refcstr(&si, "Button 2");
Rebox box = rebox_make(300, 200, 100, 60);
zori_id button = zori_new_button(-1, main_menu, &box, bs);
LOG_NOTE("Button: %d\n", button);
}
return self;
}
......
#include "eruta.h"
#include "image.h"
#include "zori.h"
#include "zori_button.h"
#include "ui.h"
/*
* UI handles the user interface of Eruta.
*
* There is only one top-level widget, however, every widget can contain
* any amout of child widgets. BBWidgets that are children of the same parent
* widget are said to be on the same level.
*
*
* The box model used in eruta is as follows:
* ......................................
* .margin .
* . +-------------border-----------+ .
* . | padding | .
* . | .......................... | .
* . | . . | .
* . | . contents . | .
* . | . . | .
* . | .......................... | .
* . | padding | .
* . +------------------------------+ .
* .margin .
* ......................................
*
* The padding determines the minimal distance between the border or the
* parent widget and it's contents and/or child widgets.
*
* The border's thickness is only relevant for visual effects. It does not change
* the layout. The border is effectively "inside" the padding of the widget.
*
* The margin of a widget determines how closely that widget may be packed
* to it's sibling widgets.
*
* The work in UI is divided between the UI and the BBWidget. The UI struct
* handles everything that depends on and/or may influence several widgets at
* once, such as event dispatching but also setting the focus, determining which
* widget is being hovered, or dragged, etc. The latter fuctions change the state
* of several widgets, so they are handled on the level of the system.
* The BBWidget class and it's child classes handle the individual state and
* actions of the various widgets individually.
*
*/
#include "ui.h"
#include "dynar.h"
#include <stdarg.h>
/** UI manages the graphical user interface and menus. */
struct UI_ {
/* Ths UI is a widget itself. How meta that is! :) */
/*BBWidget widget;*/
/* ID generator. */
int last_id;
/* The widgets in the UI, in a dynamic array. */
Dynar * widgets;
};
/** Allocates an unitinialized UI. */
UI * ui_alloc() {
return STRUCT_ALLOC(UI);
}
/** Draws the user interface */
/** Initializes the user interface. */
typedef void void_function(void);
typedef void_function * void_function_ptr;
/* This file contains the concrete implementation of the Eruta GUI
* and it's various GUI screens and pages. */
/** Generic utility */
/** Helps to scan variable arguments with a format string,
much like how scanf would work fro stdin. */
void vva_list_scan(va_list args, char * format, va_list data) {
for( ; (*format); format++) {
switch((*format)) {
case 'p': (*va_arg(data, void**) ) = va_arg(args, void*); break;
case 'd':
case 'i': (*va_arg(data, int*)) = va_arg(args, int); break;
case 'o':
case 'u': (*va_arg(data, unsigned int*))
= va_arg(args, unsigned int); break;
case 'l': (*va_arg(data, long*)) = va_arg(args, long); break;
case 's': (*va_arg(data, char**)) = va_arg(args, char*); break;
case 'f': (*va_arg(data, double*)) = va_arg(args, double); break;
case 'F': (*va_arg(data, void_function_ptr*))
= va_arg(args, void_function_ptr); break;
default:
break;
}
}
}
/** Helps to scan variable arguments with a format string,
much like how scanf would work for stdin. */
void va_list_scan(va_list args, char * format, ...) {
va_list data;
va_start(data, format);
vva_list_scan(args, format, data);
va_end(data);
}
#include "zori.h"
#include "zori_console.h"
#include "zori_screen.h"
#include "miao.h"
#include <allegro5/allegro_color.h>
#include "str.h"
#include "draw.h"
#include "monolog.h"
/*
* Pardon the pun name, but Zori is the submodule that handles the user
......@@ -232,6 +234,16 @@ int zori_widget_raise_action_event
}
int zori_widget_raise_close_event
(struct zori_widget * widget, struct zori_widget * from) {
union zori_event event;
event.close.any.type = ZORI_EVENT_CLOSE;
event.close.any.widget = widget;
event.close.from = from;
return zori_widget_raise_event(widget, &event);
}
static struct zori_root * the_zori_root = NULL;
......@@ -301,6 +313,8 @@ zori_id zori_start(struct zori_style * default_style) {
the_default_style->border.color = al_color_name("white");
the_default_style->back.color = al_color_name("green");
the_default_style->fore.color = al_color_name("white");
the_default_style->mark.color = al_color_name("lightgreen");
the_default_style->hover.color = al_color_name("yellowgreen");
if (default_style) {
......@@ -344,13 +358,73 @@ struct zori_widget *
zori_widget_add_child(struct zori_widget * parent, struct zori_widget * child) {
if (parent) {
miao_push((&parent->children), child);
child->parent = child;
child->parent = parent;
}
return child;
}
zori_id
zori_widget_margins_(struct zori_widget * widget, int left, int top, int right, int bottom) {
if (!widget) return ZORI_ID_EINVAL;
widget->outer = widget->box;
widget->outer.at.x -= left;
widget->outer.at.y -= top;
widget->outer.size.x += (left + right);
widget->outer.size.y += (top + bottom);
return widget->id;
}
zori_id
zori_widget_margin_(struct zori_widget * widget, int size) {
return zori_widget_margins_(widget, size, size, size, size);
}
zori_id zori_set_margins(zori_id id, int left, int top, int right, int bottom) {
struct zori_widget * widget = zori_get_widget(id);
return zori_widget_margins_(widget, left, top, right, bottom);
}
zori_id zori_set_margin(zori_id id, int size) {
struct zori_widget * widget = zori_get_widget(id);
return zori_widget_margin_(widget, size);
}
zori_id
zori_widget_paddings_(struct zori_widget * widget, int left, int top, int right, int bottom) {
if (!widget) return ZORI_ID_EINVAL;
widget->inner = widget->box;
widget->inner.at.x += left;
widget->inner.at.y += top;
widget->inner.size.x -= (left + right);
widget->inner.size.y -= (top + bottom);
return widget->id;
}
zori_id
zori_widget_padding_(struct zori_widget * widget, int size) {
return zori_widget_paddings_(widget, size, size, size, size);
}
zori_id zori_set_paddings(zori_id id, int left, int top, int right, int bottom) {
struct zori_widget * widget = zori_get_widget(id);
return zori_widget_paddings_(widget, left, top, right, bottom);
}
zori_id zori_set_padding(zori_id id, int size) {
struct zori_widget * widget = zori_get_widget(id);
return zori_widget_padding_(widget, size);
}
struct zori_widget * zori_widget_init
( struct zori_widget * widget,
zori_widget_type type,
zori_id id,
struct zori_widget * parent,
zori_rebox * box, struct zori_style * style) {
......@@ -360,7 +434,8 @@ struct zori_widget * zori_widget_init
miao_init(&widget->handlers);
widget->z = 0;
widget->flags = 0;
widget->id = ( (id < 0) ? widget->id : id);
widget->type = type;
widget->id = ( (id < 0) ? zori_get_unused_id() : id);
if (style) {
widget->style = *style;
......@@ -377,11 +452,26 @@ struct zori_widget * zori_widget_init
} else {
widget->box = rebox_make(0, 0, ZORI_WIDGET_DEFAULT_W, ZORI_WIDGET_DEFAULT_H);
}
zori_widget_margin_(widget, ZORI_MARGIN_DEFAULT);
zori_widget_padding_(widget, ZORI_PADDING_DEFAULT);
zori_widget_add_child(parent, widget);
zori_registry_add(the_zori_registry, widget->id, widget);
return widget;
}
struct zori_widget *
zori_widget_initall(struct zori_widget * widget, zori_widget_type type, int id,
struct zori_widget * parent, zori_rebox * box, struct zori_style * style,
struct zori_handler * handlers, size_t amount) {
if (zori_widget_init(widget, type, id, parent, box, style)) {
zori_widget_add_handlers(widget, handlers, amount);
}
return widget;
}
/* Shut down Zori and destroys all widgets. Return 0 on succes or
* negative on error.
......@@ -402,24 +492,15 @@ zori_id zori_shutdown() {
}
struct zori_widget *
zori_widget_initall(struct zori_widget * widget, int id,
struct zori_widget * parent, zori_rebox * box, struct zori_style * style,
struct zori_handler * handlers, size_t amount) {
if (zori_widget_init(widget, id, parent, box, style)) {
zori_widget_add_handlers(widget, handlers, amount);
}
return widget;
}
/* Creates a new screen widget. Normally this should be the first widget
* you create after zori_start. */
zori_id zori_new_screen(zori_display * display);
zori_id zori_new_screen(zori_id id, zori_display * display);
/* Creates a new page widget on the given screen. The page is not
* made the active page, unless if it is the first one created. */
zori_id zori_new_page(zori_id screen);
zori_id zori_new_page(zori_id id, zori_id screen);
/* Activates the page on it's display. All other pages are dectivated and
* hidden. */
......@@ -481,107 +562,72 @@ int zori_handle_system_event(zori_system_event * sysev) {
}
int zori_widget_visible(struct zori_widget * widget) {
return widget && ((widget->flags & ZORI_FLAG_HIDDEN) != ZORI_FLAG_HIDDEN);
}
int zori_widget_active(struct zori_widget * widget) {
return widget && ((widget->flags & ZORI_FLAG_DISABLED) != ZORI_FLAG_DISABLED);
}
int zori_widget_active_(struct zori_widget * widget, int set) {
if (set) {
widget->flags = widget->flags & (~ZORI_FLAG_DISABLED);
} else {
widget->flags = widget->flags | ZORI_FLAG_DISABLED;
}
return zori_widget_active(widget);
}
int zori_widget_visible_(struct zori_widget * widget, int set) {
if (set) {
widget->flags = widget->flags & (~ZORI_FLAG_HIDDEN);
} else {
widget->flags = widget->flags | ZORI_FLAG_HIDDEN;
}
return zori_widget_active(widget);
}
int zori_widget_hover(struct zori_widget * widget) {
return widget && ((widget->flags & ZORI_FLAG_HOVERED) != ZORI_FLAG_HOVERED);
}
int zori_widget_hover_(struct zori_widget * widget, int set) {
if (set) {
widget->flags = widget->flags & (~ZORI_FLAG_HOVERED);
} else {
widget->flags = widget->flags | ZORI_FLAG_HOVERED;
}
return zori_widget_hover(widget);
}
int zori_widget_marked(struct zori_widget * widget) {
return widget && ((widget->flags & ZORI_FLAG_MARKED) != ZORI_FLAG_MARKED);
}
int zori_widget_marked_(struct zori_widget * widget, int set) {
if (set) {
widget->flags = widget->flags & (~ZORI_FLAG_MARKED);
} else {
widget->flags = widget->flags | ZORI_FLAG_MARKED;
}
return zori_widget_hover(widget);
}
int zori_xy_inside_widget_p(struct zori_widget * widget, double x, double y) {
return (rebox_coordinates_inside_p(&widget->box, x, y));
}
/* Updates the state of the UI. Pass in the time passed since last update. */
void zori_update(double dt)
{
zori_widget_raise_update_event(&the_zori_root->widget, dt);
struct zori_root * root = zori_get_root();
zori_widget_raise_update_event(&root->widget, dt);
}
/* Registers an event handler for a widget. */
zori_id zori_register(zori_id id, zori_event_type type, zori_handler_func handler, void * extra);
zori_font * zori_widget_font(struct zori_widget * widget) {
return widget->style.text.font;
}
zori_color zori_widget_forecolor(struct zori_widget * widget) {
return widget->style.text.color;
int zori_result(zori_id id) {
int result = 0;
struct zori_widget * widget = zori_get_widget(id);
if (widget) {
result = widget->result;
widget->result = 0;
}
return result;
}
zori_color zori_widget_backcolor(struct zori_widget * widget) {
return widget->style.back.color;
/* Returns the first parent or super-parent of this widget for which
* predicate() returns true.
*/
struct zori_widget *
zori_widget_find_parent(struct zori_widget * widget,
bool (*predicate)(struct zori_widget * parent, void * extra),
void * extra
) {
struct zori_widget * parent;
if (!widget) return NULL;
parent = widget->parent;
while (parent) {
if (predicate(parent, extra)) return parent;
if (parent == parent->parent) {
LOG_ERROR("GUI setup not correct. Parent/child loop in widget %p.", widget);
return NULL;
}
parent = parent->parent;
}
return NULL;
}
int zori_widget_h(struct zori_widget * widget) {
return widget->box.size.y;
bool zori_widget_is_type_predicate(struct zori_widget * widget, void * extra) {
zori_widget_type * type_ptr = extra;
if(!type_ptr) return false;
return (*type_ptr) == widget->type;
}
int zori_widget_w(struct zori_widget * widget) {
return widget->box.size.x;
struct zori_widget *
zori_widget_get_parent_of_type(struct zori_widget * widget,
zori_widget_type type) {
return zori_widget_find_parent(widget, zori_widget_is_type_predicate, &type);
}
int zori_widget_x(struct zori_widget * widget) {
return widget->box.at.x;
}
int zori_widget_y(struct zori_widget * widget) {
return widget->box.at.y;
struct zori_screen * zori_widget_get_screen(struct zori_widget * widget) {
struct zori_widget * screen_widget = zori_widget_get_parent_of_type(widget, ZORI_WIDGET_TYPE_SCREEN);
return zori_widget_to_screen(screen_widget);
}
int zori_mark_widget(struct zori_widget * widget) {
struct zori_screen * screen = zori_widget_get_screen(widget);
if (screen) {
screen->cursors.keyjoy.p = widget->box.at;
}
return ZORI_HANDLE_DONE;
}
#include "monolog.h"
#include "zori.h"
#include "zori_widget.h"
#include "zori_button.h"
......@@ -18,7 +18,7 @@ int zori_button_on_mouse_axes(union zori_event * event) {
zori_widget_hover_(widget, TRUE);
} else {
zori_widget_hover_(widget, FALSE);
}
}
return ZORI_HANDLE_PASS;
}
......@@ -30,8 +30,10 @@ int zori_button_on_mouse_click(union zori_event * event) {
float x = event->sys.ev->mouse.x;
float y = event->sys.ev->mouse.y;
if (zori_xy_inside_widget_p(widget, x, y)) {
widget->result = widget->id;
LOG_NOTE("Button %d clicked: %d, result %d.\n", widget->id, widget->result);
return zori_widget_raise_action_event(widget);
}
}
return ZORI_HANDLE_PASS;
}
......@@ -39,10 +41,10 @@ int zori_button_on_mouse_click(union zori_event * event) {
void zori_draw_button(struct zori_button * button) {
float x, y, w, h;
struct zori_style * style = &button->widget.style;
x = rebox_x(&button->widget.box) + 5;
y = rebox_y(&button->widget.box) + 5;
w = rebox_w(&button->widget.box) - 10;
h = rebox_h(&button->widget.box) - 10;
x = rebox_x(&button->widget.inner);
y = rebox_y(&button->widget.inner);
w = rebox_w(&button->widget.inner);
h = rebox_h(&button->widget.inner);
zori_widget_draw_background(&button->widget);
if (button->text) {
......@@ -86,17 +88,19 @@ zori_button_init(struct zori_button * button, zori_string * text) {
return zori_button_text_(button, text);
}
struct zori_button * zori_button_new(zori_id id, zori_id parent,
struct zori_button * zori_button_new(zori_id id, zori_id parent_id,
zori_box * box, zori_string * text) {
struct zori_button * button = NULL;
button = calloc(1, sizeof(*button));
if (!button) return NULL;
zori_widget_initall(&button->widget, id, zori_get_widget(parent),
zori_widget_initall(&button->widget, ZORI_WIDGET_TYPE_BUTTON, id, zori_get_widget(parent_id),
box, NULL, ZORI_ARRAY_AND_AMOUNT(zori_button_handlers));
if (!zori_button_init(button, text)) {
free(button);
button = NULL;
}
zori_widget_hover_(&button->widget, 0);
return button;
}
......
#include "zori_caption.h"
#include "zori.h"
#include "zori_widget.h"
#include "zori_console.h"
#include "draw.h"
......@@ -462,7 +464,7 @@ struct zori_console * zori_console_initall(struct zori_console * self, int id, z
if(!self) return NULL;
/* Allow only a single console. */
if (root->console) return NULL;
if(!zori_widget_initall(&self->widget, id, &root->widget, bounds, style, ZORI_ARRAY_AND_AMOUNT(zori_console_actions) ) ) {
if(!zori_widget_initall(&self->widget, ZORI_WIDGET_TYPE_CONSOLE, id, &root->widget, bounds, style, ZORI_ARRAY_AND_AMOUNT(zori_console_actions) ) ) {
return NULL;
}
ustrlist_init(&self->text);
......