diff --git a/client/client.pro b/client/client.pro
index 5990656..d0debfc 100644
--- a/client/client.pro
+++ b/client/client.pro
@@ -52,6 +52,7 @@ HEADERS = \
src/gui/picture.h \
src/gui/radio.h \
src/gui/task.h \
+ src/gui/textbox.h \
src/thirdparty/stb_ds.h \
src/thirdparty/stb_leakcheck.h \
src/thirdparty/stb_image.h \
@@ -81,6 +82,7 @@ SOURCES = \
src/gui/picture.c \
src/gui/radio.c \
src/gui/task.c \
+ src/gui/textbox.c \
src/gui/widget.c \
src/gui/window.c \
src/gui/image.c \
diff --git a/client/src/gui/button.c b/client/src/gui/button.c
index 794bfce..9201616 100644
--- a/client/src/gui/button.c
+++ b/client/src/gui/button.c
@@ -49,7 +49,7 @@ WidgetT *buttonInit(WidgetT *button, uint16_t x, uint16_t y, char *title, widget
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);
+ b->base.h = fontHeightGet(_guiFont) + (_guiMetric[METRIC_BUTTON_VERTICAL_PADDING] * 2) + (_guiMetric[METRIC_BUTTON_BEZEL_SIZE] * 2);
return button;
}
@@ -116,10 +116,10 @@ static void buttonPaint(WidgetT *widget) {
}
// Draw background (depends on x from above).
- 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]);
+ guiDrawRectangleFilled(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] + active, b->base.y + i + _guiMetric[METRIC_BUTTON_VERTICAL_MARGIN] + active);
+ fontRender(_guiFont, b->title, _guiColor[COLOR_BUTTON_TEXT], _guiColor[COLOR_BUTTON_BACKGROUND], b->base.x + i + _guiMetric[METRIC_BUTTON_HORIZONTAL_PADDING] + active, b->base.y + i + _guiMetric[METRIC_BUTTON_VERTICAL_PADDING] + active);
GUI_CLEAR_FLAG(widget, WIDGET_FLAG_DIRTY);
}
@@ -134,6 +134,6 @@ void buttonSetClickHandler(ButtonT *button, widgetCallback 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);
+ button->base.w = (strlen(title) * fontWidthGet(_guiFont)) + (_guiMetric[METRIC_BUTTON_HORIZONTAL_PADDING] * 2) + (_guiMetric[METRIC_BUTTON_BEZEL_SIZE] * 2);
GUI_SET_FLAG((WidgetT *)button, WIDGET_FLAG_DIRTY);
}
diff --git a/client/src/gui/checkbox.c b/client/src/gui/checkbox.c
index 7344eca..a259d71 100644
--- a/client/src/gui/checkbox.c
+++ b/client/src/gui/checkbox.c
@@ -112,7 +112,7 @@ static void checkboxPaint(WidgetT *widget) {
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);
+ guiDrawRectangleFilled(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);
diff --git a/client/src/gui/gui.c b/client/src/gui/gui.c
index 0b2d1a5..9f3848c 100644
--- a/client/src/gui/gui.c
+++ b/client/src/gui/gui.c
@@ -39,6 +39,8 @@ WindowT *_guiActiveWindow = NULL;
static DesktopT *_guiDesktop = NULL;
static WidgetT *_guiLastWidgetLeft = NULL;
static uint8_t _guiLastWidgetLeftEvent = MOUSE_EVENT_NONE;
+static WidgetT *_guiFocused = NULL;
+
static uint8_t guiProcessMouseChildren(WidgetT *widget, MouseT *mouse);
@@ -202,13 +204,43 @@ void guiDrawLine(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT color) {
}
-void guiDrawFilledRectangle(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT pixel) {
+void guiDrawRectangle(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT color) {
+ guiDrawLine(x1, y1, x2, y1, color);
+ guiDrawLine(x2, y1, x2, y2, color);
+ guiDrawLine(x1, y2, x2, y2, color);
+ guiDrawLine(x1, y1, x1, y2, color);
+}
+
+
+void guiDrawRectangleFilled(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT color) {
uint16_t x;
uint16_t y;
for (y=y1; y<=y2; y++) {
for (x=x1; x<=x2; x++) {
- vbePutPixel(x, y, pixel);
+ vbePutPixel(x, y, color);
+ }
+ }
+}
+
+
+WidgetT *guiFocusGet(void) {
+ return _guiFocused;
+}
+
+
+void guiFocusSet(WidgetT *widget) {
+ // Did the focus change?
+ if (widget != _guiFocused) {
+ // Remove focus from current control.
+ if (_guiFocused) {
+ if (_guiFocused->focusMethod) _guiFocused->focusMethod(widget, 0);
+ }
+ // Change focus.
+ _guiFocused = widget;
+ // Tell new control it has focus.
+ if (_guiFocused) {
+ if (_guiFocused->focusMethod) _guiFocused->focusMethod(widget, 1);
}
}
}
@@ -225,10 +257,9 @@ 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) {
+ // Paint us. Widget handles dirty flag so they can animate if needed.
+ if (widget->paintMethod) {
widget->paintMethod(widget);
- GUI_CLEAR_FLAG(widget, WIDGET_FLAG_DIRTY);
}
// Paint all children.
@@ -246,13 +277,14 @@ void guiProcessMouse(MouseT *mouse) {
static uint8_t guiProcessMouseChildren(WidgetT *widget, MouseT *mouse) {
- size_t len;
- int16_t x;
- uint16_t mx;
- uint16_t my;
- uint16_t sx;
- uint16_t sy;
- uint8_t event = MOUSE_EVENT_NONE;
+ size_t len;
+ int16_t x;
+ uint16_t mx;
+ uint16_t my;
+ uint16_t sx;
+ uint16_t sy;
+ uint8_t event = MOUSE_EVENT_NONE;
+ static WidgetT *focusDown = NULL;
// Search children backwards for active widget before checking this widget.
len = arrlenu(widget->children);
@@ -278,6 +310,7 @@ static uint8_t guiProcessMouseChildren(WidgetT *widget, MouseT *mouse) {
// Is this the same widget we were over before?
if (_guiLastWidgetLeft != widget) {
+
// Tell previous widget we're moving out.
if (_guiLastWidgetLeft->mouseEventMethod) _guiLastWidgetLeft->mouseEventMethod(_guiLastWidgetLeft, mouse, mx, my, MOUSE_EVENT_OUT);
@@ -297,6 +330,10 @@ static uint8_t guiProcessMouseChildren(WidgetT *widget, MouseT *mouse) {
}
+ // If this is a left click, change focus to this new widget.
+ if (event == MOUSE_EVENT_LEFT_DOWN) focusDown = widget;
+ if (event == MOUSE_EVENT_LEFT_UP && widget == focusDown) guiFocusSet(widget);
+
_guiLastWidgetLeft = widget;
_guiLastWidgetLeftEvent = event;
@@ -344,14 +381,17 @@ void guiSetWidgetAndChildrenDirty(WidgetT *widget) {
DesktopT *guiStartup(void) {
- _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] + 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.
+ _guiMetric[METRIC_BUTTON_BEZEL_SIZE] = 2;
+ _guiMetric[METRIC_BUTTON_HORIZONTAL_PADDING] = 8;
+ _guiMetric[METRIC_BUTTON_VERTICAL_PADDING] = 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] + 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.
+ _guiMetric[METRIC_TEXTBOX_HORIZONTAL_PADDING] = 2;
+ _guiMetric[METRIC_TEXTBOX_VERTICAL_PADDING] = 2;
+ _guiMetric[METRIC_TEXTBOX_PADDING] = 6; // Matches other label / widget padding.
_guiColor[COLOR_BUTTON_BACKGROUND] = vbeMakeColor(168, 168, 168);
_guiColor[COLOR_BUTTON_HIGHLIGHT] = vbeMakeColor(248, 252, 248);
@@ -380,6 +420,10 @@ DesktopT *guiStartup(void) {
_guiColor[COLOR_FRAME_HIGHLIGHT] = vbeMakeColor(248, 252, 248);
_guiColor[COLOR_FRAME_SHADOW] = vbeMakeColor( 0, 0, 0);
_guiColor[COLOR_FRAME_TEXT] = vbeMakeColor( 0, 0, 0);
+ _guiColor[COLOR_TEXTBOX_HIGHLIGHT] = vbeMakeColor(248, 252, 248);
+ _guiColor[COLOR_TEXTBOX_SHADOW] = vbeMakeColor( 0, 0, 0);
+ _guiColor[COLOR_TEXTBOX_TEXT] = vbeMakeColor( 0, 0, 0);
+ _guiColor[COLOR_TEXTBOX_BACKGROUND] = vbeMakeColor(248, 252, 248);
_guiFont = fontLoad("vga8x14.dat");
diff --git a/client/src/gui/gui.h b/client/src/gui/gui.h
index 074a3cd..127cc09 100644
--- a/client/src/gui/gui.h
+++ b/client/src/gui/gui.h
@@ -47,7 +47,7 @@ enum MagicE {
MAGIC_RADIOBUTTON,
MAGIC_PICTURE,
MAGIC_FRAME,
- //MAGIC_TEXTBOX,
+ MAGIC_TEXTBOX,
//MAGIC_UPDOWN,
//MAGIC_LISTBOX,
MAGIC_COUNT
@@ -56,13 +56,16 @@ enum MagicE {
// Widget Metrics
enum MetricE {
METRIC_BUTTON_BEZEL_SIZE = 0,
- METRIC_BUTTON_HORIZONTAL_MARGIN,
- METRIC_BUTTON_VERTICAL_MARGIN,
+ METRIC_BUTTON_HORIZONTAL_PADDING,
+ METRIC_BUTTON_VERTICAL_PADDING,
METRIC_WINDOW_BORDER_WIDTH,
METRIC_WINDOW_TITLE_HEIGHT,
METRIC_WINDOW_TITLE_GRAB_HEIGHT,
METRIC_CHECKBOX_PADDING,
METRIC_RADIOBUTTON_PADDING,
+ METRIC_TEXTBOX_HORIZONTAL_PADDING,
+ METRIC_TEXTBOX_VERTICAL_PADDING,
+ METRIC_TEXTBOX_PADDING,
METRIC_COUNT
};
@@ -95,6 +98,10 @@ enum ColorE {
COLOR_FRAME_HIGHLIGHT,
COLOR_FRAME_SHADOW,
COLOR_FRAME_TEXT,
+ COLOR_TEXTBOX_HIGHLIGHT,
+ COLOR_TEXTBOX_SHADOW,
+ COLOR_TEXTBOX_TEXT,
+ COLOR_TEXTBOX_BACKGROUND,
COLOR_COUNT
};
@@ -127,8 +134,11 @@ void guiAttach(WidgetT *parent, WidgetT *child);
void guiComposite(void);
void guiDelete(WidgetT **widget);
void guiDrawHighlightFrame(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, ColorT upperLeft, ColorT lowerRight);
-void guiDrawLine(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT pixel);
-void guiDrawFilledRectangle(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT pixel);
+void guiDrawLine(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT color);
+void guiDrawRectangle(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT color);
+void guiDrawRectangleFilled(int16_t x1, int16_t y1, int16_t x2, int16_t y2, ColorT color);
+WidgetT *guiFocusGet(void);
+void guiFocusSet(WidgetT *widget);
void guiMousePositionOnWidgetGet(WidgetT *widget, MouseT *mouse, uint16_t *x, uint16_t *y);
void guiPaint(WidgetT *widget);
void guiProcessMouse(MouseT *mouse);
diff --git a/client/src/gui/textbox.c b/client/src/gui/textbox.c
new file mode 100644
index 0000000..087d0c0
--- /dev/null
+++ b/client/src/gui/textbox.c
@@ -0,0 +1,171 @@
+/*
+ * 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 "textbox.h"
+
+
+static void textboxFocusEvent(WidgetT *widget, uint8_t focused);
+static void textboxMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event);
+static void textboxPaint(WidgetT *widget);
+
+
+void textboxDel(WidgetT **widget) {
+ TextboxT *t = (TextboxT *)*widget;
+
+ if (t->title) free(t->title);
+ if (t->value) free(t->value);
+ free(t);
+ t = NULL;
+}
+
+
+static void textboxFocusEvent(WidgetT *widget, uint8_t focused) {
+
+}
+
+
+char *textboxGetValue(TextboxT *textbox) {
+ return textbox->value;
+}
+
+
+WidgetT *textboxInit(WidgetT *widget, uint16_t x, uint16_t y, uint16_t w, char *title) {
+ TextboxT *t = (TextboxT *)widget;
+
+ t->base.magic = MAGIC_TEXTBOX;
+ t->base.x = x;
+ t->base.y = y;
+ t->base.w = w;
+ t->base.h = fontHeightGet(_guiFont) + 4 + (_guiMetric[METRIC_TEXTBOX_VERTICAL_PADDING] * 2);
+ t->base.delMethod = textboxDel;
+ t->base.focusMethod = textboxFocusEvent;
+ t->base.paintMethod = textboxPaint;
+ t->base.mouseEventMethod = textboxMouseEvent;
+ t->title = NULL;
+ t->maxLength = 256;
+ t->value = (char *)malloc(t->maxLength);
+ t->caret = 0;
+
+ if (!t->value) return NULL;
+ t->value[0] = 0;
+
+ // Visible is set in textboxSetTitle
+ textboxSetTitle(t, title);
+
+ return widget;
+}
+
+
+static void textboxMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event) {
+ TextboxT *t = (TextboxT *)widget;
+
+ (void)x;
+ (void)y;
+ (void)mouse;
+
+ //***TODO*** Allow dragging text cursor with mouse.
+}
+
+
+TextboxT *textboxNew(uint16_t x, uint16_t y, uint16_t w, char *title) {
+ TextboxT *textbox = (TextboxT *)malloc(sizeof(TextboxT));
+ WidgetT *widget = NULL;
+
+ if (!textbox) return NULL;
+
+ widget = widgetInit((WidgetT *)textbox);
+ if (!widget) {
+ free(textbox);
+ return NULL;
+ }
+
+
+ if (!textbox) return NULL;
+
+ widget = widgetInit((WidgetT *)textbox);
+ if (!widget) {
+ free(textbox);
+ return NULL;
+ }
+
+ textbox = (TextboxT *)textboxInit((WidgetT *)textbox, x, y, w, title);
+
+ return textbox;
+}
+
+
+static void textboxPaint(WidgetT *widget) {
+ TextboxT *t = (TextboxT *)widget;
+ uint16_t labelWidth = (strlen(t->title) * fontWidthGet(_guiFont)) + _guiMetric[METRIC_TEXTBOX_PADDING];
+ uint16_t valueWidth = (t->visible * fontWidthGet(_guiFont)) + (_guiMetric[METRIC_TEXTBOX_HORIZONTAL_PADDING] * 2);
+ char *draw = NULL;
+
+ if (GUI_GET_FLAG(widget, WIDGET_FLAG_DIRTY)) {
+ vbeSurfaceSet(t->base.surface);
+
+ // Draw title.
+ fontRender(_guiFont, t->title, _guiColor[COLOR_TEXTBOX_TEXT], _guiColor[COLOR_WINDOW_BACKGROUND], t->base.x, t->base.y + 1 + _guiMetric[METRIC_TEXTBOX_VERTICAL_PADDING]);
+
+ // Draw outline of textbox.
+ guiDrawHighlightFrame( t->base.x + labelWidth, t->base.y, t->base.x + labelWidth + valueWidth + 2, t->base.y + t->base.h, _guiColor[COLOR_TEXTBOX_SHADOW], _guiColor[COLOR_TEXTBOX_HIGHLIGHT]);
+ guiDrawRectangle( t->base.x + labelWidth + 1, t->base.y + 1, t->base.x + labelWidth + valueWidth + 1, t->base.y + t->base.h - 1, _guiColor[COLOR_WINDOW_BACKGROUND]);
+
+ // Draw background.
+ guiDrawRectangleFilled(t->base.x + labelWidth + 2, t->base.y + 2, t->base.x + labelWidth + valueWidth, t->base.y + t->base.h - 2, _guiColor[COLOR_TEXTBOX_BACKGROUND]);
+
+ // Draw value. ***TODO*** This needs much more!
+ draw = strdup(t->value);
+ if (strlen(t->value) > t->visible) draw[t->visible] = 0;
+ fontRender(_guiFont, draw, _guiColor[COLOR_TEXTBOX_TEXT], _guiColor[COLOR_TEXTBOX_BACKGROUND], t->base.x + labelWidth + 2 + _guiMetric[METRIC_TEXTBOX_HORIZONTAL_PADDING], t->base.y + 2 + _guiMetric[METRIC_TEXTBOX_VERTICAL_PADDING]);
+ free(draw);
+
+ GUI_CLEAR_FLAG(widget, WIDGET_FLAG_DIRTY);
+ }
+}
+
+
+void textboxSetValue(TextboxT *textbox, char *value) {
+ // Copy it & truncate if needed.
+ strncpy(textbox->value, value, textbox->maxLength - 1);
+
+ GUI_SET_FLAG((WidgetT *)textbox, WIDGET_FLAG_DIRTY);
+}
+
+
+void textboxSetMaxLength(TextboxT *textbox, uint16_t length) {
+ char *temp = strdup(textbox->value);
+
+ free(textbox->value);
+ textbox->maxLength = length + 1;
+ textbox->value = (char *)malloc(textbox->maxLength);
+ textboxSetValue(textbox, temp);
+ free(temp);
+}
+
+
+void textboxSetTitle(TextboxT *textbox, char *title) {
+ if (textbox->title) free(textbox->title);
+ textbox->title = strdup(title);
+
+ // Figure out how many characters we have room to display.
+ textbox->visible = (textbox->base.w - ((strlen(title) * fontWidthGet(_guiFont)) + _guiMetric[METRIC_TEXTBOX_PADDING] + 4 + (_guiMetric[METRIC_TEXTBOX_HORIZONTAL_PADDING] * 2))) / fontWidthGet(_guiFont);
+
+ GUI_SET_FLAG((WidgetT *)textbox, WIDGET_FLAG_DIRTY);
+}
diff --git a/client/src/gui/textbox.h b/client/src/gui/textbox.h
new file mode 100644
index 0000000..85e9475
--- /dev/null
+++ b/client/src/gui/textbox.h
@@ -0,0 +1,50 @@
+/*
+ * 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 TEXTBOX_H
+#define TEXTBOX_H
+
+
+#include "gui.h"
+#include "widget.h"
+
+
+typedef struct TextboxS {
+ WidgetT base; // Must be first in every widget
+ char *title;
+ char *value;
+ uint16_t maxLength; // Maximum length of value + 1
+ uint16_t caret; // Cursor position in control (caret + offset = cursor positition in value)
+ uint16_t offset; // First character position to display in control
+ uint8_t visible; // How many characters are visible in control
+ uint8_t blink;
+} TextboxT;
+
+
+void textboxDel(WidgetT **widget);
+char *textboxGetValue(TextboxT *textbox);
+WidgetT *textboxInit(WidgetT *widget, uint16_t x, uint16_t y, uint16_t w, char *title);
+TextboxT *textboxNew(uint16_t x, uint16_t y, uint16_t w, char *title);
+void textboxSetValue(TextboxT *textbox, char *value);
+void textboxSetMaxLength(TextboxT *textbox, uint16_t length);
+void textboxSetTitle(TextboxT *textbox, char *title);
+
+
+#endif // TEXTBOX_H
diff --git a/client/src/gui/widget.c b/client/src/gui/widget.c
index ad78266..2c2bfcd 100644
--- a/client/src/gui/widget.c
+++ b/client/src/gui/widget.c
@@ -44,6 +44,7 @@ WidgetT *widgetInit(WidgetT *widget) {
widget->children = NULL;
widget->parent = NULL;
widget->delMethod = NULL;
+ widget->focusMethod = NULL;
widget->paintMethod = NULL;
widget->mouseEventMethod = NULL;
widget->userData = NULL;
diff --git a/client/src/gui/widget.h b/client/src/gui/widget.h
index edfb619..d5bc3b7 100644
--- a/client/src/gui/widget.h
+++ b/client/src/gui/widget.h
@@ -38,6 +38,7 @@ typedef struct WindowS WindowT;
typedef void (*widgetCallback)(struct WidgetS *widget);
typedef void (*widgetDelMethod)(struct WidgetS **widget);
+typedef void (*widgetFocusMethod)(struct WidgetS *widget, uint8_t focused);
typedef void (*widgetPaintMethod)(struct WidgetS *widget);
typedef void (*widgetMouseEventMethod)(struct WidgetS *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event);
@@ -56,6 +57,7 @@ typedef struct WidgetS {
WidgetT **children; // List of children
WidgetT *parent; // Parent of this widget
widgetDelMethod delMethod; // Delete method
+ widgetFocusMethod focusMethod; // Focus changed method
widgetPaintMethod paintMethod; // Paint method
widgetMouseEventMethod mouseEventMethod; // Mouse event handler
void *userData; // Anything the user wants to store
diff --git a/client/src/gui/window.c b/client/src/gui/window.c
index 9061453..5fb93ef 100644
--- a/client/src/gui/window.c
+++ b/client/src/gui/window.c
@@ -165,7 +165,7 @@ static void windowPaint(WidgetT *widget) {
// Title bar - METRIC_WINDOW_TITLE_HEIGHT pixels high. Be sure shadow and highlight are not included in the width.
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);
+ guiDrawRectangleFilled(_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);
fontRender(_guiFont, w->title, text, background, _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 16, _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 5);
diff --git a/client/src/main.c b/client/src/main.c
index 65e512d..09d6e9d 100644
--- a/client/src/main.c
+++ b/client/src/main.c
@@ -24,6 +24,8 @@
* - 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
+ * - Move setup math for paint events inside the dirty check
+ * - Methods that can change the width of a widget (such as setTitle) need to repaint the parent window as well
*
*/
@@ -46,6 +48,7 @@
#include "radio.h"
#include "picture.h"
#include "frame.h"
+#include "textbox.h"
void buttonClick(WidgetT *widget) {
@@ -72,6 +75,7 @@ void test(void *data) {
RadioT *r3b = NULL;
PictureT *p1 = NULL;
FrameT *f1 = NULL;
+ TextboxT *t1 = NULL;
int8_t key = 0;
(void)data;
@@ -106,6 +110,9 @@ void test(void *data) {
guiAttach(W(w2), W(r3b));
radioSetSelected(r1a);
radioSetSelected(r2b);
+ t1 = textboxNew(10, 60, 265, "Test Textbox");
+ textboxSetValue(t1, "Text to edit!");
+ guiAttach(W(w2), W(t1));
// Window 3
f1 = frameNew(10, 5, 175, 125, "Test Frame");