/* * Roo/E, the Kangaroo Punch Portable GUI Toolkit * Copyright (C) 2022 Scott Duensing * * http://kangaroopunch.com * * * This file is part of Roo/E. * * Roo/E is free software: you can redistribute it and/or modify it under the * terms of the GNU Affero General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) * any later version. * * Roo/E 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 Affero General Public License * along with Roo/E. If not, see . * */ #include "wmwindow.h" #include "array.h" #include "font.h" 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 WidgetT *wuWidgetUnderMouseGet(WindowT *win, EventT *event, int16_t *localX, int16_t *localY); 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; ColorT titleBackgroundColor; ColorT gadgetColor; SurfaceT *target = surfaceGet(); // 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); // Does the window itself need redrawn or just the contents? if (redrawWindow) { // 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; // Determine some colors. titleBackgroundColor = (w == _windowTop) ? GUI_DARKGRAY : GUI_LIGHTGRAY; gadgetColor = (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 + GADGET_SIZE; // Close box? if (w->flags & WIN_CLOSE) { tx1 += GADGET_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, gadgetColor); 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 -= GADGET_SIZE; w->maximize.x = tx2 + 1; w->maximize.y = y1; w->maximize.x2 = tx2 + GADGET_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, gadgetColor); surfaceLine(w->maximize.x2 - 3, w->maximize.y + 10, w->maximize.x2 - 9, w->maximize.y2 - 3, gadgetColor); 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 -= GADGET_SIZE; w->minimize.x = tx2 + 1; w->minimize.y = y1; w->minimize.x2 = tx2 + GADGET_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, gadgetColor); 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 += GADGET_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; } // 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 - (GADGET_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 - (GADGET_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; } // Inside window frame. w->bounds.x = x1; w->bounds.y = y1; w->bounds.x2 = x2; w->bounds.y2 = y2; // Resize scrollable to fill window. if (w->scroll) { w->scroll->base.r.x = x1; w->scroll->base.r.y = y1; scrollableWidthSet(w->scroll, x2 - x1); scrollableHeightSet(w->scroll, y2 - y1); } // Fixup all the widget coordinates. windowMove(w, originalX, originalY); } // Redraw window. // Draw contents. widgetChildrenPaint(W(w)); surfaceSet(target); } // 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, ...) { PointT visibleSize; PointT totalSize; va_list args; WindowT *win = NULL; int8_t sflags = SCROLLABLE_NONE; NEW(WindowT, win); memset(win, 0, sizeof(WindowT)); widgetBaseSet((WidgetT *)win, __MAGIC_WINDOW, w, h); win->base.r.x = x; win->base.r.y = y; win->title = strdup(title); win->flags = (uint8_t)flags; logWrite("windowCreate: %dx%d\n", win->base.r.w, win->base.r.h); // Cache the window so we get valid bounds values. windowCache(win, 1); visibleSize.x = win->bounds.x2 - win->bounds.x + 1; visibleSize.y = win->bounds.y2 - win->bounds.y + 1; // 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); totalSize.x = va_arg(args, int); totalSize.y = va_arg(args, int); va_end(args); sflags = SCROLLABLE_STANDARD; } else { // Use whatever the default content area is. This causes an extra draw on create. Oh well. totalSize = visibleSize; widgetDirtySet(W(win), 1); if (flags & WIN_SCROLL_H) sflags |= SCROLLABLE_SCROLL_H; if (flags & WIN_SCROLL_V) sflags |= SCROLLABLE_SCROLL_V; } win->scroll = scrollableCreate(visibleSize.x, visibleSize.y, totalSize.x, totalSize.y, sflags); win->scroll->base.flags |= WIDGET_IS_WINDOW; widgetAdd(W(win), 0, 0, W(win->scroll)); // 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; cbase.children); c++) widgetDestroy(window->base.children[c]); arrfree(window->base.children); // Free cached surface. if (window->cached) surfaceDestroy(&window->cached); // 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]; widgetDirtySet(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. widgetDirtySet(W(win), 1); widgetDirtySet(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. win->restore = win->base.r; // Expand to full contents. win->base.r.w = (win->base.r.w - (win->bounds.x2 - win->bounds.x) + win->scroll->original.x); win->base.r.h = (win->base.r.h - (win->bounds.y2 - win->bounds.y) + win->scroll->original.y); // 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_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 = 0; 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; // Does the window itself need redrawn? if (widgetDirtyGet(widget)) { widgetDirtySet(widget, 0); x = 1; // This is a flag for later that we need to update the cached surface. } // Did a widget or the window need redrawn? if (x || widgetChildrenDirty(widget)) 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->scroll->area) - 1) / ICON_SIZE; xi = (surfaceWidthGet(w->scroll->area) - 1) / ICON_SIZE; py = videoDisplayHeightGet() - ICON_SIZE; y = 0; x = 0; for (yc=0; ycscroll->area, 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 x = 0; int16_t y = 0; int16_t content = 0; PointT chrome; // Find size of window chrome. chrome.x = win->base.r.w - (win->bounds.x2 - win->bounds.x) + 1; chrome.y = win->base.r.h - (win->bounds.y2 - win->bounds.y) + 1; // Too small? if (width < (GADGET_SIZE * 4) + chrome.x) width = (GADGET_SIZE * 4) + chrome.x; if (height < (GADGET_SIZE * 4) + chrome.y) height = (GADGET_SIZE * 4) + chrome.y; // Too big? Horizontal. // Get the current view offset. x = scrollableValueHGet(win->scroll); // Width of content area of window. content = win->bounds.x2 - win->bounds.x + 1; // Do we need to take the height of the vertical scroll bar into account? if (win->scroll->scrollv) content -= win->scroll->scrollv->base.r.w; // Clamp. x = win->base.r.w - content + surfaceWidthGet(win->scroll->area); if (width > x) width = x; // Too big? Vertical. // Get the current view offset. y = scrollableValueVGet(win->scroll); // Height of content area of window. content = win->bounds.y2 - win->bounds.y + 1; // Do we need to take the height of the horizontal scroll bar into account? if (win->scroll->scrollh) content -= win->scroll->scrollh->base.r.h; // Clamp. y = win->base.r.h - content + surfaceHeightGet(win->scroll->area); if (height > y) height = y; // 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; widgetDirtySet(W(win), 1); } } void wmShutdown(void) { uint16_t i; // Delete all windows. 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 and widget. if (win) { // Find window outer bounds. x2 = win->base.r.x + win->base.r.w - 1; y2 = win->base.r.y + win->base.r.h - 1; // Are we over a widget? widgetOver = wuWidgetUnderMouseGet(win, event, &windowLocalX, &windowLocalY); } else { x2 = 0; y2 = 0; widgetOver = NULL; } // If we're over a widget that has raw input processing enabled and we haven't started a click on another widget yet, send to target for raw processing. if (widgetOver && !widgetDown && !dragging && !resizing && (widgetOver->flags & WIDGET_RAW_INPUT) && (event->buttons != 0)) { if (widgetOver->reg->click) { rawEvent.event = event; rawEvent.data = widget->data; //logWrite("Firing raw click for %s\n", widgetOver->reg->widgetName); widgetOver->reg->click(widgetOver, windowLocalX - widgetOver->r.x, windowLocalY - widgetOver->r.y, CLICK_RAW_INPUT, &rawEvent); } return; } // 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_YELLOW); 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 over a widget? widget = wuWidgetUnderMouseGet(win, event, &windowLocalX, &windowLocalY); if (widget) { if (widget->reg->click) widget->reg->click(widget, windowLocalX - widget->r.x, windowLocalY - widget->r.y, CLICK_LEFT_DOWN, widget->data); widgetDown = widget; } // 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? } } else { // Button just went down / button being held down. // Nada } // 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; // Did the left button just come up? if (event->flags & EVENT_FLAG_LEFT_UP) { // Did this click begin over a widget? if (widgetDown) { // Are we still inside the window content? if (event->x <= win->bounds.x2 && event->x >= win->bounds.x && event->y <= win->bounds.y2 && event->y >= win->bounds.y) { // Are we stll over the same widget? widget = wuWidgetUnderMouseGet(win, event, &windowLocalX, &windowLocalY); // Send to widget for processing or send cancel. Convert mouse to widget-local coordinates. if (widgetDown->reg->click) widgetDown->reg->click(widgetDown, windowLocalX - widgetDown->r.x, windowLocalY - widgetDown->r.y, (widget == widgetDown) ? CLICK_LEFT_UP : CLICK_LEFT_CANCEL, widgetDown->data); } widgetDown = NULL; } // widgetDown } } break; } // Left button processing. } // Do we have windows? } static WidgetT *wuWidgetUnderMouseGet(WindowT *win, EventT *event, int16_t *localX, int16_t *localY) { int16_t i; int16_t x2; int16_t y2; PointT offset; WidgetT *widget; // If we have scrollbars, get the offset for the view. if (win->scroll->scrollh) offset.x = hscrollValueGet(win->scroll->scrollh); if (win->scroll->scrollv) offset.y = vscrollValueGet(win->scroll->scrollv); // Find window bounds. x2 = win->base.r.x + win->base.r.w - 1; y2 = win->base.r.y + win->base.r.h - 1; // Are we over the provided window? if (event->x <= x2 && event->x >= win->base.r.x && event->y <= y2 && event->y >= win->base.r.y) { // Find window-local mouse coordinates. *localX = event->x - win->base.r.x;// + offset.x; *localY = event->y - win->base.r.y;// + offset.y; // Find widget under mouse. for (i=0; ibase.children); i++) { widget = win->base.children[i]; if (*localX >= widget->r.x && *localX < widget->r.x + widget->r.w && *localY >= widget->r.y && *localY < widget->r.y + widget->r.h) { //logWrite("Over %s\n", widget->reg->widgetName); // Return this widget. return widget; } } } return NULL; } WindowT *wmWindowOnTopGet(void) { return _windowTop; }