Bitmap fonts. Margins. Start of buttons. Many fixes.

This commit is contained in:
Scott Duensing 2021-10-14 21:15:14 -05:00
parent 7d2d0c2fc3
commit 04cce38be1
19 changed files with 440 additions and 121 deletions

View file

@ -43,6 +43,7 @@ INCLUDEPATH += \
HEADERS = \
$$LINUX_HEADERS \
src/gui/button.h \
src/thirdparty/stb_ds.h \
src/thirdparty/stb_leakcheck.h \
src/thirdparty/stb_image.h \
@ -62,6 +63,7 @@ HEADERS = \
SOURCES = \
$$LINUX_SOURCES \
src/gui/array.c \
src/gui/button.c \
src/gui/font.c \
src/gui/desktop.c \
src/gui/gui.c \

View file

@ -802,6 +802,16 @@ void vbeSurfaceDestroy(SurfaceT **surface) {
}
uint16_t vbeSurfaceHeightGet(void) {
return _activeSurface->height;
}
uint16_t vbeSurfaceWidthGet(void) {
return _activeSurface->width;
}
void vbeSurfaceSet(SurfaceT *surface) {
if (surface) {
_activeSurface = surface;

114
client/src/gui/button.c Normal file
View file

@ -0,0 +1,114 @@
/*
* Kangaroo Punch Multi Player Game Server Mark II
* Copyright (C) 2020-2021 Scott Duensing
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "button.h"
void buttonDel(WidgetT **widget) {
ButtonT *b = (ButtonT *)*widget;
if (b->title) free(b->title);
free(b);
b = NULL;
}
WidgetT *buttonInit(WidgetT *button, uint16_t x, uint16_t y, char *title, widgetCallback callback) {
ButtonT *b = (ButtonT *)button;
b->base.magic = MAGIC_BUTTON;
b->base.x = x;
b->base.y = y;
b->base.delMethod = buttonDel;
b->base.paintMethod = buttonPaint;
b->base.mouseEventMethod = buttonMouseEvent;
b->title = NULL;
b->clicked = callback;
buttonSetTitle(b, title);
// Width is set in buttonSetTitle
b->base.h = fontHeightGet(_guiFont) + (_guiMetric[METRIC_BUTTON_VERTICAL_MARGIN] * 2) + (_guiMetric[METRIC_BUTTON_BEZEL_SIZE] * 2);
return button;
}
void buttonMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y) {
ButtonT *b = (ButtonT *)widget;
(void)x;
(void)y;
// Fire callback on mouse up.
if (!mouse->buttonLeft && mouse->buttonLeftWasDown) {
if (b->clicked) b->clicked(widget);
}
}
ButtonT *buttonNew(uint16_t x, uint16_t y, char *title, widgetCallback callback) {
ButtonT *button = (ButtonT *)malloc(sizeof(ButtonT));
WidgetT *widget = NULL;
if (!button) return NULL;
widget = widgetInit((WidgetT *)button);
if (!widget) {
free(button);
return NULL;
}
button = (ButtonT *)buttonInit((WidgetT *)button, x, y, title, callback);
return button;
}
void buttonPaint(WidgetT *button) {
ButtonT *b = (ButtonT *)button;
int16_t i;
uint16_t x1 = b->base.x + b->base.marginX;
uint16_t y1 = b->base.y + b->base.marginY;
if (GUI_GET_FLAG(button, WIDGET_FLAG_DIRTY)) {
vbeSurfaceSet(b->base.surface);
// Draw bezel.
for (i=0; i<_guiMetric[METRIC_BUTTON_BEZEL_SIZE]; i++) {
guiDrawHighlightFrame(x1 + i, y1 + i, x1 + b->base.w - i, y1 + b->base.h - i, _guiColor[COLOR_BUTTON_HIGHLIGHT], _guiColor[COLOR_BUTTON_SHADOW]);
}
// Draw background (depends on x from above).
guiDrawFilledRectangle(x1 + i, y1 + i, x1 + b->base.w - i, y1 + b->base.h - i, _guiColor[COLOR_BUTTON_BACKGROUND]);
// Draw title (depends on x from above).
fontRender(_guiFont, b->title, _guiColor[COLOR_BUTTON_TEXT], _guiColor[COLOR_BUTTON_BACKGROUND], x1 + i + _guiMetric[METRIC_BUTTON_HORIZONTAL_MARGIN], y1 + i + _guiMetric[METRIC_BUTTON_VERTICAL_MARGIN]);
GUI_CLEAR_FLAG(button, WIDGET_FLAG_DIRTY);
}
}
void buttonSetTitle(ButtonT *button, char *title) {
if (button->title) free(button->title);
button->title = strdup(title);
button->base.w = (strlen(title) * fontWidthGet(_guiFont)) + (_guiMetric[METRIC_BUTTON_HORIZONTAL_MARGIN] * 2) + (_guiMetric[METRIC_BUTTON_BEZEL_SIZE] * 2);
}

44
client/src/gui/button.h Normal file
View file

@ -0,0 +1,44 @@
/*
* Kangaroo Punch Multi Player Game Server Mark II
* Copyright (C) 2020-2021 Scott Duensing
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#ifndef BUTTON_H
#define BUTTON_H
#include "gui.h"
#include "widget.h"
typedef struct ButtonS {
WidgetT base; // Must be first in every widget
char *title;
widgetCallback clicked;
} ButtonT;
void buttonDel(WidgetT **widget);
WidgetT *buttonInit(WidgetT *button, uint16_t x, uint16_t y, char *title, widgetCallback callback);
void buttonMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y);
ButtonT *buttonNew(uint16_t x, uint16_t y, char *title, widgetCallback callback);
void buttonPaint(WidgetT *button);
void buttonSetTitle(ButtonT *button, char *title);
#endif // BUTTON_H

View file

@ -42,6 +42,7 @@ WidgetT *desktopInit(WidgetT *desktop) {
d->dragOffsetY = 0;
d->dragWidget = NULL;
GUI_SET_FLAG(desktop, WIDGET_FLAG_OWNS_SURFACE);
d->base.surface = vbeSurfaceCreate(d->base.w, d->base.h);
if (!d->base.surface) {
free(d);
@ -73,9 +74,9 @@ DesktopT *desktopNew(void) {
void desktopPaint(WidgetT *desktop) {
DesktopT *d = (DesktopT *)desktop;
if (d->base.dirty) {
if (GUI_GET_FLAG(desktop, WIDGET_FLAG_DIRTY)) {
vbeSurfaceSet(d->base.surface);
vbeSurfaceClear(_guiColor[COLOR_DESKTOP]);
d->base.dirty = 0;
GUI_CLEAR_FLAG(desktop, WIDGET_FLAG_DIRTY);
}
}

View file

@ -21,6 +21,11 @@
#include "font.h"
uint16_t fontHeightGet(FontT *font) {
return font->height;
}
FontT *fontLoad(char *filename) {
FILE *in = NULL;
uint16_t size = 0;
@ -59,38 +64,46 @@ FontT *fontLoad(char *filename) {
}
void fontRender(FontT *font, uint8_t character, PixelT foreground, PixelT background, uint16_t x, uint16_t y) {
void fontRender(FontT *font, char *string, PixelT foreground, PixelT background, uint16_t x, uint16_t y) {
uint8_t cx;
uint8_t cy;
uint16_t offset;
uint8_t yl;
uint16_t yp;
uint8_t data;
uint8_t character;
uint8_t c;
//***TODO*** This only handles 8x8 fonts.
//***TODO*** This only handles 8xY fonts.
// Find character position in font grid.
cx = character % font->span;
cy = character / font->span;
for (c=0; c<strlen(string); c++) {
character = string[c];
// Find offset byte based on font bits.
offset = cy * font->span * font->height + cx;
// Find character position in font grid.
cx = character % font->span;
cy = character / font->span;
// Draw out 8 lines.
yp = y;
for (yl=0; yl<8; yl++) {
// We do 8 pixels unrolled hoping it's fast.
data = font->bits[offset];
offset += font->span;
vbePutPixel(x, yp, data & 0x80 ? foreground : background);
vbePutPixel(x + 1, yp, data & 0x40 ? foreground : background);
vbePutPixel(x + 2, yp, data & 0x20 ? foreground : background);
vbePutPixel(x + 3, yp, data & 0x10 ? foreground : background);
vbePutPixel(x + 4, yp, data & 0x08 ? foreground : background);
vbePutPixel(x + 5, yp, data & 0x04 ? foreground : background);
vbePutPixel(x + 6, yp, data & 0x02 ? foreground : background);
vbePutPixel(x + 7, yp, data & 0x01 ? foreground : background);
yp++;
// Find offset byte based on font bits.
offset = cy * font->span * font->height + cx;
// Draw out 8 lines.
yp = y;
for (yl=0; yl<font->height; yl++) {
// We do 8 pixels unrolled hoping it's fast.
data = font->bits[offset];
offset += font->span;
vbePutPixel(x, yp, data & 0x80 ? foreground : background);
vbePutPixel(x + 1, yp, data & 0x40 ? foreground : background);
vbePutPixel(x + 2, yp, data & 0x20 ? foreground : background);
vbePutPixel(x + 3, yp, data & 0x10 ? foreground : background);
vbePutPixel(x + 4, yp, data & 0x08 ? foreground : background);
vbePutPixel(x + 5, yp, data & 0x04 ? foreground : background);
vbePutPixel(x + 6, yp, data & 0x02 ? foreground : background);
vbePutPixel(x + 7, yp, data & 0x01 ? foreground : background);
yp++;
}
x += font->width;
}
}
@ -102,3 +115,8 @@ void fontUnload(FontT **font) {
free(f);
f = NULL;
}
uint16_t fontWidthGet(FontT *font) {
return font->width;
}

View file

@ -37,9 +37,11 @@ typedef struct FontS {
} FontT;
FontT *fontLoad(char *filename);
void fontRender(FontT *font, uint8_t character, PixelT foreground, PixelT background, uint16_t x, uint16_t y);
void fontUnload(FontT **font);
uint16_t fontHeightGet(FontT *font);
FontT *fontLoad(char *filename);
void fontRender(FontT *font, char *string, PixelT foreground, PixelT background, uint16_t x, uint16_t y);
void fontUnload(FontT **font);
uint16_t fontWidthGet(FontT *font);
#endif // FONT_H

View file

@ -26,15 +26,34 @@
int16_t _guiMetric[METRIC_COUNT];
PixelT _guiColor[COLOR_COUNT];
FontT *_guiFont = NULL;
static DesktopT *_guiDesktop = NULL;
void guiAttach(WidgetT *parent, WidgetT *child) {
WidgetT *p = NULL;
// Add us to the child list.
child->parent = parent;
arrput(parent->children, child);
// If this widget does not own a surface, find one for it to draw on.
if (!GUI_GET_FLAG(child, WIDGET_FLAG_OWNS_SURFACE)) {
p = child;
while (p != NULL && !GUI_GET_FLAG(p, WIDGET_FLAG_OWNS_SURFACE)) {
// Check parent.
p = p->parent;
if (p) {
// Calculate margins as we go.
child->marginX += p->marginX;
child->marginY += p->marginY;
}
}
child->surface = p->surface;
}
// New windows should be active.
if (child->magic == MAGIC_WINDOW) {
windowSetActive((WindowT *)child);
@ -53,14 +72,16 @@ void guiComposite() {
vbeSurfaceSet(NULL);
// Render us?
if (widget->surface) {
if (GUI_GET_FLAG(widget, WIDGET_FLAG_OWNS_SURFACE)) {
vbeSurfaceBlit(widget->surface, widget->x, widget->y);
}
//***TODO*** This is wrong. Should be recursive.
// Now render all surface-containing children to the VBE buffer.
if (len > 0) {
for (x=0; x<len; x++) {
if (widget->children[x]->surface) {
if (GUI_GET_FLAG(widget->children[x], WIDGET_FLAG_OWNS_SURFACE)) {
vbeSurfaceBlit(widget->children[x]->surface, widget->children[x]->x, widget->children[x]->y);
}
}
@ -169,6 +190,25 @@ void guiDrawFilledRectangle(int16_t x1, int16_t y1, int16_t x2, int16_t y2, Pixe
}
void guiPaint(WidgetT *widget) {
size_t len = arrlenu(widget->children);
size_t x;
// Paint us, if needed.
if (GUI_GET_FLAG(widget, WIDGET_FLAG_DIRTY) && widget->paintMethod) {
widget->paintMethod(widget);
GUI_CLEAR_FLAG(widget, WIDGET_FLAG_DIRTY);
}
// Paint all children.
if (len > 0) {
for (x=0; x<len; x++) {
guiPaint(widget->children[x]);
}
}
}
void guiProcessMouse(MouseT *mouse) {
size_t len;
int16_t x;
@ -179,7 +219,7 @@ void guiProcessMouse(MouseT *mouse) {
// Is the left button down?
if (mouse->buttonLeft) {
// Was it NOT down before?
// Was left button NOT down before?
if (!mouse->buttonLeftWasDown) {
// Initial click. Are we already dragging something?
if (!_guiDesktop->dragWidget) {
@ -196,17 +236,21 @@ void guiProcessMouse(MouseT *mouse) {
window = (WindowT *)child;
// Is it the active window?
if (GUI_GET_FLAG(window, WINDOW_FLAG_ACTIVE)) {
// Start dragging.
// Are we on the draggable area of the titlebar / borders?
_guiDesktop->dragOffsetX = mouse->x - window->base.x;
_guiDesktop->dragOffsetY = mouse->y - window->base.y;
_guiDesktop->dragWidget = child;
if (_guiDesktop->dragOffsetY < _guiMetric[METRIC_WINDOW_TITLE_GRAB_HEIGHT]) {
// Start dragging.
_guiDesktop->dragWidget = child;
}
}
}
break;
}
}
}
} else { // Was it NOT down before?
} else { // Was left button NOT down before?
// Still holding left button, but not the initial click. Update dragged widget location.
if (_guiDesktop->dragWidget) {
@ -219,7 +263,7 @@ void guiProcessMouse(MouseT *mouse) {
if (_guiDesktop->dragWidget->y + _guiDesktop->dragWidget->h > vbeDisplayHeightGet()) _guiDesktop->dragWidget->y = vbeDisplayHeightGet() - _guiDesktop->dragWidget->h;
}
} // Was it NOT down before?
} // Was left button NOT down before?
} else { // Left button down?
@ -257,39 +301,40 @@ void guiProcessMouse(MouseT *mouse) {
}
void guiPaint(WidgetT *widget) {
size_t len = arrlenu(widget->children);
size_t x;
// Paint us, if needed.
if (widget->dirty && widget->paintMethod) {
widget->paintMethod(widget);
widget->dirty = 0;
}
// Paint all children, if needed.
if (len > 0) {
for (x=0; x<len; x++) {
if (widget->children[x]->dirty && widget->children[x]->paintMethod) {
widget->children[x]->paintMethod(widget->children[x]);
widget->children[x]->dirty = 0;
}
}
}
}
WidgetT *guiRootGet(void) {
return (WidgetT *)_guiDesktop;
}
void guiSetWidgetAndChildrenDirty(WidgetT *widget) {
size_t len = arrlenu(widget->children);
size_t x;
// Mark us dirty.
GUI_SET_FLAG(widget, WIDGET_FLAG_DIRTY);
// Mark all children.
if (len > 0) {
for (x=0; x<len; x++) {
guiSetWidgetAndChildrenDirty(widget->children[x]);
}
}
}
DesktopT *guiStartup(void) {
_guiMetric[METRIC_WINDOW_BORDER_WIDTH] = 4; // Does not include highlight or shadow lines.
_guiMetric[METRIC_WINDOW_TITLE_HEIGHT] = 17; // Does not include highlight or shadow lines.
_guiMetric[METRIC_BUTTON_BEZEL_SIZE] = 2;
_guiMetric[METRIC_BUTTON_HORIZONTAL_MARGIN] = 8;
_guiMetric[METRIC_BUTTON_VERTICAL_MARGIN] = 2;
_guiMetric[METRIC_WINDOW_BORDER_WIDTH] = 4; // Does not include highlight or shadow lines.
_guiMetric[METRIC_WINDOW_TITLE_HEIGHT] = 17; // Does not include highlight or shadow lines.
_guiMetric[METRIC_WINDOW_TITLE_GRAB_HEIGHT] = _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + _guiMetric[METRIC_WINDOW_TITLE_HEIGHT] + 4; // Border, highlights, titlebar.
_guiColor[COLOR_BUTTON_BACKGROUND] = vbeMakePixel(168, 168, 168);
_guiColor[COLOR_BUTTON_HIGHLIGHT] = vbeMakePixel(248, 252, 248);
_guiColor[COLOR_BUTTON_SHADOW] = vbeMakePixel( 80, 84, 80);
_guiColor[COLOR_BUTTON_TEXT] = vbeMakePixel( 0, 0, 0);
_guiColor[COLOR_DESKTOP] = vbeMakePixel( 51, 153, 255);
_guiColor[COLOR_WINDOW_BACKGROUND] = vbeMakePixel(168, 168, 168);
_guiColor[COLOR_WINDOW_HIGHLIGHT] = vbeMakePixel(248, 252, 248);
@ -299,6 +344,8 @@ DesktopT *guiStartup(void) {
_guiColor[COLOR_WINDOW_TITLE_TEXT_ACTIVE] = vbeMakePixel(248, 252, 248);
_guiColor[COLOR_WINDOW_TITLE_TEXT_INACTIVE] = vbeMakePixel( 0, 0, 0);
_guiFont = fontLoad("vga8x14.dat");
// Create desktop and return it. Remember it for later.
_guiDesktop = desktopNew();
@ -307,6 +354,18 @@ DesktopT *guiStartup(void) {
void guiShutdown(void) {
fontUnload(&_guiFont);
// Delete all widgets in GUI tree.
guiDelete((WidgetT **)&_guiDesktop);
}
void *guiUserDataGet(WidgetT *widget) {
return widget->userData;
}
void guiUserDataSet(WidgetT *widget, void *userData) {
widget->userData = userData;
}

View file

@ -29,11 +29,14 @@
#include "vesa.h"
#include "array.h"
#include "mouse.h"
#include "font.h"
#define GUI_GET_FLAG(w,f) ((w)->flags & (1 << (f)))
#define GUI_SET_FLAG(w,f) ((w)->flags |= (1 << (f)))
#define GUI_CLEAR_FLAG(w,f) ((w)->flags &= (~(1 << (f))))
#define GUI_SET_FLAG(w,f) (w)->flags |= (1 << (f))
#define GUI_CLEAR_FLAG(w,f) (w)->flags &= (~(1 << (f)))
#define W(w) ((WidgetT *)w)
// Widget Magics
@ -47,14 +50,22 @@ enum MagicE {
// Widget Metrics
enum MetricE {
METRIC_WINDOW_BORDER_WIDTH = 0,
METRIC_BUTTON_BEZEL_SIZE = 0,
METRIC_BUTTON_HORIZONTAL_MARGIN,
METRIC_BUTTON_VERTICAL_MARGIN,
METRIC_WINDOW_BORDER_WIDTH,
METRIC_WINDOW_TITLE_HEIGHT,
METRIC_WINDOW_TITLE_GRAB_HEIGHT,
METRIC_COUNT
};
// Widget Colors
enum ColorE {
COLOR_DESKTOP = 0,
COLOR_BUTTON_BACKGROUND = 0,
COLOR_BUTTON_HIGHLIGHT,
COLOR_BUTTON_SHADOW,
COLOR_BUTTON_TEXT,
COLOR_DESKTOP,
COLOR_WINDOW_BACKGROUND,
COLOR_WINDOW_HIGHLIGHT,
COLOR_WINDOW_SHADOW,
@ -70,8 +81,9 @@ typedef struct WidgetS WidgetT;
typedef struct DesktopS DesktopT;
extern int16_t _guiMetric[METRIC_COUNT];
extern PixelT _guiColor[COLOR_COUNT];
extern int16_t _guiMetric[METRIC_COUNT];
extern PixelT _guiColor[COLOR_COUNT];
extern FontT *_guiFont;
void guiAttach(WidgetT *parent, WidgetT *child);
@ -83,8 +95,11 @@ void guiDrawFilledRectangle(int16_t x1, int16_t y1, int16_t x2, int16_t y2,
void guiPaint(WidgetT *widget);
void guiProcessMouse(MouseT *mouse);
WidgetT *guiRootGet(void);
void guiSetWidgetAndChildrenDirty(WidgetT *widget);
DesktopT *guiStartup(void);
void guiShutdown(void);
void *guiUserDataGet(WidgetT *widget);
void guiUserDataSet(WidgetT *widget, void *userData);
#endif // GUI_H

View file

@ -53,6 +53,8 @@ void vbeSurfaceBlit(SurfaceT *source, uint16_t x, uint16_t y);
void vbeSurfaceClear(PixelT color);
SurfaceT *vbeSurfaceCreate(uint16_t width, uint16_t height);
void vbeSurfaceDestroy(SurfaceT **surface);
uint16_t vbeSurfaceHeightGet(void);
uint16_t vbeSurfaceWidthGet(void);
void vbeSurfaceSet(SurfaceT *surface);
void vbeWaitVBlank(void);

View file

@ -22,18 +22,23 @@
WidgetT *widgetInit(WidgetT *widget) {
widget->magic = MAGIC_UNKNOWN;
widget->x = 0;
widget->y = 0;
widget->w = 0;
widget->h = 0;
widget->dirty = 1; // Force a paint right away
widget->surface = NULL;
widget->children = NULL;
widget->parent = NULL;
widget->delMethod = NULL;
widget->paintMethod = NULL;
widget->mouseDownMethod = NULL;
widget->magic = MAGIC_UNKNOWN;
widget->flags = 0;
widget->x = 0;
widget->y = 0;
widget->w = 0;
widget->h = 0;
widget->marginX = 0;
widget->marginY = 0;
widget->surface = NULL;
widget->children = NULL;
widget->parent = NULL;
widget->delMethod = NULL;
widget->paintMethod = NULL;
widget->mouseEventMethod = NULL;
widget->userData = NULL;
GUI_SET_FLAG(widget, WIDGET_FLAG_DIRTY);
return widget;
}

View file

@ -25,27 +25,37 @@
#include "gui.h"
enum WidgetE {
WIDGET_FLAG_DIRTY = 0,
WIDGET_FLAG_OWNS_SURFACE
};
struct WidgetS;
typedef void (*widgetCallback)(struct WidgetS *widget);
typedef void (*widgetDelMethod)(struct WidgetS **widget);
typedef void (*widgetPaintMethod)(struct WidgetS *widget);
typedef void (*widgetMouseDownMethod)(struct WidgetS *widget, uint16_t x, uint16_t y);
typedef void (*widgetMouseEventMethod)(struct WidgetS *widget, MouseT *mouse, uint16_t x, uint16_t y);
typedef struct WidgetS {
uint8_t magic; // Widget identifier constant
uint8_t dirty; // Does this widget need redrawn
SurfaceT *surface; // Pointer to compositable surface or NULL
int16_t x; // Position of widget on parent
int16_t y; // Position of widget on parent
uint16_t w; // Width of widget
uint16_t h; // Height of widget
struct WidgetS **children; // List of children
struct WidgetS *parent; // Parent of this widget
widgetDelMethod delMethod; // Delete method
widgetPaintMethod paintMethod; // Paint method
widgetMouseDownMethod mouseDownMethod; // Mouse button down handler
uint8_t magic; // Widget identifier constant
uint8_t flags; // See above enum
SurfaceT *surface; // Pointer to compositable surface or NULL
int16_t x; // Position of widget on parent
int16_t y; // Position of widget on parent
uint16_t w; // Width of widget
uint16_t h; // Height of widget
uint16_t marginX; // Pixels to skip when placing child widgets
uint16_t marginY; // Pixels to skip when placing child widgets
struct WidgetS **children; // List of children
struct WidgetS *parent; // Parent of this widget
widgetDelMethod delMethod; // Delete method
widgetPaintMethod paintMethod; // Paint method
widgetMouseEventMethod mouseEventMethod; // Mouse event handler
void *userData; // Anything the user wants to store
} WidgetT;

View file

@ -32,7 +32,7 @@ static void windowDeactivateAll(WidgetT *widget) {
if (widget->magic == MAGIC_WINDOW) {
// Deactivate it.
GUI_CLEAR_FLAG((WindowT *)widget, WINDOW_FLAG_ACTIVE);
widget->dirty = 1;
guiSetWidgetAndChildrenDirty(widget);
}
// Process any children.
@ -64,10 +64,15 @@ WidgetT *windowInit(WidgetT *window, uint16_t x, uint16_t y, uint16_t w, uint16_
win->base.h = h;
win->base.delMethod = windowDel;
win->base.paintMethod = windowPaint;
win->flags = 0;
win->title = NULL;
win->base.marginX += _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 2;
win->base.marginY += _guiMetric[METRIC_WINDOW_TITLE_GRAB_HEIGHT];
windowSetTitle(win, title);
GUI_SET_FLAG(window, WIDGET_FLAG_OWNS_SURFACE);
win->base.surface = vbeSurfaceCreate(win->base.w, win->base.h);
if (!win->base.surface) {
free(win->title);
@ -102,9 +107,11 @@ void windowPaint(WidgetT *window) {
uint16_t x2 = w->base.w - 1;
uint16_t y2 = w->base.h - 1;
PixelT background = GUI_GET_FLAG(w, WINDOW_FLAG_ACTIVE) ? _guiColor[COLOR_WINDOW_TITLE_ACTIVE] : _guiColor[COLOR_WINDOW_TITLE_INACTIVE];
//PixelT text = GUI_GET_FLAG(w, WINDOW_FLAG_ACTIVE) ? _guiColor[COLOR_WINDOW_TITLE_TEXT_ACTIVE] : _guiColor[COLOR_WINDOW_TITLE_TEXT_INACTIVE];
PixelT text = GUI_GET_FLAG(w, WINDOW_FLAG_ACTIVE) ? _guiColor[COLOR_WINDOW_TITLE_TEXT_ACTIVE] : _guiColor[COLOR_WINDOW_TITLE_TEXT_INACTIVE];
if (w->base.dirty) {
//***NOTE*** This doesn't obey margins since desktops don't have any.
if (GUI_GET_FLAG(window, WIDGET_FLAG_DIRTY)) {
vbeSurfaceSet(w->base.surface);
// Background.
@ -120,7 +127,9 @@ void windowPaint(WidgetT *window) {
guiDrawHighlightFrame(_guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 3, _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 3, x2 - _guiMetric[METRIC_WINDOW_BORDER_WIDTH] - 3, _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + _guiMetric[METRIC_WINDOW_TITLE_HEIGHT] + 4, _guiColor[COLOR_WINDOW_HIGHLIGHT], _guiColor[COLOR_WINDOW_SHADOW]);
guiDrawFilledRectangle(_guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 4, _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 4, x2 - _guiMetric[METRIC_WINDOW_BORDER_WIDTH] - 4, _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + _guiMetric[METRIC_WINDOW_TITLE_HEIGHT] + 2, background);
w->base.dirty = 0;
fontRender(_guiFont, w->title, text, background, _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 16, _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 5);
GUI_CLEAR_FLAG(window, WIDGET_FLAG_DIRTY);
}
}

View file

@ -34,6 +34,7 @@ static SurfaceT *_activeSurface = NULL;
static uint16_t _width = 0;
static uint16_t _height = 0;
static MouseT _mouse;
static uint8_t _windowScale = 1;
void (*vbePutPixel)(uint16_t x, uint16_t y, PixelT pixel);
@ -83,8 +84,8 @@ MouseT *mouseRead(void) {
_mouse.buttonRight = ((buttons & SDL_BUTTON_RMASK) != 0);
_mouse.buttonMiddle = ((buttons & SDL_BUTTON_MMASK) != 0);
_mouse.x = mouseX;
_mouse.y = mouseY;
_mouse.x = mouseX / _windowScale;
_mouse.y = mouseY / _windowScale;
return &_mouse;
}
@ -162,6 +163,7 @@ uint8_t vbeStartup(uint16_t xRes, uint16_t yRes, uint8_t bpp) {
(void)bpp;
_windowScale = 2;
vbePutPixel = vbePutPixel32;
SDL_Init(SDL_INIT_EVERYTHING);
@ -169,7 +171,9 @@ uint8_t vbeStartup(uint16_t xRes, uint16_t yRes, uint8_t bpp) {
_window = SDL_CreateWindow("GUI Test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, xRes, yRes, SDL_WINDOW_ALLOW_HIGHDPI);
_surface = SDL_GetWindowSurface(_window);
_renderer = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED);
SDL_RenderSetLogicalSize(_renderer, xRes, yRes);
SDL_SetWindowSize(_window, xRes * _windowScale, yRes * _windowScale);
_offScreenBuffer = vbeSurfaceCreate(xRes, yRes);
@ -232,6 +236,16 @@ void vbeSurfaceDestroy(SurfaceT **surface) {
}
uint16_t vbeSurfaceHeightGet(void) {
return _activeSurface->height;
}
uint16_t vbeSurfaceWidthGet(void) {
return _activeSurface->width;
}
void vbeSurfaceSet(SurfaceT *surface) {
SDL_Texture *texture = NULL;

View file

@ -26,18 +26,20 @@
#include "widget.h"
#include "desktop.h"
#include "window.h"
#include "button.h"
void test(void) {
MouseT *mouse = NULL;
ImageT *pointer = NULL;
PixelT alpha;
DesktopT *desktop = NULL;
WindowT *w1 = NULL;
WindowT *w2 = NULL;
WindowT *w3 = NULL;
ImageT *pointer = NULL;
PixelT alpha;
ButtonT *b1 = NULL;
vbeStartup(800, 600, 8);
vbeStartup(800, 600, 16);
mouseStartup();
desktop = guiStartup();
@ -47,9 +49,12 @@ void test(void) {
w1 = windowNew(25, 25, 300, 200, "Window 1");
w2 = windowNew(150, 150, 300, 200, "Window 2");
w3 = windowNew(300, 300, 300, 200, "Window 3");
guiAttach((WidgetT *)desktop, (WidgetT *)w1);
guiAttach((WidgetT *)desktop, (WidgetT *)w2);
guiAttach((WidgetT *)desktop, (WidgetT *)w3);
guiAttach(W(desktop), W(w1));
guiAttach(W(desktop), W(w2));
guiAttach(W(desktop), W(w3));
b1 = buttonNew(25, 25, "Test Button", NULL);
guiAttach(W(w3), W(b1));
do {
mouse = mouseRead();
@ -76,7 +81,7 @@ int main(int argc, char *argv[]) {
return 0;
}
logOpen("test.log", 1);
logOpen("test.log", 0);
test();

BIN
font/data/vga8x14.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
font/data/vga8x16.png (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -23,11 +23,7 @@
#include "stb.h"
#define BITMAP_FILE "/home/scott/code/kpmpgsmkii/font/data/vga8x8.png"
#define FONT_FILE "/home/scott/code/kpmpgsmkii/client/bin/vga8x8.dat"
int main(int argc, char *argv[]) {
void makeFont(char *source, char *target, int pixelsW, int pixelsH, int charsW, int charCount) {
unsigned char *font = NULL;
unsigned char data = 0;
FILE *out = NULL;
@ -38,25 +34,22 @@ int main(int argc, char *argv[]) {
int w;
int h;
(void)argc;
(void)argv;
// Load 8x8 font from disk. Font is in a 16x16 grid.
font = stbi_load(BITMAP_FILE, (int *)&w, (int *)&h, (int *)&n, 3);
if (!font) return 1;
// Load font atlas from disk.
font = stbi_load(source, (int *)&w, (int *)&h, (int *)&n, 3);
if (!font) return;
// Create data file for font.
out = fopen(FONT_FILE, "wb");
out = fopen(target, "wb");
if (!out) {
stbi_image_free(font);
return 2;
return;
}
// Provide some metadata for enhancement later.
fputc(8, out); // Width of characters
fputc(8, out); // Height of characters
fputc(16, out); // Number of characters per row
fputc(255, out); // Number of characters - 1
fputc(pixelsW, out); // Width of characters
fputc(pixelsH, out); // Height of characters
fputc(charsW, out); // Number of characters per row
fputc(charCount, out); // Number of characters - 1
// Convert bitmap to actual bits.
for (y=0; y<h; y++) {
@ -75,6 +68,16 @@ int main(int argc, char *argv[]) {
// Clean up.
stbi_image_free(font);
}
int main(int argc, char *argv[]) {
(void)argc;
(void)argv;
makeFont("/home/scott/code/kpmpgsmkii/font/data/vga8x8.png", "/home/scott/code/kpmpgsmkii/client/bin/vga8x8.dat", 8, 8, 16, 255);
makeFont("/home/scott/code/kpmpgsmkii/font/data/vga8x14.png", "/home/scott/code/kpmpgsmkii/client/bin/vga8x14.dat", 8, 14, 16, 255);
makeFont("/home/scott/code/kpmpgsmkii/font/data/vga8x16.png", "/home/scott/code/kpmpgsmkii/client/bin/vga8x16.dat", 8, 16, 16, 255);
// Prove we cleaned up.
stb_leakcheck_dumpmem();

View file

@ -16,7 +16,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
TEMPLATE = SUBDIRS
TEMPLATE = subdirs
CONFIG *= ORDERED
SUBDIRS = \