/* * Kangaroo Punch Multi Player 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 "window.h" static void windowDeactivateAll(WidgetT *widget); static void windowMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event); static void windowPaint(WidgetT *widget); static void windowDeactivateAll(WidgetT *widget) { size_t len = arrlenu(widget->children); size_t x; // Is this a Window? if (widget->magic == MAGIC_WINDOW) { // Deactivate it. GUI_CLEAR_FLAG(widget, WIDGET_FLAG_ACTIVE); guiSetWidgetAndChildrenDirty(widget); } // Process any children. if (len > 0) { for (x=0; xchildren[x]); } } } void windowDel(WidgetT **widget) { WindowT *w = (WindowT *)*widget; vbeSurfaceDestroy(&w->base.surface); if (w->title) free(w->title); free(w); w = NULL; } WidgetT *windowInit(WidgetT *window, uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *title) { WindowT *win = (WindowT *)window; win->base.magic = MAGIC_WINDOW; win->base.x = x; win->base.y = y; win->base.w = w; win->base.h = h; win->base.delMethod = windowDel; win->base.paintMethod = windowPaint; win->base.mouseEventMethod = windowMouseEvent; win->flags = 0; win->title = NULL; win->base.marginX += _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 2; win->base.marginY += _guiMetric[METRIC_WINDOW_TITLE_GRAB_HEIGHT]; windowSetTitle(win, title); GUI_SET_FLAG(window, WIDGET_FLAG_OWNS_SURFACE); win->base.surface = vbeSurfaceCreate(win->base.w, win->base.h); if (!win->base.surface) { free(win->title); free(win); return NULL; } return window; } static void windowMouseEvent(WidgetT *widget, MouseT *mouse, uint16_t x, uint16_t y, uint8_t event) { WindowT *window = (WindowT *)widget; (void)x; // Raise window? if (event == MOUSE_EVENT_LEFT_UP && !GUI_GET_FLAG(widget, WIDGET_FLAG_ACTIVE)) windowSetActive(window); // Start dragging? if (event == MOUSE_EVENT_LEFT_DOWN && GUI_GET_FLAG(widget, WIDGET_FLAG_ACTIVE)) { // Are we on the draggable area of the titlebar / borders? if (y < _guiMetric[METRIC_WINDOW_TITLE_GRAB_HEIGHT]) { // Start dragging. _guiDragWidget = widget; _guiDragOffsetX = mouse->x - window->base.x; _guiDragOffsetY = mouse->y - window->base.y; } } // Still dragging? We use raw mouse data here because it's possible to drag too quickly and trigger "OUT" events. if (_guiDragWidget == widget && mouse->buttonLeft && GUI_GET_FLAG(widget, WIDGET_FLAG_ACTIVE)) { // Move window. window->base.x = mouse->x - _guiDragOffsetX; window->base.y = mouse->y - _guiDragOffsetY; // Keep it on the screen. if (window->base.x < 0) window->base.x = 0; if (window->base.x + window->base.w > vbeDisplayWidthGet()) window->base.x = vbeDisplayWidthGet() - window->base.w; if (window->base.y < 0) window->base.y = 0; if (window->base.y + window->base.h > vbeDisplayHeightGet()) window->base.y = vbeDisplayHeightGet() - window->base.h; } // Be sure we stop dragging on mouse up or move out. if (_guiDragWidget == widget && event == MOUSE_EVENT_LEFT_UP) { _guiDragWidget = NULL; } } WindowT *windowNew(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *title) { WindowT *window = (WindowT *)malloc(sizeof(WindowT)); WidgetT *widget = NULL; if (!window) return NULL; widget = widgetInit((WidgetT *)window); if (!widget) { free(window); return NULL; } window = (WindowT *)windowInit((WidgetT *)window, x, y, w, h, title); return window; } static void windowPaint(WidgetT *widget) { WindowT *w = (WindowT *)widget; uint16_t x2 = w->base.w - 1; uint16_t y2 = w->base.h - 1; PixelT background = GUI_GET_FLAG(widget, WIDGET_FLAG_ACTIVE) ? _guiColor[COLOR_WINDOW_TITLE_ACTIVE] : _guiColor[COLOR_WINDOW_TITLE_INACTIVE]; PixelT text = GUI_GET_FLAG(widget, WIDGET_FLAG_ACTIVE) ? _guiColor[COLOR_WINDOW_TITLE_TEXT_ACTIVE] : _guiColor[COLOR_WINDOW_TITLE_TEXT_INACTIVE]; if (GUI_GET_FLAG(widget, WIDGET_FLAG_DIRTY)) { vbeSurfaceSet(w->base.surface); // Background. vbeSurfaceClear(_guiColor[COLOR_WINDOW_BACKGROUND]); // Outer edge. guiDrawHighlightFrame(0, 0, x2, y2, _guiColor[COLOR_WINDOW_HIGHLIGHT], _guiColor[COLOR_WINDOW_SHADOW]); // Inner edge - skip METRIC_WINDOW_BORDER_WIDTH pixels. Be sure shadow and highlight are not included in the width. guiDrawHighlightFrame(_guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 2, _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 2, x2 - _guiMetric[METRIC_WINDOW_BORDER_WIDTH] - 2, y2 - _guiMetric[METRIC_WINDOW_BORDER_WIDTH] - 2, _guiColor[COLOR_WINDOW_SHADOW], _guiColor[COLOR_WINDOW_HIGHLIGHT]); // Title bar - METRIC_WINDOW_TITLE_HEIGHT pixels high. Be sure shadow and highlight are not included in the width. guiDrawHighlightFrame(_guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 3, _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 3, x2 - _guiMetric[METRIC_WINDOW_BORDER_WIDTH] - 3, _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + _guiMetric[METRIC_WINDOW_TITLE_HEIGHT] + 4, _guiColor[COLOR_WINDOW_HIGHLIGHT], _guiColor[COLOR_WINDOW_SHADOW]); guiDrawFilledRectangle(_guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 4, _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 4, x2 - _guiMetric[METRIC_WINDOW_BORDER_WIDTH] - 4, _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + _guiMetric[METRIC_WINDOW_TITLE_HEIGHT] + 2, background); fontRender(_guiFont, w->title, text, background, _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 16, _guiMetric[METRIC_WINDOW_BORDER_WIDTH] + 5); GUI_CLEAR_FLAG(widget, WIDGET_FLAG_DIRTY); } } void windowSetActive(WindowT *window) { size_t len; int16_t i; WidgetT *parent = W(window)->parent; WidgetT *child; // Deactivate all windows. windowDeactivateAll(guiRootGet()); // Activate us. GUI_SET_FLAG(W(window), WIDGET_FLAG_ACTIVE); // Find our window in the widget list. len = arrlenu(parent->children); for (i=len-1; i>=0; i--) { child = parent->children[i]; if (child == W(window)) { // Move to the top of the widget list. arrdel(parent->children, i); arrput(parent->children, child); break; } } // Tell the GUI. _guiActiveWindow = window; } void windowSetTitle(WindowT *window, char *title) { if (window->title) free(window->title); window->title = strdup(title); }