roo_e/client/src/gui/wmwindow.c
2022-06-15 20:02:39 -05:00

472 lines
13 KiB
C

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