From 431e4a40eb71ba93f5c37f42906c4e0cc98447a6 Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Wed, 22 Jun 2022 19:08:14 -0500 Subject: [PATCH] Scrollbars work! Thumbs work! Minor DJGPP mouse event issue fixed. --- client/src/gui/surface.c | 28 +-- client/src/gui/surface.h | 2 +- client/src/gui/wmwindow.c | 351 ++++++++++++++++++++++++++++++++---- client/src/gui/wmwindow.h | 38 ++-- client/src/main.c | 2 +- client/src/platform/djgpp.c | 9 +- 6 files changed, 363 insertions(+), 67 deletions(-) diff --git a/client/src/gui/surface.c b/client/src/gui/surface.c index cdb5b35..9fab42e 100644 --- a/client/src/gui/surface.c +++ b/client/src/gui/surface.c @@ -20,10 +20,10 @@ static void surfacePixelSet16(uint16_t x, uint16_t y, ColorT color); static void surfacePixelSet32(uint16_t x, uint16_t y, ColorT color); -void surfaceBlit(int16_t targetX1, int16_t targetY1, int16_t width, int16_t height, SurfaceT *source) { - int16_t y; - int16_t x1 = 0; - int16_t y1 = 0; +void surfaceBlit(int16_t targetX, int16_t targetY, int16_t offsetX, int16_t offsetY, int16_t width, int16_t height, SurfaceT *source) { + int16_t i; + int16_t x = 0; + int16_t y = 0; size_t bytes; size_t offsetTarget; size_t offsetSource; @@ -33,25 +33,25 @@ void surfaceBlit(int16_t targetX1, int16_t targetY1, int16_t width, int16_t heig if (height <= 0 || height > source->height) height = source->height; // Clip on top and left. x1 & y1 are pixel locations inside the source bitmap. - if (targetX1 < 0) x1 = -targetX1; - if (targetY1 < 0) y1 = -targetY1; + if (targetX < 0) x = -targetX; + if (targetY < 0) y = -targetY; // Clip on right and bottom. - if (targetX1 + width > __surfaceActive->width) width -= targetX1 + width - __surfaceActive->width; - if (targetY1 + height > __surfaceActive->height) height -= targetY1 + height - __surfaceActive->height; + if (targetX + width - offsetX > __surfaceActive->width) width -= targetX + width - offsetX - __surfaceActive->width; + if (targetY + height - offsetY > __surfaceActive->height) height -= targetY + height - offsetY - __surfaceActive->height; // Are we still on the screen? - if (x1 < 0 || y1 < 0 || width < x1 || height < y1) return; + if (x < 0 || y < 0 || width < x || height < y) return; - if (targetX1 == 0 && targetY1 == 0 && __surfaceActive->width == source->width && __surfaceActive->height == source->height) { + if (targetX == 0 && targetY == 0 && __surfaceActive->width == source->width && __surfaceActive->height == source->height) { // Direct blit of entire surface. memcpy(__surfaceActive->buffer.bits8, source->buffer.bits8, source->bytes); } else { // Blit into larger surface. - offsetTarget = (targetY1 + y1) * __surfaceActive->scanline + (targetX1 + x1) * __surfaceBytesPerPixel; - offsetSource = y1 * source->scanline + x1 * __surfaceBytesPerPixel; - bytes = (width - x1) * __surfaceBytesPerPixel; - for (y=y1; yscanline + (targetX + x) * __surfaceBytesPerPixel; + offsetSource = (y + offsetY) * source->scanline + (x + offsetX) * __surfaceBytesPerPixel; + bytes = (width - x) * __surfaceBytesPerPixel; + for (i=y; ibuffer.bits8[offsetTarget], &source->buffer.bits8[offsetSource], bytes); offsetTarget += __surfaceActive->scanline; offsetSource += source->scanline; diff --git a/client/src/gui/surface.h b/client/src/gui/surface.h index 80ba64a..83a4b08 100644 --- a/client/src/gui/surface.h +++ b/client/src/gui/surface.h @@ -45,7 +45,7 @@ extern ColorT (*surfacePixelGet)(SurfaceT *surface, int16_t x, int16_t y); extern void (*surfacePixelSet)(uint16_t x, uint16_t y, ColorT color); -void surfaceBlit(int16_t targetX1, int16_t targetY1, int16_t width, int16_t height, SurfaceT *source); +void surfaceBlit(int16_t targetX1, int16_t targetY1, int16_t offsetX, int16_t offsetY, int16_t width, int16_t height, SurfaceT *source); void surfaceBlitWithTransparency(int16_t targetX, int16_t targetY, SurfaceT *source, ColorT transparent); void surfaceClear(ColorT color); ColorT surfaceColorMake(uint8_t r, uint8_t g, uint8_t b); diff --git a/client/src/gui/wmwindow.c b/client/src/gui/wmwindow.c index b7b453b..4933b7b 100644 --- a/client/src/gui/wmwindow.c +++ b/client/src/gui/wmwindow.c @@ -3,7 +3,10 @@ #include "font.h" -#define ICON_SIZE 32 +#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; @@ -15,6 +18,8 @@ static int16_t _iconCount = 0; static void windowCache(WindowT *w); +static void windowScrollHorizontalHandler(EventT *e, WindowT *w); +static void windowScrollVerticalHandler(EventT *e, WindowT *w); static void windowCache(WindowT *w) { @@ -29,6 +34,7 @@ static void windowCache(WindowT *w) { int16_t ty2; int16_t originalX; int16_t originalY; + double d; ColorT titleBackgroundColor; ColorT widgetColor; SurfaceT *target = surfaceGet(); @@ -79,11 +85,11 @@ static void windowCache(WindowT *w) { if (w->title || w->flags & WIN_CLOSE || w->flags & WIN_MAXIMIZE || w->flags & WIN_MINIMIZE) { tx1 = x1; tx2 = x2; - ty2 = y1 + 20; + ty2 = y1 + WIDGET_SIZE; // Close box? if (w->flags & WIN_CLOSE) { - tx1 += 20; + tx1 += WIDGET_SIZE; w->close.x = x1; w->close.y = y1; w->close.x2 = tx1 - 1; @@ -97,10 +103,10 @@ static void windowCache(WindowT *w) { // Maximize box? if (w->flags & WIN_MAXIMIZE) { - tx2 -= 20; + tx2 -= WIDGET_SIZE; w->maximize.x = tx2 + 1; w->maximize.y = y1; - w->maximize.x2 = tx2 + 20; + 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); @@ -114,10 +120,10 @@ static void windowCache(WindowT *w) { // Minimize box? if (w->flags & WIN_MINIMIZE) { - tx2 -= 20; + tx2 -= WIDGET_SIZE; w->minimize.x = tx2 + 1; w->minimize.y = y1; - w->minimize.x2 = tx2 + 20; + 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); @@ -133,8 +139,10 @@ static void windowCache(WindowT *w) { 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? @@ -150,6 +158,8 @@ static void windowCache(WindowT *w) { } } 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; @@ -157,23 +167,121 @@ static void windowCache(WindowT *w) { 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. - y1 += 20; 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 - 21; + 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 - 21; + 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); @@ -184,10 +292,10 @@ static void windowCache(WindowT *w) { } // 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); + 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. - windowMoveTo(w, originalX, originalY); + windowMove(w, originalX, originalY); surfaceSet(target); } @@ -212,12 +320,17 @@ WindowT *windowCreate(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *titl //***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++); + windowCache(win); + 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); - win->flags |= WIN_RESIZE; - + 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) { @@ -239,10 +352,6 @@ WindowT *windowCreate(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *titl 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); @@ -350,6 +459,7 @@ void windowMaximizeRestore(WindowT *win) { // Restore to previous size. win->flags &= ~WIN_IS_MAX; win->base.r = win->restore; + win->offset = win->restoreOffset; } else { // Maximized? @@ -357,7 +467,12 @@ void windowMaximizeRestore(WindowT *win) { win->flags |= WIN_IS_MAX; // Remember current size and position. - win->restore = win->base.r; + 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)); @@ -406,7 +521,7 @@ void windowMinimize(WindowT *win) { } -void windowMoveTo(WindowT *w, uint16_t x, uint16_t y) { +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; @@ -442,6 +557,28 @@ void windowMoveTo(WindowT *w, uint16_t x, uint16_t y) { 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; @@ -500,7 +637,7 @@ void windowPaint(struct WidgetS *widget, ...) { _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); + surfaceBlit(w->base.r.x, w->base.r.y, 0, 0, 0, 0, w->cached); } } @@ -520,15 +657,33 @@ RegisterT *windowRegister(uint8_t magic) { } -void windowResizeTo(WindowT *win, uint16_t width, uint16_t height) { +void windowResize(WindowT *win, uint16_t width, uint16_t height) { + + int16_t delta; // Too small? - if (width < 95) width = 95; - if (height < 80) height = 80; + 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) > 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)); + 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) { @@ -543,6 +698,102 @@ void windowResizeTo(WindowT *win, uint16_t width, uint16_t height) { } +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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + return; + } +} + + void wmShutdown(void) { while (arrlen(_windowList) > 0) { windowDestroy((WidgetT *)_windowList[0]); @@ -589,6 +840,9 @@ void wmUpdate(EventT *event) { 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. @@ -609,7 +863,7 @@ void wmUpdate(EventT *event) { if (event->buttons & BUTTON_LEFT) { /* - // DEBUG - draw active regions. ***TODO*** No resize grabber here. + // 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); @@ -617,13 +871,15 @@ void wmUpdate(EventT *event) { 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. - windowMoveTo(win, event->x - dragOffset.x, event->y - dragOffset.y); + windowMove(win, event->x - dragOffset.x, event->y - dragOffset.y); break; } else { // Dragging. @@ -632,7 +888,7 @@ void wmUpdate(EventT *event) { // 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); + windowResize(win, event->x - resizeOffset.x, event->y - resizeOffset.y); break; } else { // Resizing. @@ -681,6 +937,18 @@ void wmUpdate(EventT *event) { 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. @@ -734,9 +1002,24 @@ void wmUpdate(EventT *event) { } } // Minimized windows? } - } - } // Button just went down. + } 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. diff --git a/client/src/gui/wmwindow.h b/client/src/gui/wmwindow.h index f4fa7d8..9376962 100644 --- a/client/src/gui/wmwindow.h +++ b/client/src/gui/wmwindow.h @@ -15,21 +15,29 @@ #define WIN_IS_ICON 64 #define WIN_IS_MAX 128 +#define WIN_STANDARD (WIN_CLOSE | WIN_MAXIMIZE | WIN_MINIMIZE | WIN_RESIZE | WIN_SCROLL_V | WIN_SCROLL_H) + typedef struct WindowS { - WidgetT base; // Required by all widgets. - char *title; // Title of window. - uint8_t flags; // Window flags (see defines above). - RectT close; // Coordinates of close box, if any. - RectT titlebar; // Coordinates of title bar, if any. - RectT minimize; // Coordinates of minimize box, if any. - RectT maximize; // Coordinates of maximize box, if any. - RectT resize1; // First resize area. - RectT resize2; // Second resize area. - RectT bounds; // Inside edge of window frame. - RectT restore; // Size of window if they restore from maximized. - SurfaceT *content; // Actual window contents - widgets and such. - SurfaceT *cached; // Once rendered, keep a cached copy for faster redrawing. + WidgetT base; // Required by all widgets. + char *title; // Title of window. + uint8_t flags; // Window flags (see defines above). + RectT close; // Coordinates of close box, if any. + RectT titlebar; // Coordinates of title bar, if any. + RectT minimize; // Coordinates of minimize box, if any. + RectT maximize; // Coordinates of maximize box, if any. + RectT resize1; // First resize area. + RectT resize2; // Second resize area. + RectT scrollv; // Vertical scroll bar. + RectT scrollh; // Horizontal scroll bar. + RectT thumbv; // Vertical scroll bar thumb. + RectT thumbh; // Horizontal scroll bar thumb. + RectT bounds; // Inside edge of window frame. + RectT restore; // Size of window if they restore from maximized. + PointT restoreOffset; // Scroll position if they restore from maximized. + PointT offset; // Content scroll offset in window. + SurfaceT *content; // Actual window contents - widgets and such. + SurfaceT *cached; // Once rendered, keep a cached copy for faster redrawing. } WindowT; @@ -41,10 +49,10 @@ void windowDestroy(struct WidgetS *widget, ...); void windowFocusSet(WindowT *win); void windowMaximizeRestore(WindowT *win); void windowMinimize(WindowT *win); -void windowMoveTo(WindowT *win, uint16_t x, uint16_t y); +void windowMove(WindowT *win, uint16_t x, uint16_t y); void windowPaint(struct WidgetS *widget, ...); RegisterT *windowRegister(uint8_t magic); -void windowResizeTo(WindowT *win, uint16_t width, uint16_t height); +void windowResize(WindowT *win, uint16_t width, uint16_t height); void wmShutdown(void); diff --git a/client/src/main.c b/client/src/main.c index 82eeecb..26fc967 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -14,7 +14,7 @@ int main(int argc, char *argv[]) { if (guiStartup(800, 600, 16) == SUCCESS) { for (i=1; i<4; i++) { sprintf(title, "Testing %d", i); - windowCreate(i * 50, i * 50, 300, 200, title, WIN_CLOSE | WIN_MAXIMIZE | WIN_MINIMIZE); + windowCreate(i * 50, i * 50, 300, 200, title, WIN_STANDARD); } guiRun(); guiShutdown(); diff --git a/client/src/platform/djgpp.c b/client/src/platform/djgpp.c index 046242b..e81218a 100644 --- a/client/src/platform/djgpp.c +++ b/client/src/platform/djgpp.c @@ -187,8 +187,11 @@ void platformEventGet(EventT *event) { int16_t meta = bioskey(KEYBOARD_META_EXTENDED); int16_t key = 0; union REGS regs; - static int32_t lastX = 0; - static int32_t lastY = 0; + static int32_t lastX = 0; + static int32_t lastY = 0; + static int32_t lastButtons = 0; + + event->buttons = lastButtons; // Read mouse motion. regs.x.ax = MOUSE_GETMOTION; @@ -273,6 +276,8 @@ void platformEventGet(EventT *event) { if ((meta & (1 << KEY_META_CONTROL)) + (meta & (1 << KEY_META_CONTROL_LEFT)) + (meta & (1 << KEY_META_CONTROL_RIGHT))) event->kbstat |= META_CTRL; if ((meta & (1 << KEY_META_SHIFT_LEFT)) + (meta & (1 << KEY_META_SHIFT_RIGHT))) event->kbstat |= META_SHIFT; } + + lastButtons = event->buttons; }