Listbox added! Improved keyboard features for Updown and Textbox. Random crash on exit somewhere.

This commit is contained in:
Scott Duensing 2021-11-01 20:49:01 -05:00
parent 1a5cc62422
commit 71c13fdc71
13 changed files with 696 additions and 51 deletions

View file

@ -32,7 +32,7 @@ BINDIR = bin
## CHANGE THIS ##
# CFLAGS, LDFLAGS, CPPFLAGS, PREFIX can be overriden on CLI
CFLAGS := $(DEBUG) -I$(SRCDIR) -I$(SRCDIR)/dos -I$(SRCDIR)/gui -I$(SRCDIR)/thirdparty
CFLAGS := $(DEBUG) -I$(SRCDIR) -I$(SRCDIR)/dos -I$(SRCDIR)/gui -I$(SRCDIR)/thirdparty -I$(SRCDIR)/thirdparty/memwatch
CPPFLAGS :=
LDFLAGS :=
PREFIX := /usr/local

View file

@ -18,7 +18,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
mkdir -p bin obj/dos obj/gui
mkdir -p bin obj/dos obj/gui obj/thirdparty/memwatch
source /opt/cross/djgpp/setenv
make -f Makefile.djgpp
rm bin/client

View file

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

View file

@ -482,45 +482,56 @@ DesktopT *guiStartup(void) {
_guiMetric[METRIC_UPDOWN_VERTICAL_PADDING] = 2;
_guiMetric[METRIC_UPDOWN_PADDING] = 6; // Matches other label / widget padding.
_guiMetric[METRIC_UPDOWN_ARROW_PADDING] = 2;
_guiMetric[METRIC_LISTBOX_HORIZONTAL_PADDING] = 2;
_guiMetric[METRIC_LISTBOX_VERTICAL_PADDING] = 2;
_guiColor[COLOR_BUTTON_BACKGROUND] = vbeMakeColor(168, 168, 168);
_guiColor[COLOR_BUTTON_HIGHLIGHT] = vbeMakeColor(248, 252, 248);
_guiColor[COLOR_BUTTON_SHADOW] = vbeMakeColor( 80, 84, 80);
_guiColor[COLOR_BUTTON_TEXT] = vbeMakeColor( 0, 0, 0);
_guiColor[COLOR_DESKTOP] = vbeMakeColor( 51, 153, 255);
_guiColor[COLOR_WINDOW_BACKGROUND] = vbeMakeColor(168, 168, 168);
_guiColor[COLOR_WINDOW_HIGHLIGHT] = vbeMakeColor(248, 252, 248);
_guiColor[COLOR_WINDOW_SHADOW] = vbeMakeColor( 80, 84, 80);
_guiColor[COLOR_WINDOW_TITLE_ACTIVE] = vbeMakeColor( 80, 84, 80);
_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);
_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);
_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);
_guiColor[COLOR_BUTTON_BACKGROUND] = vbeMakeColor(168, 168, 168);
_guiColor[COLOR_BUTTON_HIGHLIGHT] = vbeMakeColor(248, 252, 248);
_guiColor[COLOR_BUTTON_SHADOW] = vbeMakeColor( 80, 84, 80);
_guiColor[COLOR_BUTTON_TEXT] = vbeMakeColor( 0, 0, 0);
_guiColor[COLOR_DESKTOP] = vbeMakeColor( 51, 153, 255);
_guiColor[COLOR_WINDOW_BACKGROUND] = vbeMakeColor(168, 168, 168);
_guiColor[COLOR_WINDOW_HIGHLIGHT] = vbeMakeColor(248, 252, 248);
_guiColor[COLOR_WINDOW_SHADOW] = vbeMakeColor( 80, 84, 80);
_guiColor[COLOR_WINDOW_TITLE_ACTIVE] = vbeMakeColor( 80, 84, 80);
_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);
_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);
_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);
_guiColor[COLOR_LISTBOX_HIGHLIGHT] = vbeMakeColor(248, 252, 248);
_guiColor[COLOR_LISTBOX_SHADOW] = vbeMakeColor( 0, 0, 0);
_guiColor[COLOR_LISTBOX_TEXT] = vbeMakeColor( 0, 0, 0);
_guiColor[COLOR_LISTBOX_BACKGROUND] = vbeMakeColor(248, 252, 248);
_guiColor[COLOR_LISTBOX_SELECTED_TEXT] = vbeMakeColor(248, 252, 248);
_guiColor[COLOR_LISTBOX_SELECTED_BACKGROUND] = vbeMakeColor( 0, 0, 0);
_guiColor[COLOR_LISTBOX_ARROWS_BACKGROUND] = vbeMakeColor(124, 126, 124);
_guiColor[COLOR_LISTBOX_ARROWS_ACTIVE] = vbeMakeColor(168, 168, 168);
_guiColor[COLOR_LISTBOX_ARROWS_INACTIVE] = vbeMakeColor( 80, 84, 80);
_guiFont = fontLoad("vga8x14.dat");

View file

@ -50,7 +50,7 @@ enum MagicE {
MAGIC_FRAME,
MAGIC_TEXTBOX,
MAGIC_UPDOWN,
//MAGIC_LISTBOX,
MAGIC_LISTBOX,
//MAGIC_TERMINAL,
//MAGIC_DROPDOWN,
MAGIC_COUNT
@ -73,6 +73,8 @@ enum MetricE {
METRIC_UPDOWN_ARROW_PADDING,
METRIC_UPDOWN_HORIZONTAL_PADDING,
METRIC_UPDOWN_VERTICAL_PADDING,
METRIC_LISTBOX_HORIZONTAL_PADDING,
METRIC_LISTBOX_VERTICAL_PADDING,
METRIC_COUNT
};
@ -116,6 +118,15 @@ enum ColorE {
COLOR_UPDOWN_ARROWS_BACKGROUND,
COLOR_UPDOWN_ARROWS_ACTIVE,
COLOR_UPDOWN_ARROWS_INACTIVE,
COLOR_LISTBOX_HIGHLIGHT,
COLOR_LISTBOX_SHADOW,
COLOR_LISTBOX_TEXT,
COLOR_LISTBOX_BACKGROUND,
COLOR_LISTBOX_SELECTED_TEXT,
COLOR_LISTBOX_SELECTED_BACKGROUND,
COLOR_LISTBOX_ARROWS_BACKGROUND,
COLOR_LISTBOX_ARROWS_ACTIVE,
COLOR_LISTBOX_ARROWS_INACTIVE,
COLOR_COUNT
};

402
client/src/gui/listbox.c Normal file
View file

@ -0,0 +1,402 @@
/*
* 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 "listbox.h"
static uint16_t _halfFont = 0;
static uint8_t _visibleX = 0; // How many characters are visible in control
static uint8_t _visibleY = 0; // How many characters are visible in control
static uint16_t _valueWidth = 0;
static uint16_t _valueHeight = 0;
static uint16_t _valueTop = 0;
static uint16_t _valueBottom = 0;
static uint16_t _arrowWidth = 0;
static uint16_t _arrowStart = 0;
static void listboxDel(WidgetT **widget);
static void listboxMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event);
static void listboxKeyboardEvent(WidgetT *widget, uint8_t ascii, uint8_t extended, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt);
static void listboxPaint(WidgetT *widget, RectT pos);
static void listboxScrollUp(ListboxT *listbox);
static void listboxScrollDown(ListboxT *listbox);
static void listboxSizesRecalculate(ListboxT *listbox);
void listboxAddItem(ListboxT *listbox, char *item) {
arrput(listbox->values, strdup(item));
GUI_SET_FLAG((WidgetT *)listbox, WIDGET_FLAG_DIRTY);
}
static void listboxDel(WidgetT **widget) {
ListboxT *l = (ListboxT *)*widget;
size_t len = arrlenu(l->values);
size_t x;
if (len > 0) {
for (x=0; x<len; x++) {
free(l->values[x]);
}
}
if (l->title) free(l->title);
free(l);
l = NULL;
}
uint16_t listboxGetIndex(ListboxT *listbox) {
return listbox->selected;
}
char *listboxGetValue(ListboxT *listbox) {
return listbox->values[listbox->selected];
}
WidgetT *listboxInit(WidgetT *widget, char *title) {
ListboxT *l = (ListboxT *)widget;
l->base.delMethod = listboxDel;
l->base.keyboardEventMethod = listboxKeyboardEvent;
l->base.paintMethod = listboxPaint;
l->base.mouseEventMethod = listboxMouseEvent;
l->title = NULL;
l->values = NULL;
l->selected = 0;
l->step = 1;
l->offset = 0;
// Visibles are set in listboxSetTitle
listboxSetTitle(l, title);
return widget;
}
static void listboxKeyboardEvent(WidgetT *widget, uint8_t ascii, uint8_t extended, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt) {
ListboxT *l = (ListboxT *)widget;
int16_t len = arrlenu(l->values);
uint16_t i;
(void)scancode;
(void)shift;
(void)control;
(void)alt;
listboxSizesRecalculate(l);
if (extended) {
switch (ascii) {
case 71: // HOME
l->selected = 0;
l->offset = 0;
GUI_SET_FLAG(widget, WIDGET_FLAG_DIRTY);
break;
case 79: // END
// Are there more items than visible?
if (len > _visibleY - 1) {
// Yep. Scroll & set selected.
l->selected = _visibleY - 2;
l->offset = len - _visibleY + 1;
} else {
// No. Just set selected.
l->selected = len - 1;
l->offset = 0;
}
GUI_SET_FLAG(widget, WIDGET_FLAG_DIRTY);
break;
case 73: // PAGEUP
for (i=0; i<l->step; i++) listboxScrollUp(l);
break;
case 81: // PAGEDOWN
for (i=0; i<l->step; i++) listboxScrollDown(l);
break;
case 72: // UP
listboxScrollUp(l);
break;
case 80: // DOWN
listboxScrollDown(l);
break;
} // switch
} // extended
}
static void listboxMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event) {
ListboxT *l = (ListboxT *)widget;
uint16_t o;
uint16_t valueStart;
int16_t len;
(void)x;
(void)y;
(void)mouse;
if (event == MOUSE_EVENT_LEFT_UP) {
listboxSizesRecalculate(l);
len = arrlenu(l->values);
// Clicked item in listbox.
valueStart = 1 + _guiMetric[METRIC_LISTBOX_HORIZONTAL_PADDING];
if (x > valueStart && x < valueStart + _valueWidth - 1 && y > _valueTop + 1 && y < _valueBottom - 1) {
o = (y - _valueTop) / fontHeightGet(_guiFont);
if (o < len) l->selected = o;
if (l->selected > _visibleY - 2) l->selected = _visibleY - 2; // Two because 1 is to correct _visibleY and the other 1 is because selected is zero based.
GUI_SET_FLAG(widget, WIDGET_FLAG_DIRTY);
}
_arrowStart += 1 + _guiMetric[METRIC_LISTBOX_HORIZONTAL_PADDING]; // Left of up arrow
o = _valueTop + 1 + _guiMetric[METRIC_LISTBOX_VERTICAL_PADDING]; // Top of arrows
// Did they click the up arrow?
if (x >= _arrowStart && x <= _arrowStart + fontHeightGet(_guiFont) && y >= o && y <= o + fontHeightGet(_guiFont)) {
listboxScrollUp(l);
}
// Did they click the down arrow?
o = _valueBottom - 1 - _guiMetric[METRIC_LISTBOX_VERTICAL_PADDING]; // Bottom of down arrow
if (x >= _arrowStart && x <= _arrowStart + fontHeightGet(_guiFont) && y >= o - fontHeightGet(_guiFont) && y <= o) {
listboxScrollDown(l);
}
} // MOUSE_UP
}
ListboxT *listboxNew(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *title) {
ListboxT *listbox = (ListboxT *)malloc(sizeof(ListboxT));
WidgetT *widget = NULL;
if (!listbox) return NULL;
widget = widgetInit(W(listbox), MAGIC_LISTBOX, x, y, w, h, 0, 0, 0, 0);
if (!widget) {
free(listbox);
return NULL;
}
listbox = (ListboxT *)listboxInit((WidgetT *)listbox, title);
return listbox;
}
static void listboxPaint(WidgetT *widget, RectT pos) {
ListboxT *l = (ListboxT *)widget;
uint16_t items;
uint16_t o;
uint16_t i;
int16_t len;
ColorT color;
if (GUI_GET_FLAG(widget, WIDGET_FLAG_DIRTY)) {
listboxSizesRecalculate(l);
// Move a few things into screen space, not widget space.
_arrowStart += pos.x;
_valueTop += pos.y;
_valueBottom += pos.y;
len = arrlenu(l->values);
// How many items can we draw?
items = len - l->offset;
if (items > _visibleY - 1) items = _visibleY - 1;
// Draw title.
fontRender(_guiFont, l->title, _guiColor[COLOR_LISTBOX_TEXT], _guiColor[COLOR_WINDOW_BACKGROUND], pos.x, pos.y);
// Draw outline of listbox.
guiDrawHighlightFrame(pos.x, _valueTop, pos.x + _valueWidth, _valueBottom, _guiColor[COLOR_LISTBOX_SHADOW], _guiColor[COLOR_LISTBOX_HIGHLIGHT]);
// Draw background of listbox.
guiDrawRectangleFilled(pos.x + 1, _valueTop + 1, pos.x + _valueWidth - 1, _valueBottom - 1, _guiColor[COLOR_LISTBOX_BACKGROUND]);
// Draw listbox contents.
o = _valueTop + 1 + _guiMetric[METRIC_LISTBOX_VERTICAL_PADDING];
for (i=0; i<items; i++) {
if (i == l->selected) {
guiDrawRectangleFilled(pos.x + _guiMetric[METRIC_LISTBOX_HORIZONTAL_PADDING], o, pos.x + _valueWidth - _guiMetric[METRIC_LISTBOX_HORIZONTAL_PADDING], o + fontHeightGet(_guiFont) - 1, _guiColor[COLOR_LISTBOX_SELECTED_BACKGROUND]);
fontRender(_guiFont, l->values[l->offset + i], _guiColor[COLOR_LISTBOX_SELECTED_TEXT], _guiColor[COLOR_LISTBOX_SELECTED_BACKGROUND], pos.x + 1 + _guiMetric[METRIC_LISTBOX_HORIZONTAL_PADDING], o);
} else {
fontRender(_guiFont, l->values[l->offset + i], _guiColor[COLOR_LISTBOX_TEXT], _guiColor[COLOR_LISTBOX_BACKGROUND], pos.x + 1 + _guiMetric[METRIC_LISTBOX_HORIZONTAL_PADDING], o);
}
o += fontHeightGet(_guiFont);
}
// Draw outline of arrows.
guiDrawHighlightFrame(_arrowStart, _valueTop, _arrowStart + _arrowWidth, _valueBottom, _guiColor[COLOR_LISTBOX_SHADOW], _guiColor[COLOR_LISTBOX_HIGHLIGHT]);
// Draw background of arrows.
guiDrawRectangleFilled(_arrowStart + 1, _valueTop + 1, _arrowStart + _arrowWidth - 1, _valueBottom - 1, _guiColor[COLOR_LISTBOX_ARROWS_BACKGROUND]);
// Draw up arrow
_arrowStart += _halfFont + 1 + _guiMetric[METRIC_LISTBOX_HORIZONTAL_PADDING]; // Center of up arrow
o = _valueTop + 1 + _guiMetric[METRIC_LISTBOX_VERTICAL_PADDING]; // Top of up arrow
color = l->offset + l->selected > 0 ? _guiColor[COLOR_LISTBOX_ARROWS_ACTIVE] : _guiColor[COLOR_LISTBOX_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_LISTBOX_SHADOW]);
guiDrawLine(_arrowStart - _halfFont, o + fontHeightGet(_guiFont), _arrowStart + _halfFont, o + fontHeightGet(_guiFont), _guiColor[COLOR_LISTBOX_SHADOW]);
guiDrawLine(_arrowStart, o, _arrowStart - _halfFont, o + fontHeightGet(_guiFont), _guiColor[COLOR_LISTBOX_HIGHLIGHT]);
// Draw down arrow
o = _valueBottom - 1 - _guiMetric[METRIC_LISTBOX_VERTICAL_PADDING]; // Bottom of down arrow
color = l->offset + l->selected < len - 1 ? _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]);
GUI_CLEAR_FLAG(widget, WIDGET_FLAG_DIRTY);
}
}
void listboxRemoveItem(ListboxT *listbox, char *item) {
size_t len = arrlenu(listbox->values);
size_t x;
if (len > 0) {
for (x=0; x<len; x++) {
if (strcmp(item, listbox->values[x]) == 0) {
arrdel(listbox->values, x);
if (listbox->selected > len || listbox->selected > 0) listbox->selected--;
GUI_SET_FLAG((WidgetT *)listbox, WIDGET_FLAG_DIRTY);
break;
}
}
}
}
static void listboxScrollUp(ListboxT *listbox) {
// Can we decrement?
if (listbox->selected > 0) {
listbox->selected--;
GUI_SET_FLAG((WidgetT *)listbox, WIDGET_FLAG_DIRTY);
} else {
// Can we scroll the list up?
if (listbox->offset > 0) {
listbox->offset--;
GUI_SET_FLAG((WidgetT *)listbox, WIDGET_FLAG_DIRTY);
}
}
}
static void listboxScrollDown(ListboxT *listbox) {
int16_t len = arrlenu(listbox->values);
// Can we increment?
if (listbox->selected < _visibleY - 2) {
// Is it off the end of the list?
if (listbox->offset + listbox->selected < len - 1) {
listbox->selected++;
GUI_SET_FLAG((WidgetT *)listbox, WIDGET_FLAG_DIRTY);
}
} else {
// Can we scroll the list down?
if (listbox->offset + listbox->selected < len - 1) {
listbox->offset++;
GUI_SET_FLAG((WidgetT *)listbox, WIDGET_FLAG_DIRTY);
}
}
}
void listboxSetIndex(ListboxT *listbox, uint16_t index) {
if (index < arrlenu(listbox->values)) {
listbox->selected = index;
GUI_SET_FLAG((WidgetT *)listbox, WIDGET_FLAG_DIRTY);
}
}
void listboxSetStep(ListboxT *listbox, int32_t step) {
listbox->step = step;
}
void listboxSetTitle(ListboxT *listbox, char *title) {
if (listbox->title) free(listbox->title);
listbox->title = strdup(title);
listboxSizesRecalculate(listbox);
GUI_SET_FLAG((WidgetT *)listbox, WIDGET_FLAG_DIRTY);
}
void listboxSetValue(ListboxT *listbox, char *value) {
size_t len = arrlenu(listbox->values);
size_t x;
if (len > 0) {
for (x=0; x<len; x++) {
if (strcmp(value, listbox->values[x]) == 0) {
listbox->selected = x;
GUI_SET_FLAG((WidgetT *)listbox, WIDGET_FLAG_DIRTY);
break;
}
}
}
}
static void listboxSizesRecalculate(ListboxT *listbox) {
// Arrow box width is border + (hpadding*2) + fontHeight
// Label is drawn at x,y
// One line horizontal gap between label and border
// One line vertical gap between value and arrow box
// Value border is drawn at x+1, y+2 to x+w-2-arrowWidth, y+h-2
// Value is x+1+hpadding, y+2+vpadding to x+w-1-(hpadding*2)-arrowWidth, y+h-2-(vpadding*2)
// arrowStart is x+1+valueWidth
// Set global stuff that isn't actually part of the widget state.
_halfFont = fontHeightGet(_guiFont) * 0.5;
_arrowWidth = 2 + (_guiMetric[METRIC_LISTBOX_HORIZONTAL_PADDING] * 2) + fontHeightGet(_guiFont); // Arrow width = font height
_valueWidth = listbox->base.pos.w - 1 - 1 - (_guiMetric[METRIC_LISTBOX_HORIZONTAL_PADDING] * 2) - _arrowWidth;
_valueHeight = listbox->base.pos.h - 2 - (_guiMetric[METRIC_LISTBOX_VERTICAL_PADDING] * 2);
_valueTop = fontHeightGet(_guiFont) + 1;
_valueBottom = _valueHeight;
_arrowStart = 2 + _valueWidth;
// Figure out how many characters we have room to display.
_visibleX = (_valueWidth - 2 + (_guiMetric[METRIC_LISTBOX_HORIZONTAL_PADDING] * 2)) / fontWidthGet(_guiFont);
_visibleY = (_valueHeight - 2 + (_guiMetric[METRIC_LISTBOX_VERTICAL_PADDING] * 2)) / fontHeightGet(_guiFont);
}

51
client/src/gui/listbox.h Normal file
View file

@ -0,0 +1,51 @@
/*
* 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 LISTBOX_H
#define LISTBOX_H
#include "gui.h"
#include "widget.h"
typedef struct ListboxS {
WidgetT base; // Must be first in every widget
char *title;
char **values;
uint16_t selected; // values[offset + selected]
int32_t step;
int32_t offset;
} ListboxT;
void listboxAddItem(ListboxT *listbox, char *item);
uint16_t listboxGetIndex(ListboxT *listbox);
char *listboxGetValue(ListboxT *listbox);
WidgetT *listboxInit(WidgetT *widget, char *title);
ListboxT *listboxNew(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *title);
void listboxRemoveItem(ListboxT *listbox, char *item);
void listboxSetIndex(ListboxT *listbox, uint16_t index);
void listboxSetStep(ListboxT *listbox, int32_t step);
void listboxSetTitle(ListboxT *listbox, char *title);
void listboxSetValue(ListboxT *listbox, char *value);
#endif // LISTBOX_H

View file

@ -80,6 +80,7 @@ static void textboxKeyboardEvent(WidgetT *widget, uint8_t ascii, uint8_t extende
TextboxT *t = (TextboxT *)widget;
uint16_t x;
char *temp;
(void)scancode;
(void)shift;
@ -89,6 +90,18 @@ static void textboxKeyboardEvent(WidgetT *widget, uint8_t ascii, uint8_t extende
if (extended) {
switch (ascii) {
case 71: // HOME
t->caret = 0;
t->offset = 0;
break;
case 79: // END
// We cheat and just reset the value. That moves the offset and cursor.
temp = strdup(t->value);
textboxSetValue(t, temp);
free(temp);
break;
case 75: // LEFT
// Can we move left in the value?
if (t->caret + t->offset > 0) {

View file

@ -92,6 +92,7 @@ static void updownKeyboardEvent(WidgetT *widget, uint8_t ascii, uint8_t extended
UpdownT *u = (UpdownT *)widget;
int32_t temp;
static uint8_t willBeNegative = 0;
(void)scancode;
(void)shift;
@ -101,16 +102,41 @@ static void updownKeyboardEvent(WidgetT *widget, uint8_t ascii, uint8_t extended
if (extended) {
switch (ascii) {
case 71: // HOME
u->value = u->minimum;
willBeNegative = 0;
break;
case 79: // END
u->value = u->maximum;
willBeNegative = 0;
break;
case 73: // PAGEUP
u->value += u->step;
if (u->value > u->maximum) u->value = u->maximum;
willBeNegative = 0;
break;
case 81: // PAGEDOWN
u->value -= u->step;
if (u->value < u->minimum) u->value = u->minimum;
willBeNegative = 0;
break;
case 72: // UP
if (u->value < u->maximum) u->value++;
willBeNegative = 0;
break;
case 80: // DOWN
if (u->value > u->minimum) u->value--;
willBeNegative = 0;
break;
case 83: // DELETE
u->value = (int)(u->value * 0.1);
willBeNegative = 0;
break;
} // switch
@ -120,13 +146,20 @@ static void updownKeyboardEvent(WidgetT *widget, uint8_t ascii, uint8_t extended
switch (ascii) {
case 8: // BACKSPACE
u->value = (int)(u->value * 0.1);
willBeNegative = 0;
break;
case '-': // Leading Minus
if (u->value == 0) willBeNegative = 1;
break;
default: // Number keys
if (ascii >= '0' && ascii <= '9') {
temp = u->value * 10 + (ascii - '0');
if (temp <= u->maximum) u->value = temp;
if (willBeNegative) u->value = -u->value;
if (temp <= u->maximum && temp >= u->minimum) u->value = temp;
}
willBeNegative = 0;
break;
} // switch
@ -159,6 +192,7 @@ static void updownMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_
} else {
u->value += u->step;
}
GUI_SET_FLAG(widget, WIDGET_FLAG_DIRTY);
}
}
@ -173,11 +207,10 @@ static void updownMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_
} else {
u->value -= u->step;
}
GUI_SET_FLAG(widget, WIDGET_FLAG_DIRTY);
}
}
// Ensure we redraw.
GUI_SET_FLAG(widget, WIDGET_FLAG_DIRTY);
}
}
@ -260,7 +293,7 @@ static void updownPaint(WidgetT *widget, RectT pos) {
textY = pos.y + 2 + _guiMetric[METRIC_UPDOWN_VERTICAL_PADDING];
// Draw value.
snprintf(draw, UPDOWN_MAX_DIGITS, "%d", u->value);
snprintf(draw, UPDOWN_MAX_DIGITS, "%ld", (long)u->value); // Weird typecasting prevents warnings in both DOS and Linux (32 and 64 bit)
textX += (u->visible - strlen(draw)) * fontWidthGet(_guiFont);
fontRender(_guiFont, draw, _guiColor[COLOR_UPDOWN_TEXT], _guiColor[COLOR_UPDOWN_BACKGROUND], textX, textY);
@ -318,8 +351,8 @@ static void updownSetVisible(UpdownT *updown) {
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);
maxWidth = snprintf(digits, UPDOWN_MAX_DIGITS, "%ld", (long)updown->maximum); // Weird typecasting prevents warnings in both DOS and Linux (32 and 64 bit)
minWidth = snprintf(digits, UPDOWN_MAX_DIGITS, "%ld", (long)updown->minimum); // Weird typecasting prevents warnings in both DOS and Linux (32 and 64 bit)
updown->visible = maxWidth > minWidth ? maxWidth : minWidth;

View file

@ -105,11 +105,15 @@ uint8_t keyHit(void) {
// Fix mappings to match DOS bioskey() call.
if (_scanCodeKeep == SDL_SCANCODE_ESCAPE) _ASCIIKeep = 27;
if (_scanCodeKeep == SDL_SCANCODE_BACKSPACE) _ASCIIKeep = 8;
if (_scanCodeKeep == SDL_SCANCODE_DELETE) { _extended = 1; _ASCIIKeep = 83; }
if (_scanCodeKeep == SDL_SCANCODE_HOME) { _extended = 1; _ASCIIKeep = 71; }
if (_scanCodeKeep == SDL_SCANCODE_UP) { _extended = 1; _ASCIIKeep = 72; }
if (_scanCodeKeep == SDL_SCANCODE_PAGEUP) { _extended = 1; _ASCIIKeep = 73; }
if (_scanCodeKeep == SDL_SCANCODE_LEFT) { _extended = 1; _ASCIIKeep = 75; }
if (_scanCodeKeep == SDL_SCANCODE_RIGHT) { _extended = 1; _ASCIIKeep = 77; }
if (_scanCodeKeep == SDL_SCANCODE_UP) { _extended = 1; _ASCIIKeep = 72; }
if (_scanCodeKeep == SDL_SCANCODE_END) { _extended = 1; _ASCIIKeep = 79; }
if (_scanCodeKeep == SDL_SCANCODE_DOWN) { _extended = 1; _ASCIIKeep = 80; }
if (_scanCodeKeep == SDL_SCANCODE_PAGEDOWN) { _extended = 1; _ASCIIKeep = 81; }
if (_scanCodeKeep == SDL_SCANCODE_DELETE) { _extended = 1; _ASCIIKeep = 83; }
}
return result;

View file

@ -24,14 +24,21 @@
* - 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
* - Metrics, colors, etc. should be defined in each widget and not in GUI
* - UpDown is kinda lame
* - Widgets should support a "changed" callback that can cancel the change
* - Add pgup/pgdn to UpDown
* - Add home/end to Textbox
* - Move drawing and surface code into it's own file
* - Use LabelT in all widgets that have a label
* - Find a light grey to replace white widget data areas
* - No thumb in listbox scrollbar
*
* - Random crash after adding listbox
*/
#include "stddclmr.h"
#include "os.h"
#include "vesa.h"
#include "mouse.h"
@ -53,6 +60,7 @@
#include "frame.h"
#include "textbox.h"
#include "updown.h"
#include "listbox.h"
void buttonClick(WidgetT *widget) {
@ -144,6 +152,7 @@ void test(void *data) {
TextboxT *t1 = NULL;
TextboxT *t2 = NULL;
UpdownT *u1 = NULL;
ListboxT *lb1 = NULL;
(void)data;
@ -158,6 +167,19 @@ void test(void *data) {
// Window 1
p1 = pictureNew(0, 0, "kanga.png");
guiAttach(W(w1), W(p1));
lb1 = listboxNew(155, 10, 120, 140, "List Box");
listboxAddItem(lb1, "One");
listboxAddItem(lb1, "Two");
listboxAddItem(lb1, "Three");
listboxAddItem(lb1, "Four");
listboxAddItem(lb1, "Five");
listboxAddItem(lb1, "Six");
listboxAddItem(lb1, "Seven");
listboxAddItem(lb1, "Eight");
listboxAddItem(lb1, "Nine");
listboxAddItem(lb1, "Ten");
listboxSetStep(lb1, 3);
guiAttach(W(w1), W(lb1));
// Window 2
r1a = radioNew(10, 10, "Radio 1a", 1);

95
client/src/stddclmr.h Normal file
View file

@ -0,0 +1,95 @@
#ifndef STDDCLMR_H
#define STDDCLMR_H
/*
Action figures sold separately. Add toner. All models over 18 years of age.
All rights reserved. Allow four to six weeks for delivery. An equal
opportunity employer. Any resemblance to actual persons, living or dead, is
unintentional and purely coincidental. Apply only to affected area. Approved
for veterans. As seen on TV. At participating locations only. Avoid contact
with mucous membranes. Avoid contact with skin. Avoid extreme temperatures
and store in a cool dry place. Batteries not included. Be sure each item is
properly endorsed. Beware of dog. Booths for two or more. Breaking seal
constitutes acceptance of agreement. Call toll free number before digging.
Caveat emptor. Check here if tax deductible. Close cover before striking
Colors may fade. Contains a substantial amount of non-tobacco ingredients.
Contents may settle during shipment. Contestants have been briefed on some
questions before the show. Copyright 1995 Joker's Wild. Disclaimer does
not cover hurricane, lightning, tornado, tsunami, volcanic eruption,
earthquake, flood, and other Acts of God, misuse, neglect, unauthorized
repair, damage from improper installation, broken antenna or marred cabinet,
incorrect line voltage, missing or altered serial numbers, sonic boom
vibrations, electromagnetic radiation from nuclear blasts, customer
adjustments that are not covered in the joke list, and incidents owing to
airplane crash, ship sinking, motor vehicle accidents, leaky roof, broken
glass, falling rocks, mud slides, forest fire, flying projectiles, or
dropping the item. Do not bend, fold, mutilate, or spindle. Do not place
near flammable or magnetic source. Do not puncture, incinerate, or store
above 120 degrees Fahrenheit. Do not stamp. Use other side for additional
listings. Do not use while operating a motor vehicle or heavy equipment. Do
not write below this line. Documents are provided "as is" without any
warranties expressed or implied. Don't quote me on anything. Don't quote me
on that. Driver does not carry cash. Drop in any mailbox. Edited for
television. Employees and their families are not eligible. Falling rock.
First pull up, then pull down. Flames redirected to /dev/null. For a
limited time only. For external use only. For off-road use only. For office
use only. For recreational use only. Do not disturb. Freshest if eaten
before date on carton. Hand wash only, tumble dry on low heat. If a rash,
redness, irritation, or swelling develops, discontinue use. If condition
persists, consult your physician. If defects are discovered, do not attempt
to fix them yourself, but return to an authorized service center. If
ingested, do not induce vomiting, if symptoms persist, consult a doctor.
Keep away from open flames and avoid inhaling fumes. Keep away from
sunlight, pets, and small children. Keep cool; process promptly. Limit
one-per-family please. Limited time offer, call now to ensure prompt
delivery. List at least two alternate dates. List each check separately by
bank number. List was current at time of printing. Lost ticket pays maximum
rate. May be too intense for some viewers. Must be 18 to enter. No Canadian
coins. No alcohol, dogs or horses. No anchovies unless otherwise specified.
No animals were harmed in the production of these documents. No money down.
No other warranty expressed or implied. No passes accepted for this
engagement. No postage necessary if mailed in the United States. No
preservatives added. No purchase necessary. No salt, MSG, artificial color
or flavor added. No shoes, no shirt, no service, no kidding. No solicitors.
No substitutions allowed. No transfers issued until the bus comes to a
complete stop. No user-serviceable parts inside. Not affiliated with the
American Red Cross. Not liable for damages due to use or misuse. Not
recommended for children. Not responsible for direct, indirect, incidental
or consequential damages resulting from any defect, error or failure to
perform. Not the Beatles. Objects in mirror may be closer than they appear.
One size fits all. Many suitcases look alike. Other copyright laws for
specific entries apply wherever noted. Other restrictions may apply. Package
sold by weight, not volume. Parental advisory - explicit lyrics. Penalty for
private use. Place stamp here. Please remain seated until the ride has come
to a complete stop. Possible penalties for early withdrawal. Post office will
not deliver without postage. Postage will be paid by addressee. Prerecorded
for this time zone. Price does not include taxes. Processed at location
stamped in code at top of carton. Quantities are limited while supplies last.
Read at your own risk. Record additional transactions on back of previous
stub. Replace with same type. Reproduction strictly prohibited. Restaurant
package, not for resale. Return to sender, no forwarding order on file,
unable to forward. Safety goggles may be required during use. Sanitized for
your protection. Sealed for your protection, do not use if the safety seal is
broken. See label for sequence. Shading within a garment may occur. Sign here
without admitting guilt. Simulated picture. Slightly enlarged to show detail.
Slightly higher west of the Rockies. Slippery when wet. Smoking these may be
hazardous to your health. Some assembly required. Some equipment shown is
optional. Some of the trademarks mentioned in this product appear for
identification purposes only. Subject to FCC approval. Subject to change
without notice. Substantial penalty for early withdrawal. Text may contain
material some readers may find objectionable, parental guidance is advised.
Text used in these documents is made from 100% recycled electrons and magnetic
particles. These documents do not reflect the thoughts or opinions of either
myself, my company, my friends, or my rabbit. This is not an offer to sell
securities. This offer is void where prohibited, taxed, or otherwise
restricted. This product is meant for educational purposes only. Times
approximate. Unix is a registered trademark of AT&T. Use only as directed. Use
only in a well-ventilated are. User assumes full liabilities. Void where
prohibited. We have sent the forms which seem right for you. You must be
present to win. You need not be present to win. Your canceled check is your
receipt. Your mileage may vary. I didn't do it. You can't prove anything.
This supersedes all previous notices.
*/
#endif // STDDCLMR_H