/* * 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 . * */ // 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; static uint8_t _guiHasStopped = 0; static WidgetT ***_guiDeleteList = NULL; // Widget Magic Debug Info. Don't forget to change MagicE! static char *_magicDebugNames[MAGIC_COUNT] = { "Unknown", "Desktop", "Window", "Button", "Label", "Checkbox", "RadioButton", "Picture", "Frame", "Textbox", "Updown", "Listbox", "Terminal" }; static void guiDeleteList(void); static void guiDeleteListItem(WidgetT **widget); 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; 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); } // Now render all surface-containing children to the VBE buffer. len = arrlenu(widget->children); if (len > 0) { for (x=0; xchildren[x], WIDGET_FLAG_OWNS_SURFACE)) { surfaceBlit(widget->children[x]->surface, widget->children[x]->pos.x, widget->children[x]->pos.y); } } } } void guiDebugAreaShow(WidgetT *widget) { RectT r; guiWidgetBoundsDrawableOnScreenGet(widget, &r); logWrite("%s: %dx%d\n", _magicDebugNames[widget->magic], r.w, r.h); } void guiDebugWidgetTreeDump(WidgetT *widget, uint16_t depth) { size_t len = arrlenu(widget->children); size_t x; char line[256]; for (x=0; xmagic], 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; xchildren[x], depth + 1); } } } void guiDelete(WidgetT **widget) { // Since deleting happens in widget events, it's not safe to do it // immediately. Instead, we make a list of what to delete and // then process it before painting. arrput(_guiDeleteList, widget); } static void guiDeleteList(void) { WidgetT **w = NULL; while (arrlen(_guiDeleteList) > 0) { w = arrpop(_guiDeleteList); guiDeleteListItem(w); } arrfree(_guiDeleteList); _guiDeleteList = NULL; } static void guiDeleteListItem(WidgetT **widget) { WidgetT *w = *widget; size_t len = arrlenu(w->children); size_t plen = arrlen(w->parent != NULL ? w->parent->children : 0); size_t x = 0; uint8_t nuke = 1; static uint16_t depth = 0; // Delete children. if (len > 0) { for (x=0; xchildren[x]); depth--; } } // Remove us from parent children list. if (depth == 0) { if (plen > 0) { for (x=0; xparent->children[x]) { arrdel(w->parent->children, x); break; } } // Was this the last child in the list? if (plen == 1) { // Erase the list. arrfree(w->parent->children); w->parent->children = NULL; } } } // If we were involved in a mouse event, get out of it. if (w == _guiLastWidgetLeft) _guiLastWidgetLeft = NULL; if (w == _guiFocused) _guiFocused = NULL; // Delete us. //logWrite("Deleting %s %p\n", _magicDebugNames[w->magic], w); if (w->delMethod != NULL) w->delMethod(&w); arrfree(w->children); if (w->surface != NULL) { // Only free the surface if we own it. If it's our parent's, ignore. if (w->parent != NULL) { if (w->surface == w->parent->surface) nuke = 0; } if (nuke) surfaceDestroy(&w->surface); } free(w); w = NULL; *widget = 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); } } } uint8_t guiHasStopped(void) { return _guiHasStopped; } 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; xchildren[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)) { if (!GUI_GET_FLAG(widget, WIDGET_FLAG_DISABLED)) { 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)) { if (!GUI_GET_FLAG(_guiFocused, WIDGET_FLAG_DISABLED)) { _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; // 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 Screen %dx%d\n", mouse->x, mouse->y, _magicDebugNames[widget->magic], mx, my, 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; } // 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; } } // 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) { 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) { if (!GUI_GET_FLAG(widget, WIDGET_FLAG_DISABLED)) { // 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; size_t x; RectT pos; // Process any pending widget deletions. guiDeleteList(); // Paint us. Widget handles dirty flag so they can animate if needed. if (widget->paintMethod) { surfaceSet(widget->surface); guiPaintBoundsGet(widget, &pos); widget->paintMethod(widget, !GUI_GET_FLAG(widget, WIDGET_FLAG_DISABLED), pos); } // Paint all children. len = arrlenu(widget->children); if (len > 0) { for (x=0; xchildren[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; } void guiShutdown(void) { // Unload fonts. fontUnload(&_guiFont16); fontUnload(&_guiFont14); fontUnload(&_guiFont8); _guiFont = NULL; // Delete all widgets in GUI tree. guiDelete((WidgetT **)&_guiDesktop); guiDeleteList(); } 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_BUTTON_BACKGROUND_DISABLED] = vbeColorMake(124, 126, 124); _guiColor[COLOR_BUTTON_HIGHLIGHT_DISABLED] = vbeColorMake(200, 200, 200); _guiColor[COLOR_BUTTON_SHADOW_DISABLED] = vbeColorMake( 80, 84, 80); _guiColor[COLOR_BUTTON_TEXT_DISABLED] = vbeColorMake( 84, 84, 84); _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_CHECKBOX_HIGHLIGHT_DISABLED] = vbeColorMake(200, 200, 200); _guiColor[COLOR_CHECKBOX_SHADOW_DISABLED] = vbeColorMake( 80, 84, 80); _guiColor[COLOR_CHECKBOX_ACTIVE_DISABLED] = vbeColorMake( 80, 84, 80); _guiColor[COLOR_CHECKBOX_INACTIVE_DISABLED] = vbeColorMake(124, 126, 124); _guiColor[COLOR_CHECKBOX_TEXT_DISABLED] = vbeColorMake( 84, 84, 84); _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_RADIOBUTTON_HIGHLIGHT_DISABLED] = vbeColorMake(200, 200, 200); _guiColor[COLOR_RADIOBUTTON_SHADOW_DISABLED] = vbeColorMake( 80, 84, 80); _guiColor[COLOR_RADIOBUTTON_ACTIVE_DISABLED] = vbeColorMake( 80, 84, 80); _guiColor[COLOR_RADIOBUTTON_INACTIVE_DISABLED] = vbeColorMake(124, 126, 124); _guiColor[COLOR_RADIOBUTTON_TEXT_DISABLED] = vbeColorMake( 84, 84, 84); _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 guiStop(void) { _guiHasStopped = 1; } 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; xchildren[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; } }