Bitmap fonts. Margins. Start of buttons. Many fixes.
This commit is contained in:
parent
7d2d0c2fc3
commit
04cce38be1
19 changed files with 440 additions and 121 deletions
|
@ -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 \
|
||||
|
|
|
@ -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
114
client/src/gui/button.c
Normal 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
44
client/src/gui/button.h
Normal 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
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
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
BIN
font/data/vga8x16.png
(Stored with Git LFS)
Normal file
Binary file not shown.
|
@ -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();
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
TEMPLATE = SUBDIRS
|
||||
TEMPLATE = subdirs
|
||||
CONFIG *= ORDERED
|
||||
|
||||
SUBDIRS = \
|
||||
|
|
Loading…
Add table
Reference in a new issue