#include "wmwindow.h" #include "array.h" #include "font.h" #define USE_CACHING uint8_t __MAGIC_WINDOW = 0; static WindowT **_windowList = NULL; static WindowT *_windowTop = NULL; WindowT *windowCreate(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *title, uint8_t flags, ...) { WindowT *win = NULL; NEW(WindowT, win); guiWidgetBaseSet((WidgetT *)win, __MAGIC_WINDOW, x, y, w, h); win->title = strdup(title); win->flags = flags; win->close.x = win->close.y = win->close.x2 = win->close.y2 = 0; win->titlebar.x = win->titlebar.y = win->titlebar.x2 = win->titlebar.y2 = 0; win->minimize.x = win->minimize.y = win->minimize.x2 = win->minimize.y2 = 0; win->maximize.x = win->maximize.y = win->maximize.x2 = win->maximize.y2 = 0; win->bounds.x = win->bounds.y = win->bounds.x2 = win->bounds.y2 = 0; win->cached = NULL; arrput(_windowList, win); windowFocusSet(win); return win; } void windowDestroy(struct WidgetS *widget, ...) { uint16_t i; WindowT *window = (WindowT *)widget; // Find the window to delete. for (i=0; ititle) DEL(_windowList[i]->title); // Free cached surface. if (_windowList[i]->cached) surfaceDestroy(&_windowList[i]->cached); // Delete the window. DEL(_windowList[i]); // Remove it from window list. arrdel(_windowList, i); // Fixup focus. windowFocusSet(NULL); break; } } } void windowFocusSet(WindowT *win) { int16_t i; // Do we have a focused window at the moment? if (!_windowTop || !win) { // If there's a list of windows, use the topmost. if (arrlen(_windowList) > 0) { _windowTop = _windowList[arrlen(_windowList) - 1]; guiWidgetDirtySet(W(_windowTop), 1); } else { // If no list, set it to NULL. _windowTop = NULL; } return; } // Did they even pass in a window? If not, we're just intended to fixup _windowTop. if (!win) return; // Are we already the topmost window? if (win == _windowTop) return; // Mark old focus and new focus dirty to repaint title bar. guiWidgetDirtySet(W(win), 1); guiWidgetDirtySet(W(_windowTop), 1); // Change who has focus. _windowTop = win; // Reorder window list. i = arrlen(_windowList) - 2; if (i >= 0) { for (; i>=0; i--) { if (_windowList[i] == win) { arrdel(_windowList, i); arrput(_windowList, win); break; } } } } void windowMoveTo(WindowT *w, uint16_t x, uint16_t y) { int16_t dx = x - w->base.r.x; int16_t dy = y - w->base.r.y; // This is all because we draw the window at 0,0 in order to cache it. // To keep the coordinates correct, we have to adjust them all when // the window is moved. if (w->title || w->flags & WIN_CLOSE || w->flags & WIN_MAXIMIZE || w->flags & WIN_MINIMIZE) { if (w->flags & WIN_CLOSE) { w->close.x += dx; w->close.y += dy; w->close.x2 += dx; w->close.y2 += dy; } if (w->flags & WIN_MAXIMIZE) { w->maximize.x += dx; w->maximize.y += dy; w->maximize.x2 += dx; w->maximize.y2 += dy; } if (w->flags & WIN_MINIMIZE) { w->minimize.x += dx; w->minimize.y += dy; w->minimize.x2 += dx; w->minimize.y2 += dy; } w->titlebar.x += dx; w->titlebar.y += dy; w->titlebar.x2 += dx; w->titlebar.y2 += dy; } w->bounds.x += dx; w->bounds.y += dy; w->bounds.x2 += dx; w->bounds.y2 += dy; w->base.r.x = x; w->base.r.y = y; } void windowPaint(struct WidgetS *widget, ...) { int16_t i; int16_t x1; int16_t y1; int16_t x2; int16_t y2; int16_t tx1; int16_t tx2; int16_t ty2; int16_t originalX; int16_t originalY; WindowT *w; SurfaceT *target; ColorT titleBackgroundColor; ColorT widgetColor; target = surfaceGet(); w = (WindowT *)widget; #ifdef USE_CACHING // Do we need redrawn? if (guiWidgetDirtyGet(widget)) { guiWidgetDirtySet(widget, 0); // Move the window to 0,0 to cache to it's own surface. originalX = w->base.r.x; originalY = w->base.r.y; w->base.r.x = 0; w->base.r.y = 0; // Do we have a cached surface already? if (w->cached) { // Did the size change? if ((surfaceWidthGet(w->cached) != w->base.r.w) || (surfaceHeightGet(w->cached) != w->base.r.h)) { // Yeah. We will recreate it. surfaceDestroy(&w->cached); } } // Do we need to create a surface? if (!w->cached) { w->cached = surfaceCreate(w->base.r.w, w->base.r.h); } // Draw into cache. surfaceSet(w->cached); #endif // Determine some colors. titleBackgroundColor = (w == _windowTop) ? GUI_DARKGRAY : GUI_LIGHTGRAY; widgetColor = (w == _windowTop) ? GUI_LIGHTGRAY : GUI_DARKGRAY; // Get ready! x1 = w->base.r.x; y1 = w->base.r.y; x2 = w->base.r.x + w->base.r.w - 1; y2 = w->base.r.y + w->base.r.h - 1; // Draw border. surfaceBoxHighlight(x1, y1, x2, y2, GUI_WHITE, GUI_BLACK); x1++; y1++; x2--; y2--; for (i=0; i<3; i++) { surfaceBox(x1, y1, x2, y2, GUI_LIGHTGRAY); x1++; y1++; x2--; y2--; } surfaceBoxHighlight(x1, y1, x2, y2, GUI_BLACK, GUI_WHITE); x1++; y1++; x2--; y2--; // Do we need a titlebar? if (w->title || w->flags & WIN_CLOSE || w->flags & WIN_MAXIMIZE || w->flags & WIN_MINIMIZE) { tx1 = x1; tx2 = x2; ty2 = y1 + 20; // Close box? if (w->flags & WIN_CLOSE) { tx1 += 20; w->close.x = x1; w->close.y = y1; w->close.x2 = tx1 - 1; w->close.y2 = ty2 - 1; surfaceBoxFilled(w->close.x + 1, w->close.y + 1, w->close.x2 - 1, w->close.y2 - 1, titleBackgroundColor); surfaceBoxHighlight(w->close.x + 3, w->close.y + 8, w->close.x2 - 3, w->close.y2 - 8, GUI_WHITE, widgetColor); surfaceBoxHighlight(w->close.x, w->close.y, w->close.x2, w->close.y2, GUI_WHITE, GUI_BLACK); } else { w->close.x = w->close.y = w->close.x2 = w->close.y2 = 0; } // Maximize box? if (w->flags & WIN_MAXIMIZE) { tx2 -= 20; w->maximize.x = tx2 + 1; w->maximize.y = y1; w->maximize.x2 = tx2 + 20; w->maximize.y2 = ty2 - 1; surfaceBoxFilled(w->maximize.x + 1, w->maximize.y + 1, w->maximize.x2 - 1, w->maximize.y2 - 1, titleBackgroundColor); surfaceLine(w->maximize.x + 4, w->maximize.y + 10, w->maximize.x + 10, w->maximize.y + 4, GUI_WHITE); surfaceLine(w->maximize.x + 4, w->maximize.y + 10, w->maximize.x + 10, w->maximize.y2 - 3, GUI_WHITE); surfaceLine(w->maximize.x2 - 3, w->maximize.y + 10, w->maximize.x2 - 9, w->maximize.y + 4, widgetColor); surfaceLine(w->maximize.x2 - 3, w->maximize.y + 10, w->maximize.x2 - 9, w->maximize.y2 - 3, widgetColor); surfaceBoxHighlight(w->maximize.x, w->maximize.y, w->maximize.x2, w->maximize.y2, GUI_WHITE, GUI_BLACK); } else { w->maximize.x = w->maximize.y = w->maximize.x2 = w->maximize.y2 = 0; } // Minimize box? if (w->flags & WIN_MINIMIZE) { tx2 -= 20; w->minimize.x = tx2 + 1; w->minimize.y = y1; w->minimize.x2 = tx2 + 20; w->minimize.y2 = ty2 - 1; surfaceBoxFilled(w->minimize.x + 1, w->minimize.y + 1, w->minimize.x2 - 1, w->minimize.y2 - 1, titleBackgroundColor); surfaceBoxHighlight(w->minimize.x + 7, w->minimize.y + 7, w->minimize.x2 - 7, w->minimize.y2 - 7, GUI_WHITE, widgetColor); surfaceBoxHighlight(w->minimize.x, w->minimize.y, w->minimize.x2, w->minimize.y2, GUI_WHITE, GUI_BLACK); } else { w->minimize.x = w->minimize.y = w->minimize.x2 = w->maximize.y2 = 0; } // Draw titlebar background. w->titlebar.x = tx1; w->titlebar.y = y1; w->titlebar.x2 = tx2; w->titlebar.y2 = ty2 - 1; surfaceBoxFilled(w->titlebar.x + 1, w->titlebar.y + 1, w->titlebar.x2 - 1, w->titlebar.y2 - 1, titleBackgroundColor); if (w->title) { fontSet(__guiFontVGA8x16); fontColorSet(GUI_WHITE, titleBackgroundColor); fontRender(w->title, w->titlebar.x + 12, w->titlebar.y + 2); } surfaceBoxHighlight(w->titlebar.x, w->titlebar.y, w->titlebar.x2, w->titlebar.y2, GUI_WHITE, GUI_BLACK); } else { w->close.x = w->close.y = w->close.x2 = w->close.y2 = 0; w->maximize.x = w->maximize.y = w->maximize.x2 = w->maximize.y2 = 0; w->minimize.x = w->minimize.y = w->minimize.x2 = w->maximize.y2 = 0; w->titlebar.x = w->titlebar.y = w->titlebar.x2 = w->titlebar.y2 = 0; } // Find content area. y1 += 20; w->bounds.x = x1; w->bounds.y = y1; w->bounds.x2 = x2; w->bounds.y2 = y2; // Resize handle. if (w->flags & WIN_RESIZE) { x1 = w->base.r.x + w->base.r.w - 2; x2 = x1 - 3; y1 = w->base.r.y + w->base.r.h - 21; surfaceLineH(x1, x2, y1, GUI_BLACK); surfaceLineH(x1, x2, y1 + 1, GUI_WHITE); x1 = w->base.r.x + w->base.r.w - 21; y1 = w->base.r.y + w->base.r.h - 2; y2 = y1 - 3; surfaceLineV(x1, y1, y2, GUI_BLACK); surfaceLineV(x1 + 1, y1, y2, GUI_WHITE); } // Fake Window contents. surfaceBoxFilled(w->bounds.x, w->bounds.y, w->bounds.x2, w->bounds.y2, GUI_BLACK); #ifdef USE_CACHING // Fixup all the widget coordinates. windowMoveTo(w, originalX, originalY); } // By now we have a valid cached window. Blit it. surfaceBlit(target, w->base.r.x, w->base.r.y, w->cached); #endif } RegisterT *windowRegister(uint8_t magic) { static RegisterT reg = { "Window", windowPaint, windowDestroy, NULL }; // One-time widget startup code. __MAGIC_WINDOW = magic; return ® } void wmPaint(EventT *event) { int16_t i; int16_t x2; int16_t y2; WidgetT *widget; WindowT *win; static uint8_t dragging = 0; static PointT dragOffset = { 0 }; // Do we have windows? if (arrlen(_windowList) > 0) { // Paint all windows. for (i=0; ireg->paint(widget); } // Get top window. win = _windowList[arrlen(_windowList) - 1]; // Get right/bottom of window. x2 = win->base.r.x + win->base.r.w - 1; y2 = win->base.r.y + win->base.r.h - 1; // Wrap left button processing with a 'for' so we can 'break' out of it. for (;;) { // Is the left mouse button down? if (event->buttons & BUTTON_LEFT) { // DEBUG - draw active regions. ***TODO*** No resize grabber here. surfaceSet(__guiBackBuffer); surfaceBox(win->base.r.x, win->base.r.y, x2, y2, GUI_YELLOW); surfaceBox(win->bounds.x, win->bounds.y, win->bounds.x2, win->bounds.y2, GUI_CYAN); surfaceBox(win->close.x, win->close.y, win->close.x2, win->close.y2, GUI_LIGHTBLUE); surfaceBox(win->titlebar.x, win->titlebar.y, win->titlebar.x2, win->titlebar.y2, GUI_LIGHTCYAN); surfaceBox(win->minimize.x, win->minimize.y, win->minimize.x2, win->minimize.y2, GUI_LIGHTGREEN); surfaceBox(win->maximize.x, win->maximize.y, win->maximize.x2, win->maximize.y2, GUI_RED); // Are we currently dragging? if (dragging) { // Move window to new mouse location. windowMoveTo(win, event->x - dragOffset.x, event->y - dragOffset.y); break; } else { // Dragging. // Did the button just go down? if (event->flags & EVENT_FLAG_LEFT_DOWN) { // Are we on the topmost window? if (event->x <= x2 && event->x >= win->base.r.x && event->y <= y2 && event->y >= win->base.r.y) { //***TODO*** Are we inside the window content? Most likely, check first. if (event->x <= win->bounds.x2 && event->x >= win->bounds.x && event->y <= win->bounds.y2 && event->y >= win->bounds.y) { //***TODO*** Send to window for processing. } //***TODO*** Are we inside the close button? // Are we inside the title bar to begin dragging? if (event->x <= win->titlebar.x2 && event->x >= win->titlebar.x && event->y <= win->titlebar.y2 && event->y >= win->titlebar.y) { dragging = 1; dragOffset.x = event->x - win->base.r.x; dragOffset.y = event->y - win->base.r.y; break; } //***TODO*** Are we inside the minimize button? //***TODO*** Are we inside the maximize button? } else { // On topmost window. // Not over topmost window. Search backwards to find first window we're inside. i = arrlen(_windowList) - 2; if (i >= 0) { for (; i>=0; i--) { win = _windowList[i]; // Get right/bottom of window. x2 = win->base.r.x + win->base.r.w - 1; y2 = win->base.r.y + win->base.r.h - 1; // Inside this window? if (event->x <= x2 && event->x >= win->base.r.x && event->y <= y2 && event->y >= win->base.r.y) { // Bring this window forward. windowFocusSet(win); // If we happened to be in the title bar, go ahead and start dragging. if (event->x <= win->titlebar.x2 && event->x >= win->titlebar.x && event->y <= win->titlebar.y2 && event->y >= win->titlebar.y) { dragging = 1; dragOffset.x = event->x - win->base.r.x; dragOffset.y = event->y - win->base.r.y; } break; } } } } // On topmost window. } // Button just went down. } // Dragging. } else { // Left mouse button. // Left mouse not down. // Can no longer be dragging. dragging = 0; } break; } // Left button processing. } // Do we have windows? } void wmShutdown(void) { while (arrlen(_windowList) > 0) { windowDestroy((WidgetT *)_windowList[0]); } arrfree(_windowList); } void wmStartup(void) { }