552 lines
18 KiB
C
552 lines
18 KiB
C
/*
|
|
* 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/>.
|
|
*
|
|
*/
|
|
|
|
|
|
// Inspired by https://github.com/JMarlin/wsbe
|
|
|
|
|
|
#include "gui.h"
|
|
#include "widget.h"
|
|
#include "desktop.h"
|
|
#include "window.h"
|
|
|
|
|
|
int16_t _guiMetric[METRIC_COUNT];
|
|
ColorT _guiColor[COLOR_COUNT];
|
|
FontT *_guiFont8 = NULL;
|
|
FontT *_guiFont14 = NULL;
|
|
FontT *_guiFont16 = NULL;
|
|
FontT *_guiFont = NULL;
|
|
WidgetT *_guiDragWidget = NULL;
|
|
uint16_t _guiDragOffsetX = 0;
|
|
uint16_t _guiDragOffsetY = 0;
|
|
WindowT *_guiActiveWindow = NULL;
|
|
|
|
|
|
static DesktopT *_guiDesktop = NULL;
|
|
static WidgetT *_guiLastWidgetLeft = NULL;
|
|
static uint8_t _guiLastWidgetLeftEvent = MOUSE_EVENT_NONE;
|
|
static WidgetT *_guiFocused = NULL;
|
|
|
|
|
|
// Widget Magic Debug Info
|
|
static char *_magicDebugNames[MAGIC_COUNT] = {
|
|
"Unknown",
|
|
"Desktop",
|
|
"Window",
|
|
"Button",
|
|
"Label",
|
|
"Checkbox",
|
|
"RadioButton",
|
|
"Picture",
|
|
"Frame",
|
|
"Textbox",
|
|
"Updown",
|
|
"Listbox",
|
|
"Terminal"
|
|
};
|
|
|
|
|
|
static void guiPaintBoundsGet(WidgetT *widget, RectT *pos);
|
|
static void guiKeyboardChildrenProcess(WidgetT *widget, uint8_t ascii, uint8_t extended, uint8_t scancode, uint8_t shift, uint8_t control, uint8_t alt);
|
|
static uint8_t guiMouseChildrenProcess(WidgetT *widget, MouseT *mouse);
|
|
|
|
|
|
void guiAttach(WidgetT *parent, WidgetT *child) {
|
|
WidgetT *p = NULL;
|
|
|
|
// Add us to the child list.
|
|
child->parent = parent;
|
|
arrput(parent->children, child);
|
|
|
|
// If this widget is not a window or desktop, find its parent window.
|
|
if (child->magic != MAGIC_WINDOW && child->magic != MAGIC_DESKTOP) {
|
|
p = child;
|
|
while (p != NULL && p->magic != MAGIC_WINDOW) {
|
|
p = p->parent;
|
|
}
|
|
child->window = (WindowT *)p;
|
|
}
|
|
|
|
// Caluclate clipping region - this is for the surface on which the widget is drawn, not in screen space.
|
|
if (GUI_GET_FLAG(child, WIDGET_FLAG_OWNS_SURFACE)) {
|
|
child->clip.x = child->margin.x;
|
|
child->clip.y = child->margin.y;
|
|
} else {
|
|
child->clip.x = child->pos.x;
|
|
child->clip.y = child->pos.y;
|
|
p = child;
|
|
while (p != NULL) {
|
|
child->clip.x += p->margin.x;
|
|
child->clip.y += p->margin.y;
|
|
p = p->parent;
|
|
}
|
|
}
|
|
child->clip.w = child->pos.w - child->margin.x - child->margin.w;
|
|
child->clip.h = child->pos.h - child->margin.y - child->margin.h;
|
|
|
|
// If this widget does not own a surface, find one for it to draw on.
|
|
if (!GUI_GET_FLAG(child, WIDGET_FLAG_OWNS_SURFACE)) {
|
|
p = child;
|
|
while (p != NULL && !GUI_GET_FLAG(p, WIDGET_FLAG_OWNS_SURFACE)) {
|
|
// Check parent.
|
|
p = p->parent;
|
|
}
|
|
child->surface = p->surface;
|
|
}
|
|
|
|
// New windows should be active.
|
|
if (child->magic == MAGIC_WINDOW) {
|
|
windowActiveSet((WindowT *)child);
|
|
}
|
|
}
|
|
|
|
|
|
void guiComposite() {
|
|
WidgetT *widget = (WidgetT *)_guiDesktop;
|
|
size_t len = arrlenu(widget->children);
|
|
size_t x;
|
|
|
|
// Repaint anyone who needs it.
|
|
guiPaint(widget);
|
|
|
|
surfaceSet(NULL);
|
|
|
|
// Render us?
|
|
if (GUI_GET_FLAG(widget, WIDGET_FLAG_OWNS_SURFACE)) {
|
|
surfaceBlit(widget->surface, widget->pos.x, widget->pos.y);
|
|
}
|
|
|
|
//***TODO*** This is wrong. Should be recursive.
|
|
|
|
// Now render all surface-containing children to the VBE buffer.
|
|
if (len > 0) {
|
|
for (x=0; x<len; x++) {
|
|
if (GUI_GET_FLAG(widget->children[x], WIDGET_FLAG_OWNS_SURFACE)) {
|
|
surfaceBlit(widget->children[x]->surface, widget->children[x]->pos.x, widget->children[x]->pos.y);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void guiDelete(WidgetT **widget) {
|
|
WidgetT *w = *widget;
|
|
size_t len = arrlenu(w->children);
|
|
size_t x = 0;
|
|
|
|
// Delete children.
|
|
if (len > 0) {
|
|
for (x=0; x<len; x++) {
|
|
guiDelete(&w->children[x]);
|
|
}
|
|
}
|
|
|
|
// Delete us.
|
|
w->delMethod(&w);
|
|
|
|
// Make sure we're not drawing into oblivion.
|
|
surfaceSet(NULL);
|
|
}
|
|
|
|
|
|
WidgetT *guiFocusGet(void) {
|
|
return _guiFocused;
|
|
}
|
|
|
|
|
|
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(_guiFocused, 0);
|
|
}
|
|
// Change focus.
|
|
_guiFocused = widget;
|
|
// Tell new control it has focus.
|
|
if (_guiFocused) {
|
|
if (_guiFocused->focusMethod) _guiFocused->focusMethod(_guiFocused, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void guiKeyboardChildrenProcess(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++) {
|
|
guiKeyboardChildrenProcess(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, extended, scancode, shift, control, alt);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void guiKeyboardProcess(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, extended, scancode, shift, control, alt);
|
|
}
|
|
}
|
|
|
|
// Check everyone for global keyboard handling.
|
|
guiKeyboardChildrenProcess((WidgetT *)_guiDesktop, ascii, extended, scancode, shift, control, alt);
|
|
}
|
|
|
|
|
|
void guiMousePositionOnWidgetGet(WidgetT *widget, MouseT *mouse, uint16_t *x, uint16_t *y) {
|
|
RectT r;
|
|
guiWidgetPositionOnScreenGet(widget, &r);
|
|
*x = mouse->x - r.x;
|
|
*y = mouse->y - r.y;
|
|
}
|
|
|
|
|
|
static uint8_t guiMouseChildrenProcess(WidgetT *widget, MouseT *mouse) {
|
|
size_t len;
|
|
int16_t x;
|
|
RectT r;
|
|
uint16_t mx;
|
|
uint16_t my;
|
|
uint16_t sx;
|
|
uint16_t sy;
|
|
uint8_t event = MOUSE_EVENT_NONE;
|
|
static WidgetT *focusDown = NULL;
|
|
|
|
// Search children backwards for active widget before checking this widget.
|
|
len = arrlenu(widget->children);
|
|
for (x=len-1; x>=0; --x) {
|
|
if (guiMouseChildrenProcess(widget->children[x], mouse)) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// Get widget and screen coordinates of pointer.
|
|
guiWidgetPositionOnScreenGet(widget, &r);
|
|
sx = r.x;
|
|
sy = r.y;
|
|
guiMousePositionOnWidgetGet(widget, mouse, &mx, &my);
|
|
|
|
//logWrite("Mouse %dx%d Widget %s %dx%d\n", mouse->x, mouse->y, MagicDebugNames[widget->magic], sx, sy);
|
|
|
|
// Serious hack to make window dragging work better.
|
|
// This is because it's possible to move the mouse faster than METRIC_WINDOW_TITLE_GRAB_HEIGHT pixels
|
|
// which causes it to switch widgets. So this prevents that.
|
|
if (widget->magic == MAGIC_WINDOW && widget == _guiDragWidget && mouse->buttonLeft && mouse->buttonLeftWasDown) {
|
|
widget->mouseEventMethod(widget, mouse, mx, my, MOUSE_EVENT_LEFT_HOLD);
|
|
return 1;
|
|
}
|
|
|
|
// Is the mouse inside this widget?
|
|
if (mouse->x >= sx && mouse->y >= sy && mouse->x < sx + widget->pos.w && mouse->y < sy + widget->pos.h) {
|
|
|
|
// Is this the same widget we were over before?
|
|
if (_guiLastWidgetLeft != widget) {
|
|
|
|
// Tell previous widget we're moving out.
|
|
if (_guiLastWidgetLeft->mouseEventMethod) _guiLastWidgetLeft->mouseEventMethod(_guiLastWidgetLeft, mouse, mx, my, MOUSE_EVENT_OUT);
|
|
|
|
// Tell new widget we've moved in.
|
|
event = MOUSE_EVENT_IN;
|
|
|
|
} else { // Widget Changed.
|
|
|
|
// Left button depressed?
|
|
if (_guiLastWidgetLeftEvent == MOUSE_EVENT_NONE && mouse->buttonLeft && !mouse->buttonLeftWasDown) event = MOUSE_EVENT_LEFT_DOWN;
|
|
|
|
// Left button held?
|
|
if ((_guiLastWidgetLeftEvent == MOUSE_EVENT_LEFT_DOWN || _guiLastWidgetLeftEvent == MOUSE_EVENT_LEFT_HOLD) && mouse->buttonLeft && mouse->buttonLeftWasDown) event = MOUSE_EVENT_LEFT_HOLD;
|
|
|
|
// Left button coming up?
|
|
if ((_guiLastWidgetLeftEvent == MOUSE_EVENT_LEFT_DOWN || _guiLastWidgetLeftEvent == MOUSE_EVENT_LEFT_HOLD) && !mouse->buttonLeft && mouse->buttonLeftWasDown) event = MOUSE_EVENT_LEFT_UP;
|
|
|
|
}
|
|
|
|
// If this is a left click, change focus to this new widget.
|
|
if (event == MOUSE_EVENT_LEFT_DOWN) focusDown = widget;
|
|
if (event == MOUSE_EVENT_LEFT_UP && widget == focusDown) guiFocusSet(widget);
|
|
|
|
_guiLastWidgetLeft = widget;
|
|
_guiLastWidgetLeftEvent = event;
|
|
|
|
// Is there an event?
|
|
if (event != MOUSE_EVENT_NONE) {
|
|
// Does it belong to the active window? Or does it target a window? A desktop?
|
|
if (widget->window == _guiActiveWindow || widget->magic == MAGIC_WINDOW || widget->magic == MAGIC_DESKTOP) {
|
|
// Is there a mouse handler?
|
|
if (widget->mouseEventMethod) {
|
|
// Ask child to handle event.
|
|
widget->mouseEventMethod(widget, mouse, mx, my, event);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Proper child found.
|
|
return 1;
|
|
}
|
|
|
|
// Not for this widget.
|
|
return 0;
|
|
}
|
|
|
|
|
|
void guiMouseProcess(MouseT *mouse) {
|
|
guiMouseChildrenProcess(W(_guiDesktop), mouse);
|
|
}
|
|
|
|
|
|
void guiPaint(WidgetT *widget) {
|
|
size_t len = arrlenu(widget->children);
|
|
size_t x;
|
|
RectT pos;
|
|
|
|
// Paint us. Widget handles dirty flag so they can animate if needed.
|
|
if (widget->paintMethod) {
|
|
surfaceSet(widget->surface);
|
|
guiPaintBoundsGet(widget, &pos);
|
|
widget->paintMethod(widget, pos);
|
|
}
|
|
|
|
// Paint all children.
|
|
if (len > 0) {
|
|
for (x=0; x<len; x++) {
|
|
guiPaint(widget->children[x]);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void guiPaintBoundsGet(WidgetT *widget, RectT *pos) {
|
|
if (widget->magic == MAGIC_DESKTOP || widget->magic == MAGIC_WINDOW) {
|
|
pos->x = 0;
|
|
pos->y = 0;
|
|
} else {
|
|
*pos = widget->parent->clip;
|
|
pos->x += widget->pos.x;
|
|
pos->y += widget->pos.y;
|
|
}
|
|
pos->w = widget->pos.w;
|
|
pos->h = widget->pos.h;
|
|
}
|
|
|
|
|
|
WidgetT *guiRootGet(void) {
|
|
return (WidgetT *)_guiDesktop;
|
|
}
|
|
|
|
|
|
DesktopT *guiStartup(void) {
|
|
|
|
_guiMetric[METRIC_BUTTON_BEZEL_SIZE] = 2;
|
|
_guiMetric[METRIC_BUTTON_HORIZONTAL_PADDING] = 8;
|
|
_guiMetric[METRIC_BUTTON_VERTICAL_PADDING] = 2;
|
|
_guiMetric[METRIC_WINDOW_BORDER_WIDTH] = 4; // Does not include highlight or shadow lines.
|
|
_guiMetric[METRIC_WINDOW_TITLE_HEIGHT] = 17; // Does not include highlight or shadow lines.
|
|
_guiMetric[METRIC_WINDOW_TITLE_GRAB_HEIGHT] = _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + _guiMetric[METRIC_WINDOW_TITLE_HEIGHT] + 5; // Border, highlights, titlebar.
|
|
_guiMetric[METRIC_CHECKBOX_PADDING] = 6; // Makes the 10 wide checkbox fill two character cells by padding it out to 16.
|
|
_guiMetric[METRIC_RADIOBUTTON_PADDING] = 6; // Makes the 10 wide radio button fill two character cells by padding it out to 16.
|
|
_guiMetric[METRIC_TEXTBOX_HORIZONTAL_PADDING] = 2;
|
|
_guiMetric[METRIC_TEXTBOX_VERTICAL_PADDING] = 2;
|
|
_guiMetric[METRIC_TEXTBOX_PADDING] = 6; // Matches other label / widget padding.
|
|
_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;
|
|
_guiMetric[METRIC_LISTBOX_HORIZONTAL_PADDING] = 2;
|
|
_guiMetric[METRIC_LISTBOX_VERTICAL_PADDING] = 2;
|
|
|
|
_guiColor[COLOR_BUTTON_BACKGROUND] = vbeColorMake(168, 168, 168);
|
|
_guiColor[COLOR_BUTTON_HIGHLIGHT] = vbeColorMake(248, 252, 248);
|
|
_guiColor[COLOR_BUTTON_SHADOW] = vbeColorMake( 80, 84, 80);
|
|
_guiColor[COLOR_BUTTON_TEXT] = vbeColorMake( 0, 0, 0);
|
|
_guiColor[COLOR_DESKTOP] = vbeColorMake( 51, 153, 255);
|
|
_guiColor[COLOR_WINDOW_BACKGROUND] = vbeColorMake(168, 168, 168);
|
|
_guiColor[COLOR_WINDOW_HIGHLIGHT] = vbeColorMake(248, 252, 248);
|
|
_guiColor[COLOR_WINDOW_SHADOW] = vbeColorMake( 80, 84, 80);
|
|
_guiColor[COLOR_WINDOW_TITLE_ACTIVE] = vbeColorMake( 80, 84, 80);
|
|
_guiColor[COLOR_WINDOW_TITLE_INACTIVE] = vbeColorMake(168, 168, 168);
|
|
_guiColor[COLOR_WINDOW_TITLE_TEXT_ACTIVE] = vbeColorMake(248, 252, 248);
|
|
_guiColor[COLOR_WINDOW_TITLE_TEXT_INACTIVE] = vbeColorMake( 0, 0, 0);
|
|
_guiColor[COLOR_LABEL_TEXT_INACTIVE] = vbeColorMake(248, 252, 248);
|
|
_guiColor[COLOR_LABEL_TEXT_INACTIVE] = vbeColorMake( 0, 0, 0);
|
|
_guiColor[COLOR_CHECKBOX_HIGHLIGHT] = vbeColorMake(248, 252, 248);
|
|
_guiColor[COLOR_CHECKBOX_SHADOW] = vbeColorMake( 0, 0, 0);
|
|
_guiColor[COLOR_CHECKBOX_ACTIVE] = vbeColorMake( 80, 84, 80);
|
|
_guiColor[COLOR_CHECKBOX_INACTIVE] = vbeColorMake(168, 168, 168);
|
|
_guiColor[COLOR_CHECKBOX_TEXT] = vbeColorMake( 0, 0, 0);
|
|
_guiColor[COLOR_RADIOBUTTON_HIGHLIGHT] = vbeColorMake(248, 252, 248);
|
|
_guiColor[COLOR_RADIOBUTTON_SHADOW] = vbeColorMake( 0, 0, 0);
|
|
_guiColor[COLOR_RADIOBUTTON_ACTIVE] = vbeColorMake( 80, 84, 80);
|
|
_guiColor[COLOR_RADIOBUTTON_INACTIVE] = vbeColorMake(168, 168, 168);
|
|
_guiColor[COLOR_RADIOBUTTON_TEXT] = vbeColorMake( 0, 0, 0);
|
|
_guiColor[COLOR_FRAME_HIGHLIGHT] = vbeColorMake(248, 252, 248);
|
|
_guiColor[COLOR_FRAME_SHADOW] = vbeColorMake( 0, 0, 0);
|
|
_guiColor[COLOR_FRAME_TEXT] = vbeColorMake( 0, 0, 0);
|
|
_guiColor[COLOR_TEXTBOX_HIGHLIGHT] = vbeColorMake(248, 252, 248);
|
|
_guiColor[COLOR_TEXTBOX_SHADOW] = vbeColorMake( 0, 0, 0);
|
|
_guiColor[COLOR_TEXTBOX_TEXT] = vbeColorMake( 0, 0, 0);
|
|
_guiColor[COLOR_TEXTBOX_BACKGROUND] = vbeColorMake(248, 252, 248);
|
|
_guiColor[COLOR_UPDOWN_HIGHLIGHT] = vbeColorMake(248, 252, 248);
|
|
_guiColor[COLOR_UPDOWN_SHADOW] = vbeColorMake( 0, 0, 0);
|
|
_guiColor[COLOR_UPDOWN_TEXT] = vbeColorMake( 0, 0, 0);
|
|
_guiColor[COLOR_UPDOWN_BACKGROUND] = vbeColorMake(248, 252, 248);
|
|
_guiColor[COLOR_UPDOWN_ARROWS_BACKGROUND] = vbeColorMake(124, 126, 124);
|
|
_guiColor[COLOR_UPDOWN_ARROWS_ACTIVE] = vbeColorMake(168, 168, 168);
|
|
_guiColor[COLOR_UPDOWN_ARROWS_INACTIVE] = vbeColorMake( 80, 84, 80);
|
|
_guiColor[COLOR_LISTBOX_HIGHLIGHT] = vbeColorMake(248, 252, 248);
|
|
_guiColor[COLOR_LISTBOX_SHADOW] = vbeColorMake( 0, 0, 0);
|
|
_guiColor[COLOR_LISTBOX_TEXT] = vbeColorMake( 0, 0, 0);
|
|
_guiColor[COLOR_LISTBOX_BACKGROUND] = vbeColorMake(248, 252, 248);
|
|
_guiColor[COLOR_LISTBOX_SELECTED_TEXT] = vbeColorMake(248, 252, 248);
|
|
_guiColor[COLOR_LISTBOX_SELECTED_BACKGROUND] = vbeColorMake( 0, 0, 0);
|
|
_guiColor[COLOR_LISTBOX_ARROWS_BACKGROUND] = vbeColorMake(124, 126, 124);
|
|
_guiColor[COLOR_LISTBOX_ARROWS_ACTIVE] = vbeColorMake(168, 168, 168);
|
|
_guiColor[COLOR_LISTBOX_ARROWS_INACTIVE] = vbeColorMake( 80, 84, 80);
|
|
|
|
// Load all font sizes.
|
|
_guiFont8 = fontLoad("vga8x8.dat");
|
|
_guiFont14 = fontLoad("vga8x14.dat");
|
|
_guiFont16 = fontLoad("vga8x16.dat");
|
|
|
|
_guiFont = _guiFont14; // Font to use for widgets.
|
|
|
|
// Create desktop and return it. Remember it for later.
|
|
_guiDesktop = desktopNew();
|
|
|
|
// First thing the mouse is over is the desktop.
|
|
_guiLastWidgetLeft = W(_guiDesktop);
|
|
_guiLastWidgetLeftEvent = MOUSE_EVENT_NONE;
|
|
// Synthesize a MOUSE_EVENT_IN for the desktop.
|
|
//***TODO*** Uncomment this after removing 'mouse' from the parameter list.
|
|
//if (_guiDesktop->base.mouseEventMethod) _guiDesktop->base.mouseEventMethod(W(_guiDesktop), 0, 0, MOUSE_EVENT_IN);
|
|
|
|
return _guiDesktop;
|
|
}
|
|
|
|
|
|
void guiShutdown(void) {
|
|
// Unload fonts.
|
|
fontUnload(&_guiFont16);
|
|
fontUnload(&_guiFont14);
|
|
fontUnload(&_guiFont8);
|
|
_guiFont = NULL;
|
|
|
|
// Delete all widgets in GUI tree.
|
|
guiDelete((WidgetT **)&_guiDesktop);
|
|
}
|
|
|
|
|
|
void *guiUserDataGet(WidgetT *widget) {
|
|
return widget->userData;
|
|
}
|
|
|
|
|
|
void guiUserDataSet(WidgetT *widget, void *userData) {
|
|
widget->userData = userData;
|
|
}
|
|
|
|
|
|
void guiWidgetAndChildrenDirtySet(WidgetT *widget) {
|
|
size_t len = arrlenu(widget->children);
|
|
size_t x;
|
|
|
|
// Mark us dirty.
|
|
GUI_SET_FLAG(widget, WIDGET_FLAG_DIRTY);
|
|
|
|
// Mark all children.
|
|
if (len > 0) {
|
|
for (x=0; x<len; x++) {
|
|
guiWidgetAndChildrenDirtySet(widget->children[x]);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void guiWidgetBoundsDrawableOnScreenGet(WidgetT *widget, RectT *bounds) {
|
|
guiPaintBoundsGet(widget, bounds);
|
|
if (GUI_GET_FLAG(widget, WIDGET_FLAG_OWNS_SURFACE)) {
|
|
bounds->x += widget->pos.x;
|
|
bounds->y += widget->pos.y;
|
|
} else {
|
|
bounds->x += widget->window->base.pos.x;
|
|
bounds->y += widget->window->base.pos.y;
|
|
}
|
|
bounds->x += widget->margin.x;
|
|
bounds->y += widget->margin.y;
|
|
bounds->w -= (widget->margin.x + widget->margin.w);
|
|
bounds->h -= (widget->margin.y + widget->margin.h);
|
|
}
|
|
|
|
|
|
void guiWidgetPositionOnScreenGet(WidgetT *widget, RectT *pos) {
|
|
WidgetT *p = widget->parent;
|
|
|
|
if (GUI_GET_FLAG(widget, WIDGET_FLAG_OWNS_SURFACE)) {
|
|
*pos = widget->pos;
|
|
} else {
|
|
*pos = p->clip;
|
|
pos->x += widget->pos.x + widget->window->base.pos.x;
|
|
pos->y += widget->pos.y + widget->window->base.pos.y;
|
|
pos->w = widget->pos.w;
|
|
pos->h = widget->pos.h;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void guiWidgetTreeDump(WidgetT *widget, uint16_t depth) {
|
|
size_t len = arrlenu(widget->children);
|
|
size_t x;
|
|
char line[256];
|
|
|
|
for (x=0; x<depth*2; x++) line[x] = '-';
|
|
line[x] = 0;
|
|
|
|
logWrite("%s %s P:%dx%d-%dx%d M:%dx%d-%dx%d C:%dx%d-%dx%d F:%d\n",
|
|
line,
|
|
_magicDebugNames[widget->magic],
|
|
widget->pos.x, widget->pos.y, widget->pos.w, widget->pos.h,
|
|
widget->margin.x, widget->margin.y, widget->margin.w, widget->margin.h,
|
|
widget->clip.x, widget->clip.y, widget->clip.w, widget->clip.h,
|
|
widget->flags);
|
|
|
|
// Mark all children.
|
|
if (len > 0) {
|
|
for (x=0; x<len; x++) {
|
|
guiWidgetTreeDump(widget->children[x], depth + 1);
|
|
}
|
|
}
|
|
}
|