Browse Source

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

Beoran 7 years ago
parent
commit
6c3e27555a

+ 1 - 0
cmake/ErutaFiles.cmake

@@ -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

+ 6 - 4
include/ui.h

@@ -1,12 +1,14 @@
 #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);
 
 
 

+ 147 - 19
include/zori/zori.h

@@ -1,6 +1,70 @@
 #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 page 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 <stdbool.h>
 #include "eruta.h"
 #include "rebox.h"
@@ -50,6 +114,10 @@ typedef ALLEGRO_USTR zori_text;
 #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))
@@ -74,20 +142,28 @@ typedef ALLEGRO_USTR zori_text;
  and  a pointer of a containing struct or union.
 */
 #define ZORI_CONTAINER_OF(PTR, TYPE, MEMBER) \
-        ((TYPE *)(((char *)(PTR)) - offsetof(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,
 };
 
 
@@ -135,19 +211,35 @@ 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;
+  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;
 };
 
 
@@ -195,7 +287,7 @@ static inline void * zori_event_set_data(union zori_event * event, void * data)
 /* A style part is a color, image and font applied to a part of the GUI. */
 struct zori_stylepart {
   zori_color       color;
-  zori_bitmap    * image;
+  zori_bitmap    * bitmap;
   zori_font      * font;
 };
 
@@ -205,6 +297,8 @@ struct zori_style {
   struct zori_stylepart back;
   struct zori_stylepart text;
   struct zori_stylepart border;
+  struct zori_stylepart hover;
+  struct zori_stylepart mark;
 };
 
 struct zori_widget;
@@ -218,7 +312,7 @@ enum zori_handle_result {
 };
 
 
-/* Returns whether or not a even needds to be propagated based on the 
+/* 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) {
@@ -227,7 +321,7 @@ static int zori_propagate_event_p(enum zori_handle_result result) {
      case ZORI_HANDLE_IGNORE: return !0;
      case ZORI_HANDLE_PASS:   return !0;
      default:                 return !0;
-   }
+  }
 }
 
 
@@ -254,7 +348,7 @@ struct zori_registry_entry {
 /* A widget registry as a dynamic array of entries. */
 struct zori_registry miao_of_type(struct zori_registry_entry);
 
-/* Generic flags for several zori structs. */
+/* 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,
@@ -264,10 +358,20 @@ enum zori_flag {
   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 actovation by the keyjoy cursor */
+  /* 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 { 
@@ -314,6 +418,13 @@ struct zori_widget {
   
   /* 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;
   
@@ -328,14 +439,20 @@ struct zori_widget {
   struct zori_widget_list children;
   
   /* Flags. */
-  enum zori_flag       flags;
+  enum zori_flag   flags;
+  
+  /* Type of the widget. */
+  zori_widget_type type;
+  
+  /* Generic "result", of last operation on widget, normally zero. */
+  int result;
 };
 
 
 /* An array of widget pointers. */
 struct zori_widget_array miao_of_type(struct zori_widget *);
 
-/* forward declarations. */
+/* Forward declarations. */
 struct zori_screen;
 struct zori_console;
 
@@ -428,12 +545,15 @@ 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);
@@ -443,17 +563,21 @@ 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_id id, struct zori_widget *parent, zori_rebox *box, struct zori_style *style);
+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_id id, struct zori_widget *parent, zori_rebox *box, struct zori_style *style, struct zori_handler *handlers, size_t amount);
+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);
@@ -468,9 +592,13 @@ int zori_xy_inside_widget_p(struct zori_widget * widget, double x, double y);
 
 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);
+
+
 #endif
 
 

+ 13 - 0
include/zori/zori_button.h

@@ -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
 
 

+ 10 - 0
include/zori/zori_caption.h

@@ -0,0 +1,10 @@
+#ifndef zori_caption_H_INCLUDED
+#define zori_caption_H_INCLUDED
+
+
+
+#endif
+
+
+
+

+ 2 - 0
include/zori/zori_console.h

@@ -1,6 +1,8 @@
 #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) */

+ 7 - 0
include/zori/zori_menu.h

@@ -1,6 +1,13 @@
 #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

+ 2 - 0
include/zori/zori_page.h

@@ -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, 

+ 1 - 0
include/zori/zori_root.h

@@ -1,6 +1,7 @@
 #ifndef zori_root_H_INCLUDED
 #define zori_root_H_INCLUDED
 
+#define ZORI_WIDGET_TYPE_ROOT ZORI_WIDGET_TYPE('z','r','o','o')
 
 
 #endif

+ 3 - 0
include/zori/zori_screen.h

@@ -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

+ 2 - 0
src/main.c

@@ -21,6 +21,8 @@
 #include "store.h"
 #include "scegra.h"
 #include "callrb.h"
+#include "zori.h"
+#include "zori_console.h" 
 
 
 

+ 28 - 6
src/state.c

@@ -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;
 }
 

+ 4 - 107
src/ui.c

@@ -1,116 +1,13 @@
 #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);
-}
 

+ 143 - 97
src/zori/zori.c

@@ -1,10 +1,12 @@
 
 #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;
+}
 
 

+ 13 - 9
src/zori/zori_button.c

@@ -1,4 +1,4 @@
-
+#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;
 }
 

+ 9 - 0
src/zori/zori_caption.c

@@ -0,0 +1,9 @@
+
+#include "zori_caption.h"
+
+
+
+
+
+
+

+ 3 - 1
src/zori/zori_console.c

@@ -1,5 +1,7 @@
 #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);

+ 149 - 1
src/zori/zori_menu.c

@@ -1,9 +1,157 @@
-
+#include "zori.h"
+#include "zori_widget.h"
 #include "zori_menu.h"
 
+struct zori_menu * zori_widget_to_menu(struct zori_widget * widget) {
+  if (!widget) return NULL;
+  return ZORI_CONTAINER_OF(widget, struct zori_menu, widget);
+}
+
+struct zori_widget * zori_menu_get_selected(struct zori_menu * menu) {
+  if (!menu) return NULL;
+  if (menu->selected_index < 0) return NULL;
+  return miao_unsafe_get(&menu->widget.children, menu->selected_index);
+}
+
+int zori_menu_select_index(struct zori_menu * menu, int index) {
+    struct zori_widget * selected = NULL;
+    size_t size = miao_size(&menu->widget.children);
+    if (size < 1)       return ZORI_HANDLE_ERROR;
+    if (index < 0)      return ZORI_HANDLE_DONE;
+    if ((size_t)index >= size)  return ZORI_HANDLE_DONE;
+    menu->selected_index = index;
+    selected = zori_menu_get_selected(menu);
+    zori_mark_widget(selected);
+    return ZORI_HANDLE_DONE;
+}
+
+int zori_menu_select_previous(struct zori_menu * menu) {
+  size_t size = miao_size(&menu->widget.children);
+  int new_selection = menu->selected_index - 1;
+  /* "roll over" */
+  if (new_selection < 0) { 
+    new_selection = (int)size - 1;
+  }
+  return zori_menu_select_index(menu, new_selection);
+}
+
+int zori_menu_select_next(struct zori_menu * menu) {
+  size_t size = miao_size(&menu->widget.children);
+  int new_selection = menu->selected_index + 1;
+  /* "roll over" */
+  if ((size_t)new_selection >= size) { 
+    new_selection = 0;
+  }
+  return zori_menu_select_index(menu, new_selection);
+}
+
+int zori_menu_select_first(struct zori_menu * menu) {
+  return zori_menu_select_index(menu, 0);
+}
+
+int zori_menu_activate_selected(struct zori_menu * menu) {
+  struct zori_widget * selected = zori_menu_get_selected(menu);
+  if (selected) { 
+    return zori_widget_raise_action_event(selected);
+  }
+  return ZORI_HANDLE_IGNORE;
+}
+
+
+int zori_menu_close(struct zori_menu * menu)  { 
+  struct zori_widget * parent;
+  parent = menu->widget.parent;
+  zori_widget_raise_close_event(parent, &menu->widget);
+  zori_widget_active_(&menu->widget, false);
+  return ZORI_HANDLE_DONE;
+}
+
+
+int zori_menu_on_child_close(union zori_event * event) {
+  struct zori_widget * widget = event->any.widget;
+  struct zori_menu   * menu   = zori_widget_to_menu(widget);
+  zori_mark_widget(zori_menu_get_selected(menu));
+  return ZORI_HANDLE_DONE;
+}
+  
+
+int zori_menu_on_key_down(union zori_event * event) {
+  struct zori_widget * widget = event->any.widget;
+  struct zori_menu   * menu = zori_widget_to_menu(widget);
+  struct zori_widget * item = NULL;
+  
+  
+  if (miao_out_of_bounds(&menu->widget.children, menu->selected_index)) {
+    return ZORI_HANDLE_IGNORE;
+  }
+  
+  item = miao_unsafe_get(&widget->children, menu->selected_index);
+  switch (event->sys.ev->keyboard.keycode) {
+    case ALLEGRO_KEY_UP:
+      return zori_menu_select_previous(menu);
+    
+    case ALLEGRO_KEY_DOWN:
+      return zori_menu_select_next(menu);
+          
+    case ALLEGRO_KEY_ENTER: 
+    case ALLEGRO_KEY_SPACE: 
+    case ALLEGRO_KEY_LCTRL: 
+    case ALLEGRO_KEY_RCTRL:
+      return zori_menu_activate_selected(menu);
+        
+    case ALLEGRO_KEY_BACKSPACE: 
+    case ALLEGRO_KEY_LSHIFT: 
+    case ALLEGRO_KEY_RSHIFT:
+      return zori_menu_close(menu);
+    
+    default:
+      return ZORI_HANDLE_IGNORE;
+  }  
+  return ZORI_HANDLE_IGNORE;
+}
 
 
+int zori_menu_on_draw(union zori_event * event) {
+  struct zori_widget * widget = event->any.widget;
+  zori_widget_draw_background(widget);
+  return ZORI_HANDLE_PASS;
+}
 
+struct zori_handler zori_menu_handlers[] = {
+  { ZORI_SYSTEM_EVENT_KEY_DOWN, zori_menu_on_key_down    , NULL}, 
+  { ZORI_EVENT_DRAW           , zori_menu_on_draw        , NULL}, 
+  { ZORI_EVENT_CLOSE          , zori_menu_on_child_close , NULL}, 
+}; 
 
 
+struct zori_menu * zori_menu_init(
+  struct zori_menu * menu, zori_id id, zori_id parent_id,
+  zori_rebox * box, struct zori_style * style
+  ) {
+    struct zori_widget * parent = zori_get_widget(parent_id);
+  if (!menu) return NULL;
+    menu->selected_index = 0;
+    zori_widget_initall(&menu->widget, ZORI_WIDGET_TYPE_MENU, id, parent, box, style,  
+      ZORI_ARRAY_AND_AMOUNT( zori_menu_handlers));
+    zori_widget_hover_(&menu->widget, false);
+    return menu;
+}
+    
 
+struct zori_menu * zori_menu_new(zori_id id, zori_id parent_id, zori_box * box) {
+  struct zori_menu * menu = NULL;
+  menu = calloc(1, sizeof(*menu));
+  if (!menu) return NULL;
+  if (!zori_menu_init(menu, id, parent_id, box, NULL)) {
+    free(menu);
+    return NULL;
+  }
+  return menu;
+}
+    
+    
+zori_id zori_new_menu(zori_id id, zori_id parent, zori_box * box) {
+  struct zori_menu * menu = zori_menu_new(id, parent, box);
+  if (!menu) return ZORI_ID_ENOMEM;
+  return menu->widget.id;
+}

+ 1 - 1
src/zori/zori_page.c

@@ -11,7 +11,7 @@ struct zori_page * zori_page_new(zori_id id, struct zori_widget * parent) {
   if (!parent) return NULL;
   page = calloc(1, sizeof(*page));
   if (!page) return NULL;
-  zori_widget_initall(&page->widget, id, parent, 
+  zori_widget_initall(&page->widget, ZORI_WIDGET_TYPE_PAGE, id, parent, 
                       NULL, NULL, NULL, 0); 
   return page;
 }

+ 1 - 1
src/zori/zori_screen.c

@@ -93,7 +93,7 @@ struct zori_screen * zori_screen_new(zori_id id, zori_display * display) {
   if (!screen) return NULL;
   box.size.x = al_get_display_width(display);
   box.size.y = al_get_display_height(display);
-  zori_widget_initall(&screen->widget, id, &zori_get_root()->widget, 
+  zori_widget_initall(&screen->widget, ZORI_WIDGET_TYPE_SCREEN, id, &zori_get_root()->widget, 
                       &box, NULL, ZORI_ARRAY_AND_AMOUNT(zori_screen_handlers)); 
   if (!zori_screen_init(screen, display)) {
     free(screen);

+ 115 - 2
src/zori/zori_widget.c

@@ -12,16 +12,129 @@
 void zori_widget_draw_background(struct zori_widget * widget) {
     float dx, dy, dw, dh;
     struct zori_style * style = &widget->style;
-    zori_bitmap * background  = style->back.image;
+    zori_bitmap * background  = style->back.bitmap;
+    zori_color color = style->back.color;
+    struct zori_stylepart * part =  & style->back;
     dx = rebox_x(&widget->box);
     dy = rebox_y(&widget->box);
     dw = rebox_w(&widget->box);
     dh = rebox_h(&widget->box);
     
+    if (zori_widget_hover(widget)) {
+      part = &style->hover;  
+    }
+    
+    if (zori_widget_marked(widget)) {
+      part = &style->mark;
+    }
+    
+    if (part->bitmap) {
+      background = part->bitmap;
+    }
+    
+    color = part->color;
+
     if (background) {
       image_blitscale9(background, dx, dy, dw, dh, -1, -1);
     } else {
-      draw_frame(dx, dy, dw, dh, 3, style->border.color, style->back.color);
+      draw_frame(dx, dy, dw, dh, 3, style->border.color, color);
     }
 }
 
+
+
+
+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));
+}
+
+
+/* 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;
+}
+
+zori_color zori_widget_backcolor(struct zori_widget * widget) {
+  return widget->style.back.color;
+}
+
+
+int zori_widget_h(struct zori_widget * widget) {
+  return widget->box.size.y;
+}
+
+int zori_widget_w(struct zori_widget * widget) {
+  return widget->box.size.x;
+}
+
+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;
+}
+
+
+

+ 20 - 0
test/zori/test_zori_caption.c

@@ -0,0 +1,20 @@
+/**
+* This is a test for zori_caption in $package$
+*/
+#include "si_test.h"
+#include "zori_caption.h"
+
+
+TEST_FUNC(zori_caption) {
+  TEST_DONE();
+}
+
+
+int main(void) {
+  TEST_INIT();
+  TEST_RUN(zori_caption);
+  TEST_REPORT();
+}
+
+
+