Browse Source

Move console from old bbwidget system to zori UI system.

Beoran 7 years ago
parent
commit
2e7fe02b27
14 changed files with 979 additions and 1261 deletions
  1. 1 1
      CMakeLists.txt
  2. 0 1
      cmake/ErutaFiles.cmake
  3. 6 2
      include/miao.h
  4. 2 1
      include/state.h
  5. 0 293
      include/widget.h
  6. 218 9
      include/zori.h
  7. 8 0
      src/fifi.c
  8. 2 2
      src/main.c
  9. 4 4
      src/react.c
  10. 16 14
      src/state.c
  11. 0 898
      src/widget.c
  12. 697 35
      src/zori.c
  13. 24 0
      test/test_miao.c
  14. 1 1
      test/test_widget.c

+ 1 - 1
CMakeLists.txt

@@ -20,7 +20,7 @@ include(CheckCXXSourceCompiles)
 
 if(CMAKE_COMPILER_IS_GNUCC)
 set(COMPILER_GCC 1)
-set(CMAKE_C_FLAGS "-W -Wall -Wno-unused -Wno-unknown-pragmas -g3 -gdwarf-2 -std=c99 -ffast-math -fsanitize=address -fstrict-aliasing -Wstrict-aliasing=2")
+set(CMAKE_C_FLAGS "-W -Wall -Wno-unused -Wno-unknown-pragmas -g3 -gdwarf-2 -std=c99 -ffast-math -fsanitize=address -fno-omit-frame-pointer -fstrict-aliasing -Wstrict-aliasing=2")
 # set(CMAKE_LD_FLAGS "-pg")
 # always use gnu99, debugging, all warnings except unused and unknown pragmas.
 # when compiling with gnu compiler.  

+ 0 - 1
cmake/ErutaFiles.cmake

@@ -76,7 +76,6 @@ set(ERUTA_SRC_FILES
   src/tr_thing.c
   src/ui.c
   src/utf8.c
-  src/widget.c
   src/xresor.c
   src/zori.c
 )

+ 6 - 2
include/miao.h

@@ -43,10 +43,14 @@
 #define miao_pop(ARR, DEFAULT) (((ARR)->n > 0) ? miao_unsafe_pop(ARR) : DEFAULT)
 #define miao_pop_ptr(ARR) (((ARR)->n > 0) ? miao_unsafe_pop_ptr(ARR) : NULL)
 
-#define miao_unsafe_push(ARR, VAL) ((ARR)->n++, (ARR)->a[(ARR)->n] = (VAL), &(VAL)) 
+#define miao_unsafe_push(ARR, VAL)                                    \
+          ((ARR)->a[(ARR)->n] = (VAL), (ARR)->n++, ((ARR)->a + (ARR)->n)) 
 
 #define miao_push(ARR, VAL)                                           \
-          (miao_grow(ARR, ((ARR)->n + 1)) ? miao_unsafe_push(ARR, VAL) : NULL)
+  (                                                                   \
+    (miao_grow(ARR, ((ARR)->n + 1))) ?                                \
+    miao_unsafe_push(ARR, VAL) : NULL                                 \
+  )
           
 #define miao_push_ptr(ARR)                                            \
   (                                                                   \

+ 2 - 1
include/state.h

@@ -12,6 +12,7 @@ typedef struct State_ State;
 #include "rh.h"
 #include "sprite.h"
 #include "spritelist.h"
+#include "zori.h"
 
 #define STATE_COLORS   16
 #define STATE_BLACK   0
@@ -35,7 +36,7 @@ ALLEGRO_COLOR
     float r , float g , float b , float a );
 
 Ruby * state_ruby (State * state );
-BBConsole * state_console (State * state );
+struct zori_console * state_console (State * state );
 int state_initjoystick (State * self );
 State * state_init (State * self , BOOL fullscreen );
 BOOL state_done (State * state );

+ 0 - 293
include/widget.h

@@ -1,293 +0,0 @@
-#ifndef bbwidget_H_INCLUDED
-#define bbwidget_H_INCLUDED
-
-#include "eruta.h"
-#include "image.h"
-#include "rebox.h"
-
-/* BBBBWidget, Beoran's Bad BBWidget set for allegro.  */
-
-/* Custom action types compatible with Allegro events */
-
-enum BBWidgetEventNumbers_ {
-  BBWIDGET_EVENT_DRAW   =   AL_ID('B', 'W', 'I', 0),
-  BBWIDGET_EVENT_UPDATE =   AL_ID('B', 'W', 'I', 1),
-  BBWIDGET_EVENT_DONE   =   AL_ID('B', 'W', 'I', 2),
-  BBWIDGET_EVENT_FREE   =   AL_ID('B', 'W', 'I', 3),
-  BBWIDGET_EVENT_FOCUS  =   AL_ID('B', 'W', 'I', 4)
-};
-
-
-
-
-/* Style describes the style of a widget. */
-struct Style_ {
-  Color   forecolor;
-  Color   backcolor;
-  Font  * font;
-  Image * background;
-};
-
-typedef struct Style_ Style;
-
-/* Predefine BBWidget typedef. */
-typedef struct BBWidget_ BBWidget;
-
-/* Predefine BBConsole typedef. */
-typedef struct BBConsole_ BBConsole;
-
-
-/* Predefine widget method table. */
-typedef struct BBWidgetMetab_ BBWidgetMetab;
-
-
-/* Very simple array based event handler. It's O(N) for now,
- but N is very small here, so the simplicity of creating a method table 
- is more important. */
-typedef struct BBWidgetAction_ BBWidgetAction;
-
-typedef int BBWidgetHandler(BBWidget * widget, void * data);
-
-struct BBWidgetAction_ { 
-  int              type;
-  BBWidgetHandler  * handler;
-};
-
-
-/* BBWidget flags. A widget can be : visible or not
-* listening (accepting input) input or not (if it  invisble and not listening 
-* input it's inactive, otherwise active) 
-* it can have focus or not (no focus listens to input 
-* but may ignore it, focus accepts input).
-* Selected is for checkboxes that are selected or buttons that are down, etc...
-* 
-*/
-enum BBWidgetFlags_ {
-  BBWIDGET_FLAG_VISIBLE    = 1,
-  BBWIDGET_FLAG_LISTENING  = 2,
-  BBWIDGET_FLAG_ACTIVE     = 3,
-  BBWIDGET_FLAG_FOCUSED    = 4,
-  BBWIDGET_FLAG_SELECTED   = 8,
-};
-
-/* Widget state flags . */
-typedef enum BBWidgetFlags_ BBWidgetFlags;
-
-
-
-/* Flag testing macros. */
-
-/* Checks if flag is set.
-Flag will be evaluated 2 times so must be a constant or a variable that is 
-only read. 
-*/
-#define BBWIDGET_FLAG_P(BBWIDGET, FLAG) (((BBWIDGET)->flag&(FLAG))==(FLAG))
-
-#define BBWIDGET_VISIBLE_P(BBWIDGET, FLAG) (((BBWIDGET)->flag&(FLAG))==(FLAG))
-
-#define BBWIDGET_HANDLE_OK      0
-#define BBWIDGET_HANDLE_IGNORE  1
-#define BBWIDGET_HANDLE_ERROR  -1
-
-/* BBWidget method cache for commonly used methods */
-struct BBWidgetMetab_ {
-  BBWidgetHandler * free;
-  BBWidgetHandler * done;
-  BBWidgetHandler * draw;
-  BBWidgetHandler * update;
-};
-
-/* BBWidget interface */
-struct BBWidgetIfa_ {
-  BBWidget      * self;
-  BBWidgetMetab * metab;
-};
-
-
-/* BBWidgets are individual parts of the UI.  
-As a simplification, BBWidgets are considered to occupy "panes" ordered 
-in the user interface from back to front. They do not contain other widgets 
-and do not have any generic relations between them.
-
-A note on pointer ownership: the pointers to font and image in style
-are NOT cleaned up, since style is intended to be mostly a shallow copy in which
-font and background image are repeated many times.
-*/
-struct BBWidget_ {
-  /* Event handler table. */
-  BBWidgetAction  * acts;
-  /* Method cache           */
-  BBWidgetMetab     metab;
-  /* Bounds, this is a rectangular box. */
-  Rebox           bounds;
-  /* Styling elements. */
-  Style           style;
-  /* BBWidget elements: */
-  /* Unique ID. */
-  int id;
-  /* Flags (active, disabled, etc) */
-  int flags;
-  /* Priority of widget */
-  int z;
-};
-
-// BBConsole input handler, called when a line of text (a command)
-// is typed.
-typedef int (BBConsoleCommand)
-            (BBConsole * console, const char * command, void * extra);
-
-
-/* This file was generated with:
-'cfunctions -c -aoff -n -w bbwidget_proto src/widget.c' */
-#ifndef CFH_BBWIDGET_PROTO
-#define CFH_BBWIDGET_PROTO
-
-/* From 'src/widget.c': */
-
-Style style_make (Color fore , Color back , Font * font , Image * background );
-
-Style * style_initstyle (Style * self , Style style );
-
-Style * style_init (Style * self , Color fore , Color back , Font * font , Image * background );
-
-Color style_forecolor (Style * self );
-
-Color style_backcolor (Style * self );
-
-Image * style_background (Style * self );
-
-Font * style_font (Style * self );
-
-Rebox bbwidget_bounds (BBWidget * self );
-
-int bbwidget_w (BBWidget * self );
-
-int bbwidget_h (BBWidget * self );
-
-int bbwidget_x (BBWidget * self );
-
-int bbwidget_y (BBWidget * self );
-
-int bbwidget_z (BBWidget * self );
-
-Color bbwidget_forecolor (BBWidget * self );
-
-Color bbwidget_backcolor (BBWidget * self );
-
-Font * bbwidget_font (BBWidget * self );
-
-Image * bbwidget_background (BBWidget * self );
-
-int bbwidget_flags (BBWidget * self );
-
-int bbwidget_id (BBWidget * self , int id );
-
-int bbwidget_flags_ (BBWidget * self , int flags );
-
-int bbwidget_id_ (BBWidget * self , int id );
-
-int bbwidget_flag (BBWidget * self , int flag );
-
-int bbwidget_unflag (BBWidget * self , int flag );
-
-int bbwidget_doflag (BBWidget * self , int flag , int set );
-
-int bbwidget_flag_p (BBWidget * self , int flag );
-
-int bbwidget_visible (BBWidget * self );
-
-int bbwidget_listening (BBWidget * self );
-
-int bbwidget_active (BBWidget * self );
-
-int bbwidget_focused (BBWidget * self );
-
-int bbwidget_selected (BBWidget * self );
-
-int bbwidget_visible_ (BBWidget * self , int set );
-
-int bbwidget_listening_ (BBWidget * self , int set );
-
-int bbwidget_active_ (BBWidget * self , int set );
-
-int bbwidget_focused_ (BBWidget * self , int set );
-
-int bbwidget_selected_ (BBWidget * self , int set );
-
-BBWidget * 
-bbwidget_acts_(BBWidget * self, BBWidgetAction * acts);
-
-BBWidget * 
-bbwidget_initall(BBWidget * self,  int id, BBWidgetAction * acts, 
-                 Rebox bounds, Style style);
-
-BBWidget * 
-bbwidget_initbounds(BBWidget * self, int id,  BBWidgetAction * acts, Rebox bounds);
-
-BBWidget * 
-bbwidget_initparent (BBWidget * self , int id , BBWidget * parent );
-
-BBWidget * 
-bbwidget_allocate(void);
-
-BBWidget * 
-bbwidget_done (BBWidget * widget );
-
-BBWidget * 
-bbwidget_free (BBWidget * self );
-
-void bbwidget_draw (BBWidget * self );
-
-void bbwidget_handle (BBWidget * self , ALLEGRO_EVENT * event );
-
-void bbwidget_update (BBWidget * self , ALLEGRO_EVENT * event );
-
-void bbwidget_drawroundframe (BBWidget * self );
-
-
-int bbconsole_handle(BBWidget * widget, ALLEGRO_EVENT * event);
-
-void 
-bbconsole_command_(BBConsole * self , BBConsoleCommand * command , void * data );
-
-int bbconsole_docommand (BBConsole * self , const char * text );
-
-int bbconsole_addstr (BBConsole * self , const char * str );
-
-int bbconsole_puts(BBConsole * self , const char * str );
-
-int bbconsole_vprintf(BBConsole * self, const char * format, va_list args);
-
-int bbconsole_printf(BBConsole * self, const char * format, ...);
-
-int bbconsole_draw(BBWidget * widget, void * data);
-
-void bbconsole_active_ (BBConsole * self , int active );
-
-int bbconsole_active (BBConsole * self );
-
-int bbconsole_scroll (BBConsole * self , int direction );
-
-int bbconsole_done(BBWidget * widget, void * data );
-
-int bbconsole_free(BBWidget * widget, void * data );
-
-BBConsole * bbconsole_alloc(void);
-
-BBConsole * bbconsole_initall (BBConsole * self , int id , Rebox bounds , Style style );
-
-BBConsole * bbconsole_new (int id , Rebox bounds , Style style );
-
-#endif /* CFH_BBWIDGET_PROTO */
-
-typedef BBWidget * (BBWidgetDraw)(BBWidget * self);
-
-
-
-
-
-#endif
-
-
-
-

+ 218 - 9
include/zori.h

@@ -1,11 +1,27 @@
 #ifndef zori_H_INCLUDED
 #define zori_H_INCLUDED
 
+#include <stdbool.h>
 #include "eruta.h"
 #include "rebox.h"
 #include "miao.h"
+#include "str.h"
 
-/* Typedefs for possible later portability. */
+/* 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
+
+
+/* Typedefs for possible later system library portability. */
 typedef ALLEGRO_COLOR  zori_color;
 typedef ALLEGRO_BITMAP zori_bitmap;
 typedef ALLEGRO_FONT   zori_font;
@@ -18,6 +34,11 @@ typedef Point zori_point;
 typedef Rebox zori_rebox;
 typedef int zori_id;
 
+/* 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)
+
+
 #define ZORI_ID_OK_P(ID)  ((ID) > -1)
 #define ZORI_ID_OK        ((zori_id)(0))
 #define ZORI_ID_ERROR     ((zori_id)(-1))
@@ -43,17 +64,108 @@ enum zori_custom_event_type {
   ZORI_EVENT_CUSTOM = ALLEGRO_GET_EVENT_TYPE('z', 'o', 'r', 'i'),
   ZORI_EVENT_UPDATE,
   ZORI_EVENT_DRAW,
+  ZORI_EVENT_DONE,
+  ZORI_EVENT_FREE,
 };
 
 
 struct zori_widget;
 
-struct zori_event {
-  zori_system_event    sysev;
+/* 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     * sysev;
+}; 
+
+/* 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;
+};
+
+
+/** 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;
+};
+
+
+/* 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.sysev;
+}
+
+
+/* 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;
+}
+
 
 struct zori_stylepart {
   zori_color       color;
@@ -70,7 +182,16 @@ struct zori_style {
 
 struct zori_widget;
 
-typedef int zori_handler_func(struct zori_event * event);
+/* Event handler results. */
+enum zori_handle_result {
+  ZORI_HANDLE_ERROR   = -1, 
+  ZORI_HANDLE_OK      = 0,
+  ZORI_HANDLE_IGNORE  = 1,
+};
+
+
+
+typedef enum zori_handle_result (zori_handler_func)(union zori_event * event);
 
 /* A single event handler */
 struct zori_handler {
@@ -136,6 +257,10 @@ 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;
@@ -155,9 +280,8 @@ struct zori_widget {
   struct zori_handlers handlers;
   
   /* Related widgets. */
-  struct zori_widget * parent;
-  struct zori_widget * child;
-  struct zori_widget * sibling;
+  struct zori_widget *    parent;
+  struct zori_widget_list children;
   
   /* Flags. */
   enum zori_flag       flags;
@@ -178,8 +302,6 @@ struct zori_screen;
 struct zori_root {
   /* A root is a widget. */ 
   struct zori_widget widget;
-  /* It has an array of all GUI widgets it manages. */
-  struct zori_widget_array * widgets;
   /* Current active screen widget if any. */
   struct zori_screen * active_screen;
 };
@@ -272,6 +394,93 @@ void zori_update(double dt);
 zori_id zori_register(zori_id id,  zori_event_type type, zori_handler_func handler, void * extra);
 
 
+int zori_registry_entry_compare(const void *v1, const void *v2);
+zori_id zori_registry_init(struct zori_registry *registry);
+zori_id zori_registry_add(struct zori_registry *registry, zori_id id, struct zori_widget *widget);
+struct zori_registry_entry *zori_registry_lookup_entry(struct zori_registry *registry, zori_id id);
+struct zori_widget *zori_registry_lookup(struct zori_registry *registry, zori_id id);
+zori_id zori_registry_remove(struct zori_registry *registry, zori_id id);
+int zori_handler_compare(const void *v1, const void *v2);
+struct zori_handler *zori_handlers_add(struct zori_handlers *me, zori_event_type type, zori_handler_func *handler, void *data);
+void zori_handlers_done(struct zori_handlers *me);
+void zori_handlers_init(struct zori_handlers *me);
+struct zori_handler *zori_handlers_search(struct zori_handlers *me, zori_event_type type);
+int zori_handlers_handle(struct zori_handlers *me, union zori_event *event);
+int zori_widget_raise_system_event(struct zori_widget *widget, zori_system_event *sysev);
+int zori_widget_raise_draw_event(struct zori_widget *widget);
+int zori_widget_raise_done_event(struct zori_widget *widget);
+int zori_widget_raise_free_event(struct zori_widget *widget);
+int zori_widget_raise_update_event(struct zori_widget *widget, double dt);
+int zori_widget_compare(const void *v1, const void *v2);
+struct zori_widget *zori_get_widget(zori_id id);
+zori_id zori_get_unused_id(void);
+struct zori_handler *zori_widget_add_handler(struct zori_widget *widget, zori_event_type type, zori_handler_func *handler, void *data);
+struct zori_handler *zori_widget_add_handlers(struct zori_widget *widget, struct zori_handler *handlers, size_t amount);
+zori_id zori_start(struct zori_style *default_style);
+struct zori_widget *zori_widget_done(struct zori_widget *widget);
+void zori_widget_free(struct zori_widget *widget);
+struct zori_widget *zori_widget_add_child(struct zori_widget *parent, struct zori_widget *child);
+struct zori_widget *zori_widget_init(struct zori_widget *widget, 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, struct zori_widget *parent, zori_rebox *box, struct zori_style *style, size_t amount, struct zori_handler *handlers);
+void zori_draw_all(void);
+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);
+void zori_update(double dt);
+zori_font *zori_widget_font(struct zori_widget *widget);
+zori_color zori_widget_forecolor(struct zori_widget *widget);
+zori_color zori_widget_backcolor(struct zori_widget *widget);
+int zori_widget_h(struct zori_widget *widget);
+int zori_widget_w(struct zori_widget *widget);
+int zori_widget_x(struct zori_widget *widget);
+int zori_widget_y(struct zori_widget *widget);
+
+void zori_widget_drawroundframe(struct zori_widget *self);
+
+
+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) */
+struct zori_console {
+  struct zori_widget widget;
+  USTRList  text;
+  int       count;
+  int       max;
+  int       start;
+  int       charw;
+  int       cursor;
+  char    * buf;
+  USTR    * input;
+  zori_console_command * command; // called when a command has been entered, if set.
+  void    * command_data; // command data.
+};
+
+
+
+struct zori_console *zori_widget_console(struct zori_widget *widget);
+int zori_console_docommand(struct zori_console *self, const char *text);
+int zori_console_addstr(struct zori_console *self, const char *str);
+int zori_console_addustr(struct zori_console *self, const ALLEGRO_USTR *ustr);
+int zori_console_puts(struct zori_console *self, const char *str);
+int zori_console_vprintf(struct zori_console *self, const char *format, va_list args);
+int zori_console_printf(struct zori_console *self, const char *format, ...);
+int zori_console_draw(union zori_event *zevent);
+void zori_console_active_(struct zori_console *self, int active);
+int zori_console_active(struct zori_console *self);
+int zori_console_scroll(struct zori_console *self, int direction);
+int zori_console_handle_keychar(union zori_event *zevent);
+int zori_console_handle_keydown(union zori_event *zevent);
+int zori_console_handle_mouseaxes(union zori_event *zevent);
+int zori_console_handle_ignore(union zori_event *zevent);
+int zori_console_handle(struct zori_widget *widget, zori_system_event *sevent);
+int zori_console_done(struct zori_widget *widget);
+int zori_console_free(struct zori_widget *widget);
+struct zori_console *zori_console_alloc(void);
+struct zori_console *zori_console_initall(struct zori_console *self, int id, zori_rebox *bounds, struct zori_style *style);
+struct zori_console *zori_console_new(int id, zori_rebox *bounds, struct zori_style *style);
+
+
 
 #endif
 

+ 8 - 0
src/fifi.c

@@ -81,6 +81,7 @@ ALLEGRO_PATH * fifi_data_path_ = NULL;
 
 /* Looks for the data folder in various places, return NULL if not found. */
 static ALLEGRO_PATH * fifi_find_data_path(void) {
+  char * cwd;
   ALLEGRO_PATH * path = al_get_standard_path(ALLEGRO_RESOURCES_PATH);
   // ALLEGRO_RESOURCES_PATH is normally where the program itself is installed.
   
@@ -97,6 +98,13 @@ static ALLEGRO_PATH * fifi_find_data_path(void) {
   al_append_path_component(path, "data");
   if(PATH_EXISTS(path)) return path;
   al_destroy_path(path);
+  /** $CWD /data */
+  cwd   = al_get_current_directory();
+  path  = al_create_path_for_directory(cwd);
+  al_free(cwd);
+  al_append_path_component(path, "data");
+  if(PATH_EXISTS(path)) return path;
+  al_destroy_path(path);
   return NULL;
 }
 

+ 2 - 2
src/main.c

@@ -63,11 +63,11 @@ React * main_react_key_down(React * self, ALLEGRO_KEYBOARD_EVENT * event) {
     case ALLEGRO_KEY_F1:
     case ALLEGRO_KEY_F3:
       /* Toggle the console here. */
-      bbconsole_active_(state_console(state), !bbconsole_active(state_console(state)));
+      zori_console_active_(state_console(state), !zori_console_active(state_console(state)));
     break;
     case ALLEGRO_KEY_F2:
       /* Hide the console. */
-      bbconsole_active_(state_console(state), FALSE);
+      zori_console_active_(state_console(state), FALSE);
     break;  
     case ALLEGRO_KEY_F5:
       /* Reload main script (and hence all other scripts that it loads)  on F5 */

+ 4 - 4
src/react.c

@@ -4,7 +4,7 @@
 #include "mode.h"
 #include "state.h"
 #include "react.h"
-#include "widget.h"
+#include "zori.h"
 
 /** Initializes the react structure so it does nothing at all in all cases. */
 React * react_initempty(React * self, void * data) {
@@ -99,7 +99,7 @@ React * react_react(React * self, ALLEGRO_EVENT * event) {
 React * react_poll(React * self, void * state) {
   int res;
   ALLEGRO_EVENT * event;
-  BBConsole * console = state_console(state);
+  struct zori_console * console = state_console(state);
 
   if(!self) return NULL;
   // yes an assignment is fine here :)
@@ -107,8 +107,8 @@ React * react_poll(React * self, void * state) {
     /* Let react react first, then if that fails, send to the console,
     but only if it is active. If not active, send the event to ruby */
     if(!react_react(self, event))  { 
-      if (bbconsole_active(console)) { 
-        bbconsole_handle((BBWidget *)console, event);
+      if (zori_console_active(console)) { 
+        zori_console_handle(&console->widget, event);
       } else {
         rh_poll_event(state_ruby(state_get()), event);
       }

+ 16 - 14
src/state.c

@@ -90,7 +90,7 @@ struct State_ {
    The ruby and error message GUI console.
    Implemented in C so it's usable even if there are script bugs.   
   */
-  BBConsole           * console;   
+  struct zori_console * console;   
   /* The current actor, controlled by the player. */
   Thing               * actor;
 
@@ -106,13 +106,13 @@ struct State_ {
 static State * global_state_ = NULL;
 
 /** Various loggers. One for stdout if not on windows,
- * one to a file and one to the BBConsole */
+ * one to a file and one to the Console */
 int state_console_logf(char * file, int line, char * level,
                        void * data, char * format, va_list args) {
-  BBConsole * console = data;
+  struct zori_console * console = data;
   if (console) {
-  bbconsole_printf(console, "%s %s %d:", level, file, line);
-   return bbconsole_vprintf(console, format, args);
+  zori_console_printf(console, "%s %s %d:", level, file, line);
+   return zori_console_vprintf(console, format, args);
   }
   return -1;
 }
@@ -231,7 +231,6 @@ void state_free(State * self) {
   zori_shutdown();
   
   rh_free(self->ruby);
-  bbconsole_free((BBWidget *)self->console, NULL);
   self->console = NULL; /* disable console immediately. */
   /* Deallocate stored objects. */
   store_done();
@@ -283,7 +282,7 @@ Ruby * state_ruby(State * state) {
 }
 
 /** Gets console intepreter for state. */
-BBConsole * state_console(State * state) {
+struct zori_console * state_console(State * state) {
   return state->console;
 }
 
@@ -639,17 +638,20 @@ State * state_init(State * self, BOOL fullscreen) {
   
   /* Set up console. */
   {
-    Style   style = { color_rgb(255,255,255), color_rgba(64,0,0, 191), 
-                      self->font, NULL};
+    struct zori_style style;
+    memset(&style, 0, sizeof(style));
+    style.text.font  = self->font;
+    style.text.color = color_rgb(255,255,255);
+    style.back.color = color_rgba(64,0,0, 191);
     Rebox  bounds = { {20, 20} , {600, 400} }; 
-    self->console = bbconsole_new(1, bounds, style);
+    self->console = zori_console_new(1, &bounds, &style);
     if(!self->console) {
       return state_errmsg_(self, "Out of memory when allocating console.");
     }
   }
-  bbconsole_puts(self->console, "BBConsole started ok!");
+  zori_console_puts(self->console, "Zori Console started ok!");
   // set up ruby callback for console commands 
-  bbconsole_command_(self->console, rh_run_console_command, self->ruby);
+  zori_console_command_(self->console, rh_run_console_command, self->ruby);
 
   // set up logging to console
   monolog_add_logger(self->console, &state_console_logger);
@@ -758,8 +760,8 @@ void state_draw(State * self) {
                         10, 10, 0, "FPS: %.0f", state_fps(self));
     } 
     
-    /* Draw the console (will autohide if not active). */
-    bbwidget_draw((BBWidget *)state_console(self));
+    /* Draw the ui and the console (will autohide if not active). */
+    zori_draw_all();
     state_scale_display(self);
 }
 

+ 0 - 898
src/widget.c

@@ -1,898 +0,0 @@
-
-#include "eruta.h"
-#include "mem.h"
-#include "str.h"
-#include "image.h"
-#include "dynar.h"
-#include "flags.h"
-#include "ui.h"
-#include "widget.h"
-#include "draw.h"
-#include "bad.h"
-
-
-/*
-*
-* Control of Eruta should be possible comfortably though joystick,
-* keyboard, and a single mouse only, so people with physical limitations
-* can also play the game.
-*
-* The following widgets will be needed:
-* 1 ) BBConsole to log errors and interact with ruby/state.
-* 2 ) Menu (vertical, horizontal) 
-* 3 ) Ring menu ?
-* 4 ) Notification/explanation display.
-* 5 ) Talk bubbles.
-* 6 ) Talk dialog with text display and option selection.
-* User interface is different depending on the game "mode"
-* Possible modes could be intro, start menu, 
-* configuration, image/text display (story mode/cutscenes, etc)
-* normal mode, battle mode and menu mode.
-* 
-* Idea for simplification: actually an widget and a mode
-* are comparable in that they deal with inpuut each differently, and
-* output each in differen ways. Only, a mode is a combination of 
-* UI elements. For example, the main menu is a static background 
-* with a vertical menu over it, and the menu has the focus.
-*
-* Ideas on event handling: The "problem" with allegro is that the
-* even types are sparse, and potentially stretch the whole integer range.
-* Hence, it's not realistic to use a simple array as a jump table. Some
-* more complex structure, like a hsah table, tree, etc, would be needed 
-* for fast lookup of the event handler function. Hence, and probably by 
-* design, a case statement is the best way to handle and dispatch the 
-* input. The react system I wrote has the disadvantage that it will add
-* an additional pointer indirection to that switch statement, a problem the 
-* console doesn't have. Hence, I'll go for a simple event handler
-* for every function that takes the allegro event like the console does.
-* 
-* Hence every widget will have tthe following basic interface 
-* functions: 
-* free (on destruction)
-* done (on deinit)
-* event (on any allegro or user event or action)
-* update (called every n ticks, when the widget should recalculate it's position, etc)
-* draw (called when the widget should draw itself)
-*
-*/
-
-/* Finds the BBWidgetHandler in the actions table for the given type. */
-BBWidgetHandler * bbwidgetactions_find(BBWidgetAction * actions, int type) {
-  while((actions->type != -1) && (actions->handler != NULL)) {
-    if (actions->type == type) {
-      return actions->handler;
-    }
-    actions++;
-  }
-  return NULL;
-} 
-
-
-/* Finds the BBWidgetHandler in the actions table for the given type and calls it
- * with widget and data as parameters. */
-int bbwidgetactions_handle(BBWidgetAction * actions, int type,
-                         BBWidget * widget, void * data) {
-  BBWidgetHandler * handler = bbwidgetactions_find(actions, type);
-  if (handler) { return handler(widget, data); }
-  return BBWIDGET_HANDLE_IGNORE;
-} 
-
-
-/** Makes a new style struct. */
-Style style_make(Color fore, Color back, Font * font, Image * background) {
-  Style result = { fore, back, font, background };
-  return result;
-}
-
-/** Initializes a style pointer by copying data from a style struct.  */
-Style * style_initstyle(Style * self, Style style) {
-  if(!self) return NULL;
-  (*self) = style;
-  return self;
-}
-
-/** Initializes a bounds pointer from it's member data. */
-Style * style_init( Style * self, 
-                    Color fore, Color back, Font * font, Image * background) {
-  return style_initstyle(self, style_make(fore, back, font, background));
-}
-
-/** Get foreground color of style. */
-Color   style_forecolor(Style * self)   {  
-  return self->forecolor;  
-}
-/** Get background color of style. */
-Color   style_backcolor(Style * self)   {  
-  return self->backcolor;  
-}
-/** Get background image of style. */
-Image * style_background(Style * self)  {  
-  return self->background; 
-}
-/** Get font of style. */
-Font  * style_font(Style * self)        {  
-  return self->font;       
-}
-
-
-
-// typedef struct BBWidgetMethods_ BBWidgetMethods;
-
-
-
-/** Get bounds of widget. */
-Rebox bbwidget_bounds(BBWidget * self) {
-  return self->bounds;
-}
-
-/** Get width of widget. */
-int bbwidget_w(BBWidget * self) {  
-  return rebox_w(&self->bounds); 
-}
-/** Get height of widget. */
-int bbwidget_h(BBWidget * self) {  
-  return rebox_h(&self->bounds); 
-}
-/** Get x position of widget. */
-int bbwidget_x(BBWidget * self) {  
-  return rebox_x(&self->bounds); 
-}
-/** Get y position of widget. */
-int bbwidget_y(BBWidget * self) {  
-  return rebox_y(&self->bounds); 
-}
-/** Get z position of widget. */
-int bbwidget_z(BBWidget * self) {  
-  return self->z;
-}
-
-/** Get foreground color of widget. */
-Color bbwidget_forecolor(BBWidget * self) {  
-  return style_forecolor(&self->style);  
-}
-/** Get backgrund color of widget. */
-Color bbwidget_backcolor(BBWidget * self) {  
-  return style_backcolor(&self->style);  
-}
-/** Get font of widget. */
-Font * bbwidget_font(BBWidget * self)     {  
-  return style_font(&self->style);
-}
-
-/** Get background bitmap of widget. */
-Image * bbwidget_background(BBWidget * self) {
-  return style_background(&self->style);
-}
-
-/** Gets the flags of a widget. */
-int bbwidget_flags(BBWidget * self) {
-  return self->flags;
-}
-
-/** Gets the id of a widget. */
-int bbwidget_id(BBWidget * self, int id) {
-  return self->id;
-}
-
-/** Sets all the flags of a widget at once. */
-int bbwidget_flags_(BBWidget * self, int flags) {
-  return self->flags = flags;
-}
-
-/** Sets the id of a widget. */
-int bbwidget_id_(BBWidget * self, int id) {
-  return self->id = id;
-}
-
-
-/** Sets an individual flag on the widget. */
-int bbwidget_flag(BBWidget * self, int flag) {
-  return flags_set(&self->flags, flag);
-}
-
-/** Unsets an individual flag on the widget. */
-int bbwidget_unflag(BBWidget * self, int flag) {
-  register int wflags = self->flags;
-  return flags_unset(&self->flags, flag);
-}
-
-/** Sets or unsets an individual flag on the widget. 
-If set is true the flag is set, if false it's unset. */
-int bbwidget_doflag(BBWidget * self, int flag, int set) {
-  return flags_put(&self->flags, flag, set);
-}
-
-/** Checks if an individual flag is set */
-int bbwidget_flag_p(BBWidget * self, int flag) {
-  return flags_get(self->flags, flag);
-}
-
-/** Checks if the widget is visible or not.  */
-int bbwidget_visible(BBWidget * self) {
-  return bbwidget_flag_p(self, BBWIDGET_FLAG_VISIBLE);
-}
-
-/** Checks if the widget is listening to input or not.  */
-int bbwidget_listening(BBWidget * self) {
-  return bbwidget_flag_p(self, BBWIDGET_FLAG_LISTENING);
-}
-
-/** Checks if the widget is active, hat is both visible and 
-listening to input or not.  */
-int bbwidget_active(BBWidget * self) {
-  return bbwidget_flag_p(self, BBWIDGET_FLAG_ACTIVE);
-}
-
-/** Checks if the widget is focused or not.  */
-int bbwidget_focused(BBWidget * self) {
-  return bbwidget_flag_p(self, BBWIDGET_FLAG_FOCUSED);
-}
-
-/** Checks if the widget selected or not.  */
-int bbwidget_selected(BBWidget * self) {
-  return bbwidget_flag_p(self, BBWIDGET_FLAG_SELECTED);
-}
-
-/** Sets the widget to be visible or not depending on set. */
-int bbwidget_visible_(BBWidget * self, int set) {
-  return bbwidget_doflag(self, BBWIDGET_FLAG_VISIBLE, set);
-}
-
-/** Sets the widget if the widget is listening to input or not depending
-on set. */
-int bbwidget_listening_(BBWidget * self, int set) {
-  return bbwidget_doflag(self, BBWIDGET_FLAG_VISIBLE, set);
-}
-
-/** Sets the widget to be active or not */
-int bbwidget_active_(BBWidget * self, int set) {
-  return bbwidget_doflag(self, BBWIDGET_FLAG_ACTIVE, set);
-}
-
-/** Sets if the widget is focused or not.  */
-int bbwidget_focused_(BBWidget * self, int set) {
-  return bbwidget_doflag(self, BBWIDGET_FLAG_FOCUSED, set);
-}
-
-/* Sets if the widget selected or not.  */
-int bbwidget_selected_(BBWidget * self, int set) {
-  return bbwidget_doflag(self, BBWIDGET_FLAG_SELECTED, set);
-}
-
-/* Sets up the method cache of a widget based on the actions. */
-BBWidget * bbwidget_metabfromacts(BBWidget * self, BBWidgetAction * acts) {
-  if(!self) return NULL;
-  if(!acts) return NULL;
-  self->metab.done = bbwidgetactions_find(acts, BBWIDGET_EVENT_DONE);
-  self->metab.draw = bbwidgetactions_find(acts, BBWIDGET_EVENT_DRAW);
-  self->metab.free = bbwidgetactions_find(acts, BBWIDGET_EVENT_FREE);
-  return self;
-}
-
-/* Sets up the acts, and also updates the method cache of a widget */
-BBWidget * bbwidget_acts_(BBWidget * self, BBWidgetAction * acts) {
-  if(!self) return NULL;
-  self->acts = acts;
-  return bbwidget_metabfromacts(self, acts);
-}
-
-/* Fully initializes a widget. */
-BBWidget * 
-bbwidget_initall(BBWidget * self,  int id, BBWidgetAction * acts, 
-                 Rebox bounds, Style style) {
-  if(!self) return NULL;
-  self->id     = id;
-  self->acts   = acts; 
-  bbwidget_metabfromacts(self, acts);
-  self->bounds = bounds;
-  self->style  = style;
-  bbwidget_active_(self, TRUE);
-  return self;
-}
-
-/* Initializes a widget with given bounds and style. */
-BBWidget * 
-bbwidget_initbounds(BBWidget * self, int id,  BBWidgetAction * acts, Rebox bounds) {
-  Color fg    = color_rgb(0,0,0);
-  Color bg    = color_rgb(255,0,0);
-  Style style = { fg, bg, NULL, NULL };
-  return bbwidget_initall(self, id, acts, bounds, style);
-}
-
-/* Initializes a widget from another one's bounds and style. */
-BBWidget * bbwidget_initparent(BBWidget * self, int id, BBWidget * parent) {
-  return bbwidget_initall(self, id, parent->acts, parent->bounds, parent->style);
-}
-
-/* Allocates a widget */
-BBWidget * bbwidget_allocate() {
-  return STRUCT_ALLOC(BBWidget);
-}
-
-/* Call when widget is not needed anymore. */
-BBWidget * bbwidget_done(BBWidget * widget) {
-  // do nothing there as background image and font are NOT owned.
-  return widget;
-}
-
-
-/* Frees a widget. Calls widget->methods->done(), then mem_free if
-the latter returns not NULL. Returns NULL. */
-BBWidget * bbwidget_free(BBWidget * self) {
-  if (self && self->metab.done) {
-    if(self->metab.done(self, NULL)) {
-      mem_free(self);
-    }
-  }
-  return NULL;
-}
-
-/** Generic widget drawing. */
-void bbwidget_draw(BBWidget * self) {
-  if(self->metab.draw) {
-    self->metab.draw(self, NULL);
-  }
-}
-
-
-/** Generic widget event handling. */
-void bbwidget_handle(BBWidget * self, ALLEGRO_EVENT * event) {
-  if(self->acts) {
-    bbwidgetactions_handle(self->acts, event->any.type, self, event);
-  }
-}
-
-/** Generic widget update. */
-void bbwidget_update(BBWidget * self, ALLEGRO_EVENT * event) {
-  if(self->metab.update) {
-    self->metab.update(self, event);
-  }
-}
-
-
-
-/* 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. */
-typedef struct BBTextInfo_ BBTextInfo;
-
-struct BBTextInfo_ {
-  int from_char;
-  int start_char;
-  int stop_char;
-  int maxwidth;
-};
-
-
-/* Creates a temporary ustr as per al_ref_ustr but with 
- start and stop as code positions, not byte positions. */
-const USTR * 
-ustrinfo_newref(
-  USTR_INFO * uinfo, const USTR * ustr, int start, int stop) {
-  return ustr_refustr(uinfo, ustr, 
-                      ustr_offset(ustr, start),
-                      ustr_offset(ustr, stop)
-                     );
-}
-
-
-
-/* Creates a temporary ustr that refers ustr but respecds the bounds of the 
-textinfo (start_char and enc_char) */
-const USTR * bbtextinfo_refustr(BBTextInfo * self, 
-                                USTR_INFO  * uinfo,
-                                const USTR * ustr) { 
-  return ustrinfo_newref(uinfo, ustr, self->start_char, self->stop_char);
-}
-
-
-BBTextInfo * 
-bbtextinfo_wordfromtext(BBTextInfo * self, USTR * ustr, Font * font) {
-  int found;
-  int start_pos;
-  int end_pos;
-  int now_char;
-  int end_char;
-  int len;
-  int ch;
-  if(!self) return NULL;
-  now_char         = self->from_char;
-  self->start_char = now_char;
-  ch = ustr_getnext(ustr, &now_char); 
-  while (ch > 0) { 
-    switch(ch) { 
-      case ' ': /* Found a space, here the word ends, include the space. */
-        self->stop_char = now_char;
-        return self;
-      case '\n': /* A newline ends a word, include the newline. */
-        self->stop_char = now_char;
-        return self;
-      default: /* Other characters mean the word is not finished yet. */
-        break;
-    }
-    /* XXX: Should handle the case for languages that use no spaces, 
-    * by checking with al_get_ustr_width but it's not a pressing matter yet.
-    */
-    ch = ustr_getnext(ustr, &now_char); 
-  } 
-  // no word found, just set end here and be done. 
-  self->stop_char = now_char;
-  /* return nULL to signify end */
-  return NULL;
-}
-
-
-/** Prints a ustring, since puts or printf print too much some
- times for a refstring.*/
-int ustr_print(USTR * word) {
-  size_t index;
-    for(index = 0; index < ustr_length(word) ; index++) {
-      putchar(ustr_get(word, index));
-    }
-  return index;
-}
-
-
-/** Gets the positions of the next line of text fort he given Unicode string 
-and store them in the given info. If the info's from is set to 0 or less, 
-the first line is assumed, otherwise, the line will be started from "from".
-Uses the given font to determine the width of the text as it is built.
-*/
-BBTextInfo * 
-bbtextinfo_linefromtext(BBTextInfo * self, USTR * ustr, Font * font) {
-  BBTextInfo wordinfo;
-  USTR_INFO  lineuinfo;
-  const USTR     * line;
-
-  USTR_INFO  worduinfo = { 0, 0, 0};
-  const USTR     * word;
-  int ch;
-  int index;
-  int width;
-  int last_stop;
-  self->start_char   = self->from_char;
-  wordinfo.from_char = self->from_char;
-  
-  while(bbtextinfo_wordfromtext(&wordinfo, ustr, font)) {
-    word = bbtextinfo_refustr(&wordinfo, &worduinfo, ustr);
-    line = ustrinfo_newref(&lineuinfo, ustr, self->start_char, wordinfo.stop_char);
-    width = al_get_ustr_width(font, line);
-    if (width > self->maxwidth) { 
-      /* XXX: handle case of text overflow by bluntly retuning the word as is.
-      Should split single word based on length too.
-      There is overflow if this is still the first word as see from wordinfo_start_char.
-      */
-      if (wordinfo.start_char == self->start_char) {
-        self->stop_char  = wordinfo.stop_char;
-      } else { 
-        self->stop_char  = wordinfo.start_char;
-      }
-      return self;
-    }
-    // If we get here, the word way still end on a newline character 
-    // check this case. XXX: It works like this because 
-    // stop_char is a bit wonky... it points at the first character of the 
-    // next word in this case...
-    ch = ustr_get(ustr, wordinfo.stop_char - 1);
-    if (ch == '\n') {
-      self->start_char = self->from_char;
-      self->stop_char  = wordinfo.stop_char - 1;
-      return self;
-    }
-    wordinfo.from_char = wordinfo.stop_char;
-  }
-  /* if we get here, the whole string fits. */
-  self->start_char = self->from_char;
-  self->stop_char  = wordinfo.stop_char;
-  /* Return NULL to tell caller text has been completely split up. */
-  return NULL;
-}
-
-
-
-#define BBWIDGET_BORDER 3 
-
-/** Draws a rounded frame as background for a widget. */
-void bbwidget_drawroundframe(BBWidget * self) {
-  if(!self) return;
-  draw_roundframe(bbwidget_x(self), bbwidget_y(self), 
-                  bbwidget_w(self), bbwidget_h(self),
-                  BBWIDGET_BORDER,
-                  bbwidget_forecolor(self), bbwidget_backcolor(self));
-}
-
-
-/** Skips the text info to the next word or line of text. Must be called 
-when looping over bbtextinfo_linefromtext. */
-BBTextInfo * bbtextinfo_next(BBTextInfo * self) { 
-  if(!self) return NULL;
-  self->from_char  = self->stop_char + 1;
-  self->start_char = self->from_char;
-  return self;
-}
-
-
-    
-struct BBWidgetBox_ {
-  struct BBWidget_ widget;
-};
-
-
-#ifndef COMMENT_
-/* 
-* A label is simply a piece of text that is drawn at bounds.x, bounds.y
-* using style.font in style.forecolor. No background is drawn.
-
-struct BBWidgetLabel_ {
-  BBWidget        parent;
-  USTR        * text;
-};
-
-
-BBWidgetLabel * widgetlabel_init(BBWidgetLabel * self, BBWidget * parent, Rebox bounds, 
-                          const char * text) {
-  if(!bbwidget_initbounds((BBWidget *)self, bounds)) return NULL;
-  self->text = ustr_new(text);
-  return self;
-}
-
-BBWidgetLabel * widgetlabel_done(BBWidgetLabel * self) {
-  if(!self) return NULL;
-  ustr_free(self->text);
-  return NULL;
-} 
-
-
-
-struct BBWidgetChoose_ {
-  BBWidget parent;
-  Dynar * options;
-}; 
-*/
-#endif
-
-
-
-
-
-/* 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) */
-struct BBConsole_ {
-  BBWidget  widget;
-  USTRList  text;
-  int       count;
-  int       max;
-  int       start;
-  int       charw;
-  int       cursor;
-  char    * buf;
-  USTR    * input;
-  BBConsoleCommand * command; // called when a command has been entered, if set.
-  void    * command_data; // command data.
-};
-
-
-/* Converts a widget to a console. Only works if the pointer is wrapped correctly,
- by a console. */
-BBConsole * bbwidget_console(BBWidget * widget) { 
-  if (!widget) return NULL;
-  return bad_container(widget, BBConsole, widget);
-}
-
-
-/** Sets the console's command function and data. */
-void bbconsole_command_(BBConsole * self, BBConsoleCommand * command, void * data) {
-  self->command      = command;
-  self->command_data = data;
-}
-
-/** Let the console perform a command if possible. returns nonzero on error,
-zero if OK. */
-int bbconsole_docommand(BBConsole * self, const char * text) {
-  if(!self) return -1;
-  if(!self->command) return -2;
-  return self->command(self, text, self->command_data);
-}
-
-
-
-/** Adds a line of text to the console. */
-int bbconsole_addstr(BBConsole * self, const char * str) {
-  if(!self) return -1;
-  if(!ustrlist_shiftcstr(&self->text, str)) { 
-    return -3;
-  }  
-  while(ustrlist_size(&self->text) >= self->max) { // remove last node(s)
-    ustrlist_droplast(&self->text);
-  }
-  return ustrlist_size(&self->text);
-}
-
-/** Adds a line of text to the console. */
-int bbconsole_addustr(BBConsole * self, const USTR * ustr) {
-  if(!self) return -1;
-  if(!ustrlist_shiftustr(&self->text, ustr)) { 
-    return -3;
-  }  
-  while(ustrlist_size(&self->text) >= self->max) { // remove last node(s)
-    ustrlist_droplast(&self->text);
-  }
-  return ustrlist_size(&self->text);
-}
-
-
-/** Puts a string on the console .*/
-int bbconsole_puts(BBConsole * self, const char * str) {
-  int index;
-  int size     = strlen(str);
-  int leftsize = size;
-  int lines = 0;
-  USTR_INFO uinfo;
-  BBTextInfo info = { 0, 0, 0, 0};
-  info.maxwidth   = bbwidget_w(&self->widget) - 10;
-  USTR * ustr;
-  const USTR * uline;
-  ustr = ustr_new(str);
-  while(bbtextinfo_linefromtext(&info, ustr, self->widget.style.font)) {
-    uline = bbtextinfo_refustr(&info, &uinfo, ustr);
-    bbconsole_addustr(self, uline);
-    // don't forget to skip to next line!!!
-    bbtextinfo_next(&info);
-  }
-  uline = bbtextinfo_refustr(&info, &uinfo, ustr);
-  bbconsole_addustr(self, uline);
-  ustr_free(ustr);
-  return lines;
-} 
-
-#define BBCONSOLE_VPRINTF_MAX 1024
-
-/** Prints a formatted string on the console, truncaded to 1024 characters.  */
-int bbconsole_vprintf(BBConsole * self, const char * format, va_list args) {
-  char buffer[BBCONSOLE_VPRINTF_MAX] = { '\0' };
-  vsnprintf(buffer, BBCONSOLE_VPRINTF_MAX, format, args);
-  return bbconsole_puts(self, buffer);
-}
-
-/** Prints a formatted string on the console, truncaded to 1024 characters.  */
-int bbconsole_printf(BBConsole * self, const char * format, ...) {
-  int result;
-  va_list args;
-  va_start(args, format);
-  result = bbconsole_vprintf(self, format, args);
-  va_end(args);
-  return result;
-}
-
-
-/** Draws a console. */
-int bbconsole_draw(BBWidget * widget, void * data) {
-  BBConsole * self  ;
-  Font * font       ;
-  Color color       ;
-  USTRListNode * now;
-  int high, linehigh, index, x, y, skip;
-  int linew;
-  if (!bbwidget_visible(widget)) return BBWIDGET_HANDLE_IGNORE;
-  
-  self  = bbwidget_console(widget);
-  font  = bbwidget_font(widget);
-  color = bbwidget_forecolor(widget);
-  
-  bbwidget_drawroundframe(widget);
-  high        = bbwidget_h(widget) - 10;
-  x           = bbwidget_x(widget) +  5;
-  y           = bbwidget_y(widget) -  5;
-  linehigh    = font_lineheight(font);
-  
-  now         = ustrlist_head(&self->text);
-  // skip start lines (to allow scrolling backwards) 
-  now         = ustrlist_skipnode(&self->text, self->start);
-  
-  for (index = high-(linehigh*2); index > 0; index -= linehigh) {
-    USTR * textstr;
-    if(!now) break;
-    textstr = ustrlistnode_ustr(now);
-    if(textstr) {
-      font_drawstr(font, color, x, y + index, 0, textstr);
-    }
-    now = ustrlistnode_next(now);
-  }
-  // draw input string
-  font_drawstr(font, color, x, y + high - linehigh, 0, self->input);
-  // Draw cursor
-  linew = al_get_ustr_width(font, self->input);
-  al_draw_line(x + linew, y + high - linehigh, x + linew, y + high, color, 1);
-  // draw start for debugging
-  al_draw_textf(font, color, x, y, 0, "start: %d, size: %d", self->start, 
-                ustrlist_size(&self->text));
-  return BBWIDGET_HANDLE_OK;
-}
-
-/** Activates or deactivates the console. */
-void bbconsole_active_(BBConsole * self, int active) {
-  if(!self) return;
-  bbwidget_active_(&self->widget, active);
-}
-
-/** Returns nonzero if console is active zero if not. */
-int bbconsole_active(BBConsole * self) {
-  if(!self) return 0;
-  return bbwidget_active(&self->widget);
-}
-
-/** scrolls the console 1 step in the given direction. */
-int bbconsole_scroll(BBConsole * self, int direction) {
-  if((!self) || (!direction)) return FALSE;
-  if(direction < 0) self->start--;
-  if(direction > 0) self->start++;
-  /* Clamp start between 0 and size of list. */
-  self->start = bad_clampi(self->start, 0, ustrlist_size(&self->text));
-  return BBWIDGET_HANDLE_OK;
-}
-
-
-
-/* Key input event handler for console. */
-int bbconsole_handle_keychar(BBWidget * widget, void * data) { 
-  BBConsole * self        = bbwidget_console(widget);
-  ALLEGRO_EVENT * event = (ALLEGRO_EVENT *) data;
-  int ch = event->keyboard.unichar;
-  int kc = event->keyboard.keycode;
-  switch(kc) {
-    // ignore the start-console key
-    case ALLEGRO_KEY_F1:
-    case ALLEGRO_KEY_F3:
-      return BBWIDGET_HANDLE_OK;
-    case ALLEGRO_KEY_PGUP: return bbconsole_scroll(self, 1);
-    case ALLEGRO_KEY_PGDN: return bbconsole_scroll(self, -1);
-    case ALLEGRO_KEY_BACKSPACE:
-      // remove last character typed.
-      ustr_remove_chr(self->input, ustr_offset(self->input, -1));
-      return BBWIDGET_HANDLE_OK;
-    break;    
-    case ALLEGRO_KEY_ENTER: {
-      const char * command = ustr_c(self->input);
-      // execute command
-      if(bbconsole_docommand(self, command)) { 
-        bbconsole_puts(self, "Error in running comand");
-        bbconsole_puts(self, command);
-      }
-      ustr_truncate(self->input, 0);
-      // empty string by truncating it
-      return BBWIDGET_HANDLE_OK;
-      }
-    default:
-    break;
-  }
-  
-  ustr_appendch(self->input, ch);
-  return BBWIDGET_HANDLE_OK;
-}
-
-
-/* Key down event handler for console. */
-int bbconsole_handle_keydown(BBWidget * widget, void * data) { 
-  BBConsole * self       = bbwidget_console(widget);
-  ALLEGRO_EVENT * event  = (ALLEGRO_EVENT *) data;
-  int ch = event->keyboard.unichar;
-  int kc = event->keyboard.keycode;
-  switch(kc) {
-    case ALLEGRO_KEY_F1:  
-    case ALLEGRO_KEY_F3:
-      bbconsole_active_(self, false); 
-      /* disable console if F1 is pressed. 
-       * Note: this shouldnever happen if react is set up well.
-       */ 
-      return BBWIDGET_HANDLE_OK;
-    default:
-    break;
-  }
-  return BBWIDGET_HANDLE_IGNORE;
-}
-
-/* Mouse axe event handler for console */
-int bbconsole_handle_mouseaxes(BBWidget * widget, void * data) { 
-  BBConsole * self        = bbwidget_console(widget);
-  ALLEGRO_EVENT * event = (ALLEGRO_EVENT *) data;  int z = event->mouse.dz;
-  // only capture mouse scroll wheel...
-  if(z == 0) return BBWIDGET_HANDLE_IGNORE;
-  if(z < 0) return bbconsole_scroll(self, -1);
-  if(z > 0) return bbconsole_scroll(self, +1);
-  return BBWIDGET_HANDLE_OK;
-}
-
-int bbconsole_handle_ignore(BBWidget * widget, void * data) { 
-  return BBWIDGET_HANDLE_IGNORE;
-}
-
-
-static BBWidgetAction bbconsole_actions[] = {  
-  { ALLEGRO_EVENT_KEY_DOWN  , bbconsole_handle_keydown    }, 
-  { ALLEGRO_EVENT_KEY_UP    , bbconsole_handle_ignore     },
-  { ALLEGRO_EVENT_KEY_CHAR  , bbconsole_handle_keychar    },
-  { ALLEGRO_EVENT_MOUSE_AXES, bbconsole_handle_mouseaxes  },
-  { BBWIDGET_EVENT_DRAW     , bbconsole_draw              },
-  { BBWIDGET_EVENT_DONE     , bbconsole_done              },
-  { BBWIDGET_EVENT_FREE     , bbconsole_free              },
-  { -1, NULL }
-};
-
-
-/** Let the console handle allegro events. Returns 0 if event was consumed,
-positive if not, and negative on error. */
-
-int bbconsole_handle(BBWidget * widget, ALLEGRO_EVENT * event) { 
-  if (!widget) return BBWIDGET_HANDLE_ERROR;
-  if (!bbwidget_active(widget)) return BBWIDGET_HANDLE_IGNORE;
-  return bbwidgetactions_handle(bbconsole_actions, event->type, widget, event);
-}
-
-
-/** Cleans up a console. */
-int bbconsole_done(BBWidget * widget, void * data) {
-  BBConsole * self = bbwidget_console(widget);
-  Lilis * aid;
-  if(!self) return BBWIDGET_HANDLE_IGNORE;
-  self->buf     = mem_free(self->buf);
-  ustr_free(self->input);
-  self->input   = NULL;
-  ustrlist_done(&self->text);
-  return BBWIDGET_HANDLE_OK;
-}
-
-
-/** Deallocates a console. */
-int bbconsole_free(BBWidget * widget, void * data) {
-  BBConsole * self = bbwidget_console(widget);
-  bbconsole_done(&self->widget, data);
-  mem_free(self);
-  return BBWIDGET_HANDLE_OK;
-}
-
-/** Allocates a console. */
-BBConsole * bbconsole_alloc() {
-  return STRUCT_ALLOC(BBConsole);
-}
-
-/* Amount of lines of display the console hat o keep track of. */
-#define BBCONSOLE_MAX 200
-
-/** Initializes a console. */
-BBConsole * bbconsole_initall(BBConsole * self, int id, Rebox bounds, Style style) {
-  if(!self) return NULL;
-  if(!bbwidget_initall(&self->widget, id, bbconsole_actions, bounds, style)) { 
-    return NULL;
-  }
-  ustrlist_init(&self->text);
-  // ustrlist_shiftcstr(&self->text, "empty line");
-  bbwidget_active_(&self->widget, FALSE);
-  self->count = 0;
-  // max MUST be at least 2, 3 to see anything...
-  self->max   = BBCONSOLE_MAX;
-  self->start = 0;
-  self->charw = 80; 
-  self->buf   = mem_alloc(self->charw + 1);
-   // one extra for NULL at end . 
-  if(!self->buf) { bbconsole_done(&self->widget, NULL); return NULL; }
-  self->input = ustr_new("");
-  self->cursor= 0;
-  if(!self->input) { bbconsole_done(&self->widget, NULL); return NULL; }
-  self->command      = NULL;
-  self->command_data = NULL;
-  return self;
-}
-
-/** Initializes a new console. */
-BBConsole * bbconsole_new(int id, Rebox bounds, Style style) {
-  BBConsole * self = bbconsole_alloc();
-  if(!bbconsole_initall(self, id, bounds, style)) {
-    bbconsole_free(&self->widget, NULL);
-    return NULL;
-  }
-  return self;
-}
-
-
-
-
-
-

+ 697 - 35
src/zori.c

@@ -2,6 +2,8 @@
 #include "zori.h"
 #include "miao.h"
 #include <allegro5/allegro_color.h>
+#include "str.h"
+#include "draw.h"
 
 /*
 * Pardon the pun name, but Zori is the submodule that handles the user
@@ -112,16 +114,59 @@ struct zori_handler * zori_handlers_search(struct zori_handlers * me,  zori_even
   key.data    = NULL;
   if (!me) return NULL;
   result = miao_bsearch(me, zori_handler_compare, &key);
-  return result;   
+  return result; 
 }
 
 
-int zori_handlers_handle(struct zori_handlers * me,  struct zori_event * event, struct zori_widget * widget) {    
-  struct zori_handler * handler = zori_handlers_search(me, event->sysev.type);
+int zori_handlers_handle(struct zori_handlers * me, union zori_event * event) {    
+  struct zori_handler * handler = zori_handlers_search(me, event->type);
   if (!handler) return 0;
+  event->any.data = handler->data;
   return handler->handler(event);
 }
 
+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.sysev      = sysev;
+  return zori_handlers_handle(&widget->handlers, &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_handlers_handle(&widget->handlers, &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_handlers_handle(&widget->handlers, &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_handlers_handle(&widget->handlers, &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_handlers_handle(&widget->handlers, &event);
+}
+
+
+
+
 static struct zori_root * the_zori_root = NULL;
 
 static struct zori_style * the_default_style = NULL;
@@ -136,9 +181,8 @@ int zori_widget_compare(const void * v1, const void * v2) {
 
 
 struct zori_widget * zori_get_widget(zori_id id) {
-  struct zori_widget key = {0};
-  key.id = id;
-  return miao_bsearch(the_zori_root->widgets, zori_widget_compare, &key);
+  if (!the_zori_registry) return NULL;
+  return zori_registry_lookup(the_zori_registry, id);
 }
 
 zori_id zori_get_unused_id(void) {
@@ -149,10 +193,28 @@ zori_id zori_get_unused_id(void) {
     if (id == INT_MAX) { return ZORI_ID_ERROR; }
     found = zori_get_widget(id);
   } while(found);
-    
   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) {
   if (the_zori_root) return ZORI_ID_OK;
   the_zori_root = calloc(1, sizeof(*the_zori_root));
@@ -166,7 +228,7 @@ zori_id zori_start(struct zori_style * default_style) {
   the_default_style->text.font      = al_create_builtin_font();  
   the_default_style->text.color     = al_color_name("white");  
   the_default_style->border.color   = al_color_name("white");  
-  the_default_style->back.color     = al_color_name("green");  
+  the_default_style->back.color     = al_color_name("green");
   
   if (default_style) {
     the_zori_root->widget.style = *default_style;
@@ -174,40 +236,73 @@ zori_id zori_start(struct zori_style * default_style) {
     the_zori_root->widget.style = *the_default_style;
   }
   
-  miao_init(the_zori_root->widgets);
   the_zori_root->widget.id = 0;
   zori_registry_add(the_zori_registry, the_zori_root->widget.id, &the_zori_root->widget);
-  
-  /*
-   * miao_push(the_zori_root->widgets, &the_zori_root->widget);
-   * miao_qsort(the_zori_root->widgets, zori_widget_compare);
-   */
-  
-  
   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;
   
-  if (widget->child) {
-    /* Free all children. */
-    aid  = widget->child;
-    while (aid) {
-      next = aid->sibling;
-      zori_widget_free(aid);
-      aid  = next;
-    }
+  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) {
-  /* XXX remove from widget registry too... */
-  return free(zori_widget_done(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 = child;
+  }
+  return child;
+} 
+
+struct zori_widget * zori_widget_init
+  ( struct zori_widget * widget, 
+    struct zori_widget * parent, zori_rebox * box, struct zori_style * style) {
+    
+    miao_init(&widget->children);
+    miao_init(&widget->handlers);
+    widget->z     = 0;
+    widget->flags = 0;
+    widget->id    = zori_get_unused_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, 200, 100);
+    }
+    zori_widget_add_child(parent, widget);
+    return widget;
 }
 
 
@@ -215,17 +310,32 @@ void zori_widget_free(struct zori_widget * widget) {
  * negative on error.
  */
 zori_id zori_shutdown() {
-  miao_done(the_zori_registry);
-  free(the_zori_registry);
-  the_zori_registry = NULL;
-  free(the_default_style);
-  the_default_style = NULL;
   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;
+  
+  
   return ZORI_ID_OK;
 }
 
+
+struct zori_widget * 
+zori_widget_initall(struct zori_widget * widget, 
+struct zori_widget * parent, zori_rebox * box, struct zori_style * style, 
+size_t amount, struct zori_handler * handlers) {
+  if (zori_widget_init(widget, 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);
@@ -267,18 +377,55 @@ 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 widget and it's children recursively. */
+void zori_draw_widget(struct zori_widget * widget) {
+  size_t index;
+  if (widget && zori_widget_visible(widget)) {
+    zori_widget_raise_draw_event(widget);
+    for (index = 0; index < miao_size(&widget->children); index++) {
+      struct zori_widget * child = miao_unsafe_get(&widget->children, index);
+      zori_draw_widget(child);
+    }
+  }
+}
+
 /* Draws the whole UI and all visible parts. */
 void zori_draw_all(void) {
+  zori_draw_widget(&the_zori_root->widget);
+}
 
+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);
+}
+
+
+
 /* Updates the state of the UI. Pass in the time passed since last update. */
 void zori_update(double dt) 
 {
-  struct zori_event event;
-  event.sysev.type = ZORI_EVENT_UPDATE;
-  event.sysev.user.data1 = (intptr_t)&dt;
-  zori_handlers_handle(&the_zori_root->widget.handlers, &event, &the_zori_root->widget);
+  zori_widget_raise_update_event(&the_zori_root->widget, dt);
 }
 
 /* Registers an event handler for a widget. */
@@ -286,6 +433,521 @@ zori_id zori_register(zori_id id,  zori_event_type type, zori_handler_func handl
 
 
 
+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;
+}
+
+
+/* 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. */
+struct zori_textinfo {
+  int from_char;
+  int start_char;
+  int stop_char;
+  int maxwidth;
+};
+
+
+/* Creates a temporary ustr as per al_ref_ustr but with 
+ start and stop as code positions, not byte positions. */
+static const USTR * 
+ustrinfo_newref(
+  USTR_INFO * uinfo, const USTR * ustr, int start, int stop) {
+  return ustr_refustr(uinfo, ustr, 
+                      ustr_offset(ustr, start),
+                      ustr_offset(ustr, stop)
+                     );
+}
+
+
+
+/* Creates a temporary ustr that refers ustr but respecds the bounds of the 
+textinfo (start_char and enc_char) */
+const USTR * zori_textinfo_refustr(struct zori_textinfo * self, 
+                                USTR_INFO  * uinfo,
+                                const USTR * ustr) { 
+  return ustrinfo_newref(uinfo, ustr, self->start_char, self->stop_char);
+}
+
+
+struct zori_textinfo * 
+zori_textinfo_wordfromtext(struct zori_textinfo * self, USTR * ustr, zori_font * font) {
+  int found;
+  int start_pos;
+  int end_pos;
+  int now_char;
+  int end_char;
+  int len;
+  int ch;
+  if(!self) return NULL;
+  now_char         = self->from_char;
+  self->start_char = now_char;
+  ch = ustr_getnext(ustr, &now_char); 
+  while (ch > 0) { 
+    switch(ch) { 
+      case ' ': /* Found a space, here the word ends, include the space. */
+        self->stop_char = now_char;
+        return self;
+      case '\n': /* A newline ends a word, include the newline. */
+        self->stop_char = now_char;
+        return self;
+      default: /* Other characters mean the word is not finished yet. */
+        break;
+    }
+    /* XXX: Should handle the case for languages that use no spaces, 
+    * by checking with al_get_ustr_width but it's not a pressing matter yet.
+    */
+    ch = ustr_getnext(ustr, &now_char); 
+  } 
+  // no word found, just set end here and be done. 
+  self->stop_char = now_char;
+  /* return nULL to signify end */
+  return NULL;
+}
+
+
+/** Prints a ustring, since puts or printf print too much some
+ times for a refstring.*/
+static int ustr_print(USTR * word) {
+  size_t index;
+    for(index = 0; index < ustr_length(word) ; index++) {
+      putchar(ustr_get(word, index));
+    }
+  return index;
+}
+
+
+/** Gets the positions of the next line of text fort he given Unicode string 
+and store them in the given info. If the info's from is set to 0 or less, 
+the first line is assumed, otherwise, the line will be started from "from".
+Uses the given font to determine the width of the text as it is built.
+*/
+struct zori_textinfo * 
+zori_textinfo_linefromtext(struct zori_textinfo * self, USTR * ustr, zori_font * font) {
+  struct zori_textinfo wordinfo;
+  USTR_INFO  lineuinfo;
+  const USTR     * line;
+
+  USTR_INFO  worduinfo = { 0, 0, 0};
+  const USTR     * word;
+  int ch;
+  int index;
+  int width;
+  int last_stop;
+  self->start_char   = self->from_char;
+  wordinfo.from_char = self->from_char;
+  
+  while(zori_textinfo_wordfromtext(&wordinfo, ustr, font)) {
+    word = zori_textinfo_refustr(&wordinfo, &worduinfo, ustr);
+    line = ustrinfo_newref(&lineuinfo, ustr, self->start_char, wordinfo.stop_char);
+    width = al_get_ustr_width(font, line);
+    if (width > self->maxwidth) { 
+      /* XXX: handle case of text overflow by bluntly retuning the word as is.
+      Should split single word based on length too.
+      There is overflow if this is still the first word as see from wordinfo_start_char.
+      */
+      if (wordinfo.start_char == self->start_char) {
+        self->stop_char  = wordinfo.stop_char;
+      } else { 
+        self->stop_char  = wordinfo.start_char;
+      }
+      return self;
+    }
+    // If we get here, the word way still end on a newline character 
+    // check this case. XXX: It works like this because 
+    // stop_char is a bit wonky... it points at the first character of the 
+    // next word in this case...
+    ch = ustr_get(ustr, wordinfo.stop_char - 1);
+    if (ch == '\n') {
+      self->start_char = self->from_char;
+      self->stop_char  = wordinfo.stop_char - 1;
+      return self;
+    }
+    wordinfo.from_char = wordinfo.stop_char;
+  }
+  /* if we get here, the whole string fits. */
+  self->start_char = self->from_char;
+  self->stop_char  = wordinfo.stop_char;
+  /* Return NULL to tell caller text has been completely split up. */
+  return NULL;
+}
+
+
+
+#define ZORI_WIDGET_BORDER 3 
+
+/** Draws a rounded frame as background for a widget. */
+void zori_widget_drawroundframe(struct zori_widget * self) {
+  if(!self) return;
+  draw_roundframe(zori_widget_x(self), zori_widget_y(self), 
+                  zori_widget_w(self), zori_widget_h(self),
+                  ZORI_WIDGET_BORDER,
+                  zori_widget_forecolor(self), zori_widget_backcolor(self));
+}
+
+
+/** Skips the text info to the next word or line of text. Must be called 
+when looping over zori_textinfo_linefromtext. */
+struct zori_textinfo * zori_textinfo_next(struct zori_textinfo * self) { 
+  if(!self) return NULL;
+  self->from_char  = self->stop_char + 1;
+  self->start_char = self->from_char;
+  return self;
+}
+
+
+
+/* Converts a widget to a console. Only works if the pointer is wrapped correctly,
+ by a console. */
+struct zori_console * zori_widget_console(struct zori_widget * widget) { 
+  if (!widget) return NULL;
+  return ZORI_CONTAINER_OF(widget, struct zori_console, widget);
+}
+
+
+/** Sets the console's command function and data. */
+void zori_console_command_(struct zori_console * self, zori_console_command * command, void * data) {
+  self->command      = command;
+  self->command_data = data;
+}
+
+/** Let the console perform a command if possible. returns nonzero on error,
+zero if OK. */
+int zori_console_docommand(struct zori_console * self, const char * text) {
+  if(!self) return -1;
+  if(!self->command) return -2;
+  return self->command(&self->widget, text, self->command_data);
+}
+
+
+
+/** Adds a line of text to the console. */
+int zori_console_addstr(struct zori_console * self, const char * str) {
+  if(!self) return -1;
+  if(!ustrlist_shiftcstr(&self->text, str)) { 
+    return -3;
+  }  
+  while(ustrlist_size(&self->text) >= self->max) { // remove last node(s)
+    ustrlist_droplast(&self->text);
+  }
+  return ustrlist_size(&self->text);
+}
+
+/** Adds a line of text to the console. */
+int zori_console_addustr(struct zori_console * self, const USTR * ustr) {
+  if(!self) return -1;
+  if(!ustrlist_shiftustr(&self->text, ustr)) { 
+    return -3;
+  }  
+  while(ustrlist_size(&self->text) >= self->max) { // remove last node(s)
+    ustrlist_droplast(&self->text);
+  }
+  return ustrlist_size(&self->text);
+}
+
+
+/** Puts a string on the console .*/
+int zori_console_puts(struct zori_console * self, const char * str) {
+  int index;
+  int size     = strlen(str);
+  int leftsize = size;
+  int lines = 0;
+  USTR_INFO uinfo;
+  struct zori_textinfo info = { 0, 0, 0, 0};
+  info.maxwidth   = zori_widget_w(&self->widget) - 10;
+  USTR * ustr;
+  const USTR * uline;
+  ustr = ustr_new(str);
+  while(zori_textinfo_linefromtext(&info, ustr, self->widget.style.text.font)) {
+    uline = zori_textinfo_refustr(&info, &uinfo, ustr);
+    zori_console_addustr(self, uline);
+    // don't forget to skip to next line!!!
+    zori_textinfo_next(&info);
+  }
+  uline = zori_textinfo_refustr(&info, &uinfo, ustr);
+  zori_console_addustr(self, uline);
+  ustr_free(ustr);
+  return lines;
+} 
+
+#define BBCONSOLE_VPRINTF_MAX 1024
+
+/** Prints a formatted string on the console, truncaded to 1024 characters.  */
+int zori_console_vprintf(struct zori_console * self, const char * format, va_list args) {
+  char buffer[BBCONSOLE_VPRINTF_MAX] = { '\0' };
+  vsnprintf(buffer, BBCONSOLE_VPRINTF_MAX, format, args);
+  return zori_console_puts(self, buffer);
+}
+
+/** Prints a formatted string on the console, truncaded to 1024 characters.  */
+int zori_console_printf(struct zori_console * self, const char * format, ...) {
+  int result;
+  va_list args;
+  va_start(args, format);
+  result = zori_console_vprintf(self, format, args);
+  va_end(args);
+  return result;
+}
+
+
+/** Draws a console. */
+int zori_console_draw(union zori_event * zevent) {
+  struct zori_console * self  ;
+  zori_font * font       ;
+  zori_color color       ;
+  USTRListNode * now;
+  int high, linehigh, index, x, y, skip;
+  int linew;
+  struct zori_widget * widget = zevent->any.widget;
+  
+  if (!zori_widget_visible(widget)) return ZORI_HANDLE_IGNORE;
+  
+  self  = zori_widget_console(widget);
+  font  = zori_widget_font(widget);
+  color = zori_widget_forecolor(widget);
+  
+  zori_widget_drawroundframe(widget);
+  high        = zori_widget_h(widget) - 10;
+  x           = zori_widget_x(widget) +  5;
+  y           = zori_widget_y(widget) -  5;
+  linehigh    = zori_font_lineheight(font);
+  
+  now         = ustrlist_head(&self->text);
+  // skip start lines (to allow scrolling backwards) 
+  now         = ustrlist_skipnode(&self->text, self->start);
+  
+  for (index = high-(linehigh*2); index > 0; index -= linehigh) {
+    USTR * textstr;
+    if(!now) break;
+    textstr = ustrlistnode_ustr(now);
+    if(textstr) {
+      zori_font_drawstr(font, color, x, y + index, 0, textstr);
+    }
+    now = ustrlistnode_next(now);
+  }
+  // draw input string
+  zori_font_drawstr(font, color, x, y + high - linehigh, 0, self->input);
+  // Draw cursor
+  linew = al_get_ustr_width(font, self->input);
+  al_draw_line(x + linew, y + high - linehigh, x + linew, y + high, color, 1);
+  // draw start for debugging
+  al_draw_textf(font, color, x, y, 0, "start: %d, size: %d", self->start, 
+                ustrlist_size(&self->text));
+  return ZORI_HANDLE_OK;
+}
+
+/** Activates or deactivates the console. */
+void zori_console_active_(struct zori_console * self, int active) {
+  if(!self) return;
+  zori_widget_active_(&self->widget, active);
+  zori_widget_visible_(&self->widget, active);
+}
+
+/** Returns nonzero if console is active zero if not. */
+int zori_console_active(struct zori_console * self) {
+  if(!self) return 0;
+  return zori_widget_active(&self->widget);
+}
+
+/** scrolls the console 1 step in the given direction. */
+int zori_console_scroll(struct zori_console * self, int direction) {
+  if((!self) || (!direction)) return FALSE;
+  if(direction < 0) self->start--;
+  if(direction > 0) self->start++;
+  /* Clamp start between 0 and size of list. */
+  self->start = bad_clampi(self->start, 0, ustrlist_size(&self->text));
+  return ZORI_HANDLE_OK;
+}
+
+
+
+/* Key input event handler for console. */
+int zori_console_handle_keychar(union zori_event * zevent) { 
+  struct zori_console * self  = zori_widget_console(zori_event_widget(zevent));
+  zori_system_event * event   = zori_event_system_event(zevent);
+  int ch = event->keyboard.unichar;
+  int kc = event->keyboard.keycode;
+  switch(kc) {
+    // ignore the start-console key
+    case ALLEGRO_KEY_F1:
+    case ALLEGRO_KEY_F3:
+      return ZORI_HANDLE_OK;
+    case ALLEGRO_KEY_PGUP: return zori_console_scroll(self, 1);
+    case ALLEGRO_KEY_PGDN: return zori_console_scroll(self, -1);
+    case ALLEGRO_KEY_BACKSPACE:
+      // remove last character typed.
+      ustr_remove_chr(self->input, ustr_offset(self->input, -1));
+      return ZORI_HANDLE_OK;
+    break;    
+    case ALLEGRO_KEY_ENTER: {
+      const char * command = ustr_c(self->input);
+      // execute command
+      if(zori_console_docommand(self, command)) { 
+        zori_console_puts(self, "Error in running comand");
+        zori_console_puts(self, command);
+      }
+      ustr_truncate(self->input, 0);
+      // empty string by truncating it
+      return ZORI_HANDLE_OK;
+      }
+    default:
+    break;
+  }
+  
+  ustr_appendch(self->input, ch);
+  return ZORI_HANDLE_OK;
+}
+
+
+/* Key down event handler for console. */
+int zori_console_handle_keydown(union zori_event * zevent) { 
+  struct zori_console * self  = zori_widget_console(zori_event_widget(zevent));
+  zori_system_event * event   = zori_event_system_event(zevent);
+  int ch = event->keyboard.unichar;
+  int kc = event->keyboard.keycode;
+  switch(kc) {
+    case ALLEGRO_KEY_F1:  
+    case ALLEGRO_KEY_F3:
+      zori_console_active_(self, false); 
+      /* disable console if F1 is pressed. 
+       * Note: this shouldnever happen if react is set up well.
+       */ 
+      return ZORI_HANDLE_OK;
+    default:
+    break;
+  }
+  return ZORI_HANDLE_IGNORE;
+}
+
+/* Mouse axe event handler for console */
+int zori_console_handle_mouseaxes(union zori_event * zevent) { 
+  struct zori_console * self  = zori_widget_console(zori_event_widget(zevent));
+  zori_system_event * event   = zori_event_system_event(zevent);
+  int z                       = event->mouse.dz;
+  // only capture mouse scroll wheel...
+  if(z == 0) return ZORI_HANDLE_IGNORE;
+  if(z < 0) return zori_console_scroll(self, -1);
+  if(z > 0) return zori_console_scroll(self, +1);
+  return ZORI_HANDLE_OK;
+}
+
+int zori_console_handle_ignore(union zori_event * zevent) { 
+  return ZORI_HANDLE_IGNORE;
+}
+
+
+static struct zori_handler zori_console_actions[] = {  
+  { ZORI_SYSTEM_EVENT_KEY_DOWN  , zori_console_handle_keydown   , NULL }, 
+  { ZORI_SYSTEM_EVENT_KEY_UP    , zori_console_handle_ignore    , NULL },
+  { ZORI_SYSTEM_EVENT_KEY_CHAR  , zori_console_handle_keychar   , NULL },
+  { ZORI_SYSTEM_EVENT_MOUSE_AXES, zori_console_handle_mouseaxes , NULL },
+  { ZORI_EVENT_DRAW             , zori_console_draw             , NULL },
+  { ZORI_EVENT_DONE             , zori_console_handle_ignore    , NULL },
+  { ZORI_EVENT_FREE             , zori_console_handle_ignore    , NULL },
+  { -1, NULL, NULL }
+};
+
+
+/** Let the console handle allegro events. Returns 0 if event was consumed,
+positive if not, and negative on error. */
+
+int zori_console_handle(struct zori_widget * widget, zori_system_event * sevent) { 
+  union zori_event zevent;
+  if (!widget) return ZORI_HANDLE_ERROR;
+  if (!zori_widget_active(widget)) return ZORI_HANDLE_IGNORE;
+  return zori_widget_raise_system_event(widget, sevent);
+}
+
+
+/** Cleans up a console. */
+int zori_console_done(struct zori_widget * widget) {
+  struct zori_console * self = zori_widget_console(widget);
+  if(!self) return ZORI_HANDLE_IGNORE;
+  free(self->buf);
+  self->buf     = NULL;
+  ustr_free(self->input);
+  self->input   = NULL;
+  ustrlist_done(&self->text);
+  return ZORI_HANDLE_OK;
+}
+
+
+/** Deallocates a console. */
+int zori_console_free(struct zori_widget * widget) {
+  struct zori_console * self = zori_widget_console(widget);
+  zori_console_done(&self->widget);
+  free(self);
+  return ZORI_HANDLE_OK;
+}
+
+/** Allocates a console. */
+struct zori_console * zori_console_alloc() {
+  return calloc(1 , sizeof(struct zori_console));
+}
+
+/* Amount of lines of display the console hat o keep track of. */
+#define ZORI_CONSOLE_MAX 200
+
+/** Initializes a console. */
+struct zori_console * zori_console_initall(struct zori_console * self, int id, zori_rebox * bounds, struct zori_style * style) {
+  if(!self) return NULL;
+  if(!zori_widget_initall(&self->widget, &the_zori_root->widget, bounds, style, 7, zori_console_actions)) { 
+    return NULL;
+  }
+  ustrlist_init(&self->text);
+  // ustrlist_shiftcstr(&self->text, "empty line");
+  zori_widget_active_(&self->widget, FALSE);
+  self->count = 0;
+  // max MUST be at least 2, 3 to see anything...
+  self->max   = ZORI_CONSOLE_MAX;
+  self->start = 0;
+  self->charw = 80; 
+  self->buf   = calloc(self->charw + 1, 1);
+   // one extra for NULL at end . 
+  if (!self->buf) { zori_console_done(&self->widget); return NULL; }
+  self->input = ustr_new("");
+  self->cursor= 0;
+  if (!self->input) { zori_console_done(&self->widget); return NULL; }
+  self->command      = NULL;
+  self->command_data = NULL;
+  return self;
+}
+
+/** Initializes a new console. */
+struct zori_console * zori_console_new(int id, zori_rebox * bounds, struct zori_style * style) {
+  struct zori_console * self = zori_console_alloc();
+  if(!zori_console_initall(self, id, bounds, style)) {
+    zori_console_free(&self->widget);
+    return NULL;
+  }
+  return self;
+}
+
 
 
 

+ 24 - 0
test/test_miao.c

@@ -135,12 +135,36 @@ TEST_FUNC(miao_delete) {
   TEST_INTNEQ(0, miao_delete_bsearch(a, foo_compare, &f1));
   TEST_INTEQ(4, miao_size(a));
   miao_each_with_result(a, foo_each_with_result, f3, foo_check, "foo");
+  miao_done(a);
   
   TEST_DONE();
 }
+
+TEST_FUNC(miao_push) {
+  struct foo f1, f2 , f3;
+  struct foo * e1, * e2, *e3;
+  struct foo_array a[1];
+  struct foo_array d[1];
+  
+  miao_init(a);
+  TEST_NOTNULL(miao_grow(a, 1));
+  f1.s = "0 zero";
+  miao_unsafe_push(a, f1);
+  f1.s = "2 two";
+  miao_push(a, f1);
+  f1.s = "1 one";
+  miao_push(a, f1);
+  miao_qsort(a, foo_compare);
+  miao_each_with_result(a, foo_each_with_result, f3, foo_check, "foo");
+  miao_done(a);  
+  
+  TEST_DONE();
+}
+
   
 int main(void) {
   TEST_INIT();
+  TEST_RUN(miao_push);
   TEST_RUN(miao_delete);
   TEST_RUN(miao);
   TEST_REPORT();

+ 1 - 1
test/test_widget.c

@@ -2,7 +2,7 @@
 * This is a test for widget in $package$
 */
 #include "si_test.h"
-#include "widget.h"
+// #include "widget.h"
 
 
 TEST_FUNC(widget) {