#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 ZOTI_ID_GENERATE ((zori_id)(-1)) #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, /** User definable activation event, such as a button being clicked * or a menu being opened. */ ZORI_EVENT_ACTION, /** User definable close event, such as a menu being closed. * Note that the element may be re-opened later. */ ZORI_EVENT_CLOSE, /** Child added event to ease setup. */ ZORI_EVENT_NEW_CHILD, /* Below these line are internal events that should not be set or * activated by the user of the Zori library. */ /** Activation event, such as a button being clicked or a menu being opened. * This event is ment to be used internally by the ZORI library before * the ZORI_EVENT_ACTION is raised. */ ZORI_INTERNAL_EVENT_ACTION, /** Raised just before closing a widget. Sent to the sub-widgets to indicate * that they should close as well. */ ZORI_INTERNAL_EVENT_CLOSE, }; 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; } /* Style flags */ enum zori_graphic_style_flag { ZORI_STYLE_FLAG_BORDER = 1, ZORI_STYLE_FLAG_FILL = 2, ZORI_STYLE_FLAG_DEFAULT= (ZORI_STYLE_FLAG_BORDER | ZORI_STYLE_FLAG_FILL), }; /* Font flags, from ALLEGRO font flags. */ enum zori_font_style_flag { ZORI_FONT_ALIGN_LEFT = ALLEGRO_ALIGN_LEFT, ZORI_FONT_ALIGN_CENTRE = ALLEGRO_ALIGN_CENTRE, ZORI_FONT_ALIGN_RIGHT = ALLEGRO_ALIGN_RIGHT, ZORI_FONT_ALIGN_INTEGER = ALLEGRO_ALIGN_INTEGER, }; /* Bitmap flags from allegro bitmap flags. */ enum zori_bitmap_flag { ZORI_BITMAP_FLIP_HORIZONTAL = ALLEGRO_FLIP_HORIZONTAL, ZORI_BITMAP_FLIP_VERTICAL = ALLEGRO_FLIP_VERTICAL, }; /** * For style: not all combinations make sense. How to improve this? * * What is available in CSS 1: * * font : font of the text (with many sub details in CCS not relevant here). * color: text color of an element * background-color: background color of an element. * background-image: background image of an element. * background-repeat: determines how/if the image is repeated. * background-attachment: fixed with regard to the canvas. * background-position: initial position of the background image. * word-spacing: text spacing * letter-spacing: text spacing * text-decoration: text decoration (underline, overline, etc) * vertical-align: vertical alignment, top, center, bottom based on the parent. * text-transform: * text-align: text alignment, left, right, centered. * text-indent: Text indentation. * line-height: Height of a line of text. * margin: sizes of the margin * padding: sizes of the padding * border-width: sizes of the border * border-color: color of the border * border-style: style of the border (per side) * border: border style, general of per side * width: width of the box * height: height of the box * float: floating * clear: float control * display: display type (visual, block, hiden, none, etc) * white-space: white space display. * list-style: list styling with type, image and position of the image. * * Squeezing it down to the essential, we retain: * * text.font : Font for texts. * text.color : Color for texts. * text.flags : Style flags, merges horizontal alignment, decoration, etc. * * back.color : Background color. * back.image : Background image. * back.flags : Style flags, solid background, no background, etc. * back.radius : Size of corner for image_scale9 algorithm. * * border.color : Border color. * border.size : Border size (thickness). * border.flags : Border style flags. * border.radius: Rounded corners radius. * * cursor.color : Text cursor color. * * mouse.back : Mouse cursor background information. * mouse.border : Mouse cursor border. * keyjoy.back : Keyjoy cursor background information. * keyjoy.border : Keyjoy cursor border. * * * width, height, margins and padding are not considered style but positioning. * The keyjoy and mouse cursor and a border are only available * per-screen. * * The text cursor has a color only. * * 1. Text can have a font, a color and font flags (centered, etc) * 2. A rectangle / container / widget can have a background image combined * with a background color it can have draw flags for the background image and * for the color (fill or not). Furthermore it can have a border color. * Possibly (through not suupported now) would be a background * * */ /* The style of the backgrounbd of a widget. */ struct zori_background_style { zori_color color; zori_bitmap * image; int flags; int radius; }; /* The style of the border of a widget. */ struct zori_border_style { zori_color color; zori_bitmap * image; int flags; int radius; int size; }; /* A text style has all elements needed to style a piece of text. I consists of the text color, font and font flags flags applied to a part of the GUI. */ struct zori_text_style { zori_color color; zori_font * font; int flags; }; /* A style is a set of style elements for a widget or cursor. */ struct zori_style { struct zori_background_style back; struct zori_border_style border; struct zori_text_style text; }; struct zori_widget; /* Event handler results. */ enum zori_handle_result { ZORI_HANDLE_ERROR = -1, /* An error ocurred, stop propagating to siblings as wel.*/ 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.) Sibling still receive the event. */ 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, /* The object is ready to report a "result". * What that is depends on the object. */ ZORI_FLAG_READY = 1 << 4, }; /* 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; /* Style of the cursor itself. */ struct zori_style style; /* Style for the element marked or hovered if that element responds to it. */ struct zori_style target_style; }; /* Support multiple cursors... */ struct zori_cursors { struct zori_cursor mouse; struct zori_cursor keyjoy; }; /* The type of the value of the result of a widget. */ enum zori_result_type { ZORI_RESULT_TYPE_NONE = 0, ZORI_RESULT_TYPE_INTEGER= 1, ZORI_RESULT_TYPE_STRING = 2, ZORI_RESULT_TYPE_CLOSED = 3, }; /* The value of a result of a widget, see below. */ union zori_result_value { int integer; zori_string * string; int closed; }; /* The "result" of a widget. If the flag ready is set, the * widget has a result to report . This normally happens when it was * clicked, or when a menu item was selected, */ struct zori_result { int ready; union zori_result_value value; enum zori_result_type type; void * extra; }; /* 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. */ struct zori_result 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; /** Icons of the root style (see below) */ struct zori_root_style_icons { zori_bitmap * paused; }; /* Style elements specific for the root element. These are global to the * whole GUI under the given root.*/ struct zori_root_style { struct zori_root_style_icons icons; }; /* * 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; /*- Style elements for all the GUI, not per widget. */ struct zori_root_style style; }; /* 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_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_root_widget(void); struct zori_widget *zori_get_widget(zori_id id); zori_id zori_get_unused_id(void); zori_id zori_initialize_root(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); void zori_destroy_root(void); 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); /* Use generated header file for function prototypes. */ #include "zori_proto.h" #endif