diff --git a/client/Makefile.djgpp b/client/Makefile.djgpp index e371a49..1e8cc93 100644 --- a/client/Makefile.djgpp +++ b/client/Makefile.djgpp @@ -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 diff --git a/client/build.sh b/client/build.sh index d1efe43..d521a8d 100755 --- a/client/build.sh +++ b/client/build.sh @@ -18,7 +18,7 @@ # along with this program. If not, see . # -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 diff --git a/client/client.pro b/client/client.pro index e3ca7aa..36dbe9a 100644 --- a/client/client.pro +++ b/client/client.pro @@ -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 \ diff --git a/client/src/gui/gui.c b/client/src/gui/gui.c index 077045d..6d3c39e 100644 --- a/client/src/gui/gui.c +++ b/client/src/gui/gui.c @@ -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"); diff --git a/client/src/gui/gui.h b/client/src/gui/gui.h index a48cb7e..99f97c3 100644 --- a/client/src/gui/gui.h +++ b/client/src/gui/gui.h @@ -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 }; diff --git a/client/src/gui/listbox.c b/client/src/gui/listbox.c new file mode 100644 index 0000000..6a21cba --- /dev/null +++ b/client/src/gui/listbox.c @@ -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 . + * + */ + + +#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; xvalues[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; istep; i++) listboxScrollUp(l); + break; + + case 81: // PAGEDOWN + for (i=0; istep; 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; iselected) { + 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; xvalues[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; xvalues[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); + +} diff --git a/client/src/gui/listbox.h b/client/src/gui/listbox.h new file mode 100644 index 0000000..e641349 --- /dev/null +++ b/client/src/gui/listbox.h @@ -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 . + * + */ + + +#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 diff --git a/client/src/gui/textbox.c b/client/src/gui/textbox.c index 118c26f..33e4c85 100644 --- a/client/src/gui/textbox.c +++ b/client/src/gui/textbox.c @@ -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) { diff --git a/client/src/gui/updown.c b/client/src/gui/updown.c index 64c95b5..1619f7e 100644 --- a/client/src/gui/updown.c +++ b/client/src/gui/updown.c @@ -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; diff --git a/client/src/linux/linux.c b/client/src/linux/linux.c index 09216d3..4ce95f3 100644 --- a/client/src/linux/linux.c +++ b/client/src/linux/linux.c @@ -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; diff --git a/client/src/main.c b/client/src/main.c index 4eb5a63..a1c95f5 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -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); diff --git a/client/src/stddclmr.h b/client/src/stddclmr.h new file mode 100644 index 0000000..8e6b3d9 --- /dev/null +++ b/client/src/stddclmr.h @@ -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 diff --git a/client/src/thirdparty/memwatch/test.c b/client/src/thirdparty/memwatch/test.C similarity index 100% rename from client/src/thirdparty/memwatch/test.c rename to client/src/thirdparty/memwatch/test.C