#include "wmwindow.h" #include "array.h" #include "font.h" #define ICON_SIZE 32 uint8_t __MAGIC_WINDOW = 0; static WindowT **_windowList = NULL; static WindowT *_windowTop = NULL; static int16_t _iconCount = 0; static void windowCache(WindowT *w); static void windowCache(WindowT *w) { char c; 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; ColorT titleBackgroundColor; ColorT widgetColor; SurfaceT *target = surfaceGet(); // Move the window to 0,0 to draw it into 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); // 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); // How many characters do we have room for? i = ((w->titlebar.x2 - w->titlebar.x) >> 3) - 2; // Does the title fit? if ((int16_t)strlen(w->title) <= i) { // Render entire title. fontRender(w->title, w->titlebar.x + 12, w->titlebar.y + 2); } else { // Render partial title. c = w->title[i]; w->title[i] = 0; fontRender(w->title, w->titlebar.x + 12, w->titlebar.y + 2); w->title[i] = c; } } 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) { w->resize1.x2 = w->base.r.x + w->base.r.w - 2; w->resize1.x = w->resize1.x2 - 3; w->resize1.y2 = w->base.r.y + w->base.r.h; w->resize1.y = w->resize1.y2 - 21; surfaceLineH(w->resize1.x, w->resize1.x2, w->resize1.y, GUI_BLACK); surfaceLineH(w->resize1.x, w->resize1.x2, w->resize1.y + 1, GUI_WHITE); w->resize2.x2 = w->base.r.x + w->base.r.w; w->resize2.x = w->resize2.x2 - 21; w->resize2.y2 = w->base.r.y + w->base.r.h - 2; w->resize2.y = w->resize2.y2 - 3; surfaceLineV(w->resize2.x, w->resize2.y, w->resize2.y2, GUI_BLACK); surfaceLineV(w->resize2.x + 1, w->resize2.y, w->resize2.y2, GUI_WHITE); } else { w->resize1.x = w->resize1.y = w->resize1.x2 = w->resize1.y2 = 0; w->resize2.x = w->resize2.y = w->resize2.x2 = w->resize2.y2 = 0; } // Blit contents. if (w->content) surfaceBlit(w->bounds.x, w->bounds.y, w->bounds.x2 - w->bounds.x, w->bounds.y2 - w->bounds.y, w->content); // Fixup all the widget coordinates. windowMoveTo(w, originalX, originalY); surfaceSet(target); } #include "image.h" // Passing "flags" as a default int provides proper alignment for the following va_args list. WindowT *windowCreate(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *title, int flags, ...) { int16_t width; int16_t height; va_list args; WindowT *win = NULL; SurfaceT *t = surfaceGet(); NEW(WindowT, win); memset(win, 0, sizeof(WindowT)); guiWidgetBaseSet((WidgetT *)win, __MAGIC_WINDOW, x, y, w, h); win->title = strdup(title); win->flags = (uint8_t)flags; //***DEBUG*** Hackery to get contents before we have widgets. static uint8_t image = 1; static char name[16]; width = 640; height = 480; sprintf(name, "back%d.png", image++); win->content = imageLoad(name); win->flags |= WIN_RESIZE; /* // If the window is resizable, we need to get two more arguments for the content size. if (win->flags & WIN_RESIZE) { va_start(args, flags); width = va_arg(args, int); height = va_arg(args, int); va_end(args); } else { // Use whatever the default content area is. This causes an extra draw on create. Oh well. windowCache(win); width = win->bounds.x2 - win->bounds.x; height = win->bounds.y2 - win->bounds.y; guiWidgetDirtySet(W(win), 1); } // Create content surface and clear it. win->content = surfaceCreate(width, height); surfaceSet(win->content); surfaceClear(GUI_LIGHTGRAY); surfaceSet(t); */ // Do we need scrollbars? if (width > win->bounds.x2 - win->bounds.x) win->flags |= WIN_SCROLL_H; if (height > win->bounds.y2 - win->bounds.y) win->flags |= WIN_SCROLL_V; // Add to window list. arrput(_windowList, win); // Focus us. 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); // Free content surface. if (_windowList[i]->content) surfaceDestroy(&_windowList[i]->content); // 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) { _windowTop = NULL; // If there's a list of windows, use the topmost non-minimized. if (arrlen(_windowList) > 0) { // Work backwards through the window list. i = arrlen(_windowList) - 1; for (; i>=0; i--) { if (_windowList[i]->flags & WIN_IS_ICON) { // Minimized window. Skip it. continue; } else { // Open window. Use as new top. _windowTop = _windowList[i]; guiWidgetDirtySet(W(_windowTop), 1); break; } } } } // Did they even pass in a window? If not, we're just intended to fixup _windowTop. if (!win) return; // Were we already the topmost window? if (win == _windowTop) return; // Is this window minimized? if (win->flags & WIN_IS_ICON) { // Can't receive focus - find new top. windowFocusSet(NULL); 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 windowMaximizeRestore(WindowT *win) { int16_t i; // Are we maximized? if (win->flags & WIN_IS_MAX) { // Restore to previous size. win->flags &= ~WIN_IS_MAX; win->base.r = win->restore; } else { // Maximized? // Maximize window. Reposition if needed. win->flags |= WIN_IS_MAX; // Remember current size and position. win->restore = win->base.r; // Expand to full contents. win->base.r.w = (win->base.r.w - (win->bounds.x2 - win->bounds.x) + surfaceWidthGet(win->content)); win->base.r.h = (win->base.r.h - (win->bounds.y2 - win->bounds.y) + surfaceHeightGet(win->content)); // Does this go off the screen to the right? if (win->base.r.x + win->base.r.w >= videoDisplayWidthGet()) { // Can we move it left to fit? i = videoDisplayWidthGet() - (win->base.r.x + win->base.r.w); if (i <= win->base.r.x) { // Yes, go left! win->base.r.x += i; } else { // Nope. Go all the way left and resize to fit on desktop. win->base.r.x = 0; win->base.r.w = videoDisplayWidthGet(); } } // Does this go off the screen to the bottom? if (win->base.r.y + win->base.r.h >= videoDisplayHeightGet()) { // Can we move it up to fit? i = videoDisplayHeightGet() - (win->base.r.y + win->base.r.h); if (i <= win->base.r.y) { // Yes, go up! win->base.r.y += i; } else { // Nope. Go all the way up and resize to fit on desktop. win->base.r.y = 0; win->base.r.h = videoDisplayHeightGet(); } } } // Maximized? // Update. windowCache(win); } void windowMinimize(WindowT *win) { // Minimize. win->flags |= WIN_IS_ICON; // Find topmost non-minimized window. windowFocusSet(NULL); } 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; } if (w->flags & WIN_RESIZE) { w->resize1.x += dx; w->resize1.y += dy; w->resize1.x2 += dx; w->resize1.y2 += dy; w->resize2.x += dx; w->resize2.y += dy; w->resize2.x2 += dx; w->resize2.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 x; int16_t y; int16_t px; int16_t py; int16_t xi; int16_t yi; int16_t xc; int16_t yc; WindowT *w = (WindowT *)widget; // Do we need redrawn? if (guiWidgetDirtyGet(widget)) { guiWidgetDirtySet(widget, 0); windowCache(w); } // Are we minimized? if (w->flags & WIN_IS_ICON) { // Draw iconized version of contents. There are too many counters here but it's the only way it worked reliably. yi = (surfaceHeightGet(w->content) - 1) / ICON_SIZE; xi = (surfaceWidthGet(w->content) - 1) / ICON_SIZE; py = videoDisplayHeightGet() - ICON_SIZE; y = 0; x = 0; for (yc=0; yccontent, x, y)); } py++; } _iconCount++; } else { // By now we have a valid cached window. Blit it. surfaceBlit(w->base.r.x, w->base.r.y, 0, 0, w->cached); } } RegisterT *windowRegister(uint8_t magic) { static RegisterT reg = { "Window", windowPaint, windowDestroy, NULL }; // One-time widget startup code. __MAGIC_WINDOW = magic; return ® } void windowResizeTo(WindowT *win, uint16_t width, uint16_t height) { // Too small? if (width < 95) width = 95; if (height < 80) height = 80; // Too big? if (win->bounds.w - win->bounds.x + (width - win->base.r.w) > surfaceWidthGet(win->content)) width = (win->base.r.w - (win->bounds.x2 - win->bounds.x) + surfaceWidthGet(win->content)); if (win->bounds.h - win->bounds.y + (height - win->base.r.h) > surfaceHeightGet(win->content)) height = (win->base.r.h - (win->bounds.y2 - win->bounds.y) + surfaceHeightGet(win->content)); // Did the size change? if (win->base.r.w != width || win->base.r.h !=height) { // Yes! If we were maximized, clear it - can't restore after resizing. win->flags &= ~WIN_IS_MAX; // Do resize. win->base.r.w = width; win->base.r.h =height; windowCache(win); } } void wmShutdown(void) { while (arrlen(_windowList) > 0) { windowDestroy((WidgetT *)_windowList[0]); } arrfree(_windowList); } void wmStartup(void) { // Nada } void wmUpdate(EventT *event) { int16_t i; int16_t x2; int16_t y2; uint8_t onResize = 0; WidgetT *widget; WindowT *win = NULL; static uint8_t resizing = 0; static PointT resizeOffset = { 0 }; static uint8_t dragging = 0; static PointT dragOffset = { 0 }; // Do we have windows? if (arrlen(_windowList) > 0) { // Mouse is always the default pointer unless something changes it for this frame. guiMousePointerSet(MOUSE_POINTER); // Paint all windows. _iconCount = 0; for (i=0; ireg->paint(widget); } // Get top window. win = _windowTop; // If we found a window, get right/bottom. if (win) { 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 (;;) { // Does the topmost window have a resize? if (win && win->flags & WIN_RESIZE) { // Are we over the resize of the topmost window? if ((event->x <= win->resize1.x2 && event->x >= win->resize1.x && event->y <= win->resize1.y2 && event->y >= win->resize1.y) || (event->x <= win->resize2.x2 && event->x >= win->resize2.x && event->y <= win->resize2.y2 && event->y >= win->resize2.y) || resizing) { onResize = 1; guiMousePointerSet(MOUSE_RESIZE); } } // 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. if (resizing) { // If they try to resize outside the window bounds, cancel it. if (event->x < win->base.r.x || event->y < win->base.r.y) break; // Resize it. windowResizeTo(win, event->x - resizeOffset.x, event->y - resizeOffset.y); break; } else { // Resizing. // Did the button just go down? if (event->flags & EVENT_FLAG_LEFT_DOWN) { // Are we on the topmost window? if (win && event->x <= x2 && event->x >= win->base.r.x && event->y <= y2 && event->y >= win->base.r.y) { // Are we on the resizing area? if (onResize) { resizing = 1; resizeOffset.x = event->x - win->base.r.w; resizeOffset.y = event->y - win->base.r.h; break; } // 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. } // Are we inside the close button? Does not include button frame on purpose. if (win->flags & WIN_CLOSE && event->x < win->close.x2 && event->x > win->close.x && event->y < win->close.y2 && event->y > win->close.y) { //***TODO*** Close. } // Are we inside the title bar to begin dragging? Does not include titlebar frame on purpose. 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; } // Are we inside the minimize button? Does not include button frame on purpose. if (win->flags & WIN_MINIMIZE &&event->x < win->minimize.x2 && event->x > win->minimize.x && event->y < win->minimize.y2 && event->y > win->minimize.y) { windowMinimize(win); break; } // Are we inside the maximize button? Does not include button frame on purpose. if (win->flags & WIN_MAXIMIZE &&event->x < win->maximize.x2 && event->x > win->maximize.x && event->y < win->maximize.y2 && event->y > win->maximize.y) { windowMaximizeRestore(win); break; } } else { // On topmost window. // Not over topmost window. Search backwards to find first window we're inside. i = arrlen(_windowList) - 1; if (i >= 0) { for (; i>=0; i--) { win = _windowList[i]; // Is this the current window? if (win == _windowTop) continue; // Is this window minimized? if (win->flags & WIN_IS_ICON) continue; // 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. Does not include titlebar frame on purpose. 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. // Do we have minimized windows? if (_iconCount > 0) { // Are we inside the minimized icons area? if (event->x < (ICON_SIZE + 1) * _iconCount && event->y >= videoDisplayHeightGet() - ICON_SIZE) { // Figure out which icon we're over. x2 = 0; for (i=0; iflags & WIN_IS_ICON) { x2 += ICON_SIZE; // This window? if (event->x < x2) { // Restore it and stop looking for icons. _windowList[i]->flags &= ~WIN_IS_ICON; windowFocusSet(_windowList[i]); break; } // Next! (Skips pixel between icons.) x2++; } } } // Minimized windows? } } } // Button just went down. } // Dragging. } else { // Left mouse button. // Left mouse not down. // Can no longer be dragging. dragging = 0; // Can no longer be resizing. resizing = 0; } break; } // Left button processing. } // Do we have windows? }