Basic UpDown widget added.

This commit is contained in:
Scott Duensing 2021-10-30 20:00:16 -05:00
parent da52b2f675
commit 9acf435c52
8 changed files with 363 additions and 3 deletions

View file

@ -44,6 +44,7 @@ INCLUDEPATH += \
HEADERS = \
$$LINUX_HEADERS \
src/gui/updown.h \
src/thirdparty/stb_ds.h \
src/thirdparty/stb_image.h \
src/thirdparty/memwatch/memwatch.h \
@ -73,6 +74,7 @@ HEADERS = \
SOURCES = \
$$LINUX_SOURCES \
src/gui/updown.c \
src/thirdparty/memwatch/memwatch.c \
src/gui/memory.c \
src/gui/array.c \

View file

@ -478,6 +478,10 @@ DesktopT *guiStartup(void) {
_guiMetric[METRIC_TEXTBOX_HORIZONTAL_PADDING] = 2;
_guiMetric[METRIC_TEXTBOX_VERTICAL_PADDING] = 2;
_guiMetric[METRIC_TEXTBOX_PADDING] = 6; // Matches other label / widget padding.
_guiMetric[METRIC_UPDOWN_HORIZONTAL_PADDING] = 2;
_guiMetric[METRIC_UPDOWN_VERTICAL_PADDING] = 2;
_guiMetric[METRIC_UPDOWN_PADDING] = 6; // Matches other label / widget padding.
_guiMetric[METRIC_UPDOWN_ARROW_PADDING] = 2;
_guiColor[COLOR_BUTTON_BACKGROUND] = vbeMakeColor(168, 168, 168);
_guiColor[COLOR_BUTTON_HIGHLIGHT] = vbeMakeColor(248, 252, 248);
@ -510,6 +514,13 @@ DesktopT *guiStartup(void) {
_guiColor[COLOR_TEXTBOX_SHADOW] = vbeMakeColor( 0, 0, 0);
_guiColor[COLOR_TEXTBOX_TEXT] = vbeMakeColor( 0, 0, 0);
_guiColor[COLOR_TEXTBOX_BACKGROUND] = vbeMakeColor(248, 252, 248);
_guiColor[COLOR_UPDOWN_HIGHLIGHT] = vbeMakeColor(248, 252, 248);
_guiColor[COLOR_UPDOWN_SHADOW] = vbeMakeColor( 0, 0, 0);
_guiColor[COLOR_UPDOWN_TEXT] = vbeMakeColor( 0, 0, 0);
_guiColor[COLOR_UPDOWN_BACKGROUND] = vbeMakeColor(248, 252, 248);
_guiColor[COLOR_UPDOWN_ARROWS_BACKGROUND] = vbeMakeColor(124, 126, 124);
_guiColor[COLOR_UPDOWN_ARROWS_ACTIVE] = vbeMakeColor(168, 168, 168);
_guiColor[COLOR_UPDOWN_ARROWS_INACTIVE] = vbeMakeColor( 80, 84, 80);
_guiFont = fontLoad("vga8x14.dat");

View file

@ -49,7 +49,7 @@ enum MagicE {
MAGIC_PICTURE,
MAGIC_FRAME,
MAGIC_TEXTBOX,
//MAGIC_UPDOWN,
MAGIC_UPDOWN,
//MAGIC_LISTBOX,
//MAGIC_TERMINAL,
MAGIC_COUNT
@ -68,6 +68,10 @@ enum MetricE {
METRIC_TEXTBOX_HORIZONTAL_PADDING,
METRIC_TEXTBOX_VERTICAL_PADDING,
METRIC_TEXTBOX_PADDING,
METRIC_UPDOWN_PADDING,
METRIC_UPDOWN_ARROW_PADDING,
METRIC_UPDOWN_HORIZONTAL_PADDING,
METRIC_UPDOWN_VERTICAL_PADDING,
METRIC_COUNT
};
@ -104,6 +108,13 @@ enum ColorE {
COLOR_TEXTBOX_SHADOW,
COLOR_TEXTBOX_TEXT,
COLOR_TEXTBOX_BACKGROUND,
COLOR_UPDOWN_HIGHLIGHT,
COLOR_UPDOWN_SHADOW,
COLOR_UPDOWN_TEXT,
COLOR_UPDOWN_BACKGROUND,
COLOR_UPDOWN_ARROWS_BACKGROUND,
COLOR_UPDOWN_ARROWS_ACTIVE,
COLOR_UPDOWN_ARROWS_INACTIVE,
COLOR_COUNT
};

View file

@ -24,8 +24,11 @@
#include "os.h"
#ifdef __linux__
#define MEMWATCH
#define MEMWATCH_STDIO
#endif
#include "memwatch/memwatch.h"

View file

@ -265,7 +265,7 @@ static void textboxPaint(WidgetT *widget, RectT pos) {
textX = pos.x + labelWidth + 2 + _guiMetric[METRIC_TEXTBOX_HORIZONTAL_PADDING];
textY = pos.y + 2 + _guiMetric[METRIC_TEXTBOX_VERTICAL_PADDING];
// Draw value. ***TODO*** This needs much more! Do it without strdup?
// Draw value.
draw = strdup(&t->value[t->offset]);
if (strlen(draw) > t->visible) draw[t->visible] = 0;
fontRender(_guiFont, draw, _guiColor[COLOR_TEXTBOX_TEXT], _guiColor[COLOR_TEXTBOX_BACKGROUND], textX, textY);

279
client/src/gui/updown.c Normal file
View file

@ -0,0 +1,279 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*
*/
#include "updown.h"
// 32 bit ranges from -2,147,483,648 to 2,147,483,647.
// Maximum character cells needed is 11 (10 digits + negative sign).
#define UPDOWN_MAX_DIGITS 11
// These are used in several places, so we made them global and just update as needed. See updownSizesRecalculate.
static uint16_t _labelWidth = 0;
static uint16_t _valueWidth = 0;
static uint16_t _arrowWidth = 0;
static uint16_t _arrowStart = 0;
static uint16_t _halfFont = 0;
static void updownDel(WidgetT **widget);
static void updownMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event);
static void updownPaint(WidgetT *widget, RectT pos);
static void updownSetVisible(UpdownT *updown);
static void updownSizesRecalculate(UpdownT *updown);
static void updownDel(WidgetT **widget) {
UpdownT *u = (UpdownT *)*widget;
if (u->title) free(u->title);
free(u);
u = NULL;
}
int32_t updownGetValue(UpdownT *updown) {
return updown->value;
}
WidgetT *updownInit(WidgetT *widget, int32_t min, int32_t max, int32_t step, char *title) {
UpdownT *u = (UpdownT *)widget;
u->base.delMethod = updownDel;
u->base.paintMethod = updownPaint;
u->base.mouseEventMethod = updownMouseEvent;
u->maximum = INT32_MAX;
u->minimum = INT32_MIN;
u->title = NULL;
u->value = 0;
updownSetTitle(u, title); // Needs to be set first so updownSizesRecalculate works.
updownSetMinimum(u, min);
updownSetMaximum(u, max);
updownSetStep(u, step);
return widget;
}
static void updownMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event) {
UpdownT *u = (UpdownT *)widget;
uint16_t o;
(void)x;
(void)y;
(void)mouse;
if (event == MOUSE_EVENT_LEFT_UP) {
updownSizesRecalculate(u);
_arrowStart += 1 + _guiMetric[METRIC_UPDOWN_HORIZONTAL_PADDING]; // Left of up arrow
o = 1 + _guiMetric[METRIC_UPDOWN_VERTICAL_PADDING]; // Top of arrows
// Did they click the up arrow?
if (x >= _arrowStart && x <= _arrowStart + fontHeightGet(_guiFont) && y >= o && y <= o + fontHeightGet(_guiFont)) {
// Can we increment?
if (u->value < u->maximum) {
// Clamp.
if (u->maximum - u->value < u->step) {
u->value = u->maximum;
} else {
u->value += u->step;
}
}
}
// Did they click the down arrow?
_arrowStart += fontHeightGet(_guiFont) + 1; // Left of down arrow
if (x >= _arrowStart && x <= _arrowStart + fontHeightGet(_guiFont) && y >= o && y <= o + fontHeightGet(_guiFont)) {
// Can we decrement?
if (u->value > u->minimum) {
// Clamp.
if (u->value - u->minimum < u->step) {
u->value = u->minimum;
} else {
u->value -= u->step;
}
}
}
// Ensure we redraw.
GUI_SET_FLAG(widget, WIDGET_FLAG_DIRTY);
}
}
UpdownT *updownNew(uint16_t x, uint16_t y, int32_t min, int32_t max, int32_t step, char *title) {
UpdownT *updown = (UpdownT *)malloc(sizeof(UpdownT));
WidgetT *widget = NULL;
uint16_t h = fontHeightGet(_guiFont) + 4 + (_guiMetric[METRIC_UPDOWN_VERTICAL_PADDING] * 2);
if (!updown) return NULL;
// Width is set in SetVisible.
widget = widgetInit(W(updown), MAGIC_UPDOWN, x, y, min, h, 0, 0, 0, 0);
if (!widget) {
free(updown);
return NULL;
}
updown = (UpdownT *)updownInit((WidgetT *)updown, min, max, step, title);
return updown;
}
static void updownPaint(WidgetT *widget, RectT pos) {
UpdownT *u = (UpdownT *)widget;
uint8_t i;
uint16_t textX;
uint16_t textY;
uint16_t o;
ColorT color;
char draw[UPDOWN_MAX_DIGITS + 1];
if (GUI_GET_FLAG(widget, WIDGET_FLAG_DIRTY)) {
updownSizesRecalculate(u);
_arrowStart += pos.x; // This method expects the start in screen space, not widget space.
// Draw title.
fontRender(_guiFont, u->title, _guiColor[COLOR_UPDOWN_TEXT], _guiColor[COLOR_WINDOW_BACKGROUND], pos.x, pos.y + 2 + _guiMetric[METRIC_UPDOWN_VERTICAL_PADDING]);
// Draw outline of text.
guiDrawHighlightFrame( pos.x + _labelWidth, pos.y, pos.x + _labelWidth + _valueWidth + 2, pos.y + pos.h, _guiColor[COLOR_UPDOWN_SHADOW], _guiColor[COLOR_UPDOWN_HIGHLIGHT]);
guiDrawRectangle( pos.x + _labelWidth + 1, pos.y + 1, pos.x + _labelWidth + _valueWidth + 1, pos.y + pos.h - 1, _guiColor[COLOR_WINDOW_BACKGROUND]);
// Draw text background.
guiDrawRectangleFilled(pos.x + _labelWidth + 2, pos.y + 2, pos.x + _labelWidth + _valueWidth, pos.y + pos.h - 2, _guiColor[COLOR_UPDOWN_BACKGROUND]);
// Draw arrows outline
guiDrawHighlightFrame(_arrowStart, pos.y, _arrowStart + _arrowWidth + 1, pos.y + pos.h, _guiColor[COLOR_UPDOWN_SHADOW], _guiColor[COLOR_UPDOWN_HIGHLIGHT]);
// Draw arrows background
guiDrawRectangleFilled(_arrowStart + 1, pos.y + 1, _arrowStart + _arrowWidth, pos.y + pos.h - 1, _guiColor[COLOR_UPDOWN_ARROWS_BACKGROUND]);
// Draw up arrow
_arrowStart += _halfFont + 1 + _guiMetric[METRIC_UPDOWN_HORIZONTAL_PADDING]; // Center of up arrow
o = pos.y + 1 + _guiMetric[METRIC_UPDOWN_VERTICAL_PADDING]; // Top of up arrow
color = u->value < u->maximum ? _guiColor[COLOR_UPDOWN_ARROWS_ACTIVE] : _guiColor[COLOR_UPDOWN_ARROWS_INACTIVE];
for (i=0; i<=fontHeightGet(_guiFont); i++) {
guiDrawLine(_arrowStart - i * 0.5, o + i, _arrowStart + i * 0.5, o + i, color);
}
guiDrawLine(_arrowStart, o, _arrowStart + _halfFont, o + fontHeightGet(_guiFont), _guiColor[COLOR_UPDOWN_SHADOW]);
guiDrawLine(_arrowStart - _halfFont, o + fontHeightGet(_guiFont), _arrowStart + _halfFont, o + fontHeightGet(_guiFont), _guiColor[COLOR_UPDOWN_SHADOW]);
guiDrawLine(_arrowStart, o, _arrowStart - _halfFont, o + fontHeightGet(_guiFont), _guiColor[COLOR_UPDOWN_HIGHLIGHT]);
// Draw down arrow
_arrowStart += fontHeightGet(_guiFont) + 1; // Center of down arrow
o += fontHeightGet(_guiFont); // Bottom of down arrow
color = u->value > u->minimum ? _guiColor[COLOR_UPDOWN_ARROWS_ACTIVE] : _guiColor[COLOR_UPDOWN_ARROWS_INACTIVE];
for (i=0; i<=fontHeightGet(_guiFont); i++) {
guiDrawLine(_arrowStart - i * 0.5, o - i, _arrowStart + i * 0.5, o - i, color);
}
guiDrawLine(_arrowStart, o, _arrowStart + _halfFont, o - fontHeightGet(_guiFont), _guiColor[COLOR_UPDOWN_SHADOW]);
guiDrawLine(_arrowStart - _halfFont, o - fontHeightGet(_guiFont), _arrowStart + _halfFont, o - fontHeightGet(_guiFont), _guiColor[COLOR_UPDOWN_HIGHLIGHT]);
guiDrawLine(_arrowStart, o, _arrowStart - _halfFont, o - fontHeightGet(_guiFont), _guiColor[COLOR_UPDOWN_HIGHLIGHT]);
// Where's the text display start?
textX = pos.x + _labelWidth + 2 + _guiMetric[METRIC_UPDOWN_HORIZONTAL_PADDING];
textY = pos.y + 2 + _guiMetric[METRIC_UPDOWN_VERTICAL_PADDING];
// Draw value.
snprintf(draw, UPDOWN_MAX_DIGITS, "%d", u->value);
textX += (u->visible - strlen(draw)) * fontWidthGet(_guiFont);
fontRender(_guiFont, draw, _guiColor[COLOR_UPDOWN_TEXT], _guiColor[COLOR_UPDOWN_BACKGROUND], textX, textY);
GUI_CLEAR_FLAG(widget, WIDGET_FLAG_DIRTY);
}
}
void updownSetMaximum(UpdownT *updown, int32_t maximum) {
if (maximum > updown->minimum) {
updown->maximum = maximum;
updownSetVisible(updown);
}
}
void updownSetMinimum(UpdownT *updown, int32_t minimum) {
if (minimum < updown->maximum) {
updown->minimum = minimum;
updownSetVisible(updown);
}
}
void updownSetStep(UpdownT *updown, int32_t step) {
updown->step = step;
}
void updownSetTitle(UpdownT *updown, char *title) {
if (updown->title) free(updown->title);
updown->title = strdup(title);
updownSetVisible(updown);
GUI_SET_FLAG((WidgetT *)updown, WIDGET_FLAG_DIRTY);
}
void updownSetValue(UpdownT *updown, int32_t value) {
if (value >= updown->minimum && value <= updown->maximum) {
updown->value = value;
GUI_SET_FLAG((WidgetT *)updown, WIDGET_FLAG_DIRTY);
}
}
static void updownSetVisible(UpdownT *updown) {
char digits[UPDOWN_MAX_DIGITS + 1];
int8_t maxWidth;
int8_t minWidth;
// Calculate how many characters we need to be able to display.
maxWidth = snprintf(digits, UPDOWN_MAX_DIGITS, "%d", updown->maximum);
minWidth = snprintf(digits, UPDOWN_MAX_DIGITS, "%d", updown->minimum);
updown->visible = maxWidth > minWidth ? maxWidth : minWidth;
// Calculate width of the widget.
updownSizesRecalculate(updown);
updown->base.pos.w = _arrowStart + _arrowWidth + 1;
}
static void updownSizesRecalculate(UpdownT *updown) {
// Label [ 0] ^v
// ====== - labelWidth, including space between label and text box.
// === - valueWidth (digits, padding, does not include border).
// === - arrowWidth (arrows, padding, 1px between arrows, and border).
// = - arrowStart (start of box surrounding arrows, including border).
// Set global stuff that isn't actually part of the widget state.
_halfFont = fontHeightGet(_guiFont) * 0.5;
_labelWidth = (strlen(updown->title) * fontWidthGet(_guiFont)) + _guiMetric[METRIC_UPDOWN_PADDING];
_valueWidth = (updown->visible * fontWidthGet(_guiFont)) + (_guiMetric[METRIC_UPDOWN_HORIZONTAL_PADDING] * 2);
_arrowWidth = (fontHeightGet(_guiFont) * 2) + 1 + 2 + (_guiMetric[METRIC_UPDOWN_HORIZONTAL_PADDING] * 2); // Arrow width = font height
_arrowStart = _labelWidth + _valueWidth + 2 + _guiMetric[METRIC_UPDOWN_ARROW_PADDING];
}

50
client/src/gui/updown.h Normal file
View file

@ -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 <https://www.gnu.org/licenses/>.
*
*/
#ifndef UPDOWN_H
#define UPDOWN_H
#include "gui.h"
#include "widget.h"
typedef struct UpdownS {
WidgetT base; // Must be first in every widget
char *title;
int32_t value;
int32_t maximum;
int32_t minimum;
int32_t step;
uint8_t visible; // How many characters are visible in control
} UpdownT;
int32_t updownGetValue(UpdownT *updown);
WidgetT *updownInit(WidgetT *widget, int32_t min, int32_t max, int32_t step, char *title);
UpdownT *updownNew(uint16_t x, uint16_t y, int32_t min, int32_t max, int32_t step, char *title);
void updownSetValue(UpdownT *updown, int32_t value);
void updownSetMaximum(UpdownT *updown, int32_t maximum);
void updownSetMinimum(UpdownT *updown, int32_t minimum);
void updownSetStep(UpdownT *updown, int32_t step);
void updownSetTitle(UpdownT *updown, char *title);
#endif // UPDOWN_H

View file

@ -26,7 +26,7 @@
* - 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
* - Focus events are firing for controls not in top level window
* - Metrics, colors, etc. should be defined in each widget and not in GUI
*
*/
@ -51,6 +51,7 @@
#include "picture.h"
#include "frame.h"
#include "textbox.h"
#include "updown.h"
void buttonClick(WidgetT *widget) {
@ -141,6 +142,7 @@ void test(void *data) {
FrameT *f1 = NULL;
TextboxT *t1 = NULL;
TextboxT *t2 = NULL;
UpdownT *u1 = NULL;
(void)data;
@ -177,6 +179,8 @@ void test(void *data) {
t2 = textboxNew(10, 85, 265, "Test Textbox");
textboxSetValue(t2, "Short string.");
guiAttach(W(w2), W(t2));
u1 = updownNew(10, 110, 0, 1024, 5, "UpDown");
guiAttach(W(w2), W(u1));
// Window 3
f1 = frameNew(10, 5, 175, 125, "Test Frame");