#ifndef zori_H_INCLUDED #define zori_H_INCLUDED /* * ZORI is a a GUI subsystem for use with Allegro 5 that handles * the user interface of Eruta. * * ZORI is a classic retained-mode GUI system that has an additional * pseudo-direct mode API. ZORI uses a handler system where events are handled * by call back functions which can be installed as specific handlers that are * activated on certain events. But, in the spirit of direct mode GUI's, all * widgets also keep state flags and value flags that can be used to see if they * have been activated and that can be inspected in a direct-mode GUI fashion. * With ZORI you can have your retained mode GUI pie and also eat it in direct * mode. * * In ZORI, there is only one top-level widget, the root, however, every widget * can contain any amount of child widgets. One level below the root widget are * the screen widgets which are intent to handle the GUI on one physical screen * or display. Every GUI screen can have different "pages", but only one page * should be active per screen. The idea is that a GUI page represents a single * GUI mode such as the main menu, the settings menu, the in game menu, etc, * where you definitely want only one to be active at any given time. * * The box model used in Zori will be as follows (XXX implement this): * ...................................... * .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 and/or contents of the widget and is unrelated to the padding. * * 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 top-level widgets, * namely the root and the page, and the other widgets. * The root and the page handle 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 functions change the state of several widgets, so they * are handled on the level of the container widgets. * The zori_widget and the other concrete widgets handle the individual * state and actions of the various widgets individually. * * * * */ #include #include "eruta.h" #include "rebox.h" #include "miao.h" #include "str.h" /* Defines for C11/C99/C89 portability */ #ifndef inline #define inline inline #endif /* Macros for possible later system library portability. */ #define ZORI_EVENT_TYPE_IS_USER(T) ALLEGRO_EVENT_TYPE_IS_USER(T) #define ZORI_SYSTEM_EVENT_KEY_DOWN ALLEGRO_EVENT_KEY_DOWN #define ZORI_SYSTEM_EVENT_KEY_UP ALLEGRO_EVENT_KEY_UP #define ZORI_SYSTEM_EVENT_KEY_CHAR ALLEGRO_EVENT_KEY_CHAR #define ZORI_SYSTEM_EVENT_MOUSE_AXES ALLEGRO_EVENT_MOUSE_AXES #define ZORI_SYSTEM_EVENT_MOUSE_AXES ALLEGRO_EVENT_MOUSE_AXES #define ZORI_SYSTEM_EVENT_MOUSE_BUTTON_DOWN ALLEGRO_EVENT_MOUSE_BUTTON_DOWN #define ZORI_SYSTEM_EVENT_MOUSE_BUTTON_UP ALLEGRO_EVENT_MOUSE_BUTTON_UP /* Typedefs for possible later system library portability. */ typedef ALLEGRO_COLOR zori_color; typedef ALLEGRO_BITMAP zori_bitmap; typedef ALLEGRO_FONT zori_font; typedef ALLEGRO_EVENT zori_system_event; typedef ALLEGRO_EVENT_TYPE zori_event_type; typedef ALLEGRO_DISPLAY zori_display; typedef Point zori_point; typedef Rebox zori_rebox; typedef Rebox zori_box; typedef int zori_id; typedef ALLEGRO_USTR zori_string; typedef ALLEGRO_USTR zori_text; /* Macros for possible portability. */ #define zori_font_lineheight(FONT) al_get_font_line_height(FONT) #define zori_font_drawstr(FONT, COLOR, X, Y, ATTR, TEXT) al_draw_ustr(FONT, COLOR, X, Y, ATTR, TEXT) /* Default sizes of a widget. */ #define ZORI_WIDGET_DEFAULT_W 640 #define ZORI_WIDGET_DEFAULT_H 480 /* Defaut padding and margin. */ #define ZORI_PADDING_DEFAULT 4 #define ZORI_MARGIN_DEFAULT 0 /* Zori ID's. */ #define ZORI_ID_OK_P(ID) ((ID) > -1) #define ZORI_ID_OK ((zori_id)(0)) #define ZORI_ID_ERROR ((zori_id)(-1)) #define ZORI_ID_ENOMEM ((zori_id)(-2)) #define ZORI_ID_EINVAL ((zori_id)(-3)) /** Macro for the number of elements of an array .*/ #define ZORI_ARRAY_AMOUNT(A) ((size_t)(sizeof(A))/(sizeof(*A))) /** Macro that expands to the the array itself, and the amount of elements * in that array, in that order. Only works on a statically sized array. */ #define ZORI_ARRAY_AND_AMOUNT(A) (A), ZORI_ARRAY_AMOUNT(A) /* Macro: ZORI_CONTAINER_OF(PTR, TYPE, MEMBER) This macro returns, for the given pointer, a pointer to a containing struct of type TYPE, in which PTR is a member named MEMBER. This enables cool ways of type genericity and extension in plain C. It should not run afoul of strict aliasing since it passes over a char * pointer and a pointer of a containing struct or union. */ #define ZORI_CONTAINER_OF(PTR, TYPE, MEMBER) \ ((PTR) ? ((TYPE *)(((char *)(PTR)) - offsetof(TYPE, MEMBER))) : NULL) /** Custom event types, used in conjunction with Allegro event types. */ enum zori_custom_event_type { ZORI_EVENT_CUSTOM = ALLEGRO_GET_EVENT_TYPE('z', 'o', 'r', 'i'), /** Update event, called at regular intervals to update the state of the UI.*/ ZORI_EVENT_UPDATE, /** First draw pass. */ ZORI_EVENT_DRAW, /** Second draw pass for cursors. */ ZORI_EVENT_OVERDRAW, /** Cleanup event. */ ZORI_EVENT_DONE, /** Destruction event. */ ZORI_EVENT_FREE, /** Activation event, such as a button being clicked or a menu being opened. */ ZORI_EVENT_ACTION, /** Close event, such as a menu being closed. Not for cleanup.*/ ZORI_EVENT_CLOSE, /** Child added event to ease setup. */ ZORI_EVENT_NEW_CHILD, }; struct zori_widget; /* Common fields for an event. */ struct zori_any_event { /* Type of the event, possibly copied from sysev. */ zori_event_type type; /* Widget the event is sent to. */ struct zori_widget * widget; /* Data specified in the handler. */ void * data; }; /* System event, that is coming from the underlying library used, e.g. Allegro. */ struct zori_sys_event { struct zori_any_event any; zori_system_event * ev; }; /* Update event, when UI has to update (animations, etc). */ struct zori_update_event { struct zori_any_event any; double dt; }; /* Draw event when the UI has to draw itself. */ struct zori_draw_event { struct zori_any_event any; }; /* Cleanup event. */ struct zori_done_event { struct zori_any_event any; }; /* Cleanup event. */ struct zori_free_event { struct zori_any_event any; }; /* Action event. */ struct zori_action_event { struct zori_any_event any; }; /* Close event. */ struct zori_close_event { struct zori_any_event any; /* The widget that was closed and that sent this message to it's parent. */ struct zori_widget * from; }; /* New child event. */ struct zori_new_child_event { struct zori_any_event any; /* The widget that was added as a child and that sent this message to it's parent. */ struct zori_widget * child; }; /** An event that ZORI can handle. The union is cunningly overlaid so * that the type field and the any field can be used in all cases. */ union zori_event { /* Type of the event, possibly copied from sysev. */ zori_event_type type; struct zori_any_event any; struct zori_sys_event sys; struct zori_draw_event draw; struct zori_update_event update; struct zori_done_event done; struct zori_free_event free; struct zori_action_event action; struct zori_close_event close; struct zori_new_child_event new_child; }; /* Helper functions to get widget from an event. */ static inline struct zori_widget * zori_event_widget(union zori_event * event) { if (!event) return NULL; return event->any.widget; } /* Helper functions to get data from an event. */ static inline void * zori_event_data(union zori_event * event) { if (!event) return NULL; return event->any.data; } /* Helper function that checks if an event is a sys_event. */ static inline bool zori_event_is_sys_event(union zori_event * event) { if (!event) return false; return (!ALLEGRO_EVENT_TYPE_IS_USER(event->type)); } /* Helper functions to get system event from an event. Type checks the type. */ static inline zori_system_event * zori_event_system_event(union zori_event * event) { if (!event) return NULL; if (!zori_event_is_sys_event(event)) return NULL; return event->sys.ev; } /* Helper functions to set widget for an event. */ static inline struct zori_widget * zori_event_set_widget(union zori_event * event, struct zori_widget * widget) { if (!event) return NULL; return event->any.widget = widget; } /* Helper functions to set data for an event. */ static inline void * zori_event_set_data(union zori_event * event, void * data) { if (!event) return NULL; return event->any.data = data; } /* A style part is a color, image, font, and font flags applied to a part of the GUI. */ struct zori_stylepart { zori_color color; zori_bitmap * bitmap; zori_font * font; int font_flags; }; /* A style is a set of style parts for different parts of the GUI. */ struct zori_style { struct zori_stylepart fore; struct zori_stylepart back; struct zori_stylepart text; struct zori_stylepart border; struct zori_stylepart hover; struct zori_stylepart mark; }; struct zori_widget; /* Event handler results. */ enum zori_handle_result { ZORI_HANDLE_ERROR = -1, /* An error ocurred, stop propagating to children.*/ ZORI_HANDLE_DONE = 0, /* The event was handled and consumed, no need to propagate to children automatically (widget may re-send event manually to children.)*/ ZORI_HANDLE_IGNORE = 1, /* Event wasn't handled, propagate to children. */ ZORI_HANDLE_PASS = 2, /* Event was handled, but needs to be propagated.*/ }; /* Returns whether or not a event needs to be propagated based on the * result of a handler. */ static int zori_propagate_event_p(enum zori_handle_result result) { switch(result) { case ZORI_HANDLE_ERROR: return 0; case ZORI_HANDLE_DONE: return 0; case ZORI_HANDLE_IGNORE: return !0; case ZORI_HANDLE_PASS: return !0; default: return !0; } } typedef enum zori_handle_result (zori_handler_func)(union zori_event * event); /* A single event handler */ struct zori_handler { zori_event_type type; zori_handler_func * handler; void * data; }; /* A dynamic array of event handlers event handlers. */ struct zori_handlers miao_of_type(struct zori_handler); /* An entry in a widget registry. */ struct zori_registry_entry { zori_id id; struct zori_widget * widget; }; /* A widget registry as a dynamic array of entries. */ struct zori_registry miao_of_type(struct zori_registry_entry); /* Generic state flags for several zori structs. */ enum zori_flag { /* The object is not visible, though it may still be interacted with.*/ ZORI_FLAG_HIDDEN = 1 << 0, /* The object cannot be interacted with, though it is still visible. */ ZORI_FLAG_DISABLED = 1 << 1, /* The object is both hidden and disabled. */ ZORI_FLAG_DEACTIVATED = ZORI_FLAG_HIDDEN | ZORI_FLAG_DISABLED, /* The object is being hovered by the mouse cursor. */ ZORI_FLAG_HOVERED = 1 << 2, /* The object is "marked" for activation by the keyjoy cursor */ ZORI_FLAG_MARKED = 1 << 3, }; /* Typedef for the type of a widget. * Not an enum since every widget may define this itself. */ typedef uint32_t zori_widget_type; /* Generic widget types. */ #define ZORI_WIDGET_TYPE_NONE ((zori_widget_type)(0)) /* Macro to help generate widget types. */ #define ZORI_WIDGET_TYPE(A, B, C, D) ((zori_widget_type)((A<<24) | (B<<16) | (C<<8) | D)) /* Mouse or keyboard/joystick cursor. */ struct zori_cursor { zori_point p; struct zori_widget * hover; struct zori_widget * focus; zori_bitmap * bitmap; enum zori_flag flags; }; /* Support multiple cursors... */ struct zori_cursors { struct zori_cursor mouse; struct zori_cursor keyjoy; }; /* on_enter on_enter(data = {}) on_event(*args) on_event(*data) on_key_down(*args) on_leave(name=nil) on_mouse_axes(t, x, y, z, w, dx, dy, dz, dw) on_mouse_button_down(t, x, y, z, w, b) on_mouse_button_up(t, x, y, z, w, b) on_mouse_in(x, y, from) on_mouse_out(x, y, to) on_resize */ struct zori_widget; struct zori_widget_list miao_of_type(struct zori_widget *); struct zori_widget { /* ID of the widget, used in most external API's. */ zori_id id; /* Root level widget under which this widget is active. */ struct zori_widget * root; /* Position and size of the widget. */ zori_rebox box; /* Outer rectangle of the widget, with the margin added. */ zori_rebox outer; /* Inner rectangle of the widget, with the padding removed. */ zori_rebox inner; /* Z ordering. */ int z; /* Style. */ struct zori_style style; /* Handlers. */ struct zori_handlers handlers; /* Related widgets. */ struct zori_widget * parent; struct zori_widget_list children; /* Flags. */ enum zori_flag flags; /* Type of the widget. */ zori_widget_type type; /* Generic "result", of last operation on widget, normally zero. */ int result; /* Cannot easily use the handers for destroying the widget, so in stead * provide a destructor. */ void (*destroy)(struct zori_widget * widget); }; /* An array of widget pointers. */ struct zori_widget_array miao_of_type(struct zori_widget *); /* Forward declarations. */ struct zori_screen; struct zori_console; /* * Root level widget, my spread out over several displays. * In Zori, there can only be a single root level widget active. * It's ID is always 0; */ struct zori_root { /* A root is a widget. */ struct zori_widget widget; /* Current active screen widget if any. */ struct zori_screen * active_screen; /* Current active console if any. */ struct zori_console * console; }; /* Forward declaration of a page. */ struct zori_page; struct zori_root * zori_get_root(void); /* Initializes Zori and creates a top level widget. Returns 0 on success * or negative on error. The style will be copied and set as default * if it is not NULL. Otherwise a built-in style will be used. * Not that ZORI will NOT clean up any images or fonts it uses by itself. */ zori_id zori_start(struct zori_style * default_style); /* Shut down Zori and destroys all widgets. Return 0 on succes or * negative on error. */ zori_id zori_shutdown(); /* Creates a new screen widget. Normally this should be the first widget * you create after zori_start. */ /* Activates the page on it's display. All other pages are dectivated and * hidden. */ zori_id zori_activate_page(zori_id page); /* Creates a new generic widget on the given screen with the given * dimensions. */ zori_id zori_new(zori_id screen, zori_rebox * box); /* Sets the flags of a widget. */ zori_id zori_set_flags(zori_id widget, enum zori_flag flags); /* Sets the whole style of a widget. */ zori_id zori_set_style(zori_id id, struct zori_style * style); /* Sets the background color of the widget. */ zori_id zori_set_background_color(zori_id id, zori_color color); /* Sets the foreground color of the widget. */ zori_id zori_set_foreground_color(zori_id id, zori_color color); /* Creates a new frame widget. */ zori_id zori_new_frame_widget(zori_id parent, zori_rebox box); /* Creates a new (vertical) menu widget. */ zori_id zori_new_menu_widget(zori_id parent, zori_rebox box, char * text); /* Creates a new button widget. */ zori_id zori_new_button_widget(zori_id parent, zori_rebox box, char * text); /* Creates a new conversation widget. */ zori_id zori_new_conversation_widget(zori_id parent, zori_rebox box, char * text); /* Draws the whole UI and all visible parts. */ void zori_draw_all(void); /* Updates the state of the UI. Pass in the time passed since last update. */ void zori_update(double 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); int zori_registry_entry_compare(const void *v1, const void *v2); zori_id zori_registry_init(struct zori_registry *registry); zori_id zori_registry_add(struct zori_registry *registry, zori_id id, struct zori_widget *widget); struct zori_registry_entry *zori_registry_lookup_entry(struct zori_registry *registry, zori_id id); struct zori_widget *zori_registry_lookup(struct zori_registry *registry, zori_id id); zori_id zori_registry_remove(struct zori_registry *registry, zori_id id); int zori_handler_compare(const void *v1, const void *v2); struct zori_handler *zori_handlers_add(struct zori_handlers *me, zori_event_type type, zori_handler_func *handler, void *data); void zori_handlers_done(struct zori_handlers *me); void zori_handlers_init(struct zori_handlers *me); struct zori_handler *zori_handlers_search(struct zori_handlers *me, zori_event_type type); int zori_handlers_handle(struct zori_handlers *me, union zori_event *event); int zori_widget_raise_system_event(struct zori_widget *widget, zori_system_event *sysev); int zori_widget_raise_draw_event(struct zori_widget *widget); int zori_widget_raise_done_event(struct zori_widget *widget); int zori_widget_raise_free_event(struct zori_widget *widget); int zori_widget_raise_update_event(struct zori_widget *widget, double dt); int zori_widget_raise_action_event(struct zori_widget *widget); int zori_widget_raise_close_event(struct zori_widget *widget, struct zori_widget * from); int zori_widget_compare(const void *v1, const void *v2); struct zori_widget *zori_get_widget(zori_id id); zori_id zori_get_unused_id(void); struct zori_handler *zori_widget_add_handler(struct zori_widget *widget, zori_event_type type, zori_handler_func *handler, void *data); struct zori_handler *zori_widget_add_handlers(struct zori_widget *widget, struct zori_handler *handlers, size_t amount); zori_id zori_start(struct zori_style *default_style); struct zori_widget *zori_widget_done(struct zori_widget *widget); void zori_widget_free(struct zori_widget *widget); struct zori_widget *zori_widget_add_child(struct zori_widget *parent, struct zori_widget *child); 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); zori_id zori_shutdown(void); struct zori_widget *zori_widget_initall(struct zori_widget *widget, zori_widget_type type, zori_id id, struct zori_widget *parent, zori_rebox *box, struct zori_style *style, struct zori_handler *handlers, size_t amount); void zori_draw_all(void); int zori_widget_visible(struct zori_widget *widget); int zori_widget_visible_(struct zori_widget *widget, int set); int zori_widget_active(struct zori_widget *widget); int zori_widget_active_(struct zori_widget *widget, int set); int zori_widget_hover(struct zori_widget *widget); int zori_widget_hover_(struct zori_widget *widget, int set); int zori_widget_marked(struct zori_widget *widget); int zori_widget_marked_(struct zori_widget *widget, int set); int zori_widget_active_(struct zori_widget *widget, int set); void zori_update(double dt); zori_font *zori_widget_font(struct zori_widget *widget); zori_color zori_widget_forecolor(struct zori_widget *widget); zori_color zori_widget_backcolor(struct zori_widget *widget); int zori_widget_h(struct zori_widget *widget); int zori_widget_w(struct zori_widget *widget); int zori_widget_x(struct zori_widget *widget); int zori_widget_y(struct zori_widget *widget); int zori_xy_inside_widget_p(struct zori_widget * widget, double x, double y); zori_font * zori_widget_text_font(struct zori_widget * widget); zori_font * zori_text_font(zori_id id); void zori_widget_drawroundframe(struct zori_widget *self); int zori_handle_system_event(zori_system_event * sysev); int zori_result(int zori_id); int zori_mark_widget(struct zori_widget * widget); int zori_registry_entry_compare(const void *v1, const void *v2); zori_id zori_registry_init(struct zori_registry *registry); zori_id zori_registry_add(struct zori_registry *registry, zori_id id, struct zori_widget *widget); struct zori_registry_entry *zori_registry_lookup_entry(struct zori_registry *registry, zori_id id); struct zori_widget *zori_registry_lookup(struct zori_registry *registry, zori_id id); zori_id zori_registry_remove(struct zori_registry *registry, zori_id id); int zori_handler_compare(const void *v1, const void *v2); struct zori_handler *zori_handlers_add(struct zori_handlers *me, zori_event_type type, zori_handler_func *handler, void *data); void zori_handlers_done(struct zori_handlers *me); void zori_handlers_init(struct zori_handlers *me); struct zori_handler *zori_handlers_search(struct zori_handlers *me, zori_event_type type); int zori_handlers_handle(struct zori_handlers *me, union zori_event *event); struct zori_root *zori_get_root(void); struct zori_widget *zori_get_widget(zori_id id); zori_id zori_get_unused_id(void); zori_id zori_start(struct zori_style *default_style); zori_id zori_set_margins(zori_id id, int left, int top, int right, int bottom); zori_id zori_set_margin(zori_id id, int size); zori_id zori_set_paddings(zori_id id, int left, int top, int right, int bottom); zori_id zori_set_padding(zori_id id, int size); zori_font *zori_text_font(zori_id id); zori_id zori_shutdown(void); void zori_draw_all(void); int zori_handle_system_event(zori_system_event *sysev); void zori_update(double dt); int zori_result(zori_id id); #endif