roo-e/src/gui/widgets/

This commit is contained in:
Scott Duensing 2022-06-29 18:01:37 -05:00
parent 4923843f38
commit a8168cd253
10 changed files with 157 additions and 99 deletions

View file

@ -52,6 +52,7 @@ HEADERS += \
src/gui/gui.h \
src/gui/image.h \
src/gui/surface.h \
src/gui/widgets/button.h \
src/gui/widgets/label.h \
src/gui/widgets/picture.h \
src/gui/wmwindow.h \
@ -69,6 +70,7 @@ SOURCES += \
src/gui/gui.c \
src/gui/image.c \
src/gui/surface.c \
src/gui/widgets/button.c \
src/gui/widgets/label.c \
src/gui/widgets/picture.c \
src/gui/wmwindow.c \

View file

@ -29,7 +29,9 @@
#include "array.h"
#include "wmwindow.h"
#include "widgets/button.h"
#include "widgets/label.h"
#include "widgets/picture.h"
typedef struct WidgetCatalogS {
@ -191,8 +193,12 @@ uint8_t guiStartup(int16_t width, int16_t height, int16_t depth) {
wmStartup();
// Always have to have a window type.
// Register standard widgets.
guiRegister(windowRegister);
guiRegister(labelRegister);
guiRegister(pictureRegister);
guiRegister(buttonRegister);
return SUCCESS;
}
@ -203,15 +209,13 @@ void guiStop(void) {
}
void guiWidgetBaseSet(WidgetT *widget, uint8_t magic, uint16_t x, uint16_t y, uint16_t w, uint16_t h) {
void guiWidgetBaseSet(WidgetT *widget, uint8_t magic, uint16_t w, uint16_t h) {
widget->magic = magic;
widget->r.x = x;
widget->r.y = y;
widget->r.w = w;
widget->r.h = h;
widget->reg = hmget(_widgetCatalog, magic);
if (!widget->reg) widget->reg = hmget(_widgetCatalog, magic);
widget->flags |= WIDGET_DIRTY; // Everything starts dirty to force a paint.
}
@ -235,3 +239,14 @@ void guiWidgetDirtySet(WidgetT *widget, uint8_t dirty) {
widget->flags &= ~WIDGET_DIRTY;
}
}
void guiWidgetPaintManually(WidgetT *widget, int16_t x, int16_t y) {
RectT r = widget->r;
guiWidgetDirtySet(widget, 1);
widget->r.x = x;
widget->r.y = y;
widget->reg->paint(widget);
widget->r = r;
}

View file

@ -36,12 +36,17 @@
#define WIDGET_HIDDEN 2
#define WIDGET_DISABLED 4
#define CLICK_LEFT_CANCEL 1
#define CLICK_LEFT_DOWN 2
#define CLICK_LEFT_UP 3
struct WidgetS;
struct WindowS;
typedef void (*GuiCallbackT)(void *data, ...);
typedef void (*WidgetEventT)(struct WidgetS *widget, ...);
typedef void (*ClickHandlerT)(struct WidgetS *widget, uint16_t x, uint16_t y, void *data);
typedef void (*ClickHandlerT)(struct WidgetS *widget, uint16_t x, uint16_t y, uint8_t event, void *data);
typedef struct PointS {
int16_t x;
@ -70,11 +75,11 @@ typedef struct RegisterS {
} RegisterT;
typedef struct WidgetS {
uint8_t magic; // Magic ID of widget.
RectT r; // Outer bounds of widget. NOTE: USES WIDTH/HEIGHT, NOT X2/Y2!
RegisterT *reg; // Registration information.
void *data; // Pointer to arbitrary data for user.
uint8_t flags; // Widget flags (see defines above).
uint8_t magic; // Magic ID of widget.
RectT r; // Outer bounds of widget. NOTE: USES WIDTH/HEIGHT, NOT X2/Y2!
RegisterT *reg; // Registration information.
void *data; // Pointer to arbitrary data for user.
uint8_t flags; // Widget flags (see defines above).
} WidgetT;
typedef RegisterT *(*WidgetRegisterT)(uint8_t);
@ -123,10 +128,11 @@ void guiRun(void);
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 guiWidgetBaseSet(WidgetT *widget, uint8_t magic, uint16_t w, uint16_t h);
void guiWidgetDestroy(WidgetT *widget);
uint8_t guiWidgetDirtyGet(WidgetT *widget);
void guiWidgetDirtySet(WidgetT *widget, uint8_t dirty);
void guiWidgetPaintManually(WidgetT *widget, int16_t x, int16_t y);
#endif // GUI_H

View file

@ -46,25 +46,23 @@ void labelColorSet(LabelT *label, ColorT foreground, ColorT background) {
}
LabelT *labelCreate(uint16_t x, uint16_t y, uint8_t alignment, FontT *font, char *contents, ...) {
LabelT *l = NULL;
void labelContentSet(LabelT *label, char *content) {
int16_t x;
int16_t y;
uint16_t width;
uint16_t height;
NEW(LabelT, l);
memset(l, 0, sizeof(LabelT));
l->font = font;
l->text = strdup(contents);
l->foreground = GUI_BLACK;
l->background = GUI_LIGHTGRAY;
l->modsEnabled = 0;
l->alignment = alignment;
l->pos.x = x;
l->pos.y = y;
// ***TODO*** This will not redraw properly if the size shrinks.
width = fontWidthCharactersGet(contents) * fontWidthGet(font);
height = fontHeightGet(font);
switch (alignment) {
if (label->text) DEL(label->text);
label->text = strdup(content);
width = fontWidthCharactersGet(content) * fontWidthGet(label->font);
height = fontHeightGet(label->font);
x = label->pos.x;
y = label->pos.y;
switch (label->alignment) {
case LABEL_ALIGN_LEFT:
// Passed in values are fine.
break;
@ -78,7 +76,29 @@ LabelT *labelCreate(uint16_t x, uint16_t y, uint8_t alignment, FontT *font, char
break;
}
guiWidgetBaseSet((WidgetT *)l, __MAGIC_LABEL, x, y, width, height);
// This is kind of ugly. It moves the actual position to compensate for alignment.
label->base.r.x = x;
label->base.r.y = y;
// Fixup width and height.
guiWidgetBaseSet(W(label), __MAGIC_LABEL, width, height);
}
LabelT *labelCreate(uint8_t alignment, FontT *font, char *contents, ...) {
LabelT *l = NULL;
NEW(LabelT, l);
memset(l, 0, sizeof(LabelT));
l->font = font;
l->foreground = GUI_BLACK;
l->background = GUI_LIGHTGRAY;
l->modsEnabled = 0;
l->alignment = alignment;
l->text = strdup(contents);
l->pos.x = -1; // Flag for later indicating we need to set up the widget position.
guiWidgetBaseSet(W(l), __MAGIC_LABEL, 0, 0);
return l;
}
@ -93,7 +113,19 @@ static void labelDestroy(struct WidgetS *widget, ...) {
static void labelPaint(struct WidgetS *widget, ...) {
LabelT *l = (LabelT *)widget;
LabelT *l = (LabelT *)widget;
char *temp;
// Do we need to finish setting up?
if (l->pos.x < 0) {
// Remember our pre-alignment position (or "anchor").
l->pos.x = l->base.r.x;
l->pos.y = l->base.r.y;
// Can't pass l->text to labelContentSet.
temp = strdup(l->text);
labelContentSet(l, temp);
DEL(temp);
}
if (guiWidgetDirtyGet(widget)) {
guiWidgetDirtySet(widget, 0);

View file

@ -52,7 +52,8 @@ extern uint8_t __MAGIC_LABEL; // Magic ID assigned to us from the GUI.
void labelClickSet(LabelT *label, ClickHandlerT handler, void *data);
void labelColorSet(LabelT *label, ColorT foreground, ColorT background);
LabelT *labelCreate(uint16_t x, uint16_t y, uint8_t alignment, FontT *font, char *contents, ...);
void labelContentSet(LabelT *label, char *content);
LabelT *labelCreate(uint8_t alignment, FontT *font, char *contents, ...);
RegisterT *labelRegister(uint8_t magic);

View file

@ -41,7 +41,7 @@ void pictureClickSet(PictureT *picture, ClickHandlerT handler, void *data) {
}
PictureT *pictureCreate(uint16_t x, uint16_t y, char *filename, ...) {
PictureT *pictureCreate(char *filename, ...) {
PictureT *p = NULL;
NEW(PictureT, p);
@ -51,7 +51,7 @@ PictureT *pictureCreate(uint16_t x, uint16_t y, char *filename, ...) {
DEL(p);
return NULL;
}
guiWidgetBaseSet((WidgetT *)p, __MAGIC_PICTURE, x, y, surfaceWidthGet(p->picture), surfaceHeightGet(p->picture));
guiWidgetBaseSet(W(p), __MAGIC_PICTURE, surfaceWidthGet(p->picture), surfaceHeightGet(p->picture));
return p;
}

View file

@ -40,7 +40,7 @@ extern uint8_t __MAGIC_PICTURE; // Magic ID assigned to us from the GUI.
void pictureClickSet(PictureT *picture, ClickHandlerT handler, void *data);
PictureT *pictureCreate(uint16_t x, uint16_t y, char *filename, ...);
PictureT *pictureCreate(char *filename, ...);
RegisterT *pictureRegister(uint8_t magic);

View file

@ -48,6 +48,8 @@ static void windowPaint(struct WidgetS *widget, ...);
static void windowScrollHorizontalHandler(EventT *e, WindowT *w);
static void windowScrollVerticalHandler(EventT *e, WindowT *w);
static WidgetT *wuWidgetUnderMouseGet(WindowT *win, EventT *event, int16_t *localX, int16_t *localY);
static void windowCache(WindowT *w, uint8_t redrawWindow) {
char c;
@ -63,6 +65,8 @@ static void windowCache(WindowT *w, uint8_t redrawWindow) {
int16_t originalY;
double d;
RectT originalBounds;
ColorT scrollBackgroundColor;
ColorT scrollThumbColor;
ColorT titleBackgroundColor;
ColorT widgetColor;
SurfaceT *target = surfaceGet();
@ -91,8 +95,10 @@ static void windowCache(WindowT *w, uint8_t redrawWindow) {
if (redrawWindow) {
// Determine some colors.
titleBackgroundColor = (w == _windowTop) ? GUI_DARKGRAY : GUI_LIGHTGRAY;
widgetColor = (w == _windowTop) ? GUI_LIGHTGRAY : GUI_DARKGRAY;
scrollBackgroundColor = (w == _windowTop) ? GUI_DARKGRAY : GUI_LIGHTGRAY;
scrollThumbColor = GUI_LIGHTGRAY;
titleBackgroundColor = (w == _windowTop) ? GUI_DARKGRAY : GUI_LIGHTGRAY;
widgetColor = (w == _windowTop) ? GUI_LIGHTGRAY : GUI_DARKGRAY;
// Get ready!
x1 = w->base.r.x;
@ -203,13 +209,13 @@ static void windowCache(WindowT *w, uint8_t redrawWindow) {
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);
surfaceBoxFilled(w->scrollv.x + 1, w->scrollv.y + 1, w->scrollv.x2 - 1, w->scrollv.y2 - 1, scrollBackgroundColor);
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);
fontColorSet(widgetColor, scrollBackgroundColor);
// Draw arrows.
fontRender("\x1e", w->scrollv.x + 7, w->scrollv.y + 7);
fontRender("\x1f", w->scrollv.x + 7, w->scrollv.y2 - 12);
@ -226,7 +232,7 @@ static void windowCache(WindowT *w, uint8_t redrawWindow) {
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);
surfaceBoxFilled(w->scrollh.x + 1, w->scrollh.y + 1, w->scrollh.x2 - 1, w->scrollh.y2 - 1, scrollBackgroundColor);
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);
@ -237,7 +243,7 @@ static void windowCache(WindowT *w, uint8_t redrawWindow) {
}
// Prepare font.
fontSet(__guiFontVGA8x8);
fontColorSet(widgetColor, titleBackgroundColor);
fontColorSet(widgetColor, scrollBackgroundColor);
// Draw arrows.
fontRender("\x11", w->scrollh.x + 7, w->scrollh.y + 7);
fontRender("\x10", w->scrollh.x2 - 12, w->scrollh.y + 7);
@ -270,7 +276,7 @@ static void windowCache(WindowT *w, uint8_t redrawWindow) {
// 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);
surfaceBoxFilled(w->thumbv.x + 1, w->thumbv.y + 1, w->thumbv.x2 - 1, w->thumbv.y2 - 1, scrollThumbColor);
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;
@ -290,7 +296,7 @@ static void windowCache(WindowT *w, uint8_t redrawWindow) {
// 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);
surfaceBoxFilled(w->thumbh.x + 1, w->thumbh.y + 1, w->thumbh.x2 - 1, w->thumbh.y2 - 1, scrollThumbColor);
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;
@ -357,26 +363,11 @@ WindowT *windowCreate(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *titl
NEW(WindowT, win);
memset(win, 0, sizeof(WindowT));
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.
static uint8_t image = 1;
static char name[16];
windowCache(win, 1);
width = win->bounds.x2 - win->bounds.x;
height = win->bounds.y2 - win->bounds.y;
guiWidgetDirtySet(W(win), 1);
sprintf(name, "back%d.png", image);
win->content = imageLoad(name);
if (image == 3) {
win->offset.x = 100;//(surfaceWidthGet(win->content) - width) * 0.5;
win->offset.y = 100;//(surfaceHeightGet(win->content) - height) * 0.5;
}
image++;
*/
guiWidgetBaseSet((WidgetT *)win, __MAGIC_WINDOW, w, h);
win->base.r.x = x;
win->base.r.y = y;
win->title = strdup(title);
win->flags = (uint8_t)flags;
// If the window is resizable, we need to get two more arguments for the content size.
if (win->flags & WIN_RESIZE) {
@ -408,13 +399,14 @@ WindowT *windowCreate(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *titl
static void windowDestroy(struct WidgetS *widget, ...) {
uint16_t i;
uint16_t c;
WindowT *window = (WindowT *)widget;
uint16_t i;
uint16_t c;
WindowT *window = (WindowT *)widget;
// Remove it from the window list, if it exists there.
for (i=0; i<arrlen(_windowList); i++) {
if (window == _windowList[i]) {
_windowList[i] = NULL;
arrdel(_windowList, i);
break;
}
@ -863,7 +855,9 @@ static void windowScrollVerticalHandler(EventT *e, WindowT *w) {
}
void windowWidgetAdd(WindowT *window, WidgetT *widget) {
void windowWidgetAdd(WindowT *window, int16_t x, int16_t y, WidgetT *widget) {
widget->r.x = x;
widget->r.y = y;
arrput(window->children, widget);
}
@ -883,7 +877,7 @@ void windowWidgetRemove(WindowT *window, WidgetT *widget) {
void wmShutdown(void) {
uint16_t i;
for (i=0; i<arrlen(_windowList); i++) guiWidgetDestroy((WidgetT *)_windowList[i]);
for (i=0; i<arrlen(_windowList); i++) guiWidgetDestroy(W(_windowList[i]));
arrfree(_windowList);
}
@ -996,19 +990,11 @@ void wmUpdate(EventT *event) {
break;
}
// Are we inside the window content? Most likely, check this first.
if (event->x <= win->bounds.x2 && event->x >= win->bounds.x && event->y <= win->bounds.y2 && event->y >= win->bounds.y) {
// Are we over a widget?
x = event->x - win->bounds.x + win->offset.x;
y = event->y - win->bounds.y + win->offset.y;
for (i=0; i<arrlen(win->children); i++) {
widget = win->children[i];
if (x >= widget->r.x && x < widget->r.x + widget->r.x2 && y >= widget->r.y && y < widget->r.y + widget->r.y2) {
// Remember this widget for when left-up occurs.
widgetDown = widget;
break;
}
}
// Are we over a widget?
widget = wuWidgetUnderMouseGet(win, event, &x, &y);
if (widget) {
if (widget->reg->click) widget->reg->click(widget, x - widget->r.x, y - widget->r.y, CLICK_LEFT_DOWN, widget->data);
widgetDown = widget;
}
// Are we inside the close button? Does not include button frame on purpose.
@ -1140,16 +1126,9 @@ void wmUpdate(EventT *event) {
// 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?
x = event->x - win->bounds.x + win->offset.x;
y = event->y - win->bounds.y + win->offset.y;
for (i=0; i<arrlen(win->children); i++) {
widget = win->children[i];
if (widget == widgetDown && x >= widget->r.x && x < widget->r.x + widget->r.x2 && y >= widget->r.y && y < widget->r.y + widget->r.y2) {
// Send to widget for processing.
if (widget->reg->click) widget->reg->click(widget, x - widget->r.x, y - widget->r.y, widget->data);
break;
}
}
widget = wuWidgetUnderMouseGet(win, event, &x, &y);
// Send to widget for processing or send cancel. Convert mouse to widget-local coordinates.
if (widgetDown->reg->click) widgetDown->reg->click(widgetDown, x - widgetDown->r.x, y - widgetDown->r.y, (widget == widgetDown) ? CLICK_LEFT_UP : CLICK_LEFT_CANCEL, widgetDown->data);
}
widgetDown = NULL;
} // widgetDown
@ -1161,5 +1140,27 @@ void wmUpdate(EventT *event) {
} // Left button processing.
} // Do we have windows?
}
static WidgetT *wuWidgetUnderMouseGet(WindowT *win, EventT *event, int16_t *localX, int16_t *localY) {
int16_t i;
WidgetT *widget;
// Are we over the provided window?
if (event->x <= win->bounds.x2 && event->x >= win->bounds.x && event->y <= win->bounds.y2 && event->y >= win->bounds.y) {
// Find window-local mouse coordinates.
*localX = event->x - win->bounds.x + win->offset.x;
*localY = event->y - win->bounds.y + win->offset.y;
// Find widget under mouse.
for (i=0; i<arrlen(win->children); i++) {
widget = win->children[i];
if (*localX >= widget->r.x && *localX < widget->r.x + widget->r.x2 && *localY >= widget->r.y && *localY < widget->r.y + widget->r.y2) {
// Return this widget.
return widget;
}
}
}
return NULL;
}

View file

@ -77,7 +77,7 @@ void windowMinimize(WindowT *win);
void windowMove(WindowT *win, uint16_t x, uint16_t y);
RegisterT *windowRegister(uint8_t magic);
void windowResize(WindowT *win, uint16_t width, uint16_t height);
void windowWidgetAdd(WindowT *window, WidgetT *widget);
void windowWidgetAdd(WindowT *window, int16_t x, int16_t y, WidgetT *widget);
void windowWidgetRemove(WindowT *window, WidgetT *widget);

View file

@ -27,11 +27,12 @@
#include "gui/wmwindow.h"
#include "gui/widgets/label.h"
#include "gui/widgets/picture.h"
#include "gui/widgets/button.h"
void clickHandler(WidgetT *widget, uint16_t x, uint16_t y, void *data) {
void clickHandler(WidgetT *widget, uint16_t x, uint16_t y, uint8_t event, void *data) {
(void)widget;
logWrite("%dx%d %s\n", x, y, data);
logWrite("%d %dx%d %s\n", event, x, y, data);
}
@ -39,6 +40,7 @@ int main(int argc, char *argv[]) {
char title[256];
uint16_t i;
LabelT *l = NULL;
ButtonT *b = NULL;
WindowT *w = NULL;
(void)argc;
@ -48,18 +50,17 @@ int main(int argc, char *argv[]) {
if (guiStartup(800, 600, 16) == SUCCESS) {
// Register all desired widgets with Roo/E.
guiRegister(labelRegister);
guiRegister(pictureRegister);
for (i=1; i<4; i++) {
sprintf(title, "Testing %d", i);
w = windowCreate(i * 50, i * 50, 300, 200, title, WIN_STANDARD, 640, 480);
l = labelCreate(150, 10, LABEL_ALIGN_LEFT, __guiFontVGA8x16, "Label Test");
l = labelCreate(LABEL_ALIGN_LEFT, __guiFontVGA8x16, "Label");
labelColorSet(l, __guiBaseColors[i], GUI_BLACK);
labelClickSet(l, clickHandler, title);
windowWidgetAdd(w, W(l));
windowWidgetAdd(w, 20, 10, W(l));
b = buttonCreate("Button", clickHandler, NULL);
windowWidgetAdd(w, 20, 40, W(b));
}
guiRun();
guiShutdown();