diff --git a/roo-e/roo-e.pro b/roo-e/roo-e.pro index 1150cfa..0878924 100644 --- a/roo-e/roo-e.pro +++ b/roo-e/roo-e.pro @@ -55,6 +55,7 @@ HEADERS += \ src/gui/surface.h \ src/gui/widgets/button.h \ src/gui/widgets/checkbox.h \ + src/gui/widgets/frame.h \ src/gui/widgets/label.h \ src/gui/widgets/picture.h \ src/gui/widgets/radio.h \ @@ -78,6 +79,7 @@ SOURCES += \ src/gui/surface.c \ src/gui/widgets/button.c \ src/gui/widgets/checkbox.c \ + src/gui/widgets/frame.c \ src/gui/widgets/label.c \ src/gui/widgets/picture.c \ src/gui/widgets/radio.c \ diff --git a/roo-e/src/gui/gui-all.h b/roo-e/src/gui/gui-all.h index 63db501..5f425fb 100644 --- a/roo-e/src/gui/gui-all.h +++ b/roo-e/src/gui/gui-all.h @@ -39,6 +39,7 @@ #include "widgets/vscroll.h" #include "widgets/hscroll.h" #include "widgets/scroll.h" +#include "widgets/frame.h" #endif // GUIALL_H diff --git a/roo-e/src/gui/gui.c b/roo-e/src/gui/gui.c index 591b444..1bf55dc 100644 --- a/roo-e/src/gui/gui.c +++ b/roo-e/src/gui/gui.c @@ -202,6 +202,7 @@ uint8_t guiStartup(int16_t width, int16_t height, int16_t depth) { guiRegister(vscrollRegister); guiRegister(hscrollRegister); guiRegister(scrollableRegister); // After hscroll and vscroll. + guiRegister(frameRegister); return SUCCESS; } diff --git a/roo-e/src/gui/widgets/frame.c b/roo-e/src/gui/widgets/frame.c new file mode 100644 index 0000000..47e39ef --- /dev/null +++ b/roo-e/src/gui/widgets/frame.c @@ -0,0 +1,266 @@ +/* + * Roo/E, the Kangaroo Punch Portable GUI Toolkit + * Copyright (C) 2022 Scott Duensing + * + * http://kangaroopunch.com + * + * + * This file is part of Roo/E. + * + * Roo/E is free software: you can redistribute it and/or modify it under the + * terms of the GNU Affero General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Roo/E is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Roo/E. If not, see . + * + */ + + +#include "frame.h" + +#include "array.h" + + +uint8_t __MAGIC_FRAME = 0; + + +static void frameDestroy(struct WidgetS *widget, ...); +static void framePaint(struct WidgetS *widget, ...); +static WidgetT *frameWidgetFinder(WidgetT *widget, uint16_t x, uint16_t y, int16_t *localX, int16_t *localY); + + +// Passing "flags" as a default int provides proper alignment for the following va_args list. +FrameT *frameCreate(char *title, int16_t width, int16_t height, int flags, ...) { + FrameT *f = NULL; + SurfaceT *t = surfaceGet(); + va_list args; + + NEW(FrameT, f); + memset(f, 0, sizeof(FrameT)); + + widgetBaseSet(W(f), __MAGIC_FRAME, width, height); + + // Set up desired frame style. + f->flags = flags; + + // Set up title. + if (title && strlen(title) > 0) { + f->title = (char *)malloc(strlen(title) + 3); + if (!f->title) { + DEL(f); + return NULL; + } + sprintf(f->title, " %s ", title); + } + + // Get title and optional font. + if (f->flags & FRAME_FONT) { + va_start(args, flags); + f->font = (FontT *)va_arg(args, void *); + va_end(args); + } else { + f->font = __guiFontVGA8x14; + } + + // Surface size is requested size less the font area around it (unless style is FRAME_NONE). + if (f->flags & FRAME_NONE) { + f->pixels.x = width; + f->pixels.y = height; + } else { + // With a frame, we need to adjust the height/width to be a multiple of the font size. + f->chars.x = ((float)f->base.r.w / (float)fontWidthGet(f->font)); + f->chars.y = ((float)f->base.r.h / (float)fontHeightGet(f->font)); + f->base.r.w = f->chars.x * fontWidthGet(f->font); + f->base.r.h = f->chars.y * fontHeightGet(f->font); + f->pixels.x = f->base.r.w - (fontWidthGet(f->font) * 2); + f->pixels.y = f->base.r.h - (fontHeightGet(f->font) * 2);; + } + + // Create surface. + f->surface = surfaceCreate(f->pixels.x, f->pixels.y); + if (!f->surface) { + if (f->title) free(f->title); + DEL(f); + return NULL; + } + surfaceSet(f->surface); + surfaceClear(GUI_LIGHTGRAY); + surfaceSet(t); + + return f; +} + + +static void frameDestroy(struct WidgetS *widget, ...) { + FrameT *f = (FrameT *)widget; + uint16_t c; + + // Free children. + for (c=0; cbase.children); c++) widgetDestroy(f->base.children[c]); + arrfree(f->base.children); + + if (f->title) free(f->title); + if (f->surface) surfaceDestroy(&f->surface); + + DEL(f); +} + + +static void framePaint(struct WidgetS *widget, ...) { + FrameT *f = (FrameT *)widget; + SurfaceT *t = surfaceGet(); + PointT o; + PointT w; + uint16_t x; + uint16_t x2; + uint16_t y; + uint16_t i; + unsigned char ul; + unsigned char ur; + unsigned char ll; + unsigned char lr; + unsigned char h; + unsigned char v; + unsigned char s[2]; + + if (widgetDirtyGet(widget)) { + widgetDirtySet(widget, 0); + + // If we have a frame, draw it. + if (f->flags & FRAME_NONE) { + w.x = 0; + w.y = 0; + } else { + w.x = fontWidthGet(f->font); + w.y = fontHeightGet(f->font); + if (f->flags & FRAME_SINGLE) { + // Single line frame. + ul = 218; + ur = 191; + ll = 192; + lr = 217; + h = 196; + v = 179; + } else { + // Double line frame. + ul = 201; + ur = 187; + ll = 200; + lr = 188; + h = 205; + v = 186; + } + + fontColorSet(GUI_BLACK, GUI_LIGHTGRAY); + fontModsEnabledSet(1); + s[1] = 0; + + // Top line. + x = f->base.r.x; + y = f->base.r.y; + s[0] = ul; + fontRender((char *)s, x, y); + x += w.x; + s[0] = h; + for (i=0; ichars.x - 2; i++) { + fontRender((char *)s, x, y); + x += w.x; + } + x2 = x; + s[0] = ur; + fontRender((char *)s, x, y); + // Title (this incurs some overdraw). + if (f->title) { + fontRender(f->title, f->base.r.x + (w.x * 2), y); + } + // Middle lines. + x = f->base.r.x; + y += w.y; + s[0] = v; + for (i=0; ichars.y - 2; i++) { + fontRender((char *)s, x, y); + fontRender((char *)s, x2, y); + y += w.y; + } + // Bottom line. + s[0] = ll; + fontRender((char *)s, x, y); + x += w.x; + s[0] = h; + for (i=0; ichars.x - 2; i++) { + fontRender((char *)s, x, y); + x += w.x; + } + s[0] = lr; + fontRender((char *)s, x, y); + } + + // Move the contents to 0,0 to draw it into it's own surface. + o.x = f->base.r.x; + o.y = f->base.r.y; + f->base.r.x = 0; + f->base.r.y = 0; + + // Draw children onto surface. + surfaceSet(f->surface); + widgetChildrenPaint(widget); + surfaceSet(t); + + // Put us back at the proper coodinates. + f->base.r.x = o.x; + f->base.r.y = o.y; + + // Blit. + surfaceBlit(f->base.r.x + w.x, f->base.r.y + w.y, 0, 0, 0, 0, f->surface); + } +} + + +RegisterT *frameRegister(uint8_t magic) { + static RegisterT reg = { + "Frame", + NULL, // No default on-click handler. + frameDestroy, + framePaint, + NULL, // No unregister handler. + frameWidgetFinder + }; + + // One-time widget startup code. + __MAGIC_FRAME = magic; + + return ® +} + + +static WidgetT *frameWidgetFinder(WidgetT *widget, uint16_t x, uint16_t y, int16_t *localX, int16_t *localY) { + int16_t i; + WidgetT *w; + PointT p; + FrameT *f = (FrameT *)widget; + + // Widget local mouse position. + *localX = x - widget->r.x - (f->flags & FRAME_NONE ? 0 : fontWidthGet(f->font)); + *localY = y - widget->r.y - (f->flags & FRAME_NONE ? 0 : fontHeightGet(f->font)); + + // Find widget under mouse. + for (i=0; ichildren); i++) { + w = widget->children[i]; + if (*localX >= w->r.x && *localX < w->r.x + w->r.w && *localY >= w->r.y && *localY < w->r.y + w->r.h) { + // Ask this widget who we're pointing at. + p.x = *localX; + p.y = *localY; + return w->reg->findWidget(w, p.x, p.y, localX, localY); + } + } + + // Didn't find a widget. + return NULL; +} diff --git a/roo-e/src/gui/widgets/frame.h b/roo-e/src/gui/widgets/frame.h new file mode 100644 index 0000000..8bafb35 --- /dev/null +++ b/roo-e/src/gui/widgets/frame.h @@ -0,0 +1,57 @@ +/* + * Roo/E, the Kangaroo Punch Portable GUI Toolkit + * Copyright (C) 2022 Scott Duensing + * + * http://kangaroopunch.com + * + * + * This file is part of Roo/E. + * + * Roo/E is free software: you can redistribute it and/or modify it under the + * terms of the GNU Affero General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Roo/E is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Roo/E. If not, see . + * + */ + + +#ifndef FRAME_H +#define FRAME_H + + +#include "../gui.h" + + +#define FRAME_NONE 0 +#define FRAME_SINGLE 1 +#define FRAME_DOUBLE 2 +#define FRAME_FONT 4 // User will provide font to use. + + +typedef struct FrameS { + WidgetT base; // Required by all widgets. + uint8_t flags; + SurfaceT *surface; + PointT pixels; // Size of surface in pixels. + PointT chars; // Size of frame in characters (if there's a border). + char *title; + FontT *font; +} FrameT; + + +extern uint8_t __MAGIC_FRAME; // Magic ID assigned to us from the GUI. + + +FrameT *frameCreate(char *title, int16_t width, int16_t height, int flags, ...); +RegisterT *frameRegister(uint8_t magic); + + +#endif // FRAME_H diff --git a/roo-e/src/gui/widgets/hscroll.c b/roo-e/src/gui/widgets/hscroll.c index acb1dee..84c102b 100644 --- a/roo-e/src/gui/widgets/hscroll.c +++ b/roo-e/src/gui/widgets/hscroll.c @@ -69,13 +69,12 @@ static void hscrollClick(WidgetT *widget, uint16_t x, uint16_t y, uint8_t event, return; } - /* // Clicking left of thumb? Also fakes dragging. if (x < h->thumb.x) { // Move content left. h->value -= SCROLL_SPEED_FAST; // Clip. - if (h->value < h->min) h->value = h->min; + if (h->value < 0) h->value = 0; // Update. widgetDirtySet(widget, 1); // Call the actual click event. @@ -88,14 +87,13 @@ static void hscrollClick(WidgetT *widget, uint16_t x, uint16_t y, uint8_t event, // Move content right. h->value += SCROLL_SPEED_FAST; // Clip. - if (h->value > h->max - h->base.r.w) h->value = h->max - h->base.r.w; + if (h->value > h->maxValue) h->value = h->maxValue; // Update. widgetDirtySet(widget, 1); // Call the actual click event. if (h->handler) h->handler(widget, x, y, event, h->base.data); return; } - */ } diff --git a/roo-e/src/gui/widgets/vscroll.c b/roo-e/src/gui/widgets/vscroll.c index 245d2df..9a7516d 100644 --- a/roo-e/src/gui/widgets/vscroll.c +++ b/roo-e/src/gui/widgets/vscroll.c @@ -69,13 +69,12 @@ static void vscrollClick(WidgetT *widget, uint16_t x, uint16_t y, uint8_t event, return; } - /* // Clicking above thumb? Also fakes dragging. if (y < v->thumb.y) { // Move content up. v->value -= SCROLL_SPEED_FAST; // Clip. - if (v->value < v->min) v->value = v->min; + if (v->value < 0) v->value = 0; // Update. widgetDirtySet(widget, 1); // Call the actual click event. @@ -88,14 +87,13 @@ static void vscrollClick(WidgetT *widget, uint16_t x, uint16_t y, uint8_t event, // Move content down. v->value += SCROLL_SPEED_FAST; // Clip. - if (v->value > v->max - v->base.r.h) v->value = v->max - v->base.r.h; + if (v->value > v->maxValue) v->value = v->maxValue; // Update. widgetDirtySet(widget, 1); // Call the actual click event. if (v->handler) v->handler(widget, x, y, event, v->base.data); return; } - */ } diff --git a/roo-e/src/gui/wmwindow.c b/roo-e/src/gui/wmwindow.c index f83fa0d..e8cba0b 100644 --- a/roo-e/src/gui/wmwindow.c +++ b/roo-e/src/gui/wmwindow.c @@ -639,6 +639,7 @@ static WidgetT *windowWidgetFinder(WidgetT *widget, uint16_t x, uint16_t y, int1 br.x = *localX; br.y = *localY; w = w->reg->findWidget(w, br.x, br.y, localX, localY); + if (w) logWrite("Over %s\n", w->reg->widgetName); return w; } } @@ -902,10 +903,8 @@ void wmUpdate(EventT *event) { if (widgetDown) { // Are we still inside the window content? if (event->x <= win->bounds.x2 && event->x >= win->bounds.x && event->y <= win->bounds.y2 && event->y >= win->bounds.y) { - // Are we stll over the same widget? - widget = windowWidgetFinder(W(win), event->x, event->y, &widgetLocal.x, &widgetLocal.y); - // Send to widget for processing or send cancel. - if (widgetDown->reg->click) widgetDown->reg->click(widgetDown, widgetLocal.x, widgetLocal.y, (widget == widgetDown) ? CLICK_LEFT_UP : CLICK_LEFT_CANCEL, widgetDown->data); + // Are we stll over the same widget? Send to widget for processing or send cancel. + if (widgetDown->reg->click) widgetDown->reg->click(widgetDown, widgetLocal.x, widgetLocal.y, (widgetOver == widgetDown) ? CLICK_LEFT_UP : CLICK_LEFT_CANCEL, widgetDown->data); } widgetDown = NULL; } // widgetDown diff --git a/roo-e/src/main.c b/roo-e/src/main.c index 1a1005e..0a52515 100644 --- a/roo-e/src/main.c +++ b/roo-e/src/main.c @@ -26,16 +26,6 @@ #include "gui/gui-all.h" -VscrollT *_v = NULL; -HscrollT *_h = NULL; - - -void clickHandler(WidgetT *widget, uint16_t x, uint16_t y, uint8_t event, void *data) { - (void)widget; - logWrite("%d %dx%d %s %d %d\n", event, x, y, data, vscrollValueGet(_v), hscrollValueGet(_h)); -} - - int main(int argc, char *argv[]) { char title[256]; uint16_t i; @@ -44,11 +34,10 @@ int main(int argc, char *argv[]) { ButtonT *b = NULL; CheckboxT *c = NULL; RadioT *r = NULL; - ScrollableT *s = NULL; - + FrameT *f = NULL; // frame - // scrollarea + // layout // listbox // textbox @@ -72,15 +61,25 @@ int main(int argc, char *argv[]) { l = labelCreate(LABEL_ALIGN_LEFT, __guiFontVGA8x16, "Label"); labelColorSet(l, __guiBaseColors[i], GUI_BLACK); - labelClickSet(l, clickHandler, title); widgetAdd(W(w), 20, 10, W(l)); - b = buttonCreate("Button", clickHandler, NULL); + b = buttonCreate("Button", NULL, NULL); widgetAdd(W(w), 20, 40, W(b)); c = checkboxCreate("Checkbox", 0); widgetAdd(W(w), 20, 80, W(c)); + f = frameCreate("Frame", 310, 60, FRAME_SINGLE); + widgetAdd(W(w), 20, 110, W(f)); + + r = radioCreate("Radio 1", 0, 1); + widgetAdd(W(f), 0, 5, W(r)); + r = radioCreate("Radio 2", 0, 0); + widgetAdd(W(f), 100, 5, W(r)); + r = radioCreate("Radio 3", 0, 0); + widgetAdd(W(f), 200, 5, W(r)); + + /* r = radioCreate("Radio 1", 0, 1); widgetAdd(W(w), 20, 110, W(r)); r = radioCreate("Radio 2", 0, 0); @@ -94,17 +93,7 @@ int main(int argc, char *argv[]) { widgetAdd(W(w), 120, 140, W(r)); r = radioCreate("Radio C", 1, 0); widgetAdd(W(w), 220, 140, W(r)); - - _v = vscrollCreate(100, clickHandler, NULL); - vscrollRangeSet(_v, 0, 640); - widgetAdd(W(w), 200, 5, W(_v)); - - _h = hscrollCreate(100, clickHandler, NULL); - hscrollRangeSet(_h, 0, 640); - widgetAdd(W(w), 100, 5, W(_h)); - - s = scrollableCreate(300, 200, 648, 480, SCROLLABLE_STANDARD); - widgetAdd(W(w), 20, 170, W(s)); + */ } guiRun(); guiShutdown();