#include "wmwindow.h" #include "array.h" #include "font.h" #define ICON_SIZE 32 #define WIDGET_SIZE 20 #define SCROLL_SPEED_SLOW 5 // ***TODO*** Move into GUI after we get scrollbar widgets. #define SCROLL_SPEED_FAST 25 uint8_t __MAGIC_WINDOW = 0; static WindowT **_windowList = NULL; static WindowT *_windowTop = NULL; static int16_t _iconCount = 0; static void windowCache(WindowT *w, uint8_t redrawWindow); static void windowDestroy(struct WidgetS *widget, ...); static void windowPaint(struct WidgetS *widget, ...); static void windowScrollHorizontalHandler(EventT *e, WindowT *w); static void windowScrollVerticalHandler(EventT *e, WindowT *w); static void windowCache(WindowT *w, uint8_t redrawWindow) { 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; double d; RectT originalBounds; 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); if (redrawWindow) { // 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 + WIDGET_SIZE; // Close box? if (w->flags & WIN_CLOSE) { tx1 += WIDGET_SIZE; 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 -= WIDGET_SIZE; w->maximize.x = tx2 + 1; w->maximize.y = y1; w->maximize.x2 = tx2 + WIDGET_SIZE; 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 -= WIDGET_SIZE; w->minimize.x = tx2 + 1; w->minimize.y = y1; w->minimize.x2 = tx2 + WIDGET_SIZE; 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) { // Prepare font. Don't allow color changes in titles. fontSet(__guiFontVGA8x16); fontColorSet(GUI_WHITE, titleBackgroundColor); fontModsEnabledSet(0); // 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); y1 += WIDGET_SIZE; } 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; } // Vertical Scroll Bar? if (w->flags & WIN_SCROLL_V) { w->scrollv.x2 = x2; w->scrollv.x = w->scrollv.x2 - (WIDGET_SIZE - 1); w->scrollv.y = y1; w->scrollv.y2 = y2; // Draw scroll bar. surfaceBoxFilled(w->scrollv.x + 1, w->scrollv.y + 1, w->scrollv.x2 - 1, w->scrollv.y2 - 1, titleBackgroundColor); surfaceBoxHighlight(w->scrollv.x, w->scrollv.y, w->scrollv.x2, w->scrollv.y2, GUI_WHITE, GUI_BLACK); surfaceLineH(w->scrollv.x + 1, w->scrollv.x2, w->scrollv.y + (WIDGET_SIZE - 1), GUI_BLACK); surfaceLineH(w->scrollv.x + 1, w->scrollv.x2, w->scrollv.y2 - (WIDGET_SIZE - 1), GUI_WHITE); // Prepare font. fontSet(__guiFontVGA8x8); fontColorSet(widgetColor, titleBackgroundColor); // Draw arrows. fontRender("\x1e", w->scrollv.x + 7, w->scrollv.y + 7); fontRender("\x1f", w->scrollv.x + 7, w->scrollv.y2 - 12); x2 -= (WIDGET_SIZE - 1); } else { w->scrollv.x = w->scrollv.y = w->scrollv.x2 = w->scrollv.y2 = 0; } // Horizontal Scroll Bar? if (w->flags & WIN_SCROLL_H) { w->scrollh.x = x1; w->scrollh.x2 = x2; w->scrollh.y2 = y2; w->scrollh.y = w->scrollh.y2 - (WIDGET_SIZE - 1); // Draw scroll bar. surfaceBoxFilled(w->scrollh.x + 1, w->scrollh.y + 1, w->scrollh.x2 - 1, w->scrollh.y2 - 1, titleBackgroundColor); surfaceBoxHighlight(w->scrollh.x, w->scrollh.y, w->scrollh.x2, w->scrollh.y2, GUI_WHITE, GUI_BLACK); surfaceLineV(w->scrollh.x + (WIDGET_SIZE - 1), w->scrollh.y + 1, w->scrollh.y2, GUI_BLACK); surfaceLineV(w->scrollh.x2 - (WIDGET_SIZE - 1), w->scrollh.y + 1, w->scrollh.y2, GUI_WHITE); // If we have both horizontal and vertical scroll bars, we need to fix a shadow. if (w->flags & WIN_SCROLL_V) { surfaceLineV(w->scrollh.x2 - 1, w->scrollh.y, w->scrollh.y2, GUI_BLACK); surfaceLineV(w->scrollh.x2, w->scrollh.y, w->scrollh.y2, GUI_WHITE); } // Prepare font. fontSet(__guiFontVGA8x8); fontColorSet(widgetColor, titleBackgroundColor); // Draw arrows. fontRender("\x11", w->scrollh.x + 7, w->scrollh.y + 7); fontRender("\x10", w->scrollh.x2 - 12, w->scrollh.y + 7); y2 -= (WIDGET_SIZE - 1); } else { w->scrollh.x = w->scrollh.y = w->scrollh.x2 = w->scrollh.y2 = 0; } // Find content area. w->bounds.x = x1; w->bounds.y = y1; w->bounds.x2 = x2; w->bounds.y2 = y2; // Do we have content yet? if (w->content) { // Vertical Scroll Bar Thumb? if (w->flags & WIN_SCROLL_V) { // Distance between arrow buttons on scroll bar. i = w->scrollv.y2 - w->scrollv.y - (WIDGET_SIZE * 2 - 2) - 2; // Percentage to scale content height to scroll bar height. d = (double)i / (double)surfaceHeightGet(w->content); // Find position and size of thumb. w->thumbv.x = w->scrollv.x + 1; w->thumbv.x2 = w->scrollv.x2 - 1; w->thumbv.y = w->scrollv.y + WIDGET_SIZE + (w->offset.y * d); w->thumbv.y2 = w->thumbv.y + ((w->bounds.y2 - w->bounds.y) * d); // Clamp overflow due to doubles and my off-by-one brain. if (w->thumbv.y2 >= w->thumbv.y + i - 1) w->thumbv.y2 = w->thumbv.y + i - 1; // Draw thumb. surfaceBoxFilled(w->thumbv.x + 1, w->thumbv.y + 1, w->thumbv.x2 - 1, w->thumbv.y2 - 1, widgetColor); surfaceBoxHighlight(w->thumbv.x, w->thumbv.y, w->thumbv.x2, w->thumbv.y2, GUI_WHITE, GUI_BLACK); } else { w->thumbv.x = w->thumbv.y = w->thumbv.x2 = w->thumbv.y2 = 0; } // Horizontal Scroll Bar Thumb? if (w->flags & WIN_SCROLL_H) { // Distance between arrow buttons on scroll bar. i = w->scrollh.x2 - w->scrollh.x - (WIDGET_SIZE * 2 - 2) - 2; // Percentage to scale content width to scroll bar width. d = (double)i / (double)surfaceWidthGet(w->content); // Find position and size of thumb. w->thumbh.x = w->scrollh.x + WIDGET_SIZE + (w->offset.x * d); w->thumbh.x2 = w->thumbh.x + ((w->bounds.x2 - w->bounds.x) * d); w->thumbh.y = w->scrollh.y + 1; w->thumbh.y2 = w->scrollh.y2 - 1; // Clamp overflow due to doubles and my off-by-one brain. if (w->thumbh.x2 >= w->thumbh.x + i - 1) w->thumbh.x2 = w->thumbh.x + i - 1; // Draw thumb. surfaceBoxFilled(w->thumbh.x + 1, w->thumbh.y + 1, w->thumbh.x2 - 1, w->thumbh.y2 - 1, widgetColor); surfaceBoxHighlight(w->thumbh.x, w->thumbh.y, w->thumbh.x2, w->thumbh.y2, GUI_WHITE, GUI_BLACK); } else { w->thumbh.x = w->thumbh.y = w->thumbh.x2 = w->thumbh.y2 = 0; } } else { // Do we have content yet? w->thumbv.x = w->thumbv.y = w->thumbv.x2 = w->thumbv.y2 = 0; w->thumbh.x = w->thumbh.y = w->thumbh.x2 = w->thumbh.y2 = 0; } // 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 - (WIDGET_SIZE + 5); 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 - (WIDGET_SIZE + 5); 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; } } else { // Redraw window. // Since we didn't calculate them above, adjust bounds for content blit. originalBounds = w->bounds; w->bounds.x -= originalX; w->bounds.y -= originalY; w->bounds.x2 -= originalX; w->bounds.y2 -= originalY; } // Blit contents. if (w->content) surfaceBlit(w->bounds.x, w->bounds.y, w->offset.x, w->offset.y, w->bounds.x2 - w->bounds.x, w->bounds.y2 - w->bounds.y, w->content); // Fixup all the widget coordinates. if (redrawWindow) { windowMove(w, originalX, originalY); } else { w->bounds = originalBounds; w->base.r.x = originalX; w->base.r.y = 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]; windowCache(win, 1); width = win->bounds.x2 - win->bounds.x; height = win->bounds.y2 - win->bounds.y; guiWidgetDirtySet(W(win), 1); sprintf(name, "back%d.png", image); win->content = imageLoad(name); if (image == 3) { win->offset.x = 100;//(surfaceWidthGet(win->content) - width) * 0.5; win->offset.y = 100;//(surfaceHeightGet(win->content) - height) * 0.5; } image++; */ // 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, 1); 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); // Add to window list. arrput(_windowList, win); // Focus us. windowFocusSet(win); return win; } static void windowDestroy(struct WidgetS *widget, ...) { uint16_t i; uint16_t c; WindowT *window = (WindowT *)widget; // Remove it from the window list, if it exists there. for (i=0; ititle) DEL(window->title); // Free children. for (c=0; cchildren); c++) guiWidgetDestroy(window->children[c]); arrfree(window->children); // Free cached surface. if (window->cached) surfaceDestroy(&window->cached); // Free content surface. if (window->content) surfaceDestroy(&window->content); // Delete the window. DEL(window); // Fixup focus. windowFocusSet(NULL); } 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; win->offset = win->restoreOffset; } else { // Maximized? // Maximize window. Reposition if needed. win->flags |= WIN_IS_MAX; // Remember current size and position. win->restore = win->base.r; win->restoreOffset = win->offset; // Scroll contents to home. win->offset.x = 0; win->offset.y = 0; // 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, 1); } void windowMinimize(WindowT *win) { // Minimize. win->flags |= WIN_IS_ICON; // Find topmost non-minimized window. windowFocusSet(NULL); } void windowMove(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_SCROLL_V) { w->scrollv.x += dx; w->scrollv.y += dy; w->scrollv.x2 += dx; w->scrollv.y2 += dy; w->thumbv.x += dx; w->thumbv.y += dy; w->thumbv.x2 += dx; w->thumbv.y2 += dy; } if (w->flags & WIN_SCROLL_H) { w->scrollh.x += dx; w->scrollh.y += dy; w->scrollh.x2 += dx; w->scrollh.y2 += dy; w->thumbh.x += dx; w->thumbh.y += dy; w->thumbh.x2 += dx; w->thumbh.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; } static 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; SurfaceT *t = surfaceGet(); // Are any child widgets dirty? y = 0; for (x=0; xchildren); x++) { if (w->children[x]->dirty) { y = 1; if (w->children[x]->reg->paint) { surfaceSet(w->content); w->children[x]->reg->paint(w->children[x]); } } } surfaceSet(t); // Does the window itself need redrawn? if (guiWidgetDirtyGet(widget)) { guiWidgetDirtySet(widget, 0); x = 1; } else { x = 0; } // Did a widget or the window need redrawn? if (x || y) windowCache(w, x); // 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, 0, 0, w->cached); } } RegisterT *windowRegister(uint8_t magic) { static RegisterT reg = { "Window", NULL, // Click event is special for windows. windowDestroy, // Destroy. windowPaint, // Paint. NULL // Unregister. }; // One-time widget startup code. __MAGIC_WINDOW = magic; return ® } void windowResize(WindowT *win, uint16_t width, uint16_t height) { int16_t delta; // Too small? if (width < (WIDGET_SIZE * 4) + 15) width = (WIDGET_SIZE * 4) + 15; if (height < (WIDGET_SIZE * 4) + 15) height = (WIDGET_SIZE * 4) + 15; // Too big? if (win->bounds.w - win->bounds.x + (width - win->base.r.w) + win->offset.x > surfaceWidthGet(win->content)) { // Do we have room to scroll content into view? if (win->offset.x > 0) { delta = width - win->base.r.w; if (delta > win->offset.x) delta = win->offset.x; win->offset.x -= delta; } width = (win->base.r.w - (win->bounds.x2 - win->bounds.x) - win->offset.x + surfaceWidthGet(win->content)); } if (win->bounds.h - win->bounds.y + (height - win->base.r.h) + win->offset.y > surfaceHeightGet(win->content)) { // Do we have room to scroll content into view? if (win->offset.y > 0) { delta = height - win->base.r.h; if (delta > win->offset.y) delta = win->offset.y; win->offset.y -= delta; } height = (win->base.r.h - (win->bounds.y2 - win->bounds.y) - win->offset.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, 1); } } static void windowScrollHorizontalHandler(EventT *e, WindowT *w) { // Clicking in left arrow? if (e->x <= w->scrollh.x + WIDGET_SIZE) { // Move content left. w->offset.x -= SCROLL_SPEED_SLOW; // Clip. if (w->offset.x < 0) w->offset.x = 0; // Update. windowCache(w, 1); return; } // Clicking in right arrow? if (e->x >= w->scrollh.x2 - WIDGET_SIZE) { // Move content right. w->offset.x += SCROLL_SPEED_SLOW; // Clip. if (w->offset.x > surfaceWidthGet(w->content) - (w->bounds.x2 - w->bounds.x)) w->offset.x = surfaceWidthGet(w->content) - (w->bounds.x2 - w->bounds.x); // Update. windowCache(w, 1); return; } // Clicking left of thumb? Also fakes dragging. if (e->x < w->thumbh.x) { // Move content left. w->offset.x -= SCROLL_SPEED_FAST; // Clip. if (w->offset.x < 0) w->offset.x = 0; // Update. windowCache(w, 1); return; } // Clicking right of thumb? Also fakes dragging. if (e->x > w->thumbh.x2) { // Move content right. w->offset.x += SCROLL_SPEED_FAST; // Clip. if (w->offset.x > surfaceWidthGet(w->content) - (w->bounds.x2 - w->bounds.x)) w->offset.x = surfaceWidthGet(w->content) - (w->bounds.x2 - w->bounds.x); // Update. windowCache(w, 1); return; } } static void windowScrollVerticalHandler(EventT *e, WindowT *w) { // Clicking in up arrow? if (e->y <= w->scrollv.y + WIDGET_SIZE) { // Move content up. w->offset.y -= SCROLL_SPEED_SLOW; // Clip. if (w->offset.y < 0) w->offset.y = 0; // Update. windowCache(w, 1); return; } // Clicking in down arrow? if (e->y >= w->scrollv.y2 - WIDGET_SIZE) { // Move content down. w->offset.y += SCROLL_SPEED_SLOW; // Clip. if (w->offset.y > surfaceHeightGet(w->content) - (w->bounds.y2 - w->bounds.y)) w->offset.y = surfaceHeightGet(w->content) - (w->bounds.y2 - w->bounds.y); // Update. windowCache(w, 1); return; } // Clicking above thumb? Also fakes dragging. if (e->y < w->thumbv.y) { // Move content up. w->offset.y -= SCROLL_SPEED_FAST; // Clip. if (w->offset.y < 0) w->offset.y = 0; // Update. windowCache(w, 1); return; } // Clicking below thumb? Also fakes dragging. if (e->y > w->thumbv.y2) { // Move content down. w->offset.y += SCROLL_SPEED_FAST; // Clip. if (w->offset.y > surfaceHeightGet(w->content) - (w->bounds.y2 - w->bounds.y)) w->offset.y = surfaceHeightGet(w->content) - (w->bounds.y2 - w->bounds.y); // Update. windowCache(w, 1); return; } } void windowWidgetAdd(WindowT *window, WidgetT *widget) { arrput(window->children, widget); } void windowWidgetRemove(WindowT *window, WidgetT *widget) { uint16_t i; for (i=0; ichildren); i++) { if (window->children[i] == widget) { arrdel(window->children, i); break; } } } void wmShutdown(void) { uint16_t i; for (i=0; i 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; } else { x2 = 0; y2 = 0; } // 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. 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); surfaceBox(win->scrollv.x, win->scrollv.y, win->scrollv.x2, win->scrollv.y2, GUI_BLUE); surfaceBox(win->scrollh.x, win->scrollh.y, win->scrollh.x2, win->scrollh.y2, GUI_BROWN); */ // Are we currently dragging? if (dragging) { // Move window to new mouse location. windowMove(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. windowResize(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; } // Are we inside the vertical scroll bar? Does not include frame on purpose. if (win->flags & WIN_SCROLL_V &&event->x < win->scrollv.x2 && event->x > win->scrollv.x && event->y < win->scrollv.y2 && event->y > win->scrollv.y) { windowScrollVerticalHandler(event, win); break; } // Are we inside the horizontal scroll bar? Does not include frame on purpose. if (win->flags & WIN_SCROLL_H &&event->x < win->scrollh.x2 && event->x > win->scrollh.x && event->y < win->scrollh.y2 && event->y > win->scrollh.y) { windowScrollHorizontalHandler(event, 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? } } else { // Button just went down / button being held down. // Are we inside the vertical scroll bar? Does not include frame on purpose. if (win->flags & WIN_SCROLL_V &&event->x < win->scrollv.x2 && event->x > win->scrollv.x && event->y < win->scrollv.y2 && event->y > win->scrollv.y) { windowScrollVerticalHandler(event, win); break; } // Are we inside the horizontal scroll bar? Does not include frame on purpose. if (win->flags & WIN_SCROLL_H &&event->x < win->scrollh.x2 && event->x > win->scrollh.x && event->y < win->scrollh.y2 && event->y > win->scrollh.y) { windowScrollHorizontalHandler(event, win); break; } } // Left button held down. } // Resizing. } // 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? }