diff --git a/client/client.pro b/client/client.pro
index e93b5c4..5990656 100644
--- a/client/client.pro
+++ b/client/client.pro
@@ -45,7 +45,12 @@ INCLUDEPATH += \
HEADERS = \
$$LINUX_HEADERS \
src/gui/button.h \
+ src/gui/checkbox.h \
+ src/gui/frame.h \
src/gui/keyboard.h \
+ src/gui/label.h \
+ src/gui/picture.h \
+ src/gui/radio.h \
src/gui/task.h \
src/thirdparty/stb_ds.h \
src/thirdparty/stb_leakcheck.h \
@@ -67,9 +72,14 @@ SOURCES = \
$$LINUX_SOURCES \
src/gui/array.c \
src/gui/button.c \
+ src/gui/checkbox.c \
src/gui/font.c \
src/gui/desktop.c \
+ src/gui/frame.c \
src/gui/gui.c \
+ src/gui/label.c \
+ src/gui/picture.c \
+ src/gui/radio.c \
src/gui/task.c \
src/gui/widget.c \
src/gui/window.c \
diff --git a/client/data/kanga.png b/client/data/kanga.png
new file mode 100644
index 0000000..65bf33d
--- /dev/null
+++ b/client/data/kanga.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9fdc5a05e7c468e98ebda1ab929e25677045690cebbd630217e617981be42386
+size 24155
diff --git a/client/src/gui/button.c b/client/src/gui/button.c
index a37f312..794bfce 100644
--- a/client/src/gui/button.c
+++ b/client/src/gui/button.c
@@ -20,6 +20,7 @@
#include "button.h"
+
static void buttonMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event);
static void buttonPaint(WidgetT *widget);
@@ -43,8 +44,8 @@ WidgetT *buttonInit(WidgetT *button, uint16_t x, uint16_t y, char *title, widget
b->base.paintMethod = buttonPaint;
b->base.mouseEventMethod = buttonMouseEvent;
b->title = NULL;
- b->clicked = callback;
+ buttonSetClickHandler(b, callback);
buttonSetTitle(b, title);
// Width is set in buttonSetTitle
@@ -102,8 +103,9 @@ ButtonT *buttonNew(uint16_t x, uint16_t y, char *title, widgetCallback callback)
static void buttonPaint(WidgetT *widget) {
ButtonT *b = (ButtonT *)widget;
int16_t i;
- ColorT highlight = GUI_GET_FLAG(widget, WIDGET_FLAG_ACTIVE) ? _guiColor[COLOR_BUTTON_SHADOW] : _guiColor[COLOR_BUTTON_HIGHLIGHT];
- ColorT shadow = GUI_GET_FLAG(widget, WIDGET_FLAG_ACTIVE) ? _guiColor[COLOR_BUTTON_HIGHLIGHT] : _guiColor[COLOR_BUTTON_SHADOW] ;
+ int8_t active = GUI_GET_FLAG(widget, WIDGET_FLAG_ACTIVE);
+ ColorT highlight = active ? _guiColor[COLOR_BUTTON_SHADOW] : _guiColor[COLOR_BUTTON_HIGHLIGHT];
+ ColorT shadow = active ? _guiColor[COLOR_BUTTON_HIGHLIGHT] : _guiColor[COLOR_BUTTON_SHADOW] ;
if (GUI_GET_FLAG(widget, WIDGET_FLAG_DIRTY)) {
vbeSurfaceSet(b->base.surface);
@@ -117,15 +119,21 @@ static void buttonPaint(WidgetT *widget) {
guiDrawFilledRectangle(b->base.x + i, b->base.y + i, b->base.x + b->base.w - i, b->base.y + 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], b->base.x + i + _guiMetric[METRIC_BUTTON_HORIZONTAL_MARGIN], b->base.y + i + _guiMetric[METRIC_BUTTON_VERTICAL_MARGIN]);
+ fontRender(_guiFont, b->title, _guiColor[COLOR_BUTTON_TEXT], _guiColor[COLOR_BUTTON_BACKGROUND], b->base.x + i + _guiMetric[METRIC_BUTTON_HORIZONTAL_MARGIN] + active, b->base.y + i + _guiMetric[METRIC_BUTTON_VERTICAL_MARGIN] + active);
GUI_CLEAR_FLAG(widget, WIDGET_FLAG_DIRTY);
}
}
+void buttonSetClickHandler(ButtonT *button, widgetCallback callback) {
+ button->clicked = callback;
+}
+
+
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);
+ GUI_SET_FLAG((WidgetT *)button, WIDGET_FLAG_DIRTY);
}
diff --git a/client/src/gui/button.h b/client/src/gui/button.h
index 0d2d1cc..37d7a1a 100644
--- a/client/src/gui/button.h
+++ b/client/src/gui/button.h
@@ -36,6 +36,7 @@ typedef struct ButtonS {
void buttonDel(WidgetT **widget);
WidgetT *buttonInit(WidgetT *button, uint16_t x, uint16_t y, char *title, widgetCallback callback);
ButtonT *buttonNew(uint16_t x, uint16_t y, char *title, widgetCallback callback);
+void buttonSetClickHandler(ButtonT *button, widgetCallback callback);
void buttonSetTitle(ButtonT *button, char *title);
diff --git a/client/src/gui/checkbox.c b/client/src/gui/checkbox.c
new file mode 100644
index 0000000..7344eca
--- /dev/null
+++ b/client/src/gui/checkbox.c
@@ -0,0 +1,151 @@
+/*
+ * Kangaroo Punch MultiPlayer 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 .
+ *
+ */
+
+
+#include "checkbox.h"
+
+
+static void checkboxMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event);
+static void checkboxPaint(WidgetT *widget);
+
+
+void checkboxDel(WidgetT **widget) {
+ CheckboxT *c = (CheckboxT *)*widget;
+
+ if (c->title) free(c->title);
+ free(c);
+ c = NULL;
+}
+
+
+uint8_t checkboxGetValue(CheckboxT *checkbox) {
+ return GUI_GET_FLAG((WidgetT *)checkbox, WIDGET_FLAG_ACTIVE);
+}
+
+
+WidgetT *checkboxInit(WidgetT *widget, uint16_t x, uint16_t y, char *title) {
+ CheckboxT *c = (CheckboxT *)widget;
+
+ c->base.magic = MAGIC_CHECKBOX;
+ c->base.x = x;
+ c->base.y = y;
+ c->base.delMethod = checkboxDel;
+ c->base.paintMethod = checkboxPaint;
+ c->base.mouseEventMethod = checkboxMouseEvent;
+ c->title = NULL;
+ c->clicked = NULL;
+
+ checkboxSetTitle(c, title);
+
+ // Width is set in checkboxSetTitle
+ c->base.h = fontHeightGet(_guiFont);
+
+ return widget;
+}
+
+
+static void checkboxMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event) {
+ CheckboxT *c = (CheckboxT *)widget;
+
+ (void)x;
+ (void)y;
+ (void)mouse;
+
+ if (event == MOUSE_EVENT_LEFT_UP) {
+ // Toggle value.
+ checkboxSetValue(c, !checkboxGetValue(c));
+ // Fire callback on mouse up.
+ if (c->clicked) c->clicked(widget);
+ }
+}
+
+
+CheckboxT *checkboxNew(uint16_t x, uint16_t y, char *title) {
+ CheckboxT *checkbox = (CheckboxT *)malloc(sizeof(CheckboxT));
+ WidgetT *widget = NULL;
+
+ if (!checkbox) return NULL;
+
+ widget = widgetInit((WidgetT *)checkbox);
+ if (!widget) {
+ free(checkbox);
+ return NULL;
+ }
+
+ checkbox = (CheckboxT *)checkboxInit((WidgetT *)checkbox, x, y, title);
+
+ return checkbox;
+}
+
+
+static void checkboxPaint(WidgetT *widget) {
+ CheckboxT *c = (CheckboxT *)widget;
+ int16_t o;
+ int8_t active = checkboxGetValue(c);
+ ColorT highlight = active ? _guiColor[COLOR_CHECKBOX_SHADOW] : _guiColor[COLOR_CHECKBOX_HIGHLIGHT];
+ ColorT shadow = active ? _guiColor[COLOR_CHECKBOX_HIGHLIGHT] : _guiColor[COLOR_CHECKBOX_SHADOW];
+ ColorT fill = active ? _guiColor[COLOR_CHECKBOX_ACTIVE] : _guiColor[COLOR_CHECKBOX_INACTIVE];
+
+ if (GUI_GET_FLAG(widget, WIDGET_FLAG_DIRTY)) {
+ vbeSurfaceSet(c->base.surface);
+
+ // Checkbox is 10x10 pixels. Find offset based on font height.
+ o = (_guiFont->height - 10) * 0.5;
+
+ // Draw outline of checkbox.
+ guiDrawHighlightFrame(c->base.x, c->base.y + o, c->base.x + 10, c->base.y + 10 + o, highlight, shadow);
+
+ // Draw background.
+ guiDrawFilledRectangle(c->base.x + 1, c->base.y + o + 1, c->base.x + 9, c->base.y + + o + 9, fill);
+
+ // Draw title.
+ fontRender(_guiFont, c->title, _guiColor[COLOR_CHECKBOX_TEXT], _guiColor[COLOR_WINDOW_BACKGROUND], c->base.x + 10 + _guiMetric[METRIC_CHECKBOX_PADDING], c->base.y);
+
+ GUI_CLEAR_FLAG(widget, WIDGET_FLAG_DIRTY);
+ }
+}
+
+
+void checkboxSetClickHandler(CheckboxT *checkbox, widgetCallback callback) {
+ checkbox->clicked = callback;
+}
+
+
+void checkboxSetTitle(CheckboxT *checkbox, char *title) {
+ if (checkbox->title) free(checkbox->title);
+ checkbox->title = strdup(title);
+ checkbox->base.w = (strlen(title) * fontWidthGet(_guiFont)) + 10 + _guiMetric[METRIC_CHECKBOX_PADDING];
+ GUI_SET_FLAG((WidgetT *)checkbox, WIDGET_FLAG_DIRTY);
+}
+
+
+void checkboxSetValue(CheckboxT *checkbox, uint8_t selected) {
+ if (selected) {
+ if (!checkboxGetValue(checkbox)) {
+ GUI_SET_FLAG((WidgetT *)checkbox, WIDGET_FLAG_ACTIVE);
+ GUI_SET_FLAG((WidgetT *)checkbox, WIDGET_FLAG_DIRTY);
+ }
+ } else {
+ if (checkboxGetValue(checkbox)) {
+ GUI_CLEAR_FLAG((WidgetT *)checkbox, WIDGET_FLAG_ACTIVE);
+ GUI_SET_FLAG((WidgetT *)checkbox, WIDGET_FLAG_DIRTY);
+ }
+ }
+}
+
diff --git a/client/src/gui/checkbox.h b/client/src/gui/checkbox.h
new file mode 100644
index 0000000..8838820
--- /dev/null
+++ b/client/src/gui/checkbox.h
@@ -0,0 +1,45 @@
+/*
+ * Kangaroo Punch MultiPlayer 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 .
+ *
+ */
+
+
+#ifndef CHECKBOX_H
+#define CHECKBOX_H
+
+
+#include "gui.h"
+#include "widget.h"
+
+
+typedef struct CheckboxS {
+ WidgetT base; // Must be first in every widget
+ char *title;
+ widgetCallback clicked;
+} CheckboxT;
+
+
+void checkboxDel(WidgetT **widget);
+uint8_t checkboxGetValue(CheckboxT *checkbox);
+WidgetT *checkboxInit(WidgetT *widget, uint16_t x, uint16_t y, char *title);
+CheckboxT *checkboxNew(uint16_t x, uint16_t y, char *title);
+void checkboxSetClickHandler(CheckboxT *checkbox, widgetCallback callback);
+void checkboxSetTitle(CheckboxT *checkbox, char *title);
+void checkboxSetValue(CheckboxT *checkbox, uint8_t selected);
+
+
+#endif // CHECKBOX_H
diff --git a/client/src/gui/frame.c b/client/src/gui/frame.c
new file mode 100644
index 0000000..07a38d8
--- /dev/null
+++ b/client/src/gui/frame.c
@@ -0,0 +1,101 @@
+/*
+ * Kangaroo Punch MultiPlayer 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 .
+ *
+ */
+
+
+#include "frame.h"
+
+
+static void framePaint(WidgetT *widget);
+
+
+void frameDel(WidgetT **widget) {
+ FrameT *f = (FrameT *)*widget;
+
+ if (f->title) free(f->title);
+ free(f);
+ f = NULL;
+}
+
+
+WidgetT *frameInit(WidgetT *widget, uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *title) {
+ FrameT *f = (FrameT *)widget;
+
+ f->base.magic = MAGIC_FRAME;
+ f->base.x = x;
+ f->base.y = y;
+ f->base.w = w;
+ f->base.h = h;
+ f->base.delMethod = frameDel;
+ f->base.paintMethod = framePaint;
+ f->title = NULL;
+
+ f->base.marginX += 3;
+ f->base.marginY += 9;
+
+ frameSetTitle(f, title);
+
+ return widget;
+}
+
+
+FrameT *frameNew(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *title) {
+ FrameT *frame = (FrameT *)malloc(sizeof(FrameT));
+ WidgetT *widget = NULL;
+
+ if (!frame) return NULL;
+
+ widget = widgetInit((WidgetT *)frame);
+ if (!widget) {
+ free(frame);
+ return NULL;
+ }
+
+ frame = (FrameT *)frameInit((WidgetT *)frame, x, y, w, h, title);
+
+ return frame;
+}
+
+
+static void framePaint(WidgetT *widget) {
+ FrameT *f = (FrameT *)widget;
+ uint16_t o = _guiFont->height * 0.5;
+
+ if (GUI_GET_FLAG(widget, WIDGET_FLAG_DIRTY)) {
+ vbeSurfaceSet(f->base.surface);
+
+ // Draw frame.
+ guiDrawHighlightFrame(f->base.x, f->base.y + o, f->base.x + f->base.w, f->base.y + f->base.h - o, _guiColor[COLOR_FRAME_SHADOW], _guiColor[COLOR_FRAME_HIGHLIGHT]);
+
+ // Draw title.
+ fontRender(_guiFont, f->title, _guiColor[COLOR_FRAME_TEXT], _guiColor[COLOR_WINDOW_BACKGROUND], f->base.x + 10, f->base.y);
+
+ GUI_CLEAR_FLAG(widget, WIDGET_FLAG_DIRTY);
+ }
+}
+
+
+void frameSetTitle(FrameT *frame, char *title) {
+ if (frame->title) free(frame->title);
+ frame->title = (char *)malloc(strlen(title) + 3);
+ frame->title[0] = ' ';
+ frame->title[1] = 0;
+ strcat(frame->title, title);
+ strcat(frame->title, " ");
+ GUI_SET_FLAG((WidgetT *)frame, WIDGET_FLAG_DIRTY);
+}
diff --git a/client/src/gui/frame.h b/client/src/gui/frame.h
new file mode 100644
index 0000000..687caa1
--- /dev/null
+++ b/client/src/gui/frame.h
@@ -0,0 +1,41 @@
+/*
+ * Kangaroo Punch MultiPlayer 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 .
+ *
+ */
+
+
+#ifndef FRAME_H
+#define FRAME_H
+
+
+#include "gui.h"
+#include "widget.h"
+
+
+typedef struct FrameS {
+ WidgetT base; // Must be first in every widget
+ char *title;
+} FrameT;
+
+
+void frameDel(WidgetT **widget);
+WidgetT *frameInit(WidgetT *widget, uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *title);
+FrameT *frameNew(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *title);
+void frameSetTitle(FrameT *frame, char *title);
+
+
+#endif // FRAME_H
diff --git a/client/src/gui/gui.c b/client/src/gui/gui.c
index 0f328d3..0b2d1a5 100644
--- a/client/src/gui/gui.c
+++ b/client/src/gui/gui.c
@@ -349,7 +349,9 @@ DesktopT *guiStartup(void) {
_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.
+ _guiMetric[METRIC_WINDOW_TITLE_GRAB_HEIGHT] = _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + _guiMetric[METRIC_WINDOW_TITLE_HEIGHT] + 5; // Border, highlights, titlebar.
+ _guiMetric[METRIC_CHECKBOX_PADDING] = 6; // Makes the 10 wide checkbox fill two character cells by padding it out to 16.
+ _guiMetric[METRIC_RADIOBUTTON_PADDING] = 6; // Makes the 10 wide radio button fill two character cells by padding it out to 16.
_guiColor[COLOR_BUTTON_BACKGROUND] = vbeMakeColor(168, 168, 168);
_guiColor[COLOR_BUTTON_HIGHLIGHT] = vbeMakeColor(248, 252, 248);
@@ -363,6 +365,21 @@ DesktopT *guiStartup(void) {
_guiColor[COLOR_WINDOW_TITLE_INACTIVE] = vbeMakeColor(168, 168, 168);
_guiColor[COLOR_WINDOW_TITLE_TEXT_ACTIVE] = vbeMakeColor(248, 252, 248);
_guiColor[COLOR_WINDOW_TITLE_TEXT_INACTIVE] = vbeMakeColor( 0, 0, 0);
+ _guiColor[COLOR_LABEL_TEXT_INACTIVE] = vbeMakeColor(248, 252, 248);
+ _guiColor[COLOR_LABEL_TEXT_INACTIVE] = vbeMakeColor( 0, 0, 0);
+ _guiColor[COLOR_CHECKBOX_HIGHLIGHT] = vbeMakeColor(248, 252, 248);
+ _guiColor[COLOR_CHECKBOX_SHADOW] = vbeMakeColor( 0, 0, 0);
+ _guiColor[COLOR_CHECKBOX_ACTIVE] = vbeMakeColor( 80, 84, 80);
+ _guiColor[COLOR_CHECKBOX_INACTIVE] = vbeMakeColor(168, 168, 168);
+ _guiColor[COLOR_CHECKBOX_TEXT] = vbeMakeColor( 0, 0, 0);
+ _guiColor[COLOR_RADIOBUTTON_HIGHLIGHT] = vbeMakeColor(248, 252, 248);
+ _guiColor[COLOR_RADIOBUTTON_SHADOW] = vbeMakeColor( 0, 0, 0);
+ _guiColor[COLOR_RADIOBUTTON_ACTIVE] = vbeMakeColor( 80, 84, 80);
+ _guiColor[COLOR_RADIOBUTTON_INACTIVE] = vbeMakeColor(168, 168, 168);
+ _guiColor[COLOR_RADIOBUTTON_TEXT] = vbeMakeColor( 0, 0, 0);
+ _guiColor[COLOR_FRAME_HIGHLIGHT] = vbeMakeColor(248, 252, 248);
+ _guiColor[COLOR_FRAME_SHADOW] = vbeMakeColor( 0, 0, 0);
+ _guiColor[COLOR_FRAME_TEXT] = vbeMakeColor( 0, 0, 0);
_guiFont = fontLoad("vga8x14.dat");
diff --git a/client/src/gui/gui.h b/client/src/gui/gui.h
index 593b439..074a3cd 100644
--- a/client/src/gui/gui.h
+++ b/client/src/gui/gui.h
@@ -29,9 +29,9 @@
#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_GET_FLAG(w,f) (((w)->flags & (1 << (f))) != 0)
+#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)
@@ -42,6 +42,14 @@ enum MagicE {
MAGIC_DESKTOP,
MAGIC_WINDOW,
MAGIC_BUTTON,
+ MAGIC_LABEL,
+ MAGIC_CHECKBOX,
+ MAGIC_RADIOBUTTON,
+ MAGIC_PICTURE,
+ MAGIC_FRAME,
+ //MAGIC_TEXTBOX,
+ //MAGIC_UPDOWN,
+ //MAGIC_LISTBOX,
MAGIC_COUNT
};
@@ -53,6 +61,8 @@ enum MetricE {
METRIC_WINDOW_BORDER_WIDTH,
METRIC_WINDOW_TITLE_HEIGHT,
METRIC_WINDOW_TITLE_GRAB_HEIGHT,
+ METRIC_CHECKBOX_PADDING,
+ METRIC_RADIOBUTTON_PADDING,
METRIC_COUNT
};
@@ -70,6 +80,21 @@ enum ColorE {
COLOR_WINDOW_TITLE_INACTIVE,
COLOR_WINDOW_TITLE_TEXT_ACTIVE,
COLOR_WINDOW_TITLE_TEXT_INACTIVE,
+ COLOR_LABEL_TEXT_ACTIVE,
+ COLOR_LABEL_TEXT_INACTIVE,
+ COLOR_CHECKBOX_HIGHLIGHT,
+ COLOR_CHECKBOX_SHADOW,
+ COLOR_CHECKBOX_ACTIVE,
+ COLOR_CHECKBOX_INACTIVE,
+ COLOR_CHECKBOX_TEXT,
+ COLOR_RADIOBUTTON_HIGHLIGHT,
+ COLOR_RADIOBUTTON_SHADOW,
+ COLOR_RADIOBUTTON_ACTIVE,
+ COLOR_RADIOBUTTON_INACTIVE,
+ COLOR_RADIOBUTTON_TEXT,
+ COLOR_FRAME_HIGHLIGHT,
+ COLOR_FRAME_SHADOW,
+ COLOR_FRAME_TEXT,
COLOR_COUNT
};
diff --git a/client/src/gui/image.c b/client/src/gui/image.c
index 0725b96..c318b73 100644
--- a/client/src/gui/image.c
+++ b/client/src/gui/image.c
@@ -74,6 +74,11 @@ ImageT *imageCreate(uint16_t w, uint16_t h, ColorT color) {
}
+uint16_t imageHeightGet(ImageT *image) {
+ return image->height;
+}
+
+
ImageT *imageLoad(char *filename) {
uint16_t x;
uint16_t y;
@@ -116,9 +121,15 @@ ColorT imagePixelGet(ImageT *image, uint16_t x, uint16_t y) {
void imageRender(ImageT *image, uint16_t x, uint16_t y) {
uint16_t x1;
uint16_t y1;
+ uint16_t x2 = image->width;
+ uint16_t y2 = image->height;
- for (y1=0; y1height; y1++) {
- for (x1=0; x1width; x1++) {
+ // Clip on right and bottom
+ if (x + x2 > vbeSurfaceWidthGet()) x2 -= x + x2 - vbeSurfaceWidthGet();
+ if (y + y2 > vbeSurfaceHeightGet()) y2 -= y + y2 - vbeSurfaceHeightGet();
+
+ for (y1=0; y1pixels[x1][y1]);
}
}
@@ -132,8 +143,8 @@ void imageRenderWithAlpha(ImageT *image, uint16_t x, uint16_t y, ColorT alpha) {
uint16_t y2 = image->height;
// Clip on right and bottom
- if (x + x2 > vbeDisplayWidthGet()) x2 -= x + x2 - vbeDisplayWidthGet();
- if (y + y2 > vbeDisplayHeightGet()) y2 -= y + y2 - vbeDisplayHeightGet();
+ if (x + x2 > vbeSurfaceWidthGet()) x2 -= x + x2 - vbeSurfaceWidthGet();
+ if (y + y2 > vbeSurfaceHeightGet()) y2 -= y + y2 - vbeSurfaceHeightGet();
for (y1=0; y1width;
+}
diff --git a/client/src/gui/image.h b/client/src/gui/image.h
index bbaa043..f052d69 100644
--- a/client/src/gui/image.h
+++ b/client/src/gui/image.h
@@ -35,13 +35,15 @@ typedef struct ImageS {
} ImageT;
-ImageT *imageAllocate(uint16_t w, uint16_t h);
-ImageT *imageCreate(uint16_t w, uint16_t h, ColorT color);
-ImageT *imageLoad(char *filename);
-ColorT imagePixelGet(ImageT *image, uint16_t x, uint16_t y);
-void imageRender(ImageT *image, uint16_t x, uint16_t y);
-void imageRenderWithAlpha(ImageT *image, uint16_t x, uint16_t y, ColorT alpha);
-void imageUnload(ImageT **image);
+ImageT *imageAllocate(uint16_t w, uint16_t h);
+ImageT *imageCreate(uint16_t w, uint16_t h, ColorT color);
+uint16_t imageHeightGet(ImageT *image);
+ImageT *imageLoad(char *filename);
+ColorT imagePixelGet(ImageT *image, uint16_t x, uint16_t y);
+void imageRender(ImageT *image, uint16_t x, uint16_t y);
+void imageRenderWithAlpha(ImageT *image, uint16_t x, uint16_t y, ColorT alpha);
+void imageUnload(ImageT **image);
+uint16_t imageWidthGet(ImageT *image);
#endif // IMAGE_H
diff --git a/client/src/gui/label.c b/client/src/gui/label.c
new file mode 100644
index 0000000..4c89435
--- /dev/null
+++ b/client/src/gui/label.c
@@ -0,0 +1,148 @@
+/*
+ * Kangaroo Punch MultiPlayer 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 .
+ *
+ */
+
+
+#include "label.h"
+
+
+static void labelMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event);
+static void labelPaint(WidgetT *widget);
+
+
+void labelDel(WidgetT **widget) {
+ LabelT *l = (LabelT *)*widget;
+
+ if (l->title) free(l->title);
+ free(l);
+ l = NULL;
+}
+
+
+WidgetT *labelInit(WidgetT *widget, uint16_t x, uint16_t y, char *title) {
+ LabelT *l = (LabelT *)widget;
+
+ l->base.magic = MAGIC_LABEL;
+ l->base.x = x;
+ l->base.y = y;
+ l->base.delMethod = labelDel;
+ l->base.paintMethod = labelPaint;
+ l->base.mouseEventMethod = labelMouseEvent;
+ l->title = NULL;
+ l->background = _guiColor[COLOR_WINDOW_BACKGROUND];
+ l->foreground = _guiColor[COLOR_LABEL_TEXT_INACTIVE];
+ l->clicked = NULL;
+
+ labelSetTitle(l, title);
+
+ // Width is set in labelSetTitle
+ l->base.h = fontHeightGet(_guiFont);
+
+ return widget;
+}
+
+
+static void labelMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event) {
+ LabelT *l = (LabelT *)widget;
+
+ (void)x;
+ (void)y;
+ (void)mouse;
+
+ // Label pressed?
+ if (event == MOUSE_EVENT_LEFT_HOLD) {
+ if (!GUI_GET_FLAG(widget, WIDGET_FLAG_ACTIVE)) {
+ GUI_SET_FLAG(widget, WIDGET_FLAG_ACTIVE);
+ GUI_SET_FLAG(widget, WIDGET_FLAG_DIRTY);
+ }
+ } else {
+ if (GUI_GET_FLAG(widget, WIDGET_FLAG_ACTIVE)) {
+ GUI_CLEAR_FLAG(widget, WIDGET_FLAG_ACTIVE);
+ GUI_SET_FLAG(widget, WIDGET_FLAG_DIRTY);
+ }
+ }
+
+ // Fire callback on mouse up.
+ if (event == MOUSE_EVENT_LEFT_UP) {
+ if (l->clicked) l->clicked(widget);
+ }
+}
+
+
+LabelT *labelNew(uint16_t x, uint16_t y, char *title) {
+ LabelT *label = (LabelT *)malloc(sizeof(LabelT));
+ WidgetT *widget = NULL;
+
+ if (!label) return NULL;
+
+ widget = widgetInit((WidgetT *)label);
+ if (!widget) {
+ free(label);
+ return NULL;
+ }
+
+ label = (LabelT *)labelInit((WidgetT *)label, x, y, title);
+
+ return label;
+}
+
+
+static void labelPaint(WidgetT *widget) {
+ LabelT *l = (LabelT *)widget;
+ ColorT text = GUI_GET_FLAG(widget, WIDGET_FLAG_ACTIVE) ? l->active : l->foreground;
+
+ if (GUI_GET_FLAG(widget, WIDGET_FLAG_DIRTY)) {
+ vbeSurfaceSet(l->base.surface);
+
+ // Draw title.
+ fontRender(_guiFont, l->title, text, l->background, l->base.x, l->base.y);
+
+ GUI_CLEAR_FLAG(widget, WIDGET_FLAG_DIRTY);
+ }
+}
+
+
+void labelSetActiveColor(LabelT *label, ColorT color) {
+ label->active = color;
+ GUI_SET_FLAG((WidgetT *)label, WIDGET_FLAG_DIRTY);
+}
+
+
+void labelSetBackgroundColor(LabelT *label, ColorT color) {
+ label->background = color;
+ GUI_SET_FLAG((WidgetT *)label, WIDGET_FLAG_DIRTY);
+}
+
+
+void labelSetClickHandler(LabelT *label, widgetCallback callback) {
+ label->clicked = callback;
+}
+
+
+void labelSetForegroundColor(LabelT *label, ColorT color) {
+ label->foreground = color;
+ GUI_SET_FLAG((WidgetT *)label, WIDGET_FLAG_DIRTY);
+}
+
+
+void labelSetTitle(LabelT *label, char *title) {
+ if (label->title) free(label->title);
+ label->title = strdup(title);
+ label->base.w = (strlen(title) * fontWidthGet(_guiFont));
+ GUI_SET_FLAG((WidgetT *)label, WIDGET_FLAG_DIRTY);
+}
diff --git a/client/src/gui/label.h b/client/src/gui/label.h
new file mode 100644
index 0000000..47cedb2
--- /dev/null
+++ b/client/src/gui/label.h
@@ -0,0 +1,49 @@
+/*
+ * Kangaroo Punch MultiPlayer 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 .
+ *
+ */
+
+
+#ifndef LABEL_H
+#define LABEL_H
+
+
+#include "gui.h"
+#include "widget.h"
+
+
+typedef struct LabelS {
+ WidgetT base; // Must be first in every widget
+ char *title;
+ widgetCallback clicked;
+ ColorT active;
+ ColorT foreground;
+ ColorT background;
+} LabelT;
+
+
+void labelDel(WidgetT **widget);
+WidgetT *labelInit(WidgetT *widget, uint16_t x, uint16_t y, char *title);
+LabelT *labelNew(uint16_t x, uint16_t y, char *title);
+void labelSetActiveColor(LabelT *label, ColorT color);
+void labelSetBackgroundColor(LabelT *label, ColorT color);
+void labelSetClickHandler(LabelT *label, widgetCallback callback);
+void labelSetForegroundColor(LabelT *label, ColorT color);
+void labelSetTitle(LabelT *label, char *title);
+
+
+#endif // LABEL_H
diff --git a/client/src/gui/picture.c b/client/src/gui/picture.c
new file mode 100644
index 0000000..6092972
--- /dev/null
+++ b/client/src/gui/picture.c
@@ -0,0 +1,114 @@
+/*
+ * Kangaroo Punch MultiPlayer 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 .
+ *
+ */
+
+
+#include "picture.h"
+
+
+static void pictureMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event);
+static void picturePaint(WidgetT *widget);
+
+
+void pictureDel(WidgetT **widget) {
+ PictureT *p = (PictureT *)*widget;
+
+ if (p->image) imageUnload(&p->image);
+ if (p->filename) free(p->filename);
+ free(p);
+ p = NULL;
+}
+
+
+WidgetT *pictureInit(WidgetT *widget, uint16_t x, uint16_t y, char *filename) {
+ PictureT *l = (PictureT *)widget;
+
+ l->base.magic = MAGIC_PICTURE;
+ l->base.x = x;
+ l->base.y = y;
+ l->base.delMethod = pictureDel;
+ l->base.paintMethod = picturePaint;
+ l->base.mouseEventMethod = pictureMouseEvent;
+ l->filename = strdup(filename);
+ l->clicked = NULL;
+
+ l->image = imageLoad(l->filename);
+ if (!l->image) {
+ free(l->filename);
+ return NULL;
+ }
+
+ l->base.w = imageWidthGet(l->image);
+ l->base.h = imageHeightGet(l->image);
+
+ return widget;
+}
+
+
+static void pictureMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event) {
+ PictureT *p = (PictureT *)widget;
+
+ (void)x;
+ (void)y;
+ (void)mouse;
+
+ // Fire callback on mouse up.
+ if (event == MOUSE_EVENT_LEFT_UP) {
+ if (p->clicked) p->clicked(widget);
+ }
+}
+
+
+PictureT *pictureNew(uint16_t x, uint16_t y, char *filename) {
+ PictureT *picture = (PictureT *)malloc(sizeof(PictureT));
+ WidgetT *widget = NULL;
+
+ if (!picture) return NULL;
+
+ widget = widgetInit((WidgetT *)picture);
+ if (!widget) {
+ free(picture);
+ return NULL;
+ }
+
+ picture = (PictureT *)pictureInit((WidgetT *)picture, x, y, filename);
+ if (!picture) {
+ free(picture);
+ return NULL;
+ }
+
+ return picture;
+}
+
+
+static void picturePaint(WidgetT *widget) {
+ PictureT *p = (PictureT *)widget;
+
+ if (GUI_GET_FLAG(widget, WIDGET_FLAG_DIRTY)) {
+ vbeSurfaceSet(p->base.surface);
+
+ imageRender(p->image, p->base.x, p->base.y);
+
+ GUI_CLEAR_FLAG(widget, WIDGET_FLAG_DIRTY);
+ }
+}
+
+
+void pictureSetClickHandler(PictureT *picture, widgetCallback callback) {
+ picture->clicked = callback;
+}
diff --git a/client/src/gui/picture.h b/client/src/gui/picture.h
new file mode 100644
index 0000000..735d42c
--- /dev/null
+++ b/client/src/gui/picture.h
@@ -0,0 +1,44 @@
+/*
+ * Kangaroo Punch MultiPlayer 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 .
+ *
+ */
+
+
+#ifndef PICTURE_H
+#define PICTURE_H
+
+
+#include "gui.h"
+#include "widget.h"
+#include "image.h"
+
+
+typedef struct PictureS {
+ WidgetT base; // Must be first in every widget
+ char *filename;
+ widgetCallback clicked;
+ ImageT *image;
+} PictureT;
+
+
+void pictureDel(WidgetT **widget);
+WidgetT *pictureInit(WidgetT *widget, uint16_t x, uint16_t y, char *filename);
+PictureT *pictureNew(uint16_t x, uint16_t y, char *filename);
+void pictureSetClickHandler(PictureT *picture, widgetCallback callback);
+
+
+#endif // PICTURE_H
diff --git a/client/src/gui/radio.c b/client/src/gui/radio.c
new file mode 100644
index 0000000..d8ebd60
--- /dev/null
+++ b/client/src/gui/radio.c
@@ -0,0 +1,207 @@
+/*
+ * Kangaroo Punch MultiPlayer 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 .
+ *
+ */
+
+
+#include "radio.h"
+
+
+static void radioClearSelectedInGroup(WidgetT *widget, uint32_t group);
+static RadioT *radioFindSelectedInGroup(WidgetT *widget, uint32_t group);
+static void radioMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event);
+static void radioPaint(WidgetT *widget);
+
+
+static void radioClearSelectedInGroup(WidgetT *widget, uint32_t group) {
+ size_t len = arrlenu(widget->children);
+ size_t x;
+
+ // Is this a Radio Button?
+ if (widget->magic == MAGIC_RADIOBUTTON) {
+ // Is this in our group and active?
+ if (((RadioT *)widget)->group == group && GUI_GET_FLAG(widget, WIDGET_FLAG_ACTIVE)) {
+ // Deactivate it, mark it dirty, and stop searching.
+ GUI_CLEAR_FLAG(widget, WIDGET_FLAG_ACTIVE);
+ GUI_SET_FLAG(widget, WIDGET_FLAG_DIRTY);
+ return;
+ }
+ }
+
+ // Process any children.
+ if (len > 0) {
+ for (x=0; xchildren[x], group);
+ }
+ }
+}
+
+
+void radioDel(WidgetT **widget) {
+ RadioT *r = (RadioT *)*widget;
+
+ if (r->title) free(r->title);
+ free(r);
+ r = NULL;
+}
+
+
+static RadioT *radioFindSelectedInGroup(WidgetT *widget, uint32_t group) {
+ size_t len = arrlenu(widget->children);
+ size_t x;
+ RadioT *result = NULL;
+
+ // Is this a Radio Button?
+ if (widget->magic == MAGIC_RADIOBUTTON) {
+ // Is this in our group and active?
+ if (((RadioT *)widget)->group == group && GUI_GET_FLAG(widget, WIDGET_FLAG_ACTIVE)) {
+ // Found! Stop searching.
+ return (RadioT *)widget;
+ }
+ }
+
+ // Process any children.
+ if (len > 0) {
+ for (x=0; xchildren[x], group);
+ if (result) break;
+ }
+ }
+
+ return result;
+}
+
+
+RadioT *radioGetSelected(RadioT *radio) {
+ return radioFindSelectedInGroup(guiRootGet(), radio->group);
+}
+
+
+WidgetT *radioInit(WidgetT *widget, uint16_t x, uint16_t y, char *title, uint16_t group) {
+ RadioT *r = (RadioT *)widget;
+
+ r->base.magic = MAGIC_RADIOBUTTON;
+ r->base.x = x;
+ r->base.y = y;
+ r->base.delMethod = radioDel;
+ r->base.paintMethod = radioPaint;
+ r->base.mouseEventMethod = radioMouseEvent;
+ r->title = NULL;
+ r->clicked = NULL;
+ r->group = group;
+
+ radioSetTitle(r, title);
+
+ // Width is set in radioSetTitle
+ r->base.h = fontHeightGet(_guiFont);
+
+ return widget;
+}
+
+
+static void radioMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event) {
+ RadioT *r = (RadioT *)widget;
+
+ (void)x;
+ (void)y;
+ (void)mouse;
+
+ if (event == MOUSE_EVENT_LEFT_UP) {
+ // Select us.
+ radioSetSelected(r);
+ // Fire callback on mouse up.
+ if (r->clicked) r->clicked(widget);
+ }
+}
+
+
+RadioT *radioNew(uint16_t x, uint16_t y, char *title, uint16_t group) {
+ RadioT *radio = (RadioT *)malloc(sizeof(RadioT));
+ WidgetT *widget = NULL;
+
+ if (!radio) return NULL;
+
+ widget = widgetInit((WidgetT *)radio);
+ if (!widget) {
+ free(radio);
+ return NULL;
+ }
+
+ radio = (RadioT *)radioInit((WidgetT *)radio, x, y, title, group);
+
+ return radio;
+}
+
+
+static void radioPaint(WidgetT *widget) {
+ RadioT *r = (RadioT *)widget;
+ int16_t i;
+ int16_t o;
+ uint8_t active = (radioGetSelected(r) == r);
+ ColorT highlight = active ? _guiColor[COLOR_RADIOBUTTON_SHADOW] : _guiColor[COLOR_RADIOBUTTON_HIGHLIGHT];
+ ColorT shadow = active ? _guiColor[COLOR_RADIOBUTTON_HIGHLIGHT] : _guiColor[COLOR_RADIOBUTTON_SHADOW];
+ ColorT fill = active ? _guiColor[COLOR_RADIOBUTTON_ACTIVE] : _guiColor[COLOR_RADIOBUTTON_INACTIVE];
+
+ if (GUI_GET_FLAG(widget, WIDGET_FLAG_DIRTY)) {
+ vbeSurfaceSet(r->base.surface);
+
+ // Radio button is 10x10 pixels. Find offset based on font height.
+ o = (_guiFont->height - 10) * 0.5;
+
+ // Draw outline of radio button.
+ guiDrawLine(r->base.x, r->base.y + o + 5, r->base.x + 5, r->base.y + o, highlight);
+ guiDrawLine(r->base.x + 5, r->base.y + o, r->base.x + 10, r->base.y + o + 5, highlight);
+ guiDrawLine(r->base.x, r->base.y + o + 5, r->base.x + 5, r->base.y + o + 10, shadow);
+ guiDrawLine(r->base.x + 5, r->base.y + o + 10, r->base.x + 10, r->base.y + o + 5, shadow);
+
+ // Fill radio button.
+ for (i=0; i<4; i++) {
+ guiDrawLine(r->base.x + 5 - i, r->base.y + o + i + 1, r->base.x + 5 + i, r->base.y + o + i + 1, fill);
+ guiDrawLine(r->base.x + 5 - i, r->base.y + o - i + 8, r->base.x + 5 + i, r->base.y + o - i + 8, fill);
+ }
+
+ // Draw title.
+ fontRender(_guiFont, r->title, _guiColor[COLOR_RADIOBUTTON_TEXT], _guiColor[COLOR_WINDOW_BACKGROUND], r->base.x + 10 + _guiMetric[METRIC_RADIOBUTTON_PADDING], r->base.y);
+
+ GUI_CLEAR_FLAG(widget, WIDGET_FLAG_DIRTY);
+ }
+}
+
+
+void radioSetClickHandler(RadioT *radio, widgetCallback callback) {
+ radio->clicked = callback;
+}
+
+
+void radioSetSelected(RadioT *radio) {
+ // Are we already selected?
+ if (!GUI_GET_FLAG((WidgetT *)radio, WIDGET_FLAG_ACTIVE)) {
+ // Clear whoever is selected.
+ radioClearSelectedInGroup(guiRootGet(), radio->group);
+ // Select us.
+ GUI_SET_FLAG((WidgetT *)radio, WIDGET_FLAG_ACTIVE);
+ GUI_SET_FLAG((WidgetT *)radio, WIDGET_FLAG_DIRTY);
+ }
+}
+
+
+void radioSetTitle(RadioT *radio, char *title) {
+ if (radio->title) free(radio->title);
+ radio->title = strdup(title);
+ radio->base.w = (strlen(title) * fontWidthGet(_guiFont)) + 10 + _guiMetric[METRIC_RADIOBUTTON_PADDING];
+ GUI_SET_FLAG((WidgetT *)radio, WIDGET_FLAG_DIRTY);
+}
diff --git a/client/src/gui/radio.h b/client/src/gui/radio.h
new file mode 100644
index 0000000..c56f58c
--- /dev/null
+++ b/client/src/gui/radio.h
@@ -0,0 +1,46 @@
+/*
+ * Kangaroo Punch MultiPlayer 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 .
+ *
+ */
+
+
+#ifndef RADIO_H
+#define RADIO_H
+
+
+#include "gui.h"
+#include "widget.h"
+
+
+typedef struct RadioS {
+ WidgetT base; // Must be first in every widget
+ char *title;
+ uint16_t group;
+ widgetCallback clicked;
+} RadioT;
+
+
+void radioDel(WidgetT **widget);
+RadioT *radioGetSelected(RadioT *radio);
+WidgetT *radioInit(WidgetT *widget, uint16_t x, uint16_t y, char *title, uint16_t group);
+RadioT *radioNew(uint16_t x, uint16_t y, char *title, uint16_t group);
+void radioSetClickHandler(RadioT *radio, widgetCallback callback);
+void radioSetSelected(RadioT *radio);
+void radioSetTitle(RadioT *radio, char *title);
+
+
+#endif // RADIO_H
diff --git a/client/src/gui/widget.c b/client/src/gui/widget.c
index b19f500..ad78266 100644
--- a/client/src/gui/widget.c
+++ b/client/src/gui/widget.c
@@ -21,6 +21,16 @@
#include "widget.h"
+uint16_t widgetGetHeight(WidgetT *widget) {
+ return widget->h;
+}
+
+
+uint16_t widgetGetWidth(WidgetT *widget) {
+ return widget->w;
+}
+
+
WidgetT *widgetInit(WidgetT *widget) {
widget->magic = MAGIC_UNKNOWN;
widget->flags = 0;
diff --git a/client/src/gui/widget.h b/client/src/gui/widget.h
index 9ac6267..edfb619 100644
--- a/client/src/gui/widget.h
+++ b/client/src/gui/widget.h
@@ -62,8 +62,10 @@ typedef struct WidgetS {
} WidgetT;
-WidgetT *widgetInit(WidgetT *widget);
-WidgetT *widgetNew(void);
+uint16_t widgetGetHeight(WidgetT *widget);
+uint16_t widgetGetWidth(WidgetT *widget);
+WidgetT *widgetInit(WidgetT *widget);
+WidgetT *widgetNew(void);
#endif // WIDGET_H
diff --git a/client/src/gui/window.c b/client/src/gui/window.c
index 8acc383..9061453 100644
--- a/client/src/gui/window.c
+++ b/client/src/gui/window.c
@@ -70,8 +70,8 @@ WidgetT *windowInit(WidgetT *window, uint16_t x, uint16_t y, uint16_t w, uint16_
win->flags = 0;
win->title = NULL;
- win->base.marginX += _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 2;
- win->base.marginY += _guiMetric[METRIC_WINDOW_TITLE_GRAB_HEIGHT];
+ win->base.marginX += _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 6;
+ win->base.marginY += _guiMetric[METRIC_WINDOW_TITLE_GRAB_HEIGHT] + 1;
windowSetTitle(win, title);
diff --git a/client/src/main.c b/client/src/main.c
index 3d92e67..65e512d 100644
--- a/client/src/main.c
+++ b/client/src/main.c
@@ -18,6 +18,16 @@
*/
+/*
+ * To Do:
+ *
+ * - Fix function naming to be classItemVerb (checkboxValueGet instead of checkboxGetValue)
+ * - Replace any direct data manipulation from outside a class with methods to handle it
+ * - More widget states: Ghosted, hidden
+ *
+ */
+
+
#include "os.h"
#include "vesa.h"
#include "mouse.h"
@@ -25,11 +35,17 @@
#include "task.h"
#include "image.h"
#include "font.h"
+
#include "gui.h"
#include "widget.h"
#include "desktop.h"
#include "window.h"
#include "button.h"
+#include "label.h"
+#include "checkbox.h"
+#include "radio.h"
+#include "picture.h"
+#include "frame.h"
void buttonClick(WidgetT *widget) {
@@ -38,36 +54,73 @@ void buttonClick(WidgetT *widget) {
void test(void *data) {
- MouseT *mouse = NULL;
- ImageT *pointer = NULL;
- ColorT alpha;
- DesktopT *desktop = (DesktopT *)guiRootGet();
- WindowT *w1 = NULL;
- WindowT *w2 = NULL;
- WindowT *w3 = NULL;
- ButtonT *b1 = NULL;
- int8_t key = 0;
+ MouseT *mouse = NULL;
+ ImageT *pointer = NULL;
+ ColorT alpha;
+ DesktopT *desktop = (DesktopT *)guiRootGet();
+ WindowT *w1 = NULL;
+ WindowT *w2 = NULL;
+ WindowT *w3 = NULL;
+ ButtonT *b1 = NULL;
+ LabelT *l1 = NULL;
+ CheckboxT *c1 = NULL;
+ RadioT *r1a = NULL;
+ RadioT *r2a = NULL;
+ RadioT *r3a = NULL;
+ RadioT *r1b = NULL;
+ RadioT *r2b = NULL;
+ RadioT *r3b = NULL;
+ PictureT *p1 = NULL;
+ FrameT *f1 = NULL;
+ int8_t key = 0;
(void)data;
pointer = imageLoad("mouse.png");
alpha = imagePixelGet(pointer, 5, 0);
+ // Windows
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(W(desktop), W(w1));
+ w2 = windowNew(150, 150, 300, 200, "Window 2");
guiAttach(W(desktop), W(w2));
+ w3 = windowNew(300, 300, 300, 200, "Window 3");
guiAttach(W(desktop), W(w3));
- b1 = buttonNew(25, 25, "Test Button", buttonClick);
- guiAttach(W(w3), W(b1));
+ // Window 1
+ p1 = pictureNew(0, 0, "kanga.png");
+ guiAttach(W(w1), W(p1));
+
+ // Window 2
+ r1a = radioNew(10, 10, "Radio 1a", 1);
+ guiAttach(W(w2), W(r1a));
+ r2a = radioNew(20 + widgetGetWidth(W(r1a)), 10, "Radio 2a", 1);
+ guiAttach(W(w2), W(r2a));
+ r3a = radioNew(30 + widgetGetWidth(W(r1a)) + widgetGetWidth(W(r2a)), 10, "Radio 3a", 1);
+ guiAttach(W(w2), W(r3a));
+ r1b = radioNew(10, 35, "Radio 1b", 2);
+ guiAttach(W(w2), W(r1b));
+ r2b = radioNew(20 + widgetGetWidth(W(r1b)), 35, "Radio 2b", 2);
+ guiAttach(W(w2), W(r2b));
+ r3b = radioNew(30 + widgetGetWidth(W(r1b)) + widgetGetWidth(W(r2b)), 35, "Radio 3b", 2);
+ guiAttach(W(w2), W(r3b));
+ radioSetSelected(r1a);
+ radioSetSelected(r2b);
+
+ // Window 3
+ f1 = frameNew(10, 5, 175, 125, "Test Frame");
+ guiAttach(W(w3), W(f1));
+ b1 = buttonNew(10, 10, "Test Button", buttonClick);
+ guiAttach(W(f1), W(b1));
+ l1 = labelNew(10, 40, "Test Label");
+ guiAttach(W(f1), W(l1));
+ c1 = checkboxNew(10, 65, "Test Checkbox");
+ guiAttach(W(f1), W(c1));
do {
mouse = mouseRead();
if (keyHit()) {
key = keyASCII();
- logWrite("Key %d Scan %d Alt %d\n", key, keyScanCode(), keyAlt());
} else {
key = 0;
}