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();