diff --git a/client/client.pro b/client/client.pro index 600062c..712884c 100644 --- a/client/client.pro +++ b/client/client.pro @@ -28,6 +28,7 @@ HEADERS += \ src/gui/gui.h \ src/gui/image.h \ src/gui/surface.h \ + src/gui/widgets/label.h \ src/gui/wmwindow.h \ src/os.h \ src/platform/platform.h \ @@ -43,6 +44,7 @@ SOURCES += \ src/gui/gui.c \ src/gui/image.c \ src/gui/surface.c \ + src/gui/widgets/label.c \ src/gui/wmwindow.c \ src/main.c \ src/platform/djgpp.c \ diff --git a/client/src/gui/gui.c b/client/src/gui/gui.c index bd52dbe..05cd55a 100644 --- a/client/src/gui/gui.c +++ b/client/src/gui/gui.c @@ -1,9 +1,11 @@ #include "gui.h" #include "font.h" -#include "wmwindow.h" #include "image.h" #include "array.h" +#include "wmwindow.h" +#include "widgets/label.h" + typedef struct WidgetCatalogS { uint8_t key; // Magic @@ -18,16 +20,27 @@ FontT *__guiFontVGA8x14 = NULL; FontT *__guiFontVGA8x16 = NULL; -static uint8_t _magicCount = 0; -static WidgetCatalogT *_widgetCatalog = NULL; -static uint8_t _guiRunning = 1; -static SurfaceT *_mousePointer[MOUSE_COUNT] = { 0 }; -static ColorT _mouseTransparency = 0; -static uint8_t _mouseCurrent = MOUSE_POINTER; -static PointT _mouseHotspot[MOUSE_COUNT] = { - { 0, 0 }, - { 8, 8 } - }; +static uint8_t _magicCount = 0; +static WidgetCatalogT *_widgetCatalog = NULL; +static uint8_t _guiRunning = 1; +static WidgetT **_deleteList = NULL; +static SurfaceT *_mousePointer[MOUSE_COUNT] = { 0 }; +static ColorT _mouseTransparency = 0; +static uint8_t _mouseCurrent = MOUSE_POINTER; +static PointT _mouseHotspot[MOUSE_COUNT] = { { 0, 0 }, { 8, 8 } }; + + +static void guiDeleteListProcess(void); + + +static void guiDeleteListProcess(void) { + // Process any pending deletions. + while (arrlen(_deleteList) > 0) { + _deleteList[0]->reg->destroy(_deleteList[0]); + arrdel(_deleteList, 0); + } + arrfree(_deleteList); +} void guiEventsDo(void) { @@ -50,6 +63,9 @@ void guiEventsDo(void) { // Copy to screen. videoBlit(0, 0, __guiBackBuffer); + // Process any pending deletions. + guiDeleteListProcess(); + // Emergency Exit? if (event.flags & EVENT_FLAG_KEYPRESS && event.key == KEY_ESC) guiStop(); } @@ -85,6 +101,9 @@ void guiShutdown(void) { uint8_t i; + wmShutdown(); + guiDeleteListProcess(); + DEL(__guiBaseColors); while (hmlen(_widgetCatalog) > 0) { @@ -93,8 +112,6 @@ void guiShutdown(void) { } hmfree(_widgetCatalog); - wmShutdown(); - fontUnload(&__guiFontVGA8x16); fontUnload(&__guiFontVGA8x14); fontUnload(&__guiFontVGA8x8); @@ -151,6 +168,7 @@ uint8_t guiStartup(int16_t width, int16_t height, int16_t depth) { // Register all known widgets with the GUI. guiRegister(windowRegister); + guiRegister(labelRegister); return SUCCESS; } @@ -175,6 +193,12 @@ void guiWidgetBaseSet(WidgetT *widget, uint8_t magic, uint16_t x, uint16_t y, ui } +void guiWidgetDestroy(WidgetT *widget) { + // Add to list of widgets to delete. + arrput(_deleteList, widget); +} + + uint8_t guiWidgetDirtyGet(WidgetT *widget) { return widget->dirty; } diff --git a/client/src/gui/gui.h b/client/src/gui/gui.h index b305f5c..7acefcd 100644 --- a/client/src/gui/gui.h +++ b/client/src/gui/gui.h @@ -32,8 +32,9 @@ typedef struct RectS { typedef struct RegisterS { char *widgetName; // Text name of widget. - WidgetEventT paint; // Paint routine. + WidgetEventT click; // Click handler. WidgetEventT destroy; // Destroy routine. + WidgetEventT paint; // Paint routine. GuiCallbackT unregister; // Unregister routine. } RegisterT; @@ -41,6 +42,7 @@ typedef struct WidgetS { uint8_t magic; // Magic ID of widget. RectT r; // Outer bounds, except for windows. RegisterT *reg; // Registration information. + void *data; // Pointer to arbitrary data for user. uint8_t dirty; // Is the widget dirty? } WidgetT; @@ -91,6 +93,7 @@ void guiShutdown(void); uint8_t guiStartup(int16_t width, int16_t height, int16_t depth); void guiStop(void); void guiWidgetBaseSet(WidgetT *widget, uint8_t magic, uint16_t x, uint16_t y, uint16_t w, uint16_t h); +void guiWidgetDestroy(WidgetT *widget); uint8_t guiWidgetDirtyGet(WidgetT *widget); void guiWidgetDirtySet(WidgetT *widget, uint8_t dirty); diff --git a/client/src/gui/widgets/label.c b/client/src/gui/widgets/label.c new file mode 100644 index 0000000..33fe514 --- /dev/null +++ b/client/src/gui/widgets/label.c @@ -0,0 +1,77 @@ +#include "label.h" + + +uint8_t __MAGIC_LABEL = 0; + + +static void labelDestroy(struct WidgetS *widget, ...); +static void labelPaint(struct WidgetS *widget, ...); + + +void labelClickSet(LabelT *label, WidgetEventT handler, void *data) { + label->base.reg->click = handler; + label->base.data = data; +} + + +void labelColorSet(LabelT *label, ColorT foreground, ColorT background) { + label->foreground = foreground; + label->background = background; + label->base.dirty = 1; +} + + +LabelT *labelCreate(uint16_t x, uint16_t y, uint16_t w, uint16_t h, FontT *font, char *contents, ...) { + LabelT *l = NULL; + + // ***TODO*** Auto-calculate width/height. + + NEW(LabelT, l); + memset(l, 0, sizeof(LabelT)); + guiWidgetBaseSet((WidgetT *)l, __MAGIC_LABEL, x, y, w, h); + l->font = font; + l->text = strdup(contents); + l->foreground = GUI_BLACK; + l->background = GUI_LIGHTGRAY; + l->modsEnabled = 0; + + return l; +} + + +static void labelDestroy(struct WidgetS *widget, ...) { + LabelT *l = (LabelT *)widget; + + if (l->text) DEL(l->text); + DEL(l); +} + + +static void labelPaint(struct WidgetS *widget, ...) { + LabelT *l = (LabelT *)widget; + + if (l->base.dirty) { + l->base.dirty = 0; + fontSet(l->font); + fontColorSet(l->foreground, l->background); + fontModsEnabledSet(l->modsEnabled); + // ***TODO*** Alignment, clipping, etc. + fontRender(l->text, l->base.r.x, l->base.r.y); + } +} + + +RegisterT *labelRegister(uint8_t magic) { + static RegisterT reg = { + "Label", + NULL, // No default on-click handler. + labelDestroy, + labelPaint, + NULL // No unregister handler. + }; + + // One-time widget startup code. + __MAGIC_LABEL = magic; + + return ® +} diff --git a/client/src/gui/widgets/label.h b/client/src/gui/widgets/label.h new file mode 100644 index 0000000..5ee6f8f --- /dev/null +++ b/client/src/gui/widgets/label.h @@ -0,0 +1,27 @@ +#ifndef LABEL_H +#define LABEL_H + + +#include "../gui.h" + + +typedef struct LabelS { + WidgetT base; // Required by all widgets. + char *text; // Contents of label. + FontT *font; // Font to use. + uint8_t modsEnabled; // Escape codes enabled? + ColorT foreground; // Foreground color. + ColorT background; // Background color. +} LabelT; + + +extern uint8_t __MAGIC_LABEL; // Magic ID assigned to us from the GUI. + + +void labelClickSet(LabelT *label, WidgetEventT handler, void *data); +void labelColorSet(LabelT *label, ColorT foreground, ColorT background); +LabelT *labelCreate(uint16_t x, uint16_t y, uint16_t w, uint16_t h, FontT *font, char *contents, ...); +RegisterT *labelRegister(uint8_t magic); + + +#endif // LABEL_H diff --git a/client/src/gui/wmwindow.c b/client/src/gui/wmwindow.c index 4933b7b..634bd76 100644 --- a/client/src/gui/wmwindow.c +++ b/client/src/gui/wmwindow.c @@ -17,12 +17,14 @@ static WindowT *_windowTop = NULL; static int16_t _iconCount = 0; -static void windowCache(WindowT *w); +static void windowCache(WindowT *w, uint8_t redrawWindow); +static void windowDestroy(struct WidgetS *widget, ...); +static void windowPaint(struct WidgetS *widget, ...); static void windowScrollHorizontalHandler(EventT *e, WindowT *w); static void windowScrollVerticalHandler(EventT *e, WindowT *w); -static void windowCache(WindowT *w) { +static void windowCache(WindowT *w, uint8_t redrawWindow) { char c; int16_t i; int16_t x1; @@ -35,6 +37,7 @@ static void windowCache(WindowT *w) { int16_t originalX; int16_t originalY; double d; + RectT originalBounds; ColorT titleBackgroundColor; ColorT widgetColor; SurfaceT *target = surfaceGet(); @@ -61,241 +64,258 @@ static void windowCache(WindowT *w) { // Draw into cache. surfaceSet(w->cached); - // Determine some colors. - titleBackgroundColor = (w == _windowTop) ? GUI_DARKGRAY : GUI_LIGHTGRAY; - widgetColor = (w == _windowTop) ? GUI_LIGHTGRAY : GUI_DARKGRAY; + if (redrawWindow) { + // Determine some colors. + titleBackgroundColor = (w == _windowTop) ? GUI_DARKGRAY : GUI_LIGHTGRAY; + widgetColor = (w == _windowTop) ? GUI_LIGHTGRAY : GUI_DARKGRAY; - // Get ready! - x1 = w->base.r.x; - y1 = w->base.r.y; - x2 = w->base.r.x + w->base.r.w - 1; - y2 = w->base.r.y + w->base.r.h - 1; + // Get ready! + x1 = w->base.r.x; + y1 = w->base.r.y; + x2 = w->base.r.x + w->base.r.w - 1; + y2 = w->base.r.y + w->base.r.h - 1; - // Draw border. - surfaceBoxHighlight(x1, y1, x2, y2, GUI_WHITE, GUI_BLACK); - x1++; y1++; x2--; y2--; - for (i=0; i<3; i++) { - surfaceBox(x1, y1, x2, y2, GUI_LIGHTGRAY); + // Draw border. + surfaceBoxHighlight(x1, y1, x2, y2, GUI_WHITE, GUI_BLACK); x1++; y1++; x2--; y2--; - } - surfaceBoxHighlight(x1, y1, x2, y2, GUI_BLACK, GUI_WHITE); - x1++; y1++; x2--; y2--; - - // Do we need a titlebar? - if (w->title || w->flags & WIN_CLOSE || w->flags & WIN_MAXIMIZE || w->flags & WIN_MINIMIZE) { - tx1 = x1; - tx2 = x2; - ty2 = y1 + WIDGET_SIZE; - - // Close box? - if (w->flags & WIN_CLOSE) { - tx1 += WIDGET_SIZE; - w->close.x = x1; - w->close.y = y1; - w->close.x2 = tx1 - 1; - w->close.y2 = ty2 - 1; - surfaceBoxFilled(w->close.x + 1, w->close.y + 1, w->close.x2 - 1, w->close.y2 - 1, titleBackgroundColor); - surfaceBoxHighlight(w->close.x + 3, w->close.y + 8, w->close.x2 - 3, w->close.y2 - 8, GUI_WHITE, widgetColor); - surfaceBoxHighlight(w->close.x, w->close.y, w->close.x2, w->close.y2, GUI_WHITE, GUI_BLACK); - } else { - w->close.x = w->close.y = w->close.x2 = w->close.y2 = 0; + for (i=0; i<3; i++) { + surfaceBox(x1, y1, x2, y2, GUI_LIGHTGRAY); + x1++; y1++; x2--; y2--; } + surfaceBoxHighlight(x1, y1, x2, y2, GUI_BLACK, GUI_WHITE); + x1++; y1++; x2--; y2--; - // Maximize box? - if (w->flags & WIN_MAXIMIZE) { - tx2 -= WIDGET_SIZE; - w->maximize.x = tx2 + 1; - w->maximize.y = y1; - w->maximize.x2 = tx2 + WIDGET_SIZE; - w->maximize.y2 = ty2 - 1; - surfaceBoxFilled(w->maximize.x + 1, w->maximize.y + 1, w->maximize.x2 - 1, w->maximize.y2 - 1, titleBackgroundColor); - surfaceLine(w->maximize.x + 4, w->maximize.y + 10, w->maximize.x + 10, w->maximize.y + 4, GUI_WHITE); - surfaceLine(w->maximize.x + 4, w->maximize.y + 10, w->maximize.x + 10, w->maximize.y2 - 3, GUI_WHITE); - surfaceLine(w->maximize.x2 - 3, w->maximize.y + 10, w->maximize.x2 - 9, w->maximize.y + 4, widgetColor); - surfaceLine(w->maximize.x2 - 3, w->maximize.y + 10, w->maximize.x2 - 9, w->maximize.y2 - 3, widgetColor); - surfaceBoxHighlight(w->maximize.x, w->maximize.y, w->maximize.x2, w->maximize.y2, GUI_WHITE, GUI_BLACK); - } else { - w->maximize.x = w->maximize.y = w->maximize.x2 = w->maximize.y2 = 0; - } + // Do we need a titlebar? + if (w->title || w->flags & WIN_CLOSE || w->flags & WIN_MAXIMIZE || w->flags & WIN_MINIMIZE) { + tx1 = x1; + tx2 = x2; + ty2 = y1 + WIDGET_SIZE; - // Minimize box? - if (w->flags & WIN_MINIMIZE) { - tx2 -= WIDGET_SIZE; - w->minimize.x = tx2 + 1; - w->minimize.y = y1; - w->minimize.x2 = tx2 + WIDGET_SIZE; - w->minimize.y2 = ty2 - 1; - surfaceBoxFilled(w->minimize.x + 1, w->minimize.y + 1, w->minimize.x2 - 1, w->minimize.y2 - 1, titleBackgroundColor); - surfaceBoxHighlight(w->minimize.x + 7, w->minimize.y + 7, w->minimize.x2 - 7, w->minimize.y2 - 7, GUI_WHITE, widgetColor); - surfaceBoxHighlight(w->minimize.x, w->minimize.y, w->minimize.x2, w->minimize.y2, GUI_WHITE, GUI_BLACK); - } else { - w->minimize.x = w->minimize.y = w->minimize.x2 = w->maximize.y2 = 0; - } - - // Draw titlebar background. - w->titlebar.x = tx1; - w->titlebar.y = y1; - w->titlebar.x2 = tx2; - w->titlebar.y2 = ty2 - 1; - surfaceBoxFilled(w->titlebar.x + 1, w->titlebar.y + 1, w->titlebar.x2 - 1, w->titlebar.y2 - 1, titleBackgroundColor); - if (w->title) { - // Prepare font. Don't allow color changes in titles. - fontSet(__guiFontVGA8x16); - fontColorSet(GUI_WHITE, titleBackgroundColor); - fontModsEnabledSet(0); - // How many characters do we have room for? - i = ((w->titlebar.x2 - w->titlebar.x) >> 3) - 2; - // Does the title fit? - if ((int16_t)strlen(w->title) <= i) { - // Render entire title. - fontRender(w->title, w->titlebar.x + 12, w->titlebar.y + 2); + // Close box? + if (w->flags & WIN_CLOSE) { + tx1 += WIDGET_SIZE; + w->close.x = x1; + w->close.y = y1; + w->close.x2 = tx1 - 1; + w->close.y2 = ty2 - 1; + surfaceBoxFilled(w->close.x + 1, w->close.y + 1, w->close.x2 - 1, w->close.y2 - 1, titleBackgroundColor); + surfaceBoxHighlight(w->close.x + 3, w->close.y + 8, w->close.x2 - 3, w->close.y2 - 8, GUI_WHITE, widgetColor); + surfaceBoxHighlight(w->close.x, w->close.y, w->close.x2, w->close.y2, GUI_WHITE, GUI_BLACK); } else { - // Render partial title. - c = w->title[i]; - w->title[i] = 0; - fontRender(w->title, w->titlebar.x + 12, w->titlebar.y + 2); - w->title[i] = c; + w->close.x = w->close.y = w->close.x2 = w->close.y2 = 0; } - } - surfaceBoxHighlight(w->titlebar.x, w->titlebar.y, w->titlebar.x2, w->titlebar.y2, GUI_WHITE, GUI_BLACK); - y1 += WIDGET_SIZE; - } else { - w->close.x = w->close.y = w->close.x2 = w->close.y2 = 0; - w->maximize.x = w->maximize.y = w->maximize.x2 = w->maximize.y2 = 0; - w->minimize.x = w->minimize.y = w->minimize.x2 = w->maximize.y2 = 0; - w->titlebar.x = w->titlebar.y = w->titlebar.x2 = w->titlebar.y2 = 0; - } + // Maximize box? + if (w->flags & WIN_MAXIMIZE) { + tx2 -= WIDGET_SIZE; + w->maximize.x = tx2 + 1; + w->maximize.y = y1; + w->maximize.x2 = tx2 + WIDGET_SIZE; + w->maximize.y2 = ty2 - 1; + surfaceBoxFilled(w->maximize.x + 1, w->maximize.y + 1, w->maximize.x2 - 1, w->maximize.y2 - 1, titleBackgroundColor); + surfaceLine(w->maximize.x + 4, w->maximize.y + 10, w->maximize.x + 10, w->maximize.y + 4, GUI_WHITE); + surfaceLine(w->maximize.x + 4, w->maximize.y + 10, w->maximize.x + 10, w->maximize.y2 - 3, GUI_WHITE); + surfaceLine(w->maximize.x2 - 3, w->maximize.y + 10, w->maximize.x2 - 9, w->maximize.y + 4, widgetColor); + surfaceLine(w->maximize.x2 - 3, w->maximize.y + 10, w->maximize.x2 - 9, w->maximize.y2 - 3, widgetColor); + surfaceBoxHighlight(w->maximize.x, w->maximize.y, w->maximize.x2, w->maximize.y2, GUI_WHITE, GUI_BLACK); + } else { + w->maximize.x = w->maximize.y = w->maximize.x2 = w->maximize.y2 = 0; + } - // Vertical Scroll Bar? - if (w->flags & WIN_SCROLL_V) { - w->scrollv.x2 = x2; - w->scrollv.x = w->scrollv.x2 - (WIDGET_SIZE - 1); - w->scrollv.y = y1; - w->scrollv.y2 = y2; - // Draw scroll bar. - surfaceBoxFilled(w->scrollv.x + 1, w->scrollv.y + 1, w->scrollv.x2 - 1, w->scrollv.y2 - 1, titleBackgroundColor); - surfaceBoxHighlight(w->scrollv.x, w->scrollv.y, w->scrollv.x2, w->scrollv.y2, GUI_WHITE, GUI_BLACK); - surfaceLineH(w->scrollv.x + 1, w->scrollv.x2, w->scrollv.y + (WIDGET_SIZE - 1), GUI_BLACK); - surfaceLineH(w->scrollv.x + 1, w->scrollv.x2, w->scrollv.y2 - (WIDGET_SIZE - 1), GUI_WHITE); - // Prepare font. - fontSet(__guiFontVGA8x8); - fontColorSet(widgetColor, titleBackgroundColor); - // Draw arrows. - fontRender("\x1e", w->scrollv.x + 7, w->scrollv.y + 7); - fontRender("\x1f", w->scrollv.x + 7, w->scrollv.y2 - 12); + // Minimize box? + if (w->flags & WIN_MINIMIZE) { + tx2 -= WIDGET_SIZE; + w->minimize.x = tx2 + 1; + w->minimize.y = y1; + w->minimize.x2 = tx2 + WIDGET_SIZE; + w->minimize.y2 = ty2 - 1; + surfaceBoxFilled(w->minimize.x + 1, w->minimize.y + 1, w->minimize.x2 - 1, w->minimize.y2 - 1, titleBackgroundColor); + surfaceBoxHighlight(w->minimize.x + 7, w->minimize.y + 7, w->minimize.x2 - 7, w->minimize.y2 - 7, GUI_WHITE, widgetColor); + surfaceBoxHighlight(w->minimize.x, w->minimize.y, w->minimize.x2, w->minimize.y2, GUI_WHITE, GUI_BLACK); + } else { + w->minimize.x = w->minimize.y = w->minimize.x2 = w->maximize.y2 = 0; + } - x2 -= (WIDGET_SIZE - 1); - } else { - w->scrollv.x = w->scrollv.y = w->scrollv.x2 = w->scrollv.y2 = 0; - } + // Draw titlebar background. + w->titlebar.x = tx1; + w->titlebar.y = y1; + w->titlebar.x2 = tx2; + w->titlebar.y2 = ty2 - 1; + surfaceBoxFilled(w->titlebar.x + 1, w->titlebar.y + 1, w->titlebar.x2 - 1, w->titlebar.y2 - 1, titleBackgroundColor); + if (w->title) { + // Prepare font. Don't allow color changes in titles. + fontSet(__guiFontVGA8x16); + fontColorSet(GUI_WHITE, titleBackgroundColor); + fontModsEnabledSet(0); + // How many characters do we have room for? + i = ((w->titlebar.x2 - w->titlebar.x) >> 3) - 2; + // Does the title fit? + if ((int16_t)strlen(w->title) <= i) { + // Render entire title. + fontRender(w->title, w->titlebar.x + 12, w->titlebar.y + 2); + } else { + // Render partial title. + c = w->title[i]; + w->title[i] = 0; + fontRender(w->title, w->titlebar.x + 12, w->titlebar.y + 2); + w->title[i] = c; + } + } + surfaceBoxHighlight(w->titlebar.x, w->titlebar.y, w->titlebar.x2, w->titlebar.y2, GUI_WHITE, GUI_BLACK); - // Horizontal Scroll Bar? - if (w->flags & WIN_SCROLL_H) { - w->scrollh.x = x1; - w->scrollh.x2 = x2; - w->scrollh.y2 = y2; - w->scrollh.y = w->scrollh.y2 - (WIDGET_SIZE - 1); - // Draw scroll bar. - surfaceBoxFilled(w->scrollh.x + 1, w->scrollh.y + 1, w->scrollh.x2 - 1, w->scrollh.y2 - 1, titleBackgroundColor); - surfaceBoxHighlight(w->scrollh.x, w->scrollh.y, w->scrollh.x2, w->scrollh.y2, GUI_WHITE, GUI_BLACK); - surfaceLineV(w->scrollh.x + (WIDGET_SIZE - 1), w->scrollh.y + 1, w->scrollh.y2, GUI_BLACK); - surfaceLineV(w->scrollh.x2 - (WIDGET_SIZE - 1), w->scrollh.y + 1, w->scrollh.y2, GUI_WHITE); - // If we have both horizontal and vertical scroll bars, we need to fix a shadow. - if (w->flags & WIN_SCROLL_V) { - surfaceLineV(w->scrollh.x2 - 1, w->scrollh.y, w->scrollh.y2, GUI_BLACK); - surfaceLineV(w->scrollh.x2, w->scrollh.y, w->scrollh.y2, GUI_WHITE); - } - // Prepare font. - fontSet(__guiFontVGA8x8); - fontColorSet(widgetColor, titleBackgroundColor); - // Draw arrows. - fontRender("\x11", w->scrollh.x + 7, w->scrollh.y + 7); - fontRender("\x10", w->scrollh.x2 - 12, w->scrollh.y + 7); - - y2 -= (WIDGET_SIZE - 1); - } else { - w->scrollh.x = w->scrollh.y = w->scrollh.x2 = w->scrollh.y2 = 0; - } - - // Find content area. - w->bounds.x = x1; - w->bounds.y = y1; - w->bounds.x2 = x2; - w->bounds.y2 = y2; - - // Do we have content yet? - if (w->content) { - - // Vertical Scroll Bar Thumb? - if (w->flags & WIN_SCROLL_V) { - // Distance between arrow buttons on scroll bar. - i = w->scrollv.y2 - w->scrollv.y - (WIDGET_SIZE * 2 - 2) - 2; - // Percentage to scale content height to scroll bar height. - d = (double)i / (double)surfaceHeightGet(w->content); - // Find position and size of thumb. - w->thumbv.x = w->scrollv.x + 1; - w->thumbv.x2 = w->scrollv.x2 - 1; - w->thumbv.y = w->scrollv.y + WIDGET_SIZE + (w->offset.y * d); - w->thumbv.y2 = w->thumbv.y + ((w->bounds.y2 - w->bounds.y) * d); - // Clamp overflow due to doubles and my off-by-one brain. - if (w->thumbv.y2 >= w->thumbv.y + i - 1) w->thumbv.y2 = w->thumbv.y + i - 1; - // Draw thumb. - surfaceBoxFilled(w->thumbv.x + 1, w->thumbv.y + 1, w->thumbv.x2 - 1, w->thumbv.y2 - 1, widgetColor); - surfaceBoxHighlight(w->thumbv.x, w->thumbv.y, w->thumbv.x2, w->thumbv.y2, GUI_WHITE, GUI_BLACK); + y1 += WIDGET_SIZE; } else { - w->thumbv.x = w->thumbv.y = w->thumbv.x2 = w->thumbv.y2 = 0; + w->close.x = w->close.y = w->close.x2 = w->close.y2 = 0; + w->maximize.x = w->maximize.y = w->maximize.x2 = w->maximize.y2 = 0; + w->minimize.x = w->minimize.y = w->minimize.x2 = w->maximize.y2 = 0; + w->titlebar.x = w->titlebar.y = w->titlebar.x2 = w->titlebar.y2 = 0; } - // Horizontal Scroll Bar Thumb? + // Vertical Scroll Bar? + if (w->flags & WIN_SCROLL_V) { + w->scrollv.x2 = x2; + w->scrollv.x = w->scrollv.x2 - (WIDGET_SIZE - 1); + w->scrollv.y = y1; + w->scrollv.y2 = y2; + // Draw scroll bar. + surfaceBoxFilled(w->scrollv.x + 1, w->scrollv.y + 1, w->scrollv.x2 - 1, w->scrollv.y2 - 1, titleBackgroundColor); + surfaceBoxHighlight(w->scrollv.x, w->scrollv.y, w->scrollv.x2, w->scrollv.y2, GUI_WHITE, GUI_BLACK); + surfaceLineH(w->scrollv.x + 1, w->scrollv.x2, w->scrollv.y + (WIDGET_SIZE - 1), GUI_BLACK); + surfaceLineH(w->scrollv.x + 1, w->scrollv.x2, w->scrollv.y2 - (WIDGET_SIZE - 1), GUI_WHITE); + // Prepare font. + fontSet(__guiFontVGA8x8); + fontColorSet(widgetColor, titleBackgroundColor); + // Draw arrows. + fontRender("\x1e", w->scrollv.x + 7, w->scrollv.y + 7); + fontRender("\x1f", w->scrollv.x + 7, w->scrollv.y2 - 12); + + x2 -= (WIDGET_SIZE - 1); + } else { + w->scrollv.x = w->scrollv.y = w->scrollv.x2 = w->scrollv.y2 = 0; + } + + // Horizontal Scroll Bar? if (w->flags & WIN_SCROLL_H) { - // Distance between arrow buttons on scroll bar. - i = w->scrollh.x2 - w->scrollh.x - (WIDGET_SIZE * 2 - 2) - 2; - // Percentage to scale content width to scroll bar width. - d = (double)i / (double)surfaceWidthGet(w->content); - // Find position and size of thumb. - w->thumbh.x = w->scrollh.x + WIDGET_SIZE + (w->offset.x * d); - w->thumbh.x2 = w->thumbh.x + ((w->bounds.x2 - w->bounds.x) * d); - w->thumbh.y = w->scrollh.y + 1; - w->thumbh.y2 = w->scrollh.y2 - 1; - // Clamp overflow due to doubles and my off-by-one brain. - if (w->thumbh.x2 >= w->thumbh.x + i - 1) w->thumbh.x2 = w->thumbh.x + i - 1; - // Draw thumb. - surfaceBoxFilled(w->thumbh.x + 1, w->thumbh.y + 1, w->thumbh.x2 - 1, w->thumbh.y2 - 1, widgetColor); - surfaceBoxHighlight(w->thumbh.x, w->thumbh.y, w->thumbh.x2, w->thumbh.y2, GUI_WHITE, GUI_BLACK); + w->scrollh.x = x1; + w->scrollh.x2 = x2; + w->scrollh.y2 = y2; + w->scrollh.y = w->scrollh.y2 - (WIDGET_SIZE - 1); + // Draw scroll bar. + surfaceBoxFilled(w->scrollh.x + 1, w->scrollh.y + 1, w->scrollh.x2 - 1, w->scrollh.y2 - 1, titleBackgroundColor); + surfaceBoxHighlight(w->scrollh.x, w->scrollh.y, w->scrollh.x2, w->scrollh.y2, GUI_WHITE, GUI_BLACK); + surfaceLineV(w->scrollh.x + (WIDGET_SIZE - 1), w->scrollh.y + 1, w->scrollh.y2, GUI_BLACK); + surfaceLineV(w->scrollh.x2 - (WIDGET_SIZE - 1), w->scrollh.y + 1, w->scrollh.y2, GUI_WHITE); + // If we have both horizontal and vertical scroll bars, we need to fix a shadow. + if (w->flags & WIN_SCROLL_V) { + surfaceLineV(w->scrollh.x2 - 1, w->scrollh.y, w->scrollh.y2, GUI_BLACK); + surfaceLineV(w->scrollh.x2, w->scrollh.y, w->scrollh.y2, GUI_WHITE); + } + // Prepare font. + fontSet(__guiFontVGA8x8); + fontColorSet(widgetColor, titleBackgroundColor); + // Draw arrows. + fontRender("\x11", w->scrollh.x + 7, w->scrollh.y + 7); + fontRender("\x10", w->scrollh.x2 - 12, w->scrollh.y + 7); + + y2 -= (WIDGET_SIZE - 1); } else { + w->scrollh.x = w->scrollh.y = w->scrollh.x2 = w->scrollh.y2 = 0; + } + + // Find content area. + w->bounds.x = x1; + w->bounds.y = y1; + w->bounds.x2 = x2; + w->bounds.y2 = y2; + + // Do we have content yet? + if (w->content) { + + // Vertical Scroll Bar Thumb? + if (w->flags & WIN_SCROLL_V) { + // Distance between arrow buttons on scroll bar. + i = w->scrollv.y2 - w->scrollv.y - (WIDGET_SIZE * 2 - 2) - 2; + // Percentage to scale content height to scroll bar height. + d = (double)i / (double)surfaceHeightGet(w->content); + // Find position and size of thumb. + w->thumbv.x = w->scrollv.x + 1; + w->thumbv.x2 = w->scrollv.x2 - 1; + w->thumbv.y = w->scrollv.y + WIDGET_SIZE + (w->offset.y * d); + w->thumbv.y2 = w->thumbv.y + ((w->bounds.y2 - w->bounds.y) * d); + // Clamp overflow due to doubles and my off-by-one brain. + if (w->thumbv.y2 >= w->thumbv.y + i - 1) w->thumbv.y2 = w->thumbv.y + i - 1; + // Draw thumb. + surfaceBoxFilled(w->thumbv.x + 1, w->thumbv.y + 1, w->thumbv.x2 - 1, w->thumbv.y2 - 1, widgetColor); + surfaceBoxHighlight(w->thumbv.x, w->thumbv.y, w->thumbv.x2, w->thumbv.y2, GUI_WHITE, GUI_BLACK); + } else { + w->thumbv.x = w->thumbv.y = w->thumbv.x2 = w->thumbv.y2 = 0; + } + + // Horizontal Scroll Bar Thumb? + if (w->flags & WIN_SCROLL_H) { + // Distance between arrow buttons on scroll bar. + i = w->scrollh.x2 - w->scrollh.x - (WIDGET_SIZE * 2 - 2) - 2; + // Percentage to scale content width to scroll bar width. + d = (double)i / (double)surfaceWidthGet(w->content); + // Find position and size of thumb. + w->thumbh.x = w->scrollh.x + WIDGET_SIZE + (w->offset.x * d); + w->thumbh.x2 = w->thumbh.x + ((w->bounds.x2 - w->bounds.x) * d); + w->thumbh.y = w->scrollh.y + 1; + w->thumbh.y2 = w->scrollh.y2 - 1; + // Clamp overflow due to doubles and my off-by-one brain. + if (w->thumbh.x2 >= w->thumbh.x + i - 1) w->thumbh.x2 = w->thumbh.x + i - 1; + // Draw thumb. + surfaceBoxFilled(w->thumbh.x + 1, w->thumbh.y + 1, w->thumbh.x2 - 1, w->thumbh.y2 - 1, widgetColor); + surfaceBoxHighlight(w->thumbh.x, w->thumbh.y, w->thumbh.x2, w->thumbh.y2, GUI_WHITE, GUI_BLACK); + } else { + w->thumbh.x = w->thumbh.y = w->thumbh.x2 = w->thumbh.y2 = 0; + } + + } else { // Do we have content yet? + w->thumbv.x = w->thumbv.y = w->thumbv.x2 = w->thumbv.y2 = 0; w->thumbh.x = w->thumbh.y = w->thumbh.x2 = w->thumbh.y2 = 0; } - } else { // Do we have content yet? - w->thumbv.x = w->thumbv.y = w->thumbv.x2 = w->thumbv.y2 = 0; - w->thumbh.x = w->thumbh.y = w->thumbh.x2 = w->thumbh.y2 = 0; - } + // Resize handle. + if (w->flags & WIN_RESIZE) { + w->resize1.x2 = w->base.r.x + w->base.r.w - 2; + w->resize1.x = w->resize1.x2 - 3; + w->resize1.y2 = w->base.r.y + w->base.r.h; + w->resize1.y = w->resize1.y2 - (WIDGET_SIZE + 5); + surfaceLineH(w->resize1.x, w->resize1.x2, w->resize1.y, GUI_BLACK); + surfaceLineH(w->resize1.x, w->resize1.x2, w->resize1.y + 1, GUI_WHITE); + w->resize2.x2 = w->base.r.x + w->base.r.w; + w->resize2.x = w->resize2.x2 - (WIDGET_SIZE + 5); + w->resize2.y2 = w->base.r.y + w->base.r.h - 2; + w->resize2.y = w->resize2.y2 - 3; + surfaceLineV(w->resize2.x, w->resize2.y, w->resize2.y2, GUI_BLACK); + surfaceLineV(w->resize2.x + 1, w->resize2.y, w->resize2.y2, GUI_WHITE); + } else { + w->resize1.x = w->resize1.y = w->resize1.x2 = w->resize1.y2 = 0; + w->resize2.x = w->resize2.y = w->resize2.x2 = w->resize2.y2 = 0; + } - // Resize handle. - if (w->flags & WIN_RESIZE) { - w->resize1.x2 = w->base.r.x + w->base.r.w - 2; - w->resize1.x = w->resize1.x2 - 3; - w->resize1.y2 = w->base.r.y + w->base.r.h; - w->resize1.y = w->resize1.y2 - (WIDGET_SIZE + 5); - surfaceLineH(w->resize1.x, w->resize1.x2, w->resize1.y, GUI_BLACK); - surfaceLineH(w->resize1.x, w->resize1.x2, w->resize1.y + 1, GUI_WHITE); - w->resize2.x2 = w->base.r.x + w->base.r.w; - w->resize2.x = w->resize2.x2 - (WIDGET_SIZE + 5); - w->resize2.y2 = w->base.r.y + w->base.r.h - 2; - w->resize2.y = w->resize2.y2 - 3; - surfaceLineV(w->resize2.x, w->resize2.y, w->resize2.y2, GUI_BLACK); - surfaceLineV(w->resize2.x + 1, w->resize2.y, w->resize2.y2, GUI_WHITE); - } else { - w->resize1.x = w->resize1.y = w->resize1.x2 = w->resize1.y2 = 0; - w->resize2.x = w->resize2.y = w->resize2.x2 = w->resize2.y2 = 0; + } else { // Redraw window. + + // Since we didn't calculate them above, adjust bounds for content blit. + originalBounds = w->bounds; + w->bounds.x -= originalX; + w->bounds.y -= originalY; + w->bounds.x2 -= originalX; + w->bounds.y2 -= originalY; } // Blit contents. if (w->content) surfaceBlit(w->bounds.x, w->bounds.y, w->offset.x, w->offset.y, w->bounds.x2 - w->bounds.x, w->bounds.y2 - w->bounds.y, w->content); // Fixup all the widget coordinates. - windowMove(w, originalX, originalY); + if (redrawWindow) { + windowMove(w, originalX, originalY); + } else { + w->bounds = originalBounds; + w->base.r.x = originalX; + w->base.r.y = originalY; + } surfaceSet(target); } @@ -311,16 +331,16 @@ WindowT *windowCreate(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *titl SurfaceT *t = surfaceGet(); NEW(WindowT, win); - memset(win, 0, sizeof(WindowT)); - guiWidgetBaseSet((WidgetT *)win, __MAGIC_WINDOW, x, y, w, h); + guiWidgetBaseSet((WidgetT *)win, __MAGIC_WINDOW, x, y, w, h); win->title = strdup(title); win->flags = (uint8_t)flags; - //***DEBUG*** Hackery to get contents before we have widgets. + /* + // ***DEBUG*** Hackery to get contents before we have widgets. static uint8_t image = 1; static char name[16]; - windowCache(win); + windowCache(win, 1); width = win->bounds.x2 - win->bounds.x; height = win->bounds.y2 - win->bounds.y; guiWidgetDirtySet(W(win), 1); @@ -331,7 +351,8 @@ WindowT *windowCreate(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *titl win->offset.y = 100;//(surfaceHeightGet(win->content) - height) * 0.5; } image++; - /* + */ + // If the window is resizable, we need to get two more arguments for the content size. if (win->flags & WIN_RESIZE) { va_start(args, flags); @@ -340,7 +361,7 @@ WindowT *windowCreate(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *titl va_end(args); } else { // Use whatever the default content area is. This causes an extra draw on create. Oh well. - windowCache(win); + windowCache(win, 1); width = win->bounds.x2 - win->bounds.x; height = win->bounds.y2 - win->bounds.y; guiWidgetDirtySet(W(win), 1); @@ -350,7 +371,6 @@ WindowT *windowCreate(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *titl surfaceSet(win->content); surfaceClear(GUI_LIGHTGRAY); surfaceSet(t); - */ // Add to window list. arrput(_windowList, win); @@ -362,33 +382,37 @@ WindowT *windowCreate(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *titl } -void windowDestroy(struct WidgetS *widget, ...) { +static void windowDestroy(struct WidgetS *widget, ...) { uint16_t i; + uint16_t c; WindowT *window = (WindowT *)widget; - // Find the window to delete. + // Remove it from the window list, if it exists there. for (i=0; ititle) DEL(_windowList[i]->title); - // Free cached surface. - if (_windowList[i]->cached) surfaceDestroy(&_windowList[i]->cached); - // Free content surface. - if (_windowList[i]->content) surfaceDestroy(&_windowList[i]->content); - // Delete the window. - DEL(_windowList[i]); - // Remove it from window list. arrdel(_windowList, i); - // Fixup focus. - windowFocusSet(NULL); break; } } + + // Was it the focused window? + if (window == _windowTop) { + // Find new topmost window on next call to focus. + _windowTop = NULL; + } + // Free the title. + if (window->title) DEL(window->title); + // Free children. + for (c=0; cchildren); c++) guiWidgetDestroy(window->children[c]); + arrfree(window->children); + // Free cached surface. + if (window->cached) surfaceDestroy(&window->cached); + // Free content surface. + if (window->content) surfaceDestroy(&window->content); + // Delete the window. + DEL(window); + // Fixup focus. + windowFocusSet(NULL); } @@ -509,7 +533,7 @@ void windowMaximizeRestore(WindowT *win) { } // Maximized? // Update. - windowCache(win); + windowCache(win, 1); } @@ -600,7 +624,7 @@ void windowMove(WindowT *w, uint16_t x, uint16_t y) { } -void windowPaint(struct WidgetS *widget, ...) { +static void windowPaint(struct WidgetS *widget, ...) { int16_t x; int16_t y; int16_t px; @@ -610,13 +634,32 @@ void windowPaint(struct WidgetS *widget, ...) { int16_t xc; int16_t yc; WindowT *w = (WindowT *)widget; + SurfaceT *t = surfaceGet(); - // Do we need redrawn? + // Are any child widgets dirty? + y = 0; + for (x=0; xchildren); x++) { + if (w->children[x]->dirty) { + y = 1; + if (w->children[x]->reg->paint) { + surfaceSet(w->content); + w->children[x]->reg->paint(w->children[x]); + } + } + } + surfaceSet(t); + + // Does the window itself need redrawn? if (guiWidgetDirtyGet(widget)) { guiWidgetDirtySet(widget, 0); - windowCache(w); + x = 1; + } else { + x = 0; } + // Did a widget or the window need redrawn? + if (x || y) windowCache(w, x); + // Are we minimized? if (w->flags & WIN_IS_ICON) { // Draw iconized version of contents. There are too many counters here but it's the only way it worked reliably. @@ -645,9 +688,10 @@ void windowPaint(struct WidgetS *widget, ...) { RegisterT *windowRegister(uint8_t magic) { static RegisterT reg = { "Window", - windowPaint, - windowDestroy, - NULL + NULL, // Click event is special for windows. + windowDestroy, // Destroy. + windowPaint, // Paint. + NULL // Unregister. }; // One-time widget startup code. @@ -693,7 +737,7 @@ void windowResize(WindowT *win, uint16_t width, uint16_t height) { // Do resize. win->base.r.w = width; win->base.r.h =height; - windowCache(win); + windowCache(win, 1); } } @@ -707,7 +751,7 @@ static void windowScrollHorizontalHandler(EventT *e, WindowT *w) { // Clip. if (w->offset.x < 0) w->offset.x = 0; // Update. - windowCache(w); + windowCache(w, 1); return; } @@ -718,7 +762,7 @@ static void windowScrollHorizontalHandler(EventT *e, WindowT *w) { // Clip. if (w->offset.x > surfaceWidthGet(w->content) - (w->bounds.x2 - w->bounds.x)) w->offset.x = surfaceWidthGet(w->content) - (w->bounds.x2 - w->bounds.x); // Update. - windowCache(w); + windowCache(w, 1); return; } @@ -729,7 +773,7 @@ static void windowScrollHorizontalHandler(EventT *e, WindowT *w) { // Clip. if (w->offset.x < 0) w->offset.x = 0; // Update. - windowCache(w); + windowCache(w, 1); return; } @@ -740,7 +784,7 @@ static void windowScrollHorizontalHandler(EventT *e, WindowT *w) { // Clip. if (w->offset.x > surfaceWidthGet(w->content) - (w->bounds.x2 - w->bounds.x)) w->offset.x = surfaceWidthGet(w->content) - (w->bounds.x2 - w->bounds.x); // Update. - windowCache(w); + windowCache(w, 1); return; } } @@ -755,7 +799,7 @@ static void windowScrollVerticalHandler(EventT *e, WindowT *w) { // Clip. if (w->offset.y < 0) w->offset.y = 0; // Update. - windowCache(w); + windowCache(w, 1); return; } @@ -766,7 +810,7 @@ static void windowScrollVerticalHandler(EventT *e, WindowT *w) { // Clip. if (w->offset.y > surfaceHeightGet(w->content) - (w->bounds.y2 - w->bounds.y)) w->offset.y = surfaceHeightGet(w->content) - (w->bounds.y2 - w->bounds.y); // Update. - windowCache(w); + windowCache(w, 1); return; } @@ -777,7 +821,7 @@ static void windowScrollVerticalHandler(EventT *e, WindowT *w) { // Clip. if (w->offset.y < 0) w->offset.y = 0; // Update. - windowCache(w); + windowCache(w, 1); return; } @@ -788,16 +832,33 @@ static void windowScrollVerticalHandler(EventT *e, WindowT *w) { // Clip. if (w->offset.y > surfaceHeightGet(w->content) - (w->bounds.y2 - w->bounds.y)) w->offset.y = surfaceHeightGet(w->content) - (w->bounds.y2 - w->bounds.y); // Update. - windowCache(w); + windowCache(w, 1); return; } } -void wmShutdown(void) { - while (arrlen(_windowList) > 0) { - windowDestroy((WidgetT *)_windowList[0]); +void windowWidgetAdd(WindowT *window, WidgetT *widget) { + arrput(window->children, widget); +} + + +void windowWidgetRemove(WindowT *window, WidgetT *widget) { + uint16_t i; + + for (i=0; ichildren); i++) { + if (window->children[i] == widget) { + arrdel(window->children, i); + break; + } } +} + + +void wmShutdown(void) { + uint16_t i; + + for (i=0; i