Textboxes are working! This required a huge overhaul of painting, fixing clipping bound issues, adding timer support, and other things I have probably forgotten.

This commit is contained in:
Scott Duensing 2021-10-28 21:11:56 -05:00
parent 9494dee16d
commit b17d78818f
13 changed files with 263 additions and 44 deletions

View file

@ -1,4 +1,4 @@
#!/bin/bash
#!/bin/bash -e
#
# Kangaroo Punch MultiPlayer Game Server Mark II
@ -23,3 +23,21 @@ source /opt/cross/djgpp/setenv
make -f Makefile.djgpp
rm bin/client
cp data/* bin/.
:<<'SKIP'
[[ -f bin/CLIENT.LOG ]] && rm bin/CLIENT.LOG
cat <<-'BATCH' > bin/test.bat
@echo off
:wait
if not exist flag.dat goto wait
del flag.dat
client.exe 800 600 16
BATCH
touch bin/flag.dat
while [[ ! -f bin/CLIENT.LOG ]]; do
/bin/true
done
tail -f bin/CLIENT.LOG
SKIP

View file

@ -46,8 +46,9 @@ enum MetaBitsE {
};
static int16_t _key = 0;
static int16_t _meta = 0;
static int8_t _extended = 0;
static int16_t _key = 0;
static int16_t _meta = 0;
uint8_t keyAlt(void) {
@ -56,7 +57,7 @@ uint8_t keyAlt(void) {
uint8_t keyASCII(void) {
return (uint8_t)(_key & 0x00FF);
return LOW_BYTE(_key);
}
@ -65,22 +66,43 @@ uint8_t keyControl(void) {
}
uint8_t keyExtended(void) {
return _extended;
}
uint8_t keyHit(void) {
int16_t result = bioskey(KEYBOARD_CHECK_EXTENDED);
int16_t meta = bioskey(KEYBOARD_META_EXTENDED);
if (result > 0) {
_key = bioskey(KEYBOARD_READ_EXTENDED);;
_key = bioskey(KEYBOARD_READ_EXTENDED);
_meta = meta;
// The value returned is a combination of the key's scan code in the high 8 bits
// and its ASCII code in the low 8 bits. For non-alphanumeric keys, such as the
// arrow keys, the low 8 bits are zeroed.
// Extended keys have the E0h prefix in the low 8 bits.
//logWrite("Raw key = %04X High = %02X Low = %02X\n", _key, HIGH_BYTE(_key), LOW_BYTE(_key));
if (LOW_BYTE(_key) == 0xE0) {
_extended = 1;
_key = HIGH_BYTE(_key);
} else {
_extended = 0;
}
return 1;
}
_extended = 0;
return 0;
}
uint8_t keyScanCode(void) {
return (uint8_t)((_key & 0xFF00) >> 8);
return HIGH_BYTE(_key);
}

View file

@ -58,7 +58,7 @@ static char *_magicDebugNames[MAGIC_COUNT] = {
static void guiPaintBoundsGet(WidgetT *widget, RectT *pos);
static void guiProcessKeyboardChildren(WidgetT *widget, uint8_t ascii, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt);
static void guiProcessKeyboardChildren(WidgetT *widget, uint8_t ascii, uint8_t extended, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt);
static uint8_t guiProcessMouseChildren(WidgetT *widget, MouseT *mouse);
@ -255,17 +255,24 @@ WidgetT *guiFocusGet(void) {
void guiFocusSet(WidgetT *widget) {
// Is this widget on the active window?
if (widget->window) {
if (!GUI_GET_FLAG(W(widget->window), WIDGET_FLAG_ACTIVE)) {
// Cancel focus change.
return;
}
}
// Did the focus change?
if (widget != _guiFocused) {
// Remove focus from current control.
if (_guiFocused) {
if (_guiFocused->focusMethod) _guiFocused->focusMethod(widget, 0);
if (_guiFocused->focusMethod) _guiFocused->focusMethod(_guiFocused, 0);
}
// Change focus.
_guiFocused = widget;
// Tell new control it has focus.
if (_guiFocused) {
if (_guiFocused->focusMethod) _guiFocused->focusMethod(widget, 1);
if (_guiFocused->focusMethod) _guiFocused->focusMethod(_guiFocused, 1);
}
}
}
@ -406,33 +413,33 @@ static uint8_t guiProcessMouseChildren(WidgetT *widget, MouseT *mouse) {
}
void guiProcessKeyboard(uint8_t ascii, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt) {
void guiProcessKeyboard(uint8_t ascii, uint8_t extended, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt) {
// Does the focused widget want events? Check for global keyboard so it doesn't get double events.
if (_guiFocused) {
if (_guiFocused->keyboardEventMethod && !GUI_GET_FLAG(_guiFocused, WIDGET_FLAG_ALWAYS_RECEIVE_KEYBOARD_EVENTS)) {
_guiFocused->keyboardEventMethod(_guiFocused, ascii, scancode, shift, control, alt);
_guiFocused->keyboardEventMethod(_guiFocused, ascii, extended, scancode, shift, control, alt);
}
}
// Check everyone for global keyboard handling.
guiProcessKeyboardChildren((WidgetT *)_guiDesktop, ascii, scancode, shift, control, alt);
guiProcessKeyboardChildren((WidgetT *)_guiDesktop, ascii, extended, scancode, shift, control, alt);
}
static void guiProcessKeyboardChildren(WidgetT *widget, uint8_t ascii, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt) {
static void guiProcessKeyboardChildren(WidgetT *widget, uint8_t ascii, uint8_t extended, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt) {
size_t len;
size_t x;
// Process children of this widget.
len = arrlenu(widget->children);
for (x=0; x<len; x++) {
guiProcessKeyboardChildren(widget->children[x], ascii, scancode, shift, control, alt);
guiProcessKeyboardChildren(widget->children[x], ascii, extended, scancode, shift, control, alt);
}
// Does this widget want events? Check for global keyboard so it doesn't get double events.
if (widget) {
if (widget->keyboardEventMethod && GUI_GET_FLAG(widget, WIDGET_FLAG_ALWAYS_RECEIVE_KEYBOARD_EVENTS)) {
widget->keyboardEventMethod(widget, ascii, scancode, shift, control, alt);
widget->keyboardEventMethod(widget, ascii, extended, scancode, shift, control, alt);
}
}
}

View file

@ -144,7 +144,7 @@ void guiFocusSet(WidgetT *widget);
void guiMousePositionOnWidgetGet(WidgetT *widget, MouseT *mouse, uint16_t *x, uint16_t *y);
void guiPaint(WidgetT *widget);
void guiProcessMouse(MouseT *mouse);
void guiProcessKeyboard(uint8_t ascii, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt);
void guiProcessKeyboard(uint8_t ascii, uint8_t extended, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt);
WidgetT *guiRootGet(void);
void guiSetWidgetAndChildrenDirty(WidgetT *widget);
DesktopT *guiStartup(void);

View file

@ -28,6 +28,7 @@
uint8_t keyAlt(void);
uint8_t keyASCII(void);
uint8_t keyControl(void);
uint8_t keyExtended(void);
uint8_t keyHit(void);
uint8_t keyScanCode(void);
uint8_t keyShift(void);

View file

@ -55,6 +55,8 @@ long biostime(int cmd, long newtime);
#define DIVISIBLE_BY_EIGHT(x) ((((x) >> 3) << 3) == (x))
#define HIGH_BYTE(b) ((uint8_t)(((b) & 0xFF00) >> 8))
#define LOW_BYTE(b) ((uint8_t)((b) & 0x00FF))
#endif // OS_H

View file

@ -19,11 +19,12 @@
#include "textbox.h"
#include "timer.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 textboxKeyboardEvent(WidgetT *widget, uint8_t ascii, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt);
static void textboxKeyboardEvent(WidgetT *widget, uint8_t ascii, uint8_t extended, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt);
static void textboxPaint(WidgetT *widget, RectT pos);
@ -38,7 +39,10 @@ void textboxDel(WidgetT **widget) {
static void textboxFocusEvent(WidgetT *widget, uint8_t focused) {
// Make sure cursor disappears when we lose focus.
if (!focused) {
GUI_SET_FLAG(widget, WIDGET_FLAG_DIRTY);
}
}
@ -59,6 +63,7 @@ WidgetT *textboxInit(WidgetT *widget, char *title) {
t->maxLength = 256;
t->value = (char *)malloc(t->maxLength);
t->caret = 0;
t->offset = 0;
if (!t->value) return NULL;
t->value[0] = 0;
@ -70,8 +75,115 @@ WidgetT *textboxInit(WidgetT *widget, char *title) {
}
static void textboxKeyboardEvent(WidgetT *widget, uint8_t ascii, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt) {
static void textboxKeyboardEvent(WidgetT *widget, uint8_t ascii, uint8_t extended, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt) {
TextboxT *t = (TextboxT *)widget;
uint16_t x;
(void)scancode;
(void)shift;
(void)control;
(void)alt;
if (extended) {
switch (ascii) {
case 75: // LEFT
// Can we move left in the value?
if (t->caret + t->offset > 0) {
// Is the caret on the left edge of the box?
if (t->caret == 0) {
// Can we move the string offset?
if (t->offset > 0) {
t->offset--;
}
} else {
// Move the caret left.
t->caret--;
}
}
break;
case 77: // RIGHT
// Can we move right in the value?
if (t->caret + t->offset < strlen(t->value)) {
// Is the caret on the right edge of the box?
if (t->caret == t->visible - 1) {
// Can we move the string offset?
if (t->offset < strlen(t->value)) {
t->offset++;
}
} else {
// Move the caret right.
t->caret++;
}
}
break;
case 83: // DELETE
// Is there more data to the right in the value?
if (t->caret + t->offset < strlen(t->value)) {
// Delete character under caret.
for (x = t->caret + t->offset; x<strlen(t->value); x++) {
t->value[x] = t->value[x+1];
}
}
break;
} // switch
} else { // extended
switch (ascii) {
case 8: // BACKSPACE
// Can we delete left in the value?
if (t->caret + t->offset > 0) {
// Is the caret on the left edge of the box?
if (t->caret == 0) {
// Can we move the string offset?
if (t->offset > 0) {
t->offset--;
}
} else {
// Move the caret left.
t->caret--;
}
// Delete the character to the left of the caret.
for (x = t->caret + t->offset; x<strlen(t->value); x++) {
t->value[x] = t->value[x+1];
}
}
break;
default: // Other keys
if (ascii >= 32 && ascii <= 126) {
// Insert character, if room.
if (strlen(t->value) < t->maxLength - 1) {
// Move existing characters over, if needed.
if (t->caret + t->offset < strlen(t->value)) {
for (x=strlen(t->value) + 1; x>t->caret + t->offset; x--) {
t->value[x] = t->value[x-1];
}
}
// Place typed character at caret.
t->value[t->caret + t->offset] = ascii;
// Is the caret on the right edge of the box?
if (t->caret == t->visible - 1) {
// Can we move the string offset?
if (t->offset < strlen(t->value)) {
t->offset++;
}
} else {
// Move the caret right.
t->caret++;
}
}
}
break;
} // switch
} // extended
}
@ -82,7 +194,10 @@ static void textboxMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16
(void)y;
(void)mouse;
//***TODO*** Allow dragging text cursor with mouse.
//***TODO*** Allow dragging/positioning text cursor with mouse.
if (event == MOUSE_EVENT_LEFT_HOLD) {
}
}
@ -110,8 +225,12 @@ static void textboxPaint(WidgetT *widget, RectT pos) {
char *draw = NULL;
uint16_t labelWidth;
uint16_t valueWidth;
uint16_t caretPos;
uint16_t textX;
uint16_t textY;
char cursor[2] = { 0xb1, 0 };
if (GUI_GET_FLAG(widget, WIDGET_FLAG_DIRTY)) {
if (GUI_GET_FLAG(widget, WIDGET_FLAG_DIRTY) || guiFocusGet() == widget) {
labelWidth = (strlen(t->title) * fontWidthGet(_guiFont)) + _guiMetric[METRIC_TEXTBOX_PADDING];
valueWidth = (t->visible * fontWidthGet(_guiFont)) + (_guiMetric[METRIC_TEXTBOX_HORIZONTAL_PADDING] * 2);
@ -125,12 +244,23 @@ static void textboxPaint(WidgetT *widget, RectT pos) {
// Draw background.
guiDrawRectangleFilled(pos.x + labelWidth + 2, pos.y + 2, pos.x + labelWidth + valueWidth, pos.y + pos.h - 2, _guiColor[COLOR_TEXTBOX_BACKGROUND]);
// Draw value. ***TODO*** This needs much more!
draw = strdup(t->value);
// Where's the text display start?
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 = strdup(&t->value[t->offset]);
if (strlen(t->value) > t->visible) draw[t->visible] = 0;
fontRender(_guiFont, draw, _guiColor[COLOR_TEXTBOX_TEXT], _guiColor[COLOR_TEXTBOX_BACKGROUND], pos.x + labelWidth + 2 + _guiMetric[METRIC_TEXTBOX_HORIZONTAL_PADDING], pos.y + 2 + _guiMetric[METRIC_TEXTBOX_VERTICAL_PADDING]);
fontRender(_guiFont, draw, _guiColor[COLOR_TEXTBOX_TEXT], _guiColor[COLOR_TEXTBOX_BACKGROUND], textX, textY);
free(draw);
// Draw cursor.
if (guiFocusGet() == widget && timerQuarterSecondOn) {
caretPos = textX + fontWidthGet(_guiFont) * t->caret;
//logWrite("textX %d textY %d caretPos %d\n", textX, textY, caretPos);
fontRender(_guiFont, cursor, _guiColor[COLOR_TEXTBOX_TEXT], _guiColor[COLOR_TEXTBOX_BACKGROUND], caretPos, textY);
}
GUI_CLEAR_FLAG(widget, WIDGET_FLAG_DIRTY);
}
}
@ -140,6 +270,14 @@ void textboxSetValue(TextboxT *textbox, char *value) {
// Copy it & truncate if needed.
strncpy(textbox->value, value, textbox->maxLength - 1);
// Is this longer than the area we have to display it in?
if (strlen(textbox->value) > textbox->visible - 1) {
textbox->offset = strlen(textbox->value) - (textbox->visible - 1);
}
// Set caret position.
textbox->caret = strlen(&textbox->value[textbox->offset]);
GUI_SET_FLAG((WidgetT *)textbox, WIDGET_FLAG_DIRTY);
}

View file

@ -34,7 +34,6 @@ typedef struct TextboxS {
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;

View file

@ -26,12 +26,14 @@
#define TICKS_PER_DAY (SECONDS_IN_DAY * TICKS_PER_SECOND)
uint8_t timerHalfSecondOn = 0;
uint8_t timerSecondOn = 0;
uint8_t timerQuarterSecondOn = 0;
uint8_t timerHalfSecondOn = 0;
uint8_t timerSecondOn = 0;
static long _timerLast = 0;
static uint8_t _timerSecond = 2;
static long _timerLast = 0;
static uint8_t _timerHalfSecond = 2;
static uint8_t _timerSecond = 2;
void timerShutdown(void) {
@ -58,16 +60,25 @@ void timerUpdate(void) {
delta = (now + TICKS_PER_DAY) - _timerLast;
}
if (delta > TICKS_PER_SECOND * 0.5) {
// Everything ticks off the quarter second.
if (delta > TICKS_PER_SECOND * 0.25) {
_timerLast = now;
// Half Second timer
timerHalfSecondOn = !timerHalfSecondOn;
// Quarter Second timer.
timerQuarterSecondOn = !timerQuarterSecondOn;
// Second timer
if (--_timerSecond == 0) {
_timerSecond = 2;
timerSecondOn = !timerSecondOn;
}
}
// Half Second timer.
if (--_timerHalfSecond == 0) {
_timerHalfSecond = 2;
timerHalfSecondOn = !timerHalfSecondOn;
// Second timer
if (--_timerSecond == 0) {
_timerSecond = 2;
timerSecondOn = !timerSecondOn;
} // Second.
} // Half Second.
} // Quarter Second.
}

View file

@ -25,6 +25,7 @@
#include "os.h"
extern uint8_t timerQuarterSecondOn;
extern uint8_t timerHalfSecondOn;
extern uint8_t timerSecondOn;

View file

@ -40,7 +40,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 (*widgetKeyboardEventMethod)(struct WidgetS *widget, uint8_t ascii, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt);
typedef void (*widgetKeyboardEventMethod)(struct WidgetS *widget, uint8_t ascii, uint8_t extended, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt);
typedef void (*widgetPaintMethod)(struct WidgetS *widget, RectT pos);
typedef void (*widgetMouseEventMethod)(struct WidgetS *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event);

View file

@ -46,6 +46,7 @@ static uint8_t _control = 0;
static uint8_t _shift = 0;
static uint8_t _ASCII = 0;
static uint8_t _scanCode = 0;
static uint8_t _extended = 0;
static uint8_t _ASCIIKeep = 0;
static uint8_t _scanCodeKeep = 0;
static uint8_t _keyPressed = 0;
@ -84,6 +85,11 @@ uint8_t keyControl(void) {
}
uint8_t keyExtended(void) {
return _extended;
}
uint8_t keyHit(void) {
uint8_t result = 0;
@ -91,10 +97,19 @@ uint8_t keyHit(void) {
result = _keyPressed;
if (result) {
if (_scanCode == 41) _ASCII = 27; // Fix ESC mapping.
// Default mapping.
_extended = 0;
_ASCIIKeep = _ASCII;
_scanCodeKeep = _scanCode;
_keyPressed = 0;
// 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_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_DOWN) { _extended = 1; _ASCIIKeep = 80; }
}
return result;

View file

@ -26,6 +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
*
*/
@ -97,8 +98,8 @@ void mainLoop(void *data) {
mouse = mouseRead();
if (keyHit()) {
key = keyASCII();
guiProcessKeyboard(keyASCII(), keyScanCode(), keyShift(), keyControl(), keyAlt());
//logWrite("Key '%d' Control '%d'\n", key, keyControl());
guiProcessKeyboard(keyASCII(), keyExtended(), keyScanCode(), keyShift(), keyControl(), keyAlt());
//logWrite("Key '%d' Extended '%d' Scancode '%d' Shift '%d' Control '%d' Alt '%d'\n", key, keyExtended(), keyScanCode(), keyShift(), keyControl(), keyAlt());
} else {
key = 0;
}
@ -112,7 +113,7 @@ void mainLoop(void *data) {
if (debugState > 2) debugState = 0;
}
if (debugState > 0) drawWidgetDebug(guiRootGet(), debugState - 1);
if (timerHalfSecondOn) guiDrawRectangle(0, 0, vbeSurfaceWidthGet() - 1, vbeSurfaceHeightGet() - 1, vbeMakeColor(255, 255, 255));
//if (timerHalfSecondOn) guiDrawRectangle(0, 0, vbeSurfaceWidthGet() - 1, vbeSurfaceHeightGet() - 1, vbeMakeColor(255, 255, 255));
vbeWaitVBlank();
vbePresent();
@ -139,6 +140,7 @@ void test(void *data) {
PictureT *p1 = NULL;
FrameT *f1 = NULL;
TextboxT *t1 = NULL;
TextboxT *t2 = NULL;
(void)data;
@ -170,8 +172,11 @@ void test(void *data) {
radioSetSelected(r1a);
radioSetSelected(r2b);
t1 = textboxNew(10, 60, 265, "Test Textbox");
textboxSetValue(t1, "Text to edit!");
textboxSetValue(t1, "Really long text string to edit!");
guiAttach(W(w2), W(t1));
t2 = textboxNew(10, 85, 265, "Test Textbox");
textboxSetValue(t2, "Short string.");
guiAttach(W(w2), W(t2));
// Window 3
f1 = frameNew(10, 5, 175, 125, "Test Frame");