Browse Source

Reorganize Zori GUI code and clean up a few memory leaks.

Beoran 7 years ago
parent
commit
c6aeb37e81

+ 2 - 0
cmake/ErutaFiles.cmake

@@ -85,6 +85,8 @@ set(ERUTA_SRC_FILES
   src/zori/zori_menu.c
   src/zori/zori_page.c
   src/zori/zori_root.c
+  src/zori/zori_registry.c
   src/zori/zori_screen.c
+  src/zori/zori_style.c
   src/zori/zori_widget.c
 )

+ 1 - 2
include/ui.h

@@ -36,8 +36,7 @@ struct ui_state {
 
 
 void ui_setup(void);
-void ui_state_init(struct ui_state * ui);
-
+void ui_state_init(struct ui_state * ui, zori_display * display, zori_font * font);
 
 
 #endif

+ 37 - 2
include/zori/zori.h

@@ -17,7 +17,7 @@
 * 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 
+* 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.
@@ -447,6 +447,11 @@ struct zori_widget {
   
   /* 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);
+  
 };
 
 
@@ -603,8 +608,38 @@ int zori_result(int zori_id);
 int zori_mark_widget(struct zori_widget * widget);
 
 
-#endif
+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
 
 
 

+ 57 - 1
include/zori/zori_widget.h

@@ -1,7 +1,63 @@
 #ifndef zori_widget_H_INCLUDED
 #define zori_widget_H_INCLUDED
 
-void zori_widget_draw_background(struct zori_widget * widget);
+struct zori_widget;
+
+void zori_widget_cleanup_widget_only(struct zori_widget * widget);
+void zori_widget_destroy_widget_only(struct zori_widget * widget);
+void zori_widget_call_destructor_and_destroy(struct zori_widget * widget);
+void zori_widget_destroy(struct zori_widget * widget);
+int zori_widget_accepts_event(struct zori_widget *widget, union zori_event *event);
+int zori_widget_self_raise_event(struct zori_widget *widget, union zori_event *event);
+int zori_widget_raise_event(struct zori_widget *widget, 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_overdraw_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_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);
+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);
+zori_id zori_widget_margins_(struct zori_widget *widget, int left, int top, int right, int bottom);
+zori_id zori_widget_margin_(struct zori_widget *widget, int size);
+void zori_widget_draw_background(struct zori_widget *widget);
+int zori_widget_visible(struct zori_widget *widget);
+int zori_widget_active(struct zori_widget *widget);
+int zori_widget_active_(struct zori_widget *widget, int set);
+int zori_widget_visible_(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_xy_inside_widget_p(struct zori_widget *widget, double x, double y);
+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_widget_count_children(struct zori_widget *widget);
+struct zori_widget *zori_widget_get_child(struct zori_widget *widget, int index);
+zori_id zori_widget_paddings_(struct zori_widget *widget, int left, int top, int right, int bottom);
+zori_id zori_widget_padding_(struct zori_widget *widget, int size);
+zori_font *zori_widget_text_font(struct zori_widget *widget);
+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);
+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);
+void zori_widget_handle_event(struct zori_widget *widget, union zori_event *event);
+struct zori_widget *zori_widget_find_parent(struct zori_widget *widget, _Bool (*predicate)(struct zori_widget *parent, void *extra), void *extra);
+bool zori_widget_is_type(struct zori_widget * widget, zori_widget_type type);
+_Bool zori_widget_is_type_predicate(struct zori_widget *widget, void *extra);
+struct zori_widget *zori_widget_get_parent_of_type(struct zori_widget *widget, zori_widget_type type);
+struct zori_screen *zori_widget_get_screen(struct zori_widget *widget);
+int zori_mark_widget(struct zori_widget *widget);
+
 
 #endif
 

+ 7 - 4
src/state.c

@@ -238,18 +238,20 @@ void state_free(State * self) {
   self->area    = NULL;
   /* Disable the active tile map */
   state_active_map_id_(self, -1);
+  /* Disable console immediately. */
+  self->console = NULL; 
   /* Disable gui. */
   zori_shutdown();
-  
+  /* Clean up scripting state. */
   rh_free(self->ruby);
-  self->console = NULL; /* disable console immediately. */
   /* Deallocate stored objects. */
   store_done();
  
   // font_free(self->font);
   al_destroy_display(self->display);
   camera_free(self->camera);
-
+   
+  /* Clean up alegro. */ 
   al_uninstall_system();
   
   /* End logging. */
@@ -500,7 +502,8 @@ int state_initjoystick(State * self) {
 
 /* Initialize the GUI for the Eruta engine. */
 State * state_init_gui(State * self, BOOL fullscreen) {
-  ui_state_init(&self->ui);
+  ui_state_init(&self->ui, self->display, self->font);
+  return self;
 }
 
 

+ 26 - 18
src/ui.c

@@ -7,6 +7,7 @@
 #include "zori_button.h"
 #include "zori_page.h"
 #include "zori_menu.h"
+#include "monolog.h"
 #include "ui.h"
 
 
@@ -38,52 +39,59 @@ char INTRO_TEXT2[] = "Something happened, and I was changed!";
 
 zori_id ui_state_make_sub_menu(zori_id parent, int x, int y, int w, int bh); 
 
-void ui_state_init(struct ui_state * ui) { }
-
-#ifdef COMMENT_
 
 /* Set up Zori GUI. */
 
-void ui_state_init(struct ui_state * ui, zori_displkay * display) { 
+void ui_state_init(struct ui_state * ui, 
+zori_display * display,
+zori_font * font
+) { 
 
   struct zori_style style;
   Rebox box;
   memset(&style, 0, sizeof(style));
-  style.text.font  = self->font;
+  style.text.font  = font;
   style.text.color = color_rgb(255,255,255);
   style.back.color = color_rgba(64,0,0, 191);
   if ( !ZORI_ID_OK_P(zori_start(&style)) ) {
-    return state_errmsg_(self, "Out of memory when allocating GUI.");
+    LOG_ERROR( "Out of memory when allocating GUI.");
+    return;
+  }
+  
+  if (zori_start(&style) != ZORI_ID_OK) {
+    LOG_ERROR( "Cannot set up ZORI UI screen.");
+    return;
   }
   
-  self->ui.screen = zori_new_screen(-1, self->display);
+  ui->screen = zori_new_screen(-1, display);
   
-  if(!ZORI_ID_OK_P(self->ui.screen)) {
-    return state_errmsg_(self, "Could not init GUI screen.\n");
+  if(!ZORI_ID_OK_P(ui->screen)) {
+    LOG_ERROR( "Cannot set up main screen.");
+    return;
   }
   
-  self->ui.main_page = zori_new_page(-1, self->ui.screen);
-  LOG_NOTE("Main page: %d\n", self->ui.main_page);
+  ui->main.page = zori_new_page(-1, ui->screen);
+  LOG_NOTE("Main page: %d\n", ui->main.page);
   box = rebox_make(280, 80, 140, 240);  
-  self->ui.main_menu = zori_new_menu(-1, main_page, &box); 
+  ui->main.menu = zori_new_menu(-1, ui->main.page, &box); 
   
-  LOG_NOTE("Main menu: %d\n", self->ui.main_menu);
+  LOG_NOTE("Main menu: %d\n", ui->main.menu);
   
   
   {
     Rebox box = rebox_make(300, 100, 100, 60);
-    self->ui.continue_button = zori_new_button(-1, main_menu, &box, "Continue");
-    LOG_NOTE("Button: %d\n", self->ui.continue_button);
+    ui->main.button.resume = zori_new_button(-1, ui->main.menu, &box, "Continue");
+    LOG_NOTE("Button: %d\n", ui->main.button.resume);
   }
   {
     Rebox box = rebox_make(300, 200, 100, 60);
-    self->ui.new_button = zori_new_button(-1, main_menu, &box, "New");
-    LOG_NOTE("Button: %d\n", self->ui.new_button);
+    ui->main.button.new = zori_new_button(-1, ui->main.menu, &box, "New");
+    LOG_NOTE("Button: %d\n", ui->main.button.new);
   }
   
 }
   
-
+#ifdef COMMENT_
   
   INTRO_TEXT2 = 
 

+ 43 - 420
src/zori/zori.c

@@ -1,5 +1,8 @@
 
 #include "zori.h"
+#include "zori_registry.h"
+#include "zori_style.h"
+#include "zori_widget.h"
 #include "zori_console.h"
 #include "zori_screen.h"
 #include "miao.h"
@@ -13,65 +16,6 @@
 * interface and the menus. 
 */
 
-/* registry functionality */
-
-/** Compare registry entries. */
-int zori_registry_entry_compare(const void * v1, const void * v2) {
-  const struct zori_registry_entry * entry1 = v1;
-  const struct zori_registry_entry * entry2 = v2;
-  return (entry2->id - entry1->id);
-}
-
-/** Initialize the registry. */
-zori_id zori_registry_init(struct zori_registry * registry) {
-  miao_init(registry);
-  return ZORI_ID_OK;
-}
-
-
-/** Add an entry to the registry. */
-zori_id 
-zori_registry_add(struct zori_registry * registry, zori_id id, 
-struct zori_widget * widget) {
-  struct zori_registry_entry entry = { id, widget };
-  if (miao_push(registry, entry)) { 
-    miao_qsort(registry, zori_registry_entry_compare);
-    return ZORI_ID_OK;
-  }
-  return ZORI_ID_ENOMEM;
-}
-
-/** Look up an entry in the registry. */
-struct zori_registry_entry *  
-zori_registry_lookup_entry(struct zori_registry * registry, zori_id id) {
-  struct zori_registry_entry key = { id, NULL };
-  return miao_bsearch(registry, zori_registry_entry_compare, &key);
-}
-
-
-/** Look up a widget in the registry. */
-struct zori_widget *  
-zori_registry_lookup(struct zori_registry * registry, zori_id id) {
-  struct zori_widget * result = NULL;
-  struct zori_registry_entry * entry = NULL;
-  
-  entry = zori_registry_lookup_entry(registry, id);
-  if (entry) { 
-    result = entry->widget;
-  }
-  return result;
-}
-
-/** Remove an entry from the registry. */
-zori_id 
-zori_registry_remove(struct zori_registry * registry, zori_id id) {
-  struct zori_registry_entry * entry = NULL;
-  entry = zori_registry_lookup_entry(registry, id);
-  if (entry) {
-    miao_delete_entry(registry, entry);
-  }
-  return ZORI_ID_OK;
-}
 
 
 
@@ -129,143 +73,16 @@ int zori_handlers_handle(struct zori_handlers * me, union zori_event * event) {
 }
 
 
-/** Returns whether or not a widget will even handle an event in the first place.
- * For example, a hidden widget won't draw, and a disabled widget won't 
- * accept any system events, and a NULL event doesn't accept events at all.. */
-int zori_widget_accepts_event(struct zori_widget * widget, union zori_event * event) {
-  if (!widget) return 0;
-  switch(event->any.type) {
-    case ZORI_EVENT_DRAW:
-      return zori_widget_visible(widget);
-    default:
-      return zori_widget_active(widget);
-  }
-}   
-
-
-/** Raises an event on the widget itself only. */
-int zori_widget_self_raise_event(struct zori_widget * widget, 
-union zori_event * event) {
-  enum zori_handle_result result;
-  if (zori_widget_accepts_event(widget, event)) { 
-    event->any.widget = widget;
-    return zori_handlers_handle(&widget->handlers, event);
-  }
-  /* If the event is not accepted, it is passed on to the child widgets. */
-  return ZORI_HANDLE_PASS;
-}
-
-/* Raises an event on the widget, and if necessary, propagates it on to it's 
- * children automatically and recursively. */
-int 
-zori_widget_raise_event(struct zori_widget * widget, union zori_event * event) {
-  enum zori_handle_result result = 
-  zori_widget_self_raise_event(widget, event);
-  
-  if (zori_propagate_event_p(result)) {
-    enum zori_handle_result sub;
-    size_t index;
-    for (index = 0; index < miao_size(&widget->children); index++) {
-      struct zori_widget * child = miao_unsafe_get(&widget->children, index);
-      event->any.widget = child;
-      sub = zori_widget_raise_event(child, event);
-      if (sub == ZORI_HANDLE_DONE) {
-        result = ZORI_HANDLE_DONE;
-        break;
-      }
-    }
-  }
-  return result;
-}
-
-int zori_widget_raise_system_event
-(struct zori_widget * widget, zori_system_event * sysev) {
-  union zori_event event;
-  event.sys.any.type   = sysev->type;
-  event.sys.any.widget = widget;
-  event.sys.ev         = sysev;
-  return zori_widget_raise_event(widget, &event);
-}
-
-int zori_widget_raise_draw_event(struct zori_widget * widget) {
-  union zori_event event;
-  event.draw.any.type   = ZORI_EVENT_DRAW;
-  event.draw.any.widget = widget; 
-  return zori_widget_raise_event(widget, &event);
-}
-
-int zori_widget_raise_overdraw_event(struct zori_widget * widget) {
-  union zori_event event;
-  event.draw.any.type   = ZORI_EVENT_OVERDRAW;
-  event.draw.any.widget = widget; 
-  return zori_widget_raise_event(widget, &event);
-}
-
-
-int zori_widget_raise_done_event(struct zori_widget * widget) {
-  union zori_event event;
-  event.done.any.type   = ZORI_EVENT_DONE;
-  event.done.any.widget = widget; 
-  return zori_widget_raise_event(widget, &event);
-}
-
-int zori_widget_raise_free_event(struct zori_widget * widget) {
-  union zori_event event;
-  event.free.any.type   = ZORI_EVENT_FREE;
-  event.free.any.widget = widget; 
-  return zori_widget_raise_event(widget, &event);
-}
-
-int zori_widget_raise_update_event
-(struct zori_widget * widget, double dt) {
-  union zori_event event;
-  event.update.any.type   = ZORI_EVENT_DONE;
-  event.update.any.widget = widget;
-  event.update.dt         = dt; 
-  return zori_widget_raise_event(widget, &event);
-}
-
-int zori_widget_raise_action_event
-(struct zori_widget * widget) {
-  union zori_event event;
-  event.action.any.type   = ZORI_EVENT_ACTION;
-  event.action.any.widget = widget;
-  return zori_widget_raise_event(widget, &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;
 
-static struct zori_style * the_default_style = NULL;
-
-static struct zori_registry * the_zori_registry = NULL;
-
 struct zori_root * zori_get_root(void) {
   return the_zori_root;
 }
 
-
-int zori_widget_compare(const void * v1, const void * v2) {
-  const struct zori_widget * w1 = v1, * w2 = v2;
-  return (w2->id - w1->id);
-}
-
-
 struct zori_widget * zori_get_widget(zori_id id) {
-  if (!the_zori_registry) return NULL;
-  return zori_registry_lookup(the_zori_registry, id);
+  struct zori_registry * registry = zori_get_registry();
+  if (!registry) return NULL;
+  return zori_registry_lookup(registry, id);
 }
 
 zori_id zori_get_unused_id(void) {
@@ -279,108 +96,41 @@ zori_id zori_get_unused_id(void) {
   return id;
 }
 
-struct zori_handler * zori_widget_add_handler
-(struct zori_widget * widget, zori_event_type type, zori_handler_func * handler, void * data) {  
-  if ((!widget) || (!handler)) return NULL;
-  
-  return zori_handlers_add(&widget->handlers, type, handler, data);
-}
-
-struct zori_handler * zori_widget_add_handlers
-(struct zori_widget * widget, struct zori_handler * handlers, size_t amount) {
-  size_t i;
-  for (i = 0; i < amount; i++) { 
-    struct zori_handler * handler = handlers + i;
-    if (!zori_widget_add_handler(widget, handler->type, handler->handler, handler->data)) return NULL;
-  }
-  return handlers + amount - 1;
-}
-
-
-
-zori_id zori_start(struct zori_style * default_style) {
+zori_id zori_initialize_root(void) {
   if (the_zori_root) return ZORI_ID_OK;
   the_zori_root = calloc(1, sizeof(*the_zori_root));
   if (!the_zori_root) return ZORI_ID_ENOMEM;
-  the_default_style = calloc(1, sizeof(*the_default_style));
-  if (!the_default_style) return ZORI_ID_ENOMEM;
-  the_zori_registry = calloc(1, sizeof(*the_zori_registry));
-  if (!the_zori_registry) return ZORI_ID_ENOMEM;
-  
- 
-  the_default_style->text.font      = al_create_builtin_font();  
-  the_default_style->text.color     = al_color_name("white");
-  the_default_style->text.font_flags= ALLEGRO_ALIGN_LEFT;
-  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");  
+  return ZORI_ID_OK;
+} 
 
+zori_id zori_start(struct zori_style * default_style) {
+  zori_id res;
+  struct zori_root * root = NULL;
+  root = zori_get_root();
+ 
+  if (root) return ZORI_ID_OK;
+  
+  res = zori_initialize_default_style();
+  if (!ZORI_ID_OK_P(res)) return res;
+  res = zori_initialize_registry();
+  if (!ZORI_ID_OK_P(res)) return res; 
+  
+  res = zori_initialize_root();
+  if (!ZORI_ID_OK_P(res)) return res; 
   
+  root = zori_get_root();
+ 
   if (default_style) {
-    the_zori_root->widget.style = *default_style;
+    root->widget.style = *default_style;
   } else {
-    the_zori_root->widget.style = *the_default_style;
+    root->widget.style = *zori_get_default_style();
   }
   
-  the_zori_root->widget.id = 0;
-  zori_registry_add(the_zori_registry, the_zori_root->widget.id, &the_zori_root->widget);
+  root->widget.id = 0;
+  zori_registry_add(zori_get_registry(), the_zori_root->widget.id, &the_zori_root->widget);
   return the_zori_root->widget.id;  
 }
 
-void zori_widget_free(struct zori_widget * widget);
-
-struct zori_widget * zori_widget_done(struct zori_widget * widget) {
-  size_t index;
-  struct zori_widget * aid, * next;
-  if (!widget) return widget;
-  
-  for (index = 0; index < miao_size(&widget->children); index ++) {
-    struct zori_widget * child = miao_unsafe_get(&widget->children, index);
-    zori_widget_free(child);
-  }
-    
-  miao_done(&widget->handlers);
-  miao_done(&widget->children);
-  
-  return widget;
-}
-
-void zori_widget_free(struct zori_widget * widget) {
-  if (widget) {
-    zori_registry_remove(the_zori_registry, widget->id);
-  }
-  zori_widget_done(widget);
-  free(widget);
-}
-
-struct zori_widget *  
-zori_widget_add_child(struct zori_widget * parent, struct zori_widget * child) {
-  if (parent) { 
-    miao_push((&parent->children), 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);
@@ -393,23 +143,6 @@ zori_id zori_set_margin(zori_id id, int 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);
@@ -421,14 +154,6 @@ zori_id zori_set_padding(zori_id id, int size) {
   return zori_widget_padding_(widget, size);
 }
 
-
-zori_font * zori_widget_text_font(struct zori_widget * widget) {  
-  if ((!widget)) {
-    return the_default_style->text.font;
-  }
-  return  widget->style.text.font;
-}
-
 zori_font * zori_text_font(zori_id id) {  
   struct zori_widget * widget = zori_get_widget(id);
   return zori_widget_text_font(widget);
@@ -436,54 +161,14 @@ zori_font * zori_text_font(zori_id id) {
 
 
 
-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) {
-    if (!widget) return NULL;  
-    
-    miao_init(&widget->children);
-    miao_init(&widget->handlers);
-    widget->z     = 0;
-    widget->flags = 0;
-    widget->type  = type;
-    widget->id    = ( (id < 0) ? zori_get_unused_id() : id);
-    
-    if (style) {
-      widget->style = *style;
-      if (!widget->style.text.font) {
-        widget->style.text.font = the_default_style->text.font;
-      }
-    } else {
-      widget->style = *the_default_style;
-    }
-    if (box) {
-      widget->box = *box;
-    } else if (parent) {
-      widget->box = parent->box;
-    } 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);
+/** Destroy the Zori root widget */
+void zori_destroy_root(void) {
+  if (the_zori_root) {
+    zori_widget_free(&the_zori_root->widget);
   }
-  return widget;
-}
+  /* free(the_zori_root); is not needed, as it is cleaned up a sa widget */
+  the_zori_root = NULL;
+} 
 
 
 
@@ -491,23 +176,14 @@ struct zori_handler * handlers, size_t amount) {
  * negative on error.
  */
 zori_id zori_shutdown() {
-  assert((void *)(&the_zori_root->widget) == (void *)the_zori_root);
-  zori_widget_free(&the_zori_root->widget);
-  the_zori_root = NULL;
-  free(the_default_style);
-  the_default_style = NULL;
-  /* clean up registry last so zori_widget fre can unregister it's widgets.*/
-  miao_done(the_zori_registry);
-  free(the_zori_registry);
-  the_zori_registry = NULL;
-  
-  
+  zori_destroy_root();
+  zori_destroy_default_style();
+  zori_destroy_registry();
   return ZORI_ID_OK;
 }
 
 
 
-
 /* Creates a new screen widget. Normally this should be the first widget 
  * you create after zori_start.  */
 zori_id zori_new_screen(zori_id id, zori_display * display);
@@ -550,12 +226,6 @@ zori_id zori_new_button_widget(zori_id parent, zori_rebox box, char * text);
 zori_id zori_new_conversation_widget(zori_id parent, zori_rebox box, char * text);
 
 
-/* Lets the widget handle an event and bubbles it on to it's children 
- * recursively depending on the need to do that. */
-void zori_widget_handle_event(struct zori_widget * widget, union zori_event * event) {
-  zori_widget_raise_event(widget, event);
-}
-
 /* Draws the whole UI and all visible parts. */
 void zori_draw_all(void) {
   zori_widget_raise_draw_event(&the_zori_root->widget);
@@ -566,7 +236,10 @@ void zori_draw_all(void) {
 int zori_handle_system_event(zori_system_event * sysev) {
   /* All events are passed on to the console regardless if it is active. 
    * Otherwise, the events go into the system. */
-  if (the_zori_root->console ) { 
+  if (!the_zori_root) {
+    return ZORI_ID_EINVAL;
+  } 
+  if (the_zori_root && the_zori_root->console) { 
     if (zori_console_active(the_zori_root->console)) {
       int res = zori_widget_raise_system_event(&the_zori_root->console->widget, sysev);
       if (!zori_propagate_event_p(res)) return res;
@@ -594,54 +267,4 @@ int zori_result(zori_id id) {
   return result;
 }
 
-/* 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;
-}
-
-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; 
-}
-
-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);
-}
-
-
-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;
-}
-
 

+ 10 - 0
src/zori/zori_button.c

@@ -1,9 +1,11 @@
 #include "monolog.h"
 #include "zori.h"
 #include "zori_widget.h"
+#include "zori_caption.h"
 #include "zori_button.h"
 
 struct zori_button * zori_widget_to_button(struct zori_widget * widget) {
+  if (!zori_widget_is_type(widget, ZORI_WIDGET_TYPE_BUTTON)) return NULL;
   return ZORI_CONTAINER_OF(widget, struct zori_button, widget);
 }
 
@@ -73,10 +75,18 @@ zori_button_text_(struct zori_button * button, zori_string * text) {
   return button;
 }
 
+void zori_button_destroy(struct zori_widget * widget) {
+  struct zori_button * button = zori_widget_to_button(widget);
+  if (button) {
+    zori_caption_done(&button->caption);
+  }
+}
+
 struct zori_button * 
 zori_button_init(struct zori_button * button,const char * text) {
   if (button) {
     zori_caption_init(&button->caption, text);
+    button->widget.destroy = zori_button_destroy;
   }
   return button;
 }

+ 15 - 0
src/zori/zori_console.c

@@ -1,9 +1,14 @@
+#include <assert.h>
 #include "zori.h"
 #include "zori_widget.h"
 #include "zori_console.h"
 #include "draw.h"
 
 
+struct zori_console * zori_widget_to_console(struct zori_widget * widget) {
+  if (!zori_widget_is_type(widget, ZORI_WIDGET_TYPE_CONSOLE)) return NULL;  
+  return ZORI_CONTAINER_OF(widget, struct zori_console, widget);
+}
 
 /* Helper struct that keeps track of the BYTE positions within 
 a c string or USTR where a line or word starts or ends with a given maxwidth. */
@@ -454,6 +459,13 @@ struct zori_console * zori_console_alloc() {
   return calloc(1 , sizeof(struct zori_console));
 }
 
+/** Destructor for console. */
+void zori_console_destroy(struct zori_widget * widget) {
+  struct zori_console * console = zori_widget_to_console(widget);  
+  zori_console_done(console);
+}
+
+
 /* Amount of lines of display the console hat o keep track of. */
 #define ZORI_CONSOLE_MAX 200
 
@@ -462,6 +474,7 @@ struct zori_console * zori_console_initall(struct zori_console * self, int id, z
   struct zori_root * root = zori_get_root();
   
   if(!self) return NULL;
+  assert(root);
   /* Allow only a single console. */
   if (root->console) return NULL;
   if(!zori_widget_initall(&self->widget, ZORI_WIDGET_TYPE_CONSOLE, id, &root->widget, bounds, style,  ZORI_ARRAY_AND_AMOUNT(zori_console_actions) ) ) { 
@@ -486,6 +499,8 @@ struct zori_console * zori_console_initall(struct zori_console * self, int id, z
   self->command_data = NULL;
   /* Ony one console may be active. */
   root->console = self;
+  self->widget.destroy = zori_console_destroy;
+  
   return self;
 }
 

+ 3 - 0
src/zori/zori_longtext.c

@@ -1,6 +1,8 @@
 
 #include "monolog.h"
 #include "zori.h"
+#include "zori_widget.h"
+#include "zori_caption.h"
 #include "zori_longtext.h"
 
 
@@ -8,6 +10,7 @@
 
 
 struct zori_longtext * zori_widget_to_longtext(struct zori_widget * widget) {
+  if (!zori_widget_is_type(widget, ZORI_WIDGET_TYPE_LONGTEXT)) return NULL;
   return ZORI_CONTAINER_OF(widget, struct zori_longtext, widget);
 }
 

+ 2 - 1
src/zori/zori_menu.c

@@ -1,9 +1,10 @@
 #include "zori.h"
 #include "zori_widget.h"
+#include "zori_caption.h"
 #include "zori_menu.h"
 
 struct zori_menu * zori_widget_to_menu(struct zori_widget * widget) {
-  if (!widget) return NULL;
+  if (!zori_widget_is_type(widget, ZORI_WIDGET_TYPE_MENU)) return NULL;
   return ZORI_CONTAINER_OF(widget, struct zori_menu, widget);
 }
 

+ 4 - 1
src/zori/zori_page.c

@@ -1,8 +1,11 @@
-
+#include "zori.h"
+#include "zori_widget.h"
+#include "zori_caption.h"
 #include "zori_page.h"
 
 
 struct zori_page * zori_widget_to_page(struct zori_widget * widget) {
+  if (!zori_widget_is_type(widget, ZORI_WIDGET_TYPE_PAGE)) return NULL;
   return ZORI_CONTAINER_OF(widget, struct zori_page, widget);
 }
 

+ 4 - 0
src/zori/zori_screen.c

@@ -1,8 +1,12 @@
 #include "zori.h"
+#include "zori_widget.h"
+#include "zori_caption.h"
 #include "zori_page.h"
 #include "zori_screen.h"
 
+
 struct zori_screen * zori_widget_to_screen(struct zori_widget * widget) {
+  if (!zori_widget_is_type(widget, ZORI_WIDGET_TYPE_SCREEN)) return NULL;
   return ZORI_CONTAINER_OF(widget, struct zori_screen, widget);
 }
 

+ 355 - 0
src/zori/zori_widget.c

@@ -1,8 +1,207 @@
 #include "zori.h"
 #include "zori_widget.h"
+#include "zori_registry.h"
+#include "zori_style.h"
+#include "zori_screen.h"
+#include "monolog.h"
 #include "draw.h"
 
 
+/** Returns whether or not a widget will even handle an event in the first place.
+ * For example, a hidden widget won't draw, and a disabled widget won't 
+ * accept any system events, and a NULL widget doesn't accept events at all.. */
+int zori_widget_accepts_event(struct zori_widget * widget, union zori_event * event) {
+  if (!widget) return 0;
+  switch(event->any.type) {
+    case ZORI_EVENT_DRAW:
+      return zori_widget_visible(widget);
+    default:
+      return zori_widget_active(widget);
+  }
+}   
+
+
+/** Raises an event on the widget itself only. */
+int zori_widget_self_raise_event(struct zori_widget * widget, 
+union zori_event * event) {
+  enum zori_handle_result result;
+  if (zori_widget_accepts_event(widget, event)) { 
+    event->any.widget = widget;
+    return zori_handlers_handle(&widget->handlers, event);
+  }
+  /* If the event is not accepted, it is passed on to the child widgets. */
+  return ZORI_HANDLE_PASS;
+}
+
+/* Raises an event on the widget, and if necessary, propagates it on to it's 
+ * children automatically and recursively. */
+int 
+zori_widget_raise_event(struct zori_widget * widget, union zori_event * event) {
+  if (!widget) return ZORI_HANDLE_DONE;
+  
+  enum zori_handle_result result = 
+  zori_widget_self_raise_event(widget, event);
+  
+  if (zori_propagate_event_p(result)) {
+    enum zori_handle_result sub;
+    size_t index;
+    for (index = 0; index < miao_size(&widget->children); index++) {
+      struct zori_widget * child = miao_unsafe_get(&widget->children, index);
+      event->any.widget = child;
+      sub = zori_widget_raise_event(child, event);
+      if (sub == ZORI_HANDLE_DONE) {
+        result = ZORI_HANDLE_DONE;
+        break;
+      }
+    }
+  }
+  return result;
+}
+
+int zori_widget_raise_system_event
+(struct zori_widget * widget, zori_system_event * sysev) {
+  union zori_event event;
+  event.sys.any.type   = sysev->type;
+  event.sys.any.widget = widget;
+  event.sys.ev         = sysev;
+  return zori_widget_raise_event(widget, &event);
+}
+
+int zori_widget_raise_draw_event(struct zori_widget * widget) {
+  union zori_event event;
+  event.draw.any.type   = ZORI_EVENT_DRAW;
+  event.draw.any.widget = widget; 
+  return zori_widget_raise_event(widget, &event);
+}
+
+int zori_widget_raise_overdraw_event(struct zori_widget * widget) {
+  union zori_event event;
+  event.draw.any.type   = ZORI_EVENT_OVERDRAW;
+  event.draw.any.widget = widget; 
+  return zori_widget_raise_event(widget, &event);
+}
+
+
+int zori_widget_raise_done_event(struct zori_widget * widget) {
+  union zori_event event;
+  event.done.any.type   = ZORI_EVENT_DONE;
+  event.done.any.widget = widget; 
+  return zori_widget_raise_event(widget, &event);
+}
+
+int zori_widget_raise_free_event(struct zori_widget * widget) {
+  union zori_event event;
+  event.free.any.type   = ZORI_EVENT_FREE;
+  event.free.any.widget = widget; 
+  return zori_widget_raise_event(widget, &event);
+}
+
+int zori_widget_raise_update_event
+(struct zori_widget * widget, double dt) {
+  union zori_event event;
+  event.update.any.type   = ZORI_EVENT_DONE;
+  event.update.any.widget = widget;
+  event.update.dt         = dt; 
+  return zori_widget_raise_event(widget, &event);
+}
+
+int zori_widget_raise_action_event
+(struct zori_widget * widget) {
+  union zori_event event;
+  event.action.any.type   = ZORI_EVENT_ACTION;
+  event.action.any.widget = widget;
+  return zori_widget_raise_event(widget, &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);
+}
+
+
+int zori_widget_compare(const void * v1, const void * v2) {
+  const struct zori_widget * w1 = v1, * w2 = v2;
+  return (w2->id - w1->id);
+}
+
+struct zori_handler * zori_widget_add_handler
+(struct zori_widget * widget, zori_event_type type, zori_handler_func * handler, void * data) {  
+  if ((!widget) || (!handler)) return NULL;
+  
+  return zori_handlers_add(&widget->handlers, type, handler, data);
+}
+
+struct zori_handler * zori_widget_add_handlers
+(struct zori_widget * widget, struct zori_handler * handlers, size_t amount) {
+  size_t i;
+  for (i = 0; i < amount; i++) { 
+    struct zori_handler * handler = handlers + i;
+    if (!zori_widget_add_handler(widget, handler->type, handler->handler, handler->data)) return NULL;
+  }
+  return handlers + amount - 1;
+}
+
+
+
+void zori_widget_free(struct zori_widget * widget);
+
+struct zori_widget * zori_widget_done(struct zori_widget * widget) {
+  size_t index;
+  struct zori_widget * aid, * next;
+  if (!widget) return widget;
+  
+  for (index = 0; index < miao_size(&widget->children); index ++) {
+    struct zori_widget * child = miao_unsafe_get(&widget->children, index);
+    zori_widget_free(child);
+  }
+  
+  if (widget->destroy) {
+      widget->destroy(widget);
+  }
+      
+  miao_done(&widget->handlers);
+  miao_done(&widget->children);
+  
+  return widget;
+}
+
+void zori_widget_free(struct zori_widget * widget) {
+  if (widget) {
+    zori_registry_remove(zori_get_registry(), widget->id);
+  }
+  zori_widget_done(widget);
+  free(widget);
+}
+
+struct zori_widget *  
+zori_widget_add_child(struct zori_widget * parent, struct zori_widget * child) {
+  if (parent) { 
+    miao_push((&parent->children), 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);
+}
 
 
 
@@ -138,3 +337,159 @@ int zori_widget_y(struct zori_widget * widget) {
 
 
 
+/* Gets amount of child widgets of this widget, 0 if none, -1 on error.*/
+int zori_widget_count_children(struct zori_widget * widget) { 
+  if (!widget) return -1;
+  return miao_size(&widget->children); 
+}
+
+/* Returns the index-th child of his widget, or NULL on error or if no such child exists. */
+struct zori_widget * zori_widget_get_child(struct zori_widget * widget, int index) {
+    int count = zori_widget_count_children(widget);
+    if (count < 1) return NULL;
+    if (index < 0) return NULL;
+    if (index >= count) return NULL;
+    return miao_unsafe_get(&widget->children, index);
+}
+
+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_font * zori_widget_text_font(struct zori_widget * widget) {  
+  struct zori_style * style;
+  if (!widget) {
+    style = zori_get_default_style();
+  } else {
+    style = & widget->style;
+  }
+  return style->text.font;
+}
+
+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) {
+    if (!widget) return NULL;
+    struct zori_style * default_style = zori_get_default_style(); 
+    
+    miao_init(&widget->children);
+    miao_init(&widget->handlers);
+    widget->destroy = NULL;
+    widget->z     = 0;
+    widget->flags = 0;
+    widget->type  = type;
+    widget->id    = ( (id < 0) ? zori_get_unused_id() : id);
+    
+    if (style) {
+      widget->style = *style;
+      if (!widget->style.text.font) {
+        widget->style.text.font = default_style->text.font;
+      }
+    } else {
+      widget->style = *default_style;
+    }
+    if (box) {
+      widget->box = *box;
+    } else if (parent) {
+      widget->box = parent->box;
+    } 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(zori_get_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;
+}
+
+/* Lets the widget handle an event and bubbles it on to it's children 
+ * recursively depending on the need to do that. */
+void zori_widget_handle_event(struct zori_widget * widget, union zori_event * event) {
+  zori_widget_raise_event(widget, event);
+}
+
+
+/* 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;
+}
+
+bool zori_widget_is_type(struct zori_widget * widget, zori_widget_type type) {
+  if(!widget) return false;
+  return (type) == widget->type; 
+}
+
+bool zori_widget_is_type_predicate(struct zori_widget * widget, void * extra) {
+  zori_widget_type * type_ptr = extra;
+  if(!type_ptr) return false;
+  return zori_widget_is_type(widget, (*type_ptr)); 
+}
+
+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);
+}
+
+
+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;
+}
+
+
+