robmcmullen-apple2/src/gui/element.cpp

333 lines
8.2 KiB
C++

//
// ELEMENT.CPP
//
// Graphical User Interface base class
// by James Hammons
//
// JLH = James Hammons <jlhamm@acm.org>
//
// WHO WHEN WHAT
// --- ---------- ------------------------------------------------------------
// JLH 02/02/2006 Created this file
// JLH 02/13/2006 Added backbuffer and rendering functions
// JLH 03/02/2006 Moved backbuffer destruction to destructor, added parent
// corner discovery
//
#include "element.h"
#include "guimisc.h" // Various support functions
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
#define MASK_R 0xFF000000
#define MASK_G 0x00FF0000
#define MASK_B 0x0000FF00
#define MASK_A 0x000000FF
#else
#define MASK_R 0x000000FF
#define MASK_G 0x0000FF00
#define MASK_B 0x00FF0000
#define MASK_A 0xFF000000
#endif
//#define DEBUG_ELEMENT
#ifdef DEBUG_ELEMENT
#include "log.h"
#endif
// Initialize class variables
SDL_Surface * Element::screen = NULL;
bool Element::needToRefreshScreen = false;
Element::Element(uint32_t x/*= 0*/, uint32_t y/*= 0*/, uint32_t w/*= 0*/, uint32_t h/*= 0*/,
Element * parentElement/*= NULL*/): parent(parentElement), backstore(NULL), visible(true)
{
extents.x = x,
extents.y = y,
extents.w = w,
extents.h = h;
coverList.push_back(extents);
}
Element::Element(uint32_t x, uint32_t y, uint32_t w, uint32_t h,
uint8_t fgR/*= 0xFF*/, uint8_t fgG/*= 0xFF*/, uint8_t fgB/*= 0xFF*/, uint8_t fgA/*= 0xFF*/,
uint8_t bgR/*= 0x00*/, uint8_t bgG/*= 0x00*/, uint8_t bgB/*= 0x00*/, uint8_t bgA/*= 0xFF*/,
Element * parentElement/*= NULL*/): parent(parentElement), backstore(NULL), visible(true)
{
extents.x = x,
extents.y = y,
extents.w = w,
extents.h = h;
coverList.push_back(extents);
#if 0
// This *should* allow us to store our colors in an endian safe way... :-/
uint8_t * c = (uint8_t *)&fgColor;
c[0] = fgR, c[1] = fgG, c[2] = fgB, c[3] = fgA;
c = (uint8_t *)&bgColor;
c[0] = bgR, c[1] = bgG, c[2] = bgB, c[3] = bgA;
#else
fgColor = SDL_MapRGBA(screen->format, fgR, fgG, fgB, fgA);
bgColor = SDL_MapRGBA(screen->format, bgR, bgG, bgB, bgA);
#endif
}
Element::~Element()
{
if (backstore)
{
RestoreScreenFromBackstore();
SDL_FreeSurface(backstore);
needToRefreshScreen = true;
}
}
bool Element::Inside(uint32_t x, uint32_t y)
{
return (x >= (uint32_t)extents.x && x < (uint32_t)(extents.x + extents.w)
&& y >= (uint32_t)extents.y && y < (uint32_t)(extents.y + extents.h) ? true : false);
}
//Badly named--!!! FIX !!! [DONE]
//SDL_Rect Element::GetParentCorner(void)
SDL_Rect Element::GetScreenCoords(void)
{
SDL_Rect rect;
rect.x = extents.x, rect.y = extents.y;
// First, traverse the parent tree to get the absolute screen address...
Element * currentParent = parent;
while (currentParent)
{
rect.x += currentParent->extents.x;
rect.y += currentParent->extents.y;
currentParent = currentParent->parent;
}
return rect;
}
#if 1
//May use this in the future...
SDL_Rect Element::GetParentRect(void)
{
// If there is no parent, then return the entire screen as the parent's
// rectangle.
SDL_Rect rect;
rect.x = 0, rect.y = 0, rect.w = screen->w, rect.h = screen->h;
if (parent)
{
rect.x = parent->extents.x;
rect.y = parent->extents.y;
rect.w = parent->extents.w;
rect.h = parent->extents.h;
}
return rect;
}
#endif
SDL_Rect Element::GetExtents(void)
{
return extents;
}
//kludge
#include "settings.h"
void Element::CreateBackstore(void)
{
backstore = SDL_CreateRGBSurface(SDL_SWSURFACE, extents.w, extents.h, 32,
MASK_R, MASK_G, MASK_B, 0x00);
//#define TEST_GL
#ifdef TEST_GL
printf("Element: About to do SDL_BlitSurface...\n");
#endif
//kludge
if (settings.useOpenGL)
return;
//Since screen is the main screen surface, OpenGL doesn't like it being touched.
//How to fix? Dunno.
SDL_BlitSurface(screen, &extents, backstore, NULL);
#ifdef TEST_GL
printf("Element: SDL_BlitSurface...Done.\n");
#endif
}
void Element::RestoreScreenFromBackstore(void)
{
SDL_Rect r;
r.x = extents.x;
r.y = extents.y;
SDL_BlitSurface(backstore, NULL, screen, &r);
}
void Element::SaveScreenToBackstore(void)
{
SDL_BlitSurface(screen, &extents, backstore, NULL);
}
void Element::ResetCoverageList(void)
{
// Setup our coverage list with the entire window area
coverList.empty();
coverList.push_back(extents);
}
void Element::AdjustCoverageList(SDL_Rect r)
{
//Prolly should have a bool here to set whether or not to do this crap, since it
//takes a little time...
// Here's where we do the coverage list voodoo... :-)
/*
Steps:
o Check for intersection. If no intersection, then no need to divide rects.
o Loop through current rects. If rect is completely inside passed in rect, remove from list.
o Loop through remaining rects. If rect intersects, decompose to four rects and
exclude degenerate rects, push rest into the coverage list.
*/
// std::list<Element *>::reverse_iterator ri;
// std::list<SDL_Rect>::iterator i;
// Loop through rects and remove those completely covered by passed in rect.
/* for(i=coverList.begin(); i!=coverList.end(); i++)
{
// if (RectanglesIntersect(r, *i))
if (RectangleFirstInsideSecond(*i, r))
{
//This is not right--do a while loop instead of a for loop?
// Remove it from the list...
std::list<SDL_Rect>::iterator next = coverList.erase(i);
}
}
*/
// Loop through rects and remove those completely covered by passed in rect.
std::list<SDL_Rect>::iterator i = coverList.begin();
while (i != coverList.end())
{
if (RectangleFirstInsideSecond(*i, r))
i = coverList.erase(i); // This will also advance i to the next item!
else
i++;
}
//This may not be needed if nothing follows the loop below...!
// if (coverList.empty())
// return;
// Check for intersection. If no intersection, then no need to divide rects.
i = coverList.begin();
while (i != coverList.end())
{
if (RectanglesIntersect(r, *i))
{
// Do the decomposition here. There will always be at least *one* rectangle
// generated by this algorithm, so we know we're OK in removing the original
// from the list. The general pattern looks like this:
//
// +------+
// |1 |
// +-+--+-+
// |2|//|3| <- Rectangle "r" is in the center
// +-+--+-+
// |4 |
// +------+
//
// Even if r extends beyond the bounds of the rectangle under consideration,
// that's OK because we test to see that the rectangle isn't degenerate
// before adding it to the list.
//Should probably use a separate list here and splice it in when we're done here...
//Or, could use push_front() to avoid the problem... Neat! Doesn't require a separate list!
//But, we need to remove the currently referenced rect... Another while loop!
//This approach won't work--if no rect1 then we're screwed! [FIXED]
//Now *that* will work...
SDL_Rect current = *i;
uint32_t bottomOfRect1 = current.y;
// uint32_t rightOfRect2 = current.x;
// uint32_t leftOfRect3 = current.x + current.w;
uint32_t topOfRect4 = current.y + current.h;
// Rectangle #1 (top)
if (r.y > current.y) // Simple rectangle degeneracy test...
{
bottomOfRect1 = r.y;
SDL_Rect rect = current;
rect.h = r.y - current.y;
coverList.push_front(rect);
}
// Rectangle #4 (bottom)
if (r.y + r.h < current.y + current.h)
{
topOfRect4 = r.y + r.h;
SDL_Rect rect = current;
rect.y = r.y + r.h;
rect.h = (current.y + current.h) - (r.y + r.h);
coverList.push_front(rect);
}
// Rectangle #2 (left side)
if (r.x > current.x)
{
SDL_Rect rect = current;
rect.w = r.x - current.x;
rect.y = bottomOfRect1;
rect.h = topOfRect4 - bottomOfRect1;
coverList.push_front(rect);
}
// Rectangle #3 (right side)
if (r.x + r.w < current.x + current.w)
{
SDL_Rect rect;
rect.x = r.x + r.w;
rect.w = (current.x + current.w) - (r.x + r.w);
rect.y = bottomOfRect1;
rect.h = topOfRect4 - bottomOfRect1;
coverList.push_front(rect);
}
i = coverList.erase(i); // This will also advance i to the next item!
}
else
i++;
}
}
void Element::SetVisible(bool visibility)
{
visible = visibility;
}
//
// Class methods
//
void Element::SetScreen(SDL_Surface * s)
{
screen = s;
}
bool Element::ScreenNeedsRefreshing(void)
{
return needToRefreshScreen;
}
void Element::ScreenWasRefreshed(void)
{
needToRefreshScreen = false;
}