Index: ps/trunk/source/ps/Overlay.h
===================================================================
--- ps/trunk/source/ps/Overlay.h (revision 1524)
+++ ps/trunk/source/ps/Overlay.h (revision 1525)
@@ -1,306 +1,309 @@
/*
COverlay
by Rich Cross, rich@0ad.wildfiregames.com
--Overview--
Class representing 2D screen overlays; includes functionality for overlay
position, color, texture and borders.
*/
#ifndef COVERLAY_H
#define COVERLAY_H
#include "lib.h"
struct CColor
{
CColor() : r(-1.f), g(-1.f), b(-1.f), a(1.f) {}
CColor(float cr,float cg,float cb,float ca) : r(cr), g(cg), b(cb), a(ca) {}
bool operator == (const CColor &color) const
{
return r==color.r &&
g==color.g &&
b==color.b &&
a==color.a;
}
bool operator != (const CColor &color) const
{
return !(*this==color);
}
+ // For passing to glColor[34]fv:
+ const float* FloatArray() const { return &r; }
+
float r, g, b, a;
};
// yuck - MFC already defines CRect/CSize classes ...
#define CRect PS_CRect
#define CSize PS_CSize
class CPos;
class CSize;
/**
* @author Gustav Larsson
*
* Rectangle class used for screen rectangles. It's very similiar to the MS
* CRect, but with FLOATS because it's meant to be used with OpenGL which
* takes float values.
*
* Changed to floats 2004-08-31 /GL
*/
class CRect
{
public:
CRect();
CRect(const CPos &pos);
CRect(const CSize &size);
CRect(const CPos &upperleft, const CPos &bottomright);
CRect(const CPos &pos, const CSize &size);
CRect(const float &_l, const float &_t, const float &_r, const float &_b);
// Operators
void operator = (const CRect& a);
bool operator == (const CRect& a) const;
bool operator != (const CRect& a) const;
CRect operator - (void) const;
CRect operator + (void) const;
CRect operator + (const CRect& a) const;
CRect operator + (const CPos& a) const;
CRect operator + (const CSize& a) const;
CRect operator - (const CRect& a) const;
CRect operator - (const CPos& a) const;
CRect operator - (const CSize& a) const;
void operator += (const CRect& a);
void operator += (const CPos& a);
void operator += (const CSize& a);
void operator -= (const CRect& a);
void operator -= (const CPos& a);
void operator -= (const CSize& a);
/**
* @return Width of Rectangle
*/
float GetWidth() const;
/**
* @return Height of Rectangle
*/
float GetHeight() const;
/**
* Get Size
*/
CSize GetSize() const;
/**
* Get Position equivalent to top/left corner
*/
CPos TopLeft() const;
/**
* Get Position equivalent to bottom/right corner
*/
CPos BottomRight() const;
/**
* Get Position equivalent to the center of the rectangle
*/
CPos CenterPoint() const;
/**
* Evalutates if point is within the rectangle
* @param point CPos representing point
* @return true if inside.
*/
bool PointInside(const CPos &point) const;
CRect Scale(float x, float y) const;
/**
* Returning CPos representing each corner.
*/
public:
/**
* Dimensions
*/
float left, top, right, bottom;
};
/**
* @author Gustav Larsson
*
* Made to represent screen positions and delta values.
* @see CRect
* @see CSize
*/
class CPos
{
public:
CPos();
CPos(const float &_x, const float &_y);
// Operators
void operator = (const CPos& a);
bool operator == (const CPos& a) const;
bool operator != (const CPos& a) const;
CPos operator - (void) const;
CPos operator + (void) const;
CPos operator + (const CPos& a) const;
CPos operator + (const CSize& a) const;
CPos operator - (const CPos& a) const;
CPos operator - (const CSize& a) const;
void operator += (const CPos& a);
void operator += (const CSize& a);
void operator -= (const CPos& a);
void operator -= (const CSize& a);
public:
/**
* Position
*/
float x, y;
};
/**
* @author Gustav Larsson
*
* Made to represent a screen size, should in philosophy
* be made of unsigned ints, but for the sake of compatibility
* with CRect and CPos it's not.
* @see CRect
* @see CPos
*/
class CSize
{
public:
CSize();
CSize(const CRect &rect);
CSize(const CPos &pos);
CSize(const float &_cx, const float &_cy);
// Operators
void operator = (const CSize& a);
bool operator == (const CSize& a) const;
bool operator != (const CSize& a) const;
CSize operator - (void) const;
CSize operator + (void) const;
CSize operator + (const CSize& a) const;
CSize operator - (const CSize& a) const;
CSize operator / (const float &a) const;
CSize operator * (const float &a) const;
void operator += (const CSize& a);
void operator -= (const CSize& a);
void operator /= (const float& a);
void operator *= (const float& a);
public:
/**
* Size
*/
float cx, cy;
};
//--------------------------------------------------------
// Includes / Compiler directives
//--------------------------------------------------------
#include "Texture.h"
//--------------------------------------------------------
// Macros
//--------------------------------------------------------
//--------------------------------------------------------
// Types
//--------------------------------------------------------
//--------------------------------------------------------
// Error declarations
//--------------------------------------------------------
//--------------------------------------------------------
// Declarations
//--------------------------------------------------------
/**
* @author Rich Cross
*
* Overlay class definition.
*/
class COverlay
{
public:
/**
* Default constructor; creates an overlay that won't actually be renderable
*/
COverlay();
/**
* Constructor with setup for more common parameters
*/
COverlay(const CRect& rect,int z,const CColor& color,const char* texturename="",bool hasBorder=false,
const CColor& bordercolor=CColor(0,0,0,0));
/**
* Destructor
*/
~COverlay();
/**
* Get coordinates
*/
const CRect& GetRect() const { return m_Rect; }
/**
* Get depth
*/
int GetZ() const { return m_Z; }
/**
* Get texture (not const as Renderer need to modify texture to store handle)
*/
CTexture& GetTexture() { return m_Texture; }
/**
* Get color
*/
const CColor& GetColor() const { return m_Color; }
/**
* Get border flag
*/
bool HasBorder() const { return m_HasBorder; }
/**
* Get border color
*/
const CColor& GetBorderColor() const { return m_BorderColor; }
private:
/// screen space coordinates of overlay
CRect m_Rect;
/// depth of overlay, for correctly overlapping overlays; higher z implies in-front-of behaviour
int m_Z;
/// texture to use in rendering overlay; can be a null texture
CTexture m_Texture;
/// overlay color
CColor m_Color;
// flag indicating whether to render overlay using a border
bool m_HasBorder;
/// border color
CColor m_BorderColor;
};
#endif
Index: ps/trunk/source/ps/XeroXMB.h
===================================================================
--- ps/trunk/source/ps/XeroXMB.h (revision 1524)
+++ ps/trunk/source/ps/XeroXMB.h (revision 1525)
@@ -1,222 +1,220 @@
-/* $Id: XeroXMB.h,v 1.7 2004/11/11 07:09:32 markt Exp $
+/* $Id$
Xeromyces - XMB reading library
Philip Taylor (philip@zaynar.demon.co.uk / @wildfiregames.com)
*/
/*
Brief outline:
XMB is a binary representation of XML, with some limitations
but much more efficiency (particularly for loading simple data
classes that don't need much initialisation).
Main limitations:
* Only handles UTF16 internally. (It's meant to be a feature, but
can be detrimental if it's always being converted back to
ASCII.)
* Can't correctly handle mixed text/elements inside elements -
"
Text
" and " Te xt
" are
considered identical.
* Tries to avoid using strings - you usually have to load the
numeric IDs and use them instead.
* Case-sensitive (but converts all element/attribute names in
the XML file to lowercase, so you only have to be careful in
the code)
Theoretical file structure:
XMB_File {
char Header[4]; // because everyone has one; currently "XMB0"
- int Checksum; // CRC32 of original XML file, to detect changes
-
int ElementNameCount;
ZStrA ElementNames[];
int AttributeNameCount;
ZStrA AttributeNames[];
XMB_Node Root;
}
XMB_Node {
0) int Length; // of entire struct, so it can be skipped over
4) int ElementName;
8) int AttributeCount;
12) int ChildCount;
16) int ChildrenOffset; // == sizeof(Text)+sizeof(Attributes)
20) XMB_Text Text;
XMB_Attribute Attributes[];
XMB_Node Children[];
}
XMB_Attribute {
int Name;
ZStrW Value;
}
ZStrA {
int Length; // in bytes
char* Text; // null-terminated ASCII
}
ZStrW {
int Length; // in bytes
char16* Text; // null-terminated UTF16
}
XMB_Text {
20) int Length; // 0 if there's no text, else 4+sizeof(Text) in bytes including terminator
// If Length != 0:
24) int LineNumber; // for e.g. debugging scripts
28) char16* Text; // null-terminated UTF16
}
*/
#ifndef _XEROXMB_H_
#define _XEROXMB_H_
// Define to use a std::map for name lookups rather than a linear search.
// (The map is usually slower.)
//#define XERO_USEMAP
#include
#include "ps/utf16string.h"
#ifdef XERO_USEMAP
# include
#endif
// File headers, to make sure it doesn't try loading anything other than an XMB
extern const int HeaderMagic;
extern const char* HeaderMagicStr;
class XMBElement;
class XMBElementList;
class XMBAttributeList;
class XMBFile
{
public:
XMBFile() : m_Pointer(NULL) {};
// Initialise from the contents of an XMB file.
// FileData must remain allocated and unchanged while
// the XMBFile is being used.
void Initialise(char* FileData);
// Returns the root element
XMBElement getRoot() const;
// Returns internal ID for a given ASCII element/attribute string.
int getElementID(const char* Name) const;
int getAttributeID(const char* Name) const;
// For lazy people (e.g. me) when speed isn't vital:
// Returns element/attribute string for a given internal ID
std::string getElementString(const int ID) const;
std::string getAttributeString(const int ID) const;
private:
char* m_Pointer;
#ifdef XERO_USEMAP
std::map m_ElementNames;
std::map m_AttributeNames;
#else
int m_ElementNameCount;
int m_AttributeNameCount;
char* m_ElementPointer;
char* m_AttributePointer;
#endif
std::string ReadZStrA();
};
class XMBElement
{
public:
XMBElement(char* offset)
: m_Pointer(offset) {}
int getNodeName() const;
XMBElementList getChildNodes() const;
XMBAttributeList getAttributes() const;
utf16string getText() const;
int getLineNumber() const;
private:
// Pointer to the start of the node
char* m_Pointer;
};
class XMBElementList
{
public:
XMBElementList(char* offset, int count)
: Count(count),
m_Pointer(offset),
m_LastItemID(-2) {} // use -2 because it isn't x-1 where x is a non-negative integer
XMBElement item(const int id); // returns Children[id]
int Count;
private:
char* m_Pointer;
// For optimised sequential access:
int m_LastItemID;
char* m_LastPointer;
};
struct XMBAttribute
{
XMBAttribute(int name, utf16string value)
: Name(name), Value(value) {};
int Name;
utf16string Value;
};
class XMBAttributeList
{
public:
XMBAttributeList(char* offset, int count)
: Count(count), m_Pointer(offset), m_LastItemID( -2 ) {};
// Get the attribute value directly (unlike Xerces)
utf16string getNamedItem(const int AttributeName) const;
// Returns an attribute by position in the list
XMBAttribute item(const int id);
int Count;
private:
// Pointer to start of attribute list
char* m_Pointer;
// For optimised sequential access:
int m_LastItemID;
char* m_LastPointer;
};
#endif // _XEROXMB_H_
Index: ps/trunk/source/gui/CInput.cpp
===================================================================
--- ps/trunk/source/gui/CInput.cpp (revision 1524)
+++ ps/trunk/source/gui/CInput.cpp (revision 1525)
@@ -1,832 +1,832 @@
/*
CInput
by Gustav Larsson
gee@pyro.nu
*/
#include "precompiled.h"
#include "GUI.h"
#include "CInput.h"
#include "ps/Font.h"
#include "ogl.h"
// TODO Gee: new
#include "OverlayText.h"
#include "lib/res/unifont.h"
#include "ps/CLogger.h"
#define LOG_CATEGORY "gui"
using namespace std;
//-------------------------------------------------------------------
// Constructor / Destructor
//-------------------------------------------------------------------
CInput::CInput() : m_iBufferPos(0)
{
AddSetting(GUIST_float, "buffer-zone");
AddSetting(GUIST_CStrW, "caption");
AddSetting(GUIST_CStr, "font");
AddSetting(GUIST_bool, "scrollbar");
AddSetting(GUIST_CStr, "scrollbar-style");
AddSetting(GUIST_CGUISpriteInstance, "sprite");
- AddSetting(GUIST_int, "icon-id");
+ AddSetting(GUIST_int, "cell-id");
AddSetting(GUIST_CColor, "textcolor");
// TODO Gee: (2004-08-14)
// Add a setting for buffer zone
//AddSetting(GUIST_int, "
//GUI::SetSetting(this, "ghost", true);
GUI::SetSetting(this, "scrollbar", false);
// Add scroll-bar
CGUIScrollBarVertical * bar = new CGUIScrollBarVertical();
bar->SetRightAligned(true);
bar->SetUseEdgeButtons(true);
AddScrollBar(bar);
UpdateText(); // will create an empty row, just so we have somewhere to position the insertion marker
}
CInput::~CInput()
{
}
int CInput::ManuallyHandleEvent(const SDL_Event* ev)
{
assert(m_iBufferPos != -1);
int szChar = ev->key.keysym.sym;
wchar_t cooked = (wchar_t)ev->key.keysym.unicode;
// Since the GUI framework doesn't handle to set settings
// in Unicode (CStrW), we'll simply retrieve the actual
// pointer and edit that.
CStrW *pCaption = (CStrW*)m_Settings["caption"].m_pSetting;
switch (szChar){
//TODOcase '\r':
/* case '\n':
// TODO Gee: (2004-09-07) New line? I should just add '\n'
*pCaption += wchar_t('\n');
++m_iBufferPos;
break;
*/
/* case '\r':
// TODO Gee: (2004-09-07) New line? I should just add '\n'
*pCaption += wchar_t('\n');
++m_iBufferPos;
break;
*/
case '\t':
/* Auto Complete */
// TODO Gee: (2004-09-07) What to do with tab?
break;
case '\b':
// TODO Gee: (2004-09-07) What is this?
if (pCaption->Length() == 0 ||
m_iBufferPos == 0)
break;
if (m_iBufferPos == pCaption->Length())
*pCaption = pCaption->Left( (long) pCaption->Length()-1);
else
*pCaption = pCaption->Left( m_iBufferPos-1 ) +
pCaption->Right( (long) pCaption->Length()-m_iBufferPos );
UpdateText(m_iBufferPos-1, m_iBufferPos, m_iBufferPos-1);
--m_iBufferPos;
break;
case SDLK_DELETE:
if (pCaption->Length() == 0 ||
m_iBufferPos == pCaption->Length())
break;
*pCaption = pCaption->Left( m_iBufferPos ) +
pCaption->Right( (long) pCaption->Length()-(m_iBufferPos+1) );
UpdateText(m_iBufferPos, m_iBufferPos+1, m_iBufferPos);
break;
case SDLK_HOME:
m_iBufferPos = 0;
break;
case SDLK_END:
m_iBufferPos = (long) pCaption->Length();
break;
case SDLK_LEFT:
if (m_iBufferPos)
--m_iBufferPos;
break;
case SDLK_RIGHT:
if (m_iBufferPos != pCaption->Length())
++m_iBufferPos;
break;
case SDLK_UP:
/*if (m_deqBufHistory.size() && iHistoryPos != (int)m_deqBufHistory.size() - 1)
{
iHistoryPos++;
SetBuffer(m_deqBufHistory.at(iHistoryPos).data());
}*/
break;
case SDLK_DOWN:
/*if (iHistoryPos != -1) iHistoryPos--;
if (iHistoryPos != -1)
SetBuffer(m_deqBufHistory.at(iHistoryPos).data());
else FlushBuffer();*/
break;
case SDLK_PAGEUP:
//if (m_iMsgHistPos != (int)m_deqMsgHistory.size()) m_iMsgHistPos++;
break;
case SDLK_PAGEDOWN:
//if (m_iMsgHistPos != 1) m_iMsgHistPos--;
break;
/* END: Message History Lookup */
case '\r':
cooked = '\n'; // Change to '\n' and do default:
// NOTE: Fall-through
default: //Insert a character
if (cooked == 0)
return EV_PASS; // Important, because we didn't use any key
if (m_iBufferPos == pCaption->Length())
*pCaption += cooked;
else
*pCaption = pCaption->Left(m_iBufferPos) + CStrW(cooked) +
pCaption->Right((long) pCaption->Length()-m_iBufferPos);
UpdateText(m_iBufferPos, m_iBufferPos, m_iBufferPos+1);
++m_iBufferPos;
// TODO
//UpdateText(m_iBufferPos, m_iBufferPos, m_iBufferPos+4);
//m_iBufferPos+=4;
break;
}
//UpdateText();
return EV_HANDLED;
}
void CInput::HandleMessage(const SGUIMessage &Message)
{
// TODO Gee:
IGUIScrollBarOwner::HandleMessage(Message);
switch (Message.type)
{
case GUIM_SETTINGS_UPDATED:
bool scrollbar;
GUI::GetSetting(this, "scrollbar", scrollbar);
// Update scroll-bar
// TODO Gee: (2004-09-01) Is this really updated each time it should?
if (scrollbar &&
(Message.value == CStr("size") || Message.value == CStr("z") ||
Message.value == CStr("absolute")))
{
GetScrollBar(0).SetX( m_CachedActualSize.right );
GetScrollBar(0).SetY( m_CachedActualSize.top );
GetScrollBar(0).SetZ( GetBufferedZ() );
GetScrollBar(0).SetLength( m_CachedActualSize.bottom - m_CachedActualSize.top );
}
// Update scrollbar
if (Message.value == CStr("scrollbar-style"))
{
CStr scrollbar_style;
GUI::GetSetting(this, Message.value, scrollbar_style);
GetScrollBar(0).SetScrollBarStyle( scrollbar_style );
}
if (Message.value == CStr("caption"))
{
UpdateText();
}
break;
case GUIM_MOUSE_PRESS_LEFT:
// Okay, this section is about pressing the mouse and
// choosing where the point should be placed. For
// instance, if we press between a and b, the point
// should of course be placed accordingly. Other
// special cases are handled like the input box norms.
{
CPos mouse = GetMousePos();
// Pointer to caption, will come in handy
CStrW *pCaption = (CStrW*)m_Settings["caption"].m_pSetting;
// Now get the height of the font.
CFont font ("Console");
float spacing = (float)font.GetLineSpacing();
float height = (float)font.GetHeight();
// Change mouse position relative to text.
// Include scrolling.
mouse -= m_CachedActualSize.TopLeft();
//if ((m_CharacterPositions.size()-1) * spacing + height < mouse.y)
// m_iBufferPos = pCaption->Length();
int row = (int)(mouse.y / spacing);//m_CharachterPositions.size()
if (row > (int)m_CharacterPositions.size()-1)
row = (int)m_CharacterPositions.size()-1;
// TODO Gee (2004-11-21): Okay, I need a 'list' for some reasons, but I would really
// be able to get the specific element here. This is hopefully a temporary hack.
list::iterator current = m_CharacterPositions.begin();
advance(current, row);
//m_iBufferPos = m_CharacterPositions.get.m_ListStart;
m_iBufferPos = current->m_ListStart;
// Okay, no loop through the glyphs to find the appropriate X position
float previous=0.f;
int i=0;
for (vector::iterator it=current->m_ListOfX.begin();
it!=current->m_ListOfX.end();
++it, ++i)
{
if (*it >= mouse.x)
{
if (i != 0)
{
if (mouse.x - previous > *it - mouse.x)
m_iBufferPos += i+1;
else
m_iBufferPos += i;
}
// else let the value be current->m_ListStart which it already is.
// check if the previous or the current is closest.
break;
}
previous = *it;
}
// If a position wasn't found, we will assume the last
// character of that line.
if (i == current->m_ListOfX.size())
m_iBufferPos += i;
break;
}
case GUIM_MOUSE_WHEEL_DOWN:
GetScrollBar(0).ScrollMinus();
// Since the scroll was changed, let's simulate a mouse movement
// to check if scrollbar now is hovered
HandleMessage(SGUIMessage(GUIM_MOUSE_MOTION));
break;
case GUIM_MOUSE_WHEEL_UP:
GetScrollBar(0).ScrollPlus();
// Since the scroll was changed, let's simulate a mouse movement
// to check if scrollbar now is hovered
HandleMessage(SGUIMessage(GUIM_MOUSE_MOTION));
break;
case GUIM_LOAD:
//SetFocus();
case GUIM_GOT_FOCUS:
m_iBufferPos = 0;
break;
case GUIM_LOST_FOCUS:
m_iBufferPos = -1;
break;
default:
break;
}
}
void CInput::Draw()
{
float bz = GetBufferedZ();
// First call draw on ScrollBarOwner
bool scrollbar;
GUI::GetSetting(this, "scrollbar", scrollbar);
if (scrollbar)
{
// Draw scrollbar
IGUIScrollBarOwner::Draw();
}
if (GetGUI())
{
CGUISpriteInstance *sprite;
- int icon_id;
+ int cell_id;
GUI::GetSettingPointer(this, "sprite", sprite);
- GUI::GetSetting(this, "icon-id", icon_id);
+ GUI::GetSetting(this, "cell-id", cell_id);
- GetGUI()->DrawSprite(*sprite, icon_id, bz, m_CachedActualSize);
+ GetGUI()->DrawSprite(*sprite, cell_id, bz, m_CachedActualSize);
float scroll=0.f;
if (scrollbar)
{
scroll = GetScrollBar(0).GetPos();
}
CColor color;
GUI::GetSetting(this, "textcolor", color);
glEnable(GL_TEXTURE_2D);
glDisable(GL_CULL_FACE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glDisable(GL_ALPHA_TEST);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
CFont font ("Console");
font.Bind();
glPushMatrix();
CStrW caption;
GUI::GetSetting(this, "caption", caption);
// Get the height of this font.
float h = (float)font.GetHeight();
float ls = (float)font.GetLineSpacing();
glTranslatef((GLfloat)int(m_CachedActualSize.left), (GLfloat)int(m_CachedActualSize.top+h), bz);
glColor4f(1.f, 1.f, 1.f, 1.f);
float buffered_y=0.f;
for (list::const_iterator it = m_CharacterPositions.begin();
it != m_CharacterPositions.end();
++it)
{
if (buffered_y > m_CachedActualSize.GetHeight())
break;
glPushMatrix();
// We might as well use 'i' here, because we need it
for (int i=0; i < (int)it->m_ListOfX.size(); ++i)
{
if (it->m_ListStart + i == m_iBufferPos)
{
// U+FE33: PRESENTATION FORM FOR VERTICAL LOW LINE
// (sort of like a | which is aligned to the left of most characters)
glPushMatrix();
glwprintf(L"%lc", 0xFE33);
glPopMatrix();
}
glwprintf(L"%lc", caption[it->m_ListStart + i]);
}
if (it->m_ListStart + it->m_ListOfX.size() == m_iBufferPos)
{
glwprintf(L"%lc", 0xFE33);
}
glPopMatrix();
glTranslatef(0.f, ls, 0.f);
buffered_y += ls;
}
glPopMatrix();
glDisable(GL_TEXTURE_2D);
}
}
void CInput::UpdateText(int from, int to_before, int to_after)
{
CStrW caption;
GUI::GetSetting(this, "caption", caption);
SRow row;
row.m_ListStart = 0;
int to;
if (to_before == -1)
to = (int)caption.Length();
CFont font ("Console");
//LOG(ERROR, LOG_CATEGORY, "Point 1 %d %d", to_before, to_after);
list::iterator current_line;
// Used to ... TODO
int check_point_row_start = -1;
int check_point_row_end = -1;
// Reset
if (from == 0 && to_before == -1)
{
m_CharacterPositions.clear();
current_line = m_CharacterPositions.begin();
}
else
{
assert(to_before != -1);
//LOG(ERROR, LOG_CATEGORY, "Point 2 - %d %d", from, to_before);
list::iterator destroy_row_from, destroy_row_to;
// Iterate, and remove everything between 'from' and 'to_before'
// actually remove the entire lines they are on, it'll all have
// to be redone. And when going along, we'll delete a row at a time
// when continuing to see how much more after 'to' we need to remake.
int i=0;
for (list::iterator it=m_CharacterPositions.begin();
it!=m_CharacterPositions.end(); ++it, ++i)
{
if (destroy_row_from == list::iterator() &&
it->m_ListStart > from)
{
// Destroy the previous line, and all to 'to_before'
//if (i >= 2)
// destroy_row_from = it-2;
//else
// destroy_row_from = it-1;
destroy_row_from = it;
--destroy_row_from;
// For the rare case that we might remove characters to a word
// so that it should go on the previous line, we need to
// by standards re-do the whole previous line too (if one
// exists)
if (destroy_row_from != m_CharacterPositions.begin())
--destroy_row_from;
}
if (destroy_row_to == list::iterator() &&
it->m_ListStart > to_before)
{
destroy_row_to = it;
// If it isn't the last row, we'll add another row to delete,
// just so we can see if the last restorted line is
// identical to what it was before. If it isn't, then we'll
// have to continue.
// 'check_point_row_start' is where we store how the that
// line looked.
if (destroy_row_to != m_CharacterPositions.end())
{
check_point_row_start = destroy_row_to->m_ListStart;
check_point_row_end = check_point_row_start + (int)destroy_row_to->m_ListOfX.size();
if (destroy_row_to->m_ListOfX.empty())
++check_point_row_end;
}
++destroy_row_to;
break;
}
}
if (destroy_row_from == list::iterator())
{
destroy_row_from = m_CharacterPositions.end();
--destroy_row_from;
current_line = destroy_row_from;
}
if (destroy_row_to == list::iterator())
{
destroy_row_to = m_CharacterPositions.end();
check_point_row_start = -1;
}
// set 'from' to the row we'll destroy from
// and 'to' to the row we'll destroy to
from = destroy_row_from->m_ListStart;
if (destroy_row_to != m_CharacterPositions.end())
to = destroy_row_to->m_ListStart; // notice it will iterate [from, to), so it will never reach to.
else
to = (int)caption.Length();
// Setup the first row
row.m_ListStart = destroy_row_from->m_ListStart;
//LOG(ERROR, LOG_CATEGORY, "Point 3 %d", to);
// Set current line, new rows will be added before current_line, so
// we'll choose the destroy_row_to, because it won't be deleted
// in the coming erase.
current_line = destroy_row_to;
list::iterator temp_it = destroy_row_to;
--temp_it;
CStr c_caption1(caption.GetSubstring(destroy_row_from->m_ListStart, (temp_it->m_ListStart + temp_it->m_ListOfX.size()) -destroy_row_from->m_ListStart));
//LOG(ERROR, LOG_CATEGORY, "Now destroying: \"%s\"", c_caption1.c_str());
m_CharacterPositions.erase(destroy_row_from, destroy_row_to);
//LOG(ERROR, LOG_CATEGORY, "--> %d %d", from, to);
////CStr c_caption(caption.GetSubstring(from, to-from));
//LOG(ERROR, LOG_CATEGORY, "Re-doing string: \"%s\"", c_caption.c_str());
// If there has been a change in number of characters
// we need to change all m_ListStart that comes after
// the interval we just destroyed. We'll change all
// values with the delta change of the string length.
int delta = to_after - to_before;
if (delta != 0)
{
for (list::iterator it=current_line;
it!=m_CharacterPositions.end();
++it)
{
it->m_ListStart += delta;
}
// Update our check point too!
check_point_row_start += delta;
check_point_row_end += delta;
if (to != caption.Length())
to += delta;
}
}
int last_word_started=from;
int last_list_start=-1;
float x_pos = 0.f;
//if (to_before != -1)
// return;
//LOG(ERROR, LOG_CATEGORY, "%d %d", from, to);
for (int i=from; i::iterator pos( &m_CharacterPositions[current_line] );
m_CharacterPositions.insert( pos, row );
++current_line;
}*/
if (i==to-1)
break; // it will be added outside
CStr c_caption1(caption.GetSubstring(row.m_ListStart, row.m_ListOfX.size()));
//LOG(ERROR, LOG_CATEGORY, "Adding1: \"%s\" (%d,%d,%d)", c_caption1.c_str(), from, to, i);
current_line = m_CharacterPositions.insert( current_line, row );
++current_line;
// Setup the next row:
row.m_ListOfX.clear();
row.m_ListStart = i+1;
x_pos = 0.f;
// If it's done now, no more word-wrapping
// needs to be done.
//if (i==to-1)
// break;
}
else
{
if (caption[i] == wchar_t(' ')/* || TODO Gee (2004-10-13): the '-' disappears, fix.
caption[i] == wchar_t('-')*/)
last_word_started = i+1;
x_pos += (float)font.GetCharacterWidth(caption[i]);
//LOG(ERROR, LOG_CATEGORY, "%c %f", (char)caption[i], (float)font->GetCharacterWidth(caption[i]));
if (x_pos >= m_CachedActualSize.GetWidth())
{
// The following decides whether it will word-wrap a word,
// or if it's only one word on the line, where it has to
// break the word apart.
if (last_word_started == row.m_ListStart)
{
last_word_started = i;
row.m_ListOfX.resize(row.m_ListOfX.size() - (i-last_word_started));
//row.m_ListOfX.push_back( x_pos );
//continue;
}
else
{
// regular word-wrap
row.m_ListOfX.resize(row.m_ListOfX.size() - (i-last_word_started+1));
}
// Now, create a new line:
// notice: when we enter a newline, you can stand with the cursor
// both before and after that character, being on different
// rows. With automatic word-wrapping, that is not possible. Which
// is intuitively correct.
CStr c_caption1(caption.GetSubstring(row.m_ListStart, row.m_ListOfX.size()));
//LOG(ERROR, LOG_CATEGORY, "Adding2: \"%s\" - %d [%d, %d[", c_caption1.c_str(), last_word_started, from, to);
current_line = m_CharacterPositions.insert( current_line, row );
++current_line;
// Setup the next row:
row.m_ListOfX.clear();
row.m_ListStart = last_word_started;
i=last_word_started-1;
x_pos = 0.f;
}
else
// Get width of this character:
row.m_ListOfX.push_back( x_pos );
}
// Check if it's the last iteration, and we're not revising the whole string
// because in that case, more word-wrapping might be needed.
// also check if the current line isn't the end
if (to_before != -1 && i == to-1 && current_line != m_CharacterPositions.end())
{
//LOG(ERROR, LOG_CATEGORY, "row.m_ListStart = %d", row.m_ListStart);
//LOG(ERROR, LOG_CATEGORY, "check_point_row_start = %d", check_point_row_start);
//LOG(ERROR, LOG_CATEGORY, "last_list_start = %d", last_list_start);
/* if (last_list_start != -1 &&
last_list_start == row.m_ListStart)
{
to = current_line->m_ListStart;
LOG(ERROR, LOG_CATEGORY, "*** %d %d", i, to);
}
else
{
*/
// check all rows and see if any existing
//LOG(ERROR, LOG_CATEGORY, "(%d %d) (%d %d)", i, to, row.m_ListStart, check_point_row_start);
if (row.m_ListStart != check_point_row_start)
{
list::iterator destroy_row_from, destroy_row_to;
// Iterate, and remove everything between 'from' and 'to_before'
// actually remove the entire lines they are on, it'll all have
// to be redone. And when going along, we'll delete a row at a time
// when continuing to see how much more after 'to' we need to remake.
int i=0;
for (list::iterator it=m_CharacterPositions.begin();
it!=m_CharacterPositions.end(); ++it, ++i)
{
if (destroy_row_from == list::iterator() &&
it->m_ListStart > check_point_row_start)
{
// Destroy the previous line, and all to 'to_before'
//if (i >= 2)
// destroy_row_from = it-2;
//else
// destroy_row_from = it-1;
destroy_row_from = it;
//--destroy_row_from;
//LOG(ERROR, LOG_CATEGORY, "[ %d %d %d", i, it->m_ListStart, check_point_row_start);
}
if (destroy_row_to == list::iterator() &&
it->m_ListStart > check_point_row_end)
{
destroy_row_to = it;
// If it isn't the last row, we'll add another row to delete,
// just so we can see if the last restorted line is
// identical to what it was before. If it isn't, then we'll
// have to continue.
// 'check_point_row_start' is where we store how the that
// line looked.
// if (destroy_row_to !=
if (destroy_row_to != m_CharacterPositions.end())
{
check_point_row_start = destroy_row_to->m_ListStart;
check_point_row_end = check_point_row_start + (int)destroy_row_to->m_ListOfX.size();
if (destroy_row_to->m_ListOfX.empty())
++check_point_row_end;
//LOG(ERROR, LOG_CATEGORY, "] %d %d %d", i, destroy_row_to->m_ListStart, check_point_row_end);
}
else
check_point_row_start = check_point_row_end = -1;
++destroy_row_to;
break;
}
}
if (destroy_row_from == list::iterator())
{
destroy_row_from = m_CharacterPositions.end();
--destroy_row_from;
current_line = destroy_row_from;
}
if (destroy_row_to == list::iterator())
{
destroy_row_to = m_CharacterPositions.end();
check_point_row_start = check_point_row_end = -1;
}
// set 'from' to the from row we'll destroy
// and 'to' to 'to_after'
from = destroy_row_from->m_ListStart;
if (destroy_row_to != m_CharacterPositions.end())
to = destroy_row_to->m_ListStart; // notice it will iterate [from, to[, so it will never reach to.
else
to = (int)caption.Length();
//LOG(ERROR, LOG_CATEGORY, "Point 3 %d", to);
// Set current line, new rows will be added before current_line, so
// we'll choose the destroy_row_to, because it won't be deleted
// in the coming erase.
current_line = destroy_row_to;
m_CharacterPositions.erase(destroy_row_from, destroy_row_to);
//LOG(ERROR, LOG_CATEGORY, "--> %d %d", from, to);
CStr c_caption(caption.GetSubstring(from, to-from));
//LOG(ERROR, LOG_CATEGORY, "Re-doing string: \"%s\"", c_caption.c_str());
/*if (current_line != m_CharacterPositions.end())
{
current_line = m_CharacterPositions.erase(current_line);
if (current_line != m_CharacterPositions.end())
{
check_point_row_start = current_line->m_ListStart;
to = check_point_row_start;
}
else
{
to = caption.Length();
}
}
else
{
check_point_row_start = -1; // just one more row, which is the last, we don't need this anymore
to = caption.Length();
}*/
/*if (destroy_row_to != m_CharacterPositions.end())
to = destroy_row_to->m_ListStart-1;
else
to = caption.Length();*/
}
// else, the for loop will end naturally.
//}
//last_list_start = row.m_ListStart;
}
}
// add the final row (even if empty)
CStr c_caption1(caption.GetSubstring(row.m_ListStart, row.m_ListOfX.size()));
//LOG(ERROR, LOG_CATEGORY, "Adding3: \"%s\"", c_caption1.c_str());
m_CharacterPositions.insert( current_line, row );
//++current_line;
}
Index: ps/trunk/source/gui/IGUIButtonBehavior.cpp
===================================================================
--- ps/trunk/source/gui/IGUIButtonBehavior.cpp (revision 1524)
+++ ps/trunk/source/gui/IGUIButtonBehavior.cpp (revision 1525)
@@ -1,116 +1,116 @@
/*
IGUIButtonBehavior
by Gustav Larsson
gee@pyro.nu
*/
#include "precompiled.h"
#include "GUI.h"
using namespace std;
//-------------------------------------------------------------------
// Constructor / Destructor
//-------------------------------------------------------------------
IGUIButtonBehavior::IGUIButtonBehavior() : m_Pressed(false)
{
}
IGUIButtonBehavior::~IGUIButtonBehavior()
{
}
void IGUIButtonBehavior::HandleMessage(const SGUIMessage &Message)
{
// TODO Gee: easier access functions
switch (Message.type)
{
case GUIM_MOUSE_PRESS_LEFT:
{
bool enabled;
GUI::GetSetting(this, "enabled", enabled);
if (!enabled)
break;
m_Pressed = true;
} break;
case GUIM_MOUSE_RELEASE_LEFT:
{
bool enabled;
GUI::GetSetting(this, "enabled", enabled);
if (!enabled)
break;
if (m_Pressed)
{
m_Pressed = false;
// BUTTON WAS CLICKED
HandleMessage(GUIM_PRESSED);
ScriptEvent("press");
}
} break;
default:
break;
}
}
CColor IGUIButtonBehavior::ChooseColor()
{
CColor color, color_over, color_pressed, color_disabled;
// Yes, the object must possess these settings. They are standard
GUI::GetSetting(this, "textcolor", color);
GUI::GetSetting(this, "textcolor-over", color_over);
GUI::GetSetting(this, "textcolor-pressed", color_pressed);
GUI::GetSetting(this, "textcolor-disabled", color_disabled);
bool enabled;
GUI::GetSetting(this, "enabled", enabled);
if (!enabled)
{
return GUI<>::FallBackColor(color_disabled, color);
}
else
if (m_MouseHovering)
{
if (m_Pressed)
return GUI<>::FallBackColor(color_pressed, color);
else
return GUI<>::FallBackColor(color_over, color);
}
else return color;
}
void IGUIButtonBehavior::DrawButton(const CRect &rect,
const float &z,
CGUISpriteInstance& sprite,
CGUISpriteInstance& sprite_over,
CGUISpriteInstance& sprite_pressed,
CGUISpriteInstance& sprite_disabled,
- int icon_id)
+ int cell_id)
{
if (GetGUI())
{
bool enabled;
GUI::GetSetting(this, "enabled", enabled);
if (!enabled)
{
- GetGUI()->DrawSprite(GUI<>::FallBackSprite(sprite_disabled, sprite), icon_id, z, rect);
+ GetGUI()->DrawSprite(GUI<>::FallBackSprite(sprite_disabled, sprite), cell_id, z, rect);
}
else
if (m_MouseHovering)
{
if (m_Pressed)
- GetGUI()->DrawSprite(GUI<>::FallBackSprite(sprite_pressed, sprite), icon_id, z, rect);
+ GetGUI()->DrawSprite(GUI<>::FallBackSprite(sprite_pressed, sprite), cell_id, z, rect);
else
- GetGUI()->DrawSprite(GUI<>::FallBackSprite(sprite_over, sprite), icon_id, z, rect);
+ GetGUI()->DrawSprite(GUI<>::FallBackSprite(sprite_over, sprite), cell_id, z, rect);
}
- else GetGUI()->DrawSprite(sprite, icon_id, z, rect);
+ else GetGUI()->DrawSprite(sprite, cell_id, z, rect);
}
}
Index: ps/trunk/source/gui/CButton.cpp
===================================================================
--- ps/trunk/source/gui/CButton.cpp (revision 1524)
+++ ps/trunk/source/gui/CButton.cpp (revision 1525)
@@ -1,147 +1,147 @@
/*
CButton
by Gustav Larsson
gee@pyro.nu
*/
#include "precompiled.h"
#include "GUI.h"
#include "CButton.h"
#include "ogl.h"
using namespace std;
//-------------------------------------------------------------------
// Constructor / Destructor
//-------------------------------------------------------------------
CButton::CButton()
{
AddSetting(GUIST_float, "buffer-zone");
AddSetting(GUIST_CGUIString, "caption");
AddSetting(GUIST_CStr, "font");
AddSetting(GUIST_CGUISpriteInstance, "sprite");
AddSetting(GUIST_CGUISpriteInstance, "sprite-over");
AddSetting(GUIST_CGUISpriteInstance, "sprite-pressed");
AddSetting(GUIST_CGUISpriteInstance, "sprite-disabled");
- AddSetting(GUIST_int, "icon-id");
+ AddSetting(GUIST_int, "cell-id");
AddSetting(GUIST_EAlign, "text-align");
AddSetting(GUIST_EVAlign, "text-valign");
AddSetting(GUIST_CColor, "textcolor");
AddSetting(GUIST_CColor, "textcolor-over");
AddSetting(GUIST_CColor, "textcolor-pressed");
AddSetting(GUIST_CColor, "textcolor-disabled");
// Add text
AddText(new SGUIText());
}
CButton::~CButton()
{
}
void CButton::SetupText()
{
if (!GetGUI())
return;
assert(m_GeneratedTexts.size()>=1);
CStr font;
if (GUI::GetSetting(this, "font", font) != PS_OK || font.Length()==0)
// Use the default if none is specified
// TODO Gee: (2004-08-14) Default should not be hard-coded, but be in styles!
font = "default";
CGUIString caption;
GUI::GetSetting(this, "caption", caption);
*m_GeneratedTexts[0] = GetGUI()->GenerateText(caption, font, m_CachedActualSize.GetWidth(), 0, this);
// Set position of text
// Check which alignment to use!
EAlign align;
EVAlign valign;
float bz;
GUI::GetSetting(this, "text-align", align);
GUI::GetSetting(this, "text-valign", valign);
GUI::GetSetting(this, "buffer-zone", bz);
switch (align)
{
case EAlign_Left:
m_TextPos.x = m_CachedActualSize.left + bz;
break;
case EAlign_Center:
// Round to integer pixel values, else the fonts look awful
m_TextPos.x = floorf(m_CachedActualSize.CenterPoint().x - m_GeneratedTexts[0]->m_Size.cx/2.f);
break;
case EAlign_Right:
m_TextPos.x = m_CachedActualSize.right - m_GeneratedTexts[0]->m_Size.cx - bz;
break;
default:
debug_warn("Broken EAlign in CButton::SetupText()");
break;
}
switch (valign)
{
case EVAlign_Top:
m_TextPos.y = m_CachedActualSize.top + bz;
break;
case EVAlign_Center:
// Round to integer pixel values, else the fonts look awful
m_TextPos.y = floorf(m_CachedActualSize.CenterPoint().y - m_GeneratedTexts[0]->m_Size.cy/2.f);
break;
case EVAlign_Bottom:
m_TextPos.y = m_CachedActualSize.bottom - m_GeneratedTexts[0]->m_Size.cy - bz;
break;
default:
debug_warn("Broken EVAlign in CButton::SetupText()");
break;
}
}
void CButton::HandleMessage(const SGUIMessage &Message)
{
// Important
IGUIButtonBehavior::HandleMessage(Message);
IGUITextOwner::HandleMessage(Message);
switch (Message.type)
{
case GUIM_PRESSED:
// GetGUI()->TEMPmessage = "Button " + string((const TCHAR*)m_Name) + " was pressed!";
break;
default:
break;
}
}
void CButton::Draw()
{
float bz = GetBufferedZ();
CGUISpriteInstance *sprite, *sprite_over, *sprite_pressed, *sprite_disabled;
- int icon_id;
+ int cell_id;
GUI::GetSettingPointer(this, "sprite", sprite);
GUI::GetSettingPointer(this, "sprite-over", sprite_over);
GUI::GetSettingPointer(this, "sprite-pressed", sprite_pressed);
GUI::GetSettingPointer(this, "sprite-disabled", sprite_disabled);
- GUI::GetSetting(this, "icon-id", icon_id);
+ GUI::GetSetting(this, "cell-id", cell_id);
DrawButton(m_CachedActualSize,
bz,
*sprite,
*sprite_over,
*sprite_pressed,
*sprite_disabled,
- icon_id);
+ cell_id);
CColor color = ChooseColor();
IGUITextOwner::Draw(0, color, m_TextPos, bz+0.1f);
}
Index: ps/trunk/source/gui/GUIRenderer.h
===================================================================
--- ps/trunk/source/gui/GUIRenderer.h (revision 1524)
+++ ps/trunk/source/gui/GUIRenderer.h (revision 1525)
@@ -1,47 +1,51 @@
#ifndef GUIRenderer_h
#define GUIRenderer_h
#include "lib/res/handle.h"
#include "ps/Overlay.h"
#include "ps/CStr.h"
#include
+struct SGUIImageEffects;
+
namespace GUIRenderer
{
struct Handle_rfcnt_tex
{
Handle h;
Handle_rfcnt_tex();
Handle_rfcnt_tex(Handle h_);
Handle_rfcnt_tex(const Handle_rfcnt_tex& that);
~Handle_rfcnt_tex();
Handle_rfcnt_tex& operator=(Handle h_);
};
struct SDrawCall
{
Handle_rfcnt_tex m_TexHandle;
bool m_EnableBlending;
+ SGUIImageEffects* m_Effects;
+
CRect m_Vertices;
CRect m_TexCoords;
float m_DeltaZ;
CColor m_BorderColor; // == CColor() for no border
CColor m_BackColor;
};
typedef std::vector DrawCalls;
}
#include "gui/CGUISprite.h"
namespace GUIRenderer
{
- void UpdateDrawCallCache(DrawCalls &Calls, CStr &SpriteName, CRect& Size, int IconID, std::map &Sprites);
+ void UpdateDrawCallCache(DrawCalls &Calls, CStr &SpriteName, CRect& Size, int CellID, std::map &Sprites);
void Draw(DrawCalls &Calls);
}
#endif // GUIRenderer_h
Index: ps/trunk/source/gui/CGUISprite.h
===================================================================
--- ps/trunk/source/gui/CGUISprite.h (revision 1524)
+++ ps/trunk/source/gui/CGUISprite.h (revision 1525)
@@ -1,149 +1,161 @@
/*
A GUI Sprite
by Gustav Larsson
gee@pyro.nu
--Overview--
A GUI Sprite, which is actually a collage of several
sprites.
--Usage--
Used internally and declared in XML files, read documentations
on how.
--More info--
Check GUI.h
*/
#ifndef CGUISprite_H
#define CGUISprite_H
//--------------------------------------------------------
// Includes / Compiler directives
//--------------------------------------------------------
#include "GUIutil.h"
#include "Overlay.h"
#include "lib/res/ogl_tex.h"
//--------------------------------------------------------
// Macros
//--------------------------------------------------------
//--------------------------------------------------------
// Types
//--------------------------------------------------------
//--------------------------------------------------------
// Error declarations
//--------------------------------------------------------
//--------------------------------------------------------
// Declarations
//--------------------------------------------------------
+
+struct SGUIImageEffects
+{
+ CColor m_AddColor;
+ CColor m_MultiplyColor;
+};
+
+
/**
* @author Gustav Larsson
*
* A CGUISprite is actually a collage of several real
* sprites, this struct represents is such real sprite.
*/
struct SGUIImage
{
- SGUIImage() : m_Border(false), m_DeltaZ(0.f) {}
+ SGUIImage() : m_Effects(NULL), m_Border(false), m_DeltaZ(0.f) {}
// Filename of the texture
CStr m_TextureName;
// Image placement (relative to object)
CClientArea m_Size;
// Texture placement (relative to image placement)
CClientArea m_TextureSize;
// Because OpenGL wants textures in squares with a power of 2 (64x64, 256x256)
// it's sometimes tedious to adjust this. So this value simulates which area
// is the real texture
CRect m_TexturePlacementInFile;
// For textures that contain a collection of icons (e.g. unit portraits), this
- // will be set to the size of one icon. An object's icon-id will determine
+ // will be set to the size of one icon. An object's cell-id will determine
// which part of the texture is used.
- // Equal to CSize(0,0) for non-icon textures.
- CSize m_IconSize;
+ // Equal to CSize(0,0) for non-celled textures.
+ CSize m_CellSize;
+
+ // Visual effects (e.g. colour modulation)
+ SGUIImageEffects* m_Effects;
// Color
CColor m_BackColor;
CColor m_BorderColor;
// 0 or 1 pixel border is the only option
bool m_Border;
/**
* Z value modification of the image.
* Inputted in XML as x-level, although it just an easier and safer
* way of declaring delta-z.
*/
float m_DeltaZ;
};
+
/**
* @author Gustav Larsson
*
* The GUI sprite, is actually several real sprites (images)
* like a collage. View the section in the GUI
* TDD for more information.
*
* Drawing routine is located in CGUI
*
* @see CGUI#DrawSprite
*/
class CGUISprite
{
public:
CGUISprite() {}
virtual ~CGUISprite() {}
/**
* Adds an image to the sprite collage.
*
* @param image Adds this image to the sprite collage.
*/
void AddImage(const SGUIImage &image) { m_Images.push_back(image); }
/// List of images
std::vector m_Images;
};
#include "GUIRenderer.h"
// An instance of a sprite, usually stored in IGUIObjects - basically a string
// giving the sprite's name, but with some extra data to cache rendering
// calculations between draw calls.
class CGUISpriteInstance
{
public:
CGUISpriteInstance();
CGUISpriteInstance(CStr SpriteName);
CGUISpriteInstance &operator=(CStr SpriteName);
- void Draw(CRect Size, int IconID, std::map &Sprites);
+ void Draw(CRect Size, int CellID, std::map &Sprites);
void Invalidate();
bool IsEmpty() const;
CStr GetName() { return m_SpriteName; }
private:
CStr m_SpriteName;
// Stored drawing calls, for more efficient rendering
GUIRenderer::DrawCalls m_DrawCallCache;
// Relevant details of previously rendered sprite; the cache is invalidated
// whenever any of these values changes.
CRect m_CachedSize;
- int m_CachedIconID;
+ int m_CachedCellID;
};
#endif
Index: ps/trunk/source/gui/GUIutil.h
===================================================================
--- ps/trunk/source/gui/GUIutil.h (revision 1524)
+++ ps/trunk/source/gui/GUIutil.h (revision 1525)
@@ -1,432 +1,437 @@
/*
GUI util
by Gustav Larsson
gee@pyro.nu
--Overview--
Contains help class GUI<>, which gives us templated
parameter to all functions within GUI.
--More info--
Check GUI.h
*/
#ifndef GUIutil_H
#define GUIutil_H
//--------------------------------------------------------
// Includes / Compiler directives
//--------------------------------------------------------
#include "Parser.h"
// TODO Gee: New
#include "Overlay.h"
//--------------------------------------------------------
// Help Classes/Structs for the GUI
//--------------------------------------------------------
class CClientArea;
class CGUIString;
template
bool __ParseString(const CStr& Value, T &tOutput);
// Icon, you create them in the XML file with root element
// you use them in text owned by different objects... Such as CText.
struct SGUIIcon
{
+ SGUIIcon() : m_CellID(0) {}
+
// Texture name of icon
CStr m_TextureName;
// Size
CSize m_Size;
+
+ // Cell of texture to use; ignored unless the texture has specified cell-size
+ int m_CellID;
};
/**
* @author Gustav Larsson
*
* Client Area is a rectangle relative to a parent rectangle
*
* You can input the whole value of the Client Area by
* string. Like used in the GUI.
*/
class CClientArea
{
public:
CClientArea();
CClientArea(const CStr& Value);
/// Pixel modifiers
CRect pixel;
/// Percent modifiers
CRect percent;
/**
* Get client area rectangle when the parent is given
*/
CRect GetClientArea(const CRect &parent) const;
/**
* The ClientArea can be set from a string looking like:
*
* "0 0 100% 100%"
* "50%-10 50%-10 50%+10 50%+10"
*
* i.e. First percent modifier, then + or - and the pixel modifier.
* Although you can use just the percent or the pixel modifier. Notice
* though that the percent modifier must always be the first when
* both modifiers are inputted.
*
* @return true if success, false if failure. If false then the client area
* will be unchanged.
*/
bool SetClientArea(const CStr& Value);
};
//--------------------------------------------------------
// Forward declarations
//--------------------------------------------------------
class CGUI;
class IGUIObject;
struct SGUIMessage;
class CGUISpriteInstance;
/**
* @author Gustav Larsson
*
* Base class to only the class GUI. This superclass is
* kind of a templateless extention of the class GUI.
* Used for other functions to friend with, because it
* can't friend with GUI since it's templated (at least
* not on all compilers we're using).
*/
class CInternalCGUIAccessorBase
{
protected:
/// Get object pointer
static IGUIObject * GetObjectPointer(CGUI &GUIinstance, const CStr& Object);
/// const version
static const IGUIObject * GetObjectPointer(const CGUI &GUIinstance, const CStr& Object);
/// Wrapper for ResetStates
static void QueryResetting(IGUIObject *pObject);
static void HandleMessage(IGUIObject *pObject, const SGUIMessage &message);
};
#ifndef NDEBUG
// Used to ensure type-safety, sort of
template void CheckType(const IGUIObject* obj, const CStr& setting);
#endif
/**
* @author Gustav Larsson
*
* Includes static functions that needs one template
* argument.
*
* int is only to please functions that doesn't even use T
* and are only within this class because it's convenient
*/
template
class GUI : public CInternalCGUIAccessorBase
{
// Private functions further ahead
friend class CGUI;
friend class IGUIObject;
friend class CInternalCGUIAccessorBase;
public:
// Like GetSetting (below), but doesn't make a copy of the value
// (so it can be modified later)
static PS_RESULT GetSettingPointer(const IGUIObject *pObject, const CStr& Setting, T* &Value);
/**
* Retrieves a setting by name from object pointer
*
* @param pObject Object pointer
* @param Setting Setting by name
* @param Value Stores value here, note type T!
*/
static PS_RESULT GetSetting(const IGUIObject *pObject, const CStr& Setting, T &Value);
/**
* Sets a value by name using a real datatype as input.
*
* This is the official way of setting a setting, no other
* way should only cautiously be used!
*
* @param pObject Object pointer
* @param Setting Setting by name
* @param Value Sets value to this, note type T!
*/
static PS_RESULT SetSetting(IGUIObject *pObject, const CStr& Setting, const T &Value);
#ifdef g_GUI
/**
* Adapter that uses the singleton g_GUI
* Can safely be removed.
*/
static PS_RESULT GetSetting(
const CStr& Object,
const CStr& Setting, T &Value)
{
return GetSetting(g_GUI, Object, Setting, Value);
}
#endif // g_GUI
/**
* Retrieves a setting by settings name and object name
*
* @param GUI GUI Object const ref
* @param Object Object name
* @param Setting Setting by name
* @param Value Stores value here, note type T!
*/
static PS_RESULT GetSetting(
const CGUI &GUIinstance, const CStr& Object,
const CStr& Setting, T &Value)
{
if (!GUIinstance.ObjectExists(Object))
return PS_OBJECT_FAIL;
// Retrieve pointer and call sibling function
const IGUIObject *pObject = GetObjectPointer(GUIinstance, Object);
return GetSetting(pObject, Setting, Value);
}
#ifdef g_GUI
/**
* Adapter that uses the singleton g_GUI
* Can safely be removed.
*/
static PS_RESULT SetSetting(
const CStr& Object, const CStr& Setting, const T &Value)
{
return SetSetting(g_GUI, Object, Setting, Value);
}
#endif // g_GUI
/**
* Sets a value by setting and object name using a real
* datatype as input
*
* This is just a wrapper so that we can type the object name
* and not input the actual pointer.
*
* @param GUI GUI Object, reference since we'll be changing values
* @param Object Object name
* @param Setting Setting by name
* @param Value Sets value to this, note type T!
*/
static PS_RESULT SetSetting(
CGUI &GUIinstance, const CStr& Object,
const CStr& Setting, const T &Value)
{
if (!GUIinstance.ObjectExists(Object))
return PS_OBJECT_FAIL;
// Retrieve pointer and call sibling function
// Important, we don't want to use this T, we want
// to use the standard T, since that will be the
// one with the friend relationship
IGUIObject *pObject = GetObjectPointer(GUIinstance, Object);
return SetSetting(pObject, Setting, Value);
}
/**
* This will return the value of the first sprite if it's not null,
* if it is null, it will return the value of the second sprite, if
* that one is null, then null it is.
*
* @param prim Primary sprite that should be used
* @param sec Secondary sprite if Primary should fail
* @return Resulting string
*/
static CGUISpriteInstance& FallBackSprite(
CGUISpriteInstance& prim,
CGUISpriteInstance& sec)
{
// CStr() == empty string, null
return (prim.IsEmpty() ? sec : prim);
}
/**
* Same principle as FallBackSprite
*
* @param prim Primary color that should be used
* @param sec Secondary color if Primary should fail
* @return Resulting color
* @see FallBackSprite
*/
static CColor FallBackColor(const CColor &prim, const CColor &sec)
{
// CColor() == null.
return ((prim!=CColor())?(prim):(sec));
}
/**
* Sets a value by setting and object name using a real
* datatype as input.
*
* This is just a wrapper for _mem_ParseString() which really
* works the magic.
*
* @param Value The value in string form, like "0 0 100% 100%"
* @param tOutput Parsed value of type T
* @return True at success.
*
* @see _mem_ParseString()
*/
static bool ParseString(const CStr& Value, T &tOutput)
{
return __ParseString(Value, tOutput);
}
private:
// templated typedef of function pointer
typedef void (IGUIObject::*void_Object_pFunction_argT)(const T &arg);
typedef void (IGUIObject::*void_Object_pFunction_argRefT)(T &arg);
typedef void (IGUIObject::*void_Object_pFunction)();
/**
* If you want to call a IGUIObject-function
* on not just an object, but also on ALL of their children
* you want to use this recursion system.
* It recurses an object calling a function on itself
* and all children (and so forth).
*
* Restrictions: \n
* You can also set restrictions, so that if the recursion
* reaches an objects with certain setup, it just doesn't
* call the function on the object, nor it's children for
* that matter. i.e. it cuts that object off from the
* recursion tree. What setups that can cause restrictions
* are hardcoded and specific. Check out the defines
* GUIRR_* for all different setups.
*
* Error reports are either logged or thrown out of RecurseObject.
* Always use it with try/catch!
*
* @param RR Recurse Restrictions, set to 0 if no restrictions
* @param pObject Top object, this is where the iteration starts
* @param pFunc Function to recurse
* @param Argument Argument for pFunc of type T
* @throws PS_RESULT Depends on what pFunc might throw. PS_RESULT is standard.
* Itself doesn't throw anything.
*/
static void RecurseObject(const int &RR, IGUIObject *pObject, void_Object_pFunction_argT pFunc, const T &Argument)
{
// TODO Gee: Don't run this for the base object.
if (CheckIfRestricted(RR, pObject))
return;
(pObject->*pFunc)(Argument);
// Iterate children
vector_pObjects::iterator it;
for (it = pObject->ChildrenItBegin(); it != pObject->ChildrenItEnd(); ++it)
{
RecurseObject(RR, *it, pFunc, Argument);
}
}
/**
* Argument is reference.
*
* @see RecurseObject()
*/
static void RecurseObject(const int &RR, IGUIObject *pObject, void_Object_pFunction_argRefT pFunc, T &Argument)
{
if (CheckIfRestricted(RR, pObject))
return;
(pObject->*pFunc)(Argument);
// Iterate children
vector_pObjects::iterator it;
for (it = pObject->ChildrenItBegin(); it != pObject->ChildrenItEnd(); ++it)
{
RecurseObject(RR, *it, pFunc, Argument);
}
}
/**
* With no argument.
*
* @see RecurseObject()
*/
static void RecurseObject(const int &RR, IGUIObject *pObject, void_Object_pFunction pFunc)
{
if (CheckIfRestricted(RR, pObject))
return;
(pObject->*pFunc)();
// Iterate children
vector_pObjects::iterator it;
for (it = pObject->ChildrenItBegin(); it != pObject->ChildrenItEnd(); ++it)
{
RecurseObject(RR, *it, pFunc);
}
}
private:
/**
* Checks restrictions for the iteration, for instance if
* you tell the recursor to avoid all hidden objects, it
* will, and this function checks a certain object's
* restriction values.
*
* @param RR What kind of restriction, for instance hidden or disabled
* @param pObject Object
* @return true if restricted
*/
static bool CheckIfRestricted(const int &RR, IGUIObject *pObject)
{
if (RR & GUIRR_HIDDEN)
{
bool hidden;
GUI::GetSetting(pObject, "hidden", hidden);
if (hidden)
return true;
}
if (RR & GUIRR_DISABLED)
{
bool enabled;
GUI::GetSetting(pObject, "enabled", enabled);
if (!enabled)
return true;
}
if (RR & GUIRR_GHOST)
{
bool ghost;
GUI::GetSetting(pObject, "ghost", ghost);
if (ghost)
return true;
}
// false means not restricted
return false;
}
};
#endif
Index: ps/trunk/source/gui/CText.cpp
===================================================================
--- ps/trunk/source/gui/CText.cpp (revision 1524)
+++ ps/trunk/source/gui/CText.cpp (revision 1525)
@@ -1,184 +1,184 @@
/*
CText
by Gustav Larsson
gee@pyro.nu
*/
#include "precompiled.h"
#include "GUI.h"
#include "CText.h"
#include "ogl.h"
// TODO Gee: new
#include "OverlayText.h"
using namespace std;
//-------------------------------------------------------------------
// Constructor / Destructor
//-------------------------------------------------------------------
CText::CText()
{
AddSetting(GUIST_float, "buffer-zone");
AddSetting(GUIST_CGUIString, "caption");
AddSetting(GUIST_CStr, "font");
AddSetting(GUIST_bool, "scrollbar");
AddSetting(GUIST_CStr, "scrollbar-style");
AddSetting(GUIST_CGUISpriteInstance, "sprite");
- AddSetting(GUIST_int, "icon-id");
+ AddSetting(GUIST_int, "cell-id");
AddSetting(GUIST_CColor, "textcolor");
// TODO Gee: (2004-08-14)
// Add a setting for buffer zone
//AddSetting(GUIST_int, "
//GUI::SetSetting(this, "ghost", true);
GUI::SetSetting(this, "scrollbar", false);
// Add scroll-bar
CGUIScrollBarVertical * bar = new CGUIScrollBarVertical();
bar->SetRightAligned(true);
bar->SetUseEdgeButtons(true);
AddScrollBar(bar);
// Add text
AddText(new SGUIText());
}
CText::~CText()
{
}
void CText::SetupText()
{
if (!GetGUI())
return;
assert(m_GeneratedTexts.size()>=1);
CColor color;
CStr font;
if (GUI::GetSetting(this, "font", font) != PS_OK || font.Length()==0)
// Use the default if none is specified
// TODO Gee: (2004-08-14) Don't define standard like this. Do it with the default style.
font = "default";
CGUIString caption;
bool scrollbar;
GUI::GetSetting(this, "textcolor", color);
GUI::GetSetting(this, "caption", caption);
GUI::GetSetting(this, "scrollbar", scrollbar);
float width = m_CachedActualSize.GetWidth();
// remove scrollbar if applicable
if (scrollbar && GetScrollBar(0).GetStyle())
width -= GetScrollBar(0).GetStyle()->m_Width;
float buffer_zone=0.f;
GUI::GetSetting(this, "buffer-zone", buffer_zone);
*m_GeneratedTexts[0] = GetGUI()->GenerateText(caption, font, width, buffer_zone, this);
// Setup scrollbar
if (scrollbar)
{
GetScrollBar(0).SetScrollRange( m_GeneratedTexts[0]->m_Size.cy );
GetScrollBar(0).SetScrollSpace( m_CachedActualSize.GetHeight() );
}
}
void CText::HandleMessage(const SGUIMessage &Message)
{
// TODO Gee:
IGUIScrollBarOwner::HandleMessage(Message);
IGUITextOwner::HandleMessage(Message);
switch (Message.type)
{
case GUIM_SETTINGS_UPDATED:
bool scrollbar;
GUI::GetSetting(this, "scrollbar", scrollbar);
// Update scroll-bar
// TODO Gee: (2004-09-01) Is this really updated each time it should?
if (scrollbar &&
(Message.value == CStr("size") || Message.value == CStr("z") ||
Message.value == CStr("absolute")))
{
GetScrollBar(0).SetX( m_CachedActualSize.right );
GetScrollBar(0).SetY( m_CachedActualSize.top );
GetScrollBar(0).SetZ( GetBufferedZ() );
GetScrollBar(0).SetLength( m_CachedActualSize.bottom - m_CachedActualSize.top );
}
// Update scrollbar
if (Message.value == CStr("scrollbar-style"))
{
CStr scrollbar_style;
GUI::GetSetting(this, Message.value, scrollbar_style);
GetScrollBar(0).SetScrollBarStyle( scrollbar_style );
// Update text
SetupText();
}
break;
case GUIM_MOUSE_WHEEL_DOWN:
GetScrollBar(0).ScrollMinus();
// Since the scroll was changed, let's simulate a mouse movement
// to check if scrollbar now is hovered
HandleMessage(SGUIMessage(GUIM_MOUSE_MOTION));
break;
case GUIM_MOUSE_WHEEL_UP:
GetScrollBar(0).ScrollPlus();
// Since the scroll was changed, let's simulate a mouse movement
// to check if scrollbar now is hovered
HandleMessage(SGUIMessage(GUIM_MOUSE_MOTION));
break;
default:
break;
}
}
void CText::Draw()
{
float bz = GetBufferedZ();
// First call draw on ScrollBarOwner
bool scrollbar;
GUI::GetSetting(this, "scrollbar", scrollbar);
if (scrollbar)
{
// Draw scrollbar
IGUIScrollBarOwner::Draw();
}
if (GetGUI())
{
CGUISpriteInstance *sprite;
- int icon_id;
+ int cell_id;
GUI::GetSettingPointer(this, "sprite", sprite);
- GUI::GetSetting(this, "icon-id", icon_id);
+ GUI::GetSetting(this, "cell-id", cell_id);
- GetGUI()->DrawSprite(*sprite, icon_id, bz, m_CachedActualSize);
+ GetGUI()->DrawSprite(*sprite, cell_id, bz, m_CachedActualSize);
float scroll=0.f;
if (scrollbar)
{
scroll = GetScrollBar(0).GetPos();
}
CColor color;
GUI::GetSetting(this, "textcolor", color);
// Draw text
IGUITextOwner::Draw(0, color, m_CachedActualSize.TopLeft() - CPos(0.f, scroll), bz+0.1f);
}
}
Index: ps/trunk/source/gui/CGUI.cpp
===================================================================
--- ps/trunk/source/gui/CGUI.cpp (revision 1524)
+++ ps/trunk/source/gui/CGUI.cpp (revision 1525)
@@ -1,1659 +1,1746 @@
/*
CGUI
by Gustav Larsson
gee@pyro.nu
*/
#include "precompiled.h"
#include
#include
#include
#include "lib/res/unifont.h"
#include "GUI.h"
// Types - when including them into the engine.
#include "CButton.h"
#include "CImage.h"
#include "CText.h"
#include "CCheckBox.h"
#include "CRadioButton.h"
#include "CInput.h"
#include "CProgressBar.h"
#include "MiniMap.h"
#include "ps/Xeromyces.h"
#include "ps/Font.h"
#include "Pyrogenesis.h"
#include "input.h"
#include "OverlayText.h"
// TODO Gee: Whatever include CRect/CPos/CSize
#include "Overlay.h"
#include "scripting/ScriptingHost.h"
#include "Hotkey.h"
// namespaces used
using namespace std;
#include "ps/CLogger.h"
#define LOG_CATEGORY "gui"
// Class for global JavaScript object
JSClass GUIClass = {
"GUIClass", 0,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
};
// Globals used.
extern int g_xres, g_yres;
extern bool keys[SDLK_LAST];
//-------------------------------------------------------------------
// called from main loop when (input) events are received.
// event is passed to other handlers if false is returned.
// trampoline: we don't want to make the implementation (in CGUI) static
//-------------------------------------------------------------------
int gui_handler(const SDL_Event* ev)
{
return g_GUI.HandleEvent(ev);
}
int CGUI::HandleEvent(const SDL_Event* ev)
{
int ret = EV_PASS;
if (ev->type == SDL_GUIHOTKEYPRESS)
{
const CStr& objectName = *(CStr*) ev->user.code;
IGUIObject* object = FindObjectByName(objectName);
if (! object)
{
LOG(ERROR, LOG_CATEGORY, "Cannot find hotkeyed object '%s'", objectName.c_str());
}
else
{
object->HandleMessage( SGUIMessage( GUIM_PRESSED ) );
object->ScriptEvent("press");
}
}
else if (ev->type == SDL_MOUSEMOTION)
{
// Yes the mouse position is stored as float to avoid
// constant conversations when operating in a
// float-based environment.
m_MousePos = CPos((float)ev->motion.x, (float)ev->motion.y);
GUI::RecurseObject(GUIRR_HIDDEN | GUIRR_GHOST, m_BaseObject,
&IGUIObject::HandleMessage,
SGUIMessage(GUIM_MOUSE_MOTION));
}
// Update m_MouseButtons. (BUTTONUP is handled later.)
else if (ev->type == SDL_MOUSEBUTTONDOWN)
{
// (0,1,2) = (LMB,RMB,MMB)
if (ev->button.button < 3)
m_MouseButtons |= (1 << ev->button.button);
}
// JW: (pre|post)process omitted; what're they for? why would we need any special button_released handling?
// Only one object can be hovered
IGUIObject *pNearest = NULL;
// TODO Gee: (2004-09-08) Big TODO, don't do the below if the SDL_Event is something like a keypress!
try
{
// TODO Gee: Optimizations needed!
// these two recursive function are quite overhead heavy.
// pNearest will after this point at the hovered object, possibly NULL
GUI::RecurseObject(GUIRR_HIDDEN | GUIRR_GHOST, m_BaseObject,
&IGUIObject::ChooseMouseOverAndClosest,
pNearest);
if (ev->type == SDL_MOUSEMOTION && pNearest)
pNearest->ScriptEvent("mousemove");
// Now we'll call UpdateMouseOver on *all* objects,
// we'll input the one hovered, and they will each
// update their own data and send messages accordingly
GUI::RecurseObject(GUIRR_HIDDEN | GUIRR_GHOST, m_BaseObject,
&IGUIObject::UpdateMouseOver,
pNearest);
if (ev->type == SDL_MOUSEBUTTONDOWN)
{
switch (ev->button.button)
{
case SDL_BUTTON_LEFT:
if (pNearest)
{
if (pNearest != m_FocusedObject)
{
// Update focused object
if (m_FocusedObject)
m_FocusedObject->HandleMessage(SGUIMessage(GUIM_LOST_FOCUS));
m_FocusedObject = pNearest;
m_FocusedObject->HandleMessage(SGUIMessage(GUIM_GOT_FOCUS));
}
pNearest->HandleMessage(SGUIMessage(GUIM_MOUSE_PRESS_LEFT));
pNearest->ScriptEvent("mouseleftpress");
// Block event, so things on the map (behind the GUI) won't be pressed
LOG(ERROR, LOG_CATEGORY, "Left click blocked");
ret = EV_HANDLED;
}
break;
case SDL_BUTTON_WHEELDOWN: // wheel down
if (pNearest)
{
pNearest->HandleMessage(SGUIMessage(GUIM_MOUSE_WHEEL_DOWN));
pNearest->ScriptEvent("mousewheeldown");
ret = EV_HANDLED;
}
break;
case SDL_BUTTON_WHEELUP: // wheel up
if (pNearest)
{
pNearest->HandleMessage(SGUIMessage(GUIM_MOUSE_WHEEL_UP));
pNearest->ScriptEvent("mousewheelup");
ret = EV_HANDLED;
}
break;
default:
break;
}
}
else
if (ev->type == SDL_MOUSEBUTTONUP)
{
if (ev->button.button == SDL_BUTTON_LEFT)
{
if (pNearest)
{
pNearest->HandleMessage(SGUIMessage(GUIM_MOUSE_RELEASE_LEFT));
pNearest->ScriptEvent("mouseleftrelease");
ret = EV_HANDLED;
}
}
// Reset all states on all visible objects
GUI<>::RecurseObject(GUIRR_HIDDEN, m_BaseObject,
&IGUIObject::ResetStates);
// It will have reset the mouse over of the current hovered, so we'll
// have to restore that
if (pNearest)
pNearest->m_MouseHovering = true;
}
}
catch (PS_RESULT e)
{
UNUSED(e);
debug_warn("CGUI::HandleEvent error");
// TODO Gee: Handle
}
// JW: what's the difference between mPress and mDown? what's the code below responsible for?
/*
// Generally if just mouse is clicked
if (m_pInput->mDown(NEMM_BUTTON1) && pNearest)
{
pNearest->HandleMessage(GUIM_MOUSE_DOWN_LEFT);
}
*/
// BUTTONUP's effect on m_MouseButtons is handled after
// everything else, so that e.g. 'press' handlers (activated
// on button up) see which mouse button had been pressed.
if (ev->type == SDL_MOUSEBUTTONUP)
{
// (0,1,2) = (LMB,RMB,MMB)
if (ev->button.button < 3)
m_MouseButtons &= ~(1 << ev->button.button);
}
// Handle keys for input boxes
if (GetFocusedObject() && ev->type == SDL_KEYDOWN)
{
if( (ev->key.keysym.sym != SDLK_ESCAPE ) &&
!keys[SDLK_LCTRL] && !keys[SDLK_RCTRL] &&
!keys[SDLK_LALT] && !keys[SDLK_RALT])
{
ret = GetFocusedObject()->ManuallyHandleEvent(ev);
}
// else will return EV_PASS because we never used the button.
}
return ret;
}
void CGUI::TickObjects()
{
CStr action = "tick";
GUI::RecurseObject(0, m_BaseObject,
&IGUIObject::ScriptEvent, action);
}
//-------------------------------------------------------------------
// Constructor / Destructor
//-------------------------------------------------------------------
CGUI::CGUI() : m_InternalNameNumber(0), m_MouseButtons(0), m_FocusedObject(NULL)
{
m_BaseObject = new CGUIDummyObject;
m_BaseObject->SetGUI(this);
// Construct the parent object for all GUI JavaScript things
m_ScriptObject = JS_NewObject(g_ScriptingHost.getContext(), &GUIClass, NULL, NULL);
assert(m_ScriptObject != NULL); // How should it handle errors?
JS_AddRoot(g_ScriptingHost.getContext(), &m_ScriptObject);
// This will make this invisible, not add
//m_BaseObject->SetName(BASE_OBJECT_NAME);
}
CGUI::~CGUI()
{
if (m_BaseObject)
delete m_BaseObject;
if (m_ScriptObject)
// Let it be garbage-collected
JS_RemoveRoot(g_ScriptingHost.getContext(), &m_ScriptObject);
}
//-------------------------------------------------------------------
// Functions
//-------------------------------------------------------------------
IGUIObject *CGUI::ConstructObject(const CStr& str)
{
if (m_ObjectTypes.count(str) > 0)
return (*m_ObjectTypes[str])();
else
{
// Error reporting will be handled with the NULL return.
return NULL;
}
}
void CGUI::Initialize()
{
// Add base types!
// You can also add types outside the GUI to extend the flexibility of the GUI.
// Pyrogenesis though will have all the object types inserted from here.
AddObjectType("empty", &CGUIDummyObject::ConstructObject);
AddObjectType("button", &CButton::ConstructObject);
AddObjectType("image", &CImage::ConstructObject);
AddObjectType("text", &CText::ConstructObject);
AddObjectType("checkbox", &CCheckBox::ConstructObject);
AddObjectType("radiobutton", &CRadioButton::ConstructObject);
AddObjectType("progressbar", &CProgressBar::ConstructObject);
AddObjectType("minimap", &CMiniMap::ConstructObject);
AddObjectType("input", &CInput::ConstructObject);
}
void CGUI::Process()
{
/*
// TODO Gee: check if m_pInput is valid, otherwise return
/// assert(m_pInput);
// Pre-process all objects
try
{
GUI::RecurseObject(0, m_BaseObject, &IGUIObject::HandleMessage, GUIM_PREPROCESS);
}
catch (PS_RESULT e)
{
return;
}
// Check mouse over
try
{
// Only one object can be hovered
// check which one it is, if any !
IGUIObject *pNearest = NULL;
GUI::RecurseObject(GUIRR_HIDDEN, m_BaseObject, &IGUIObject::ChooseMouseOverAndClosest, pNearest);
// Now we'll call UpdateMouseOver on *all* objects,
// we'll input the one hovered, and they will each
// update their own data and send messages accordingly
GUI::RecurseObject(GUIRR_HIDDEN, m_BaseObject, &IGUIObject::UpdateMouseOver, pNearest);
// If pressed
if (m_pInput->mPress(NEMM_BUTTON1) && pNearest)
{
pNearest->HandleMessage(GUIM_MOUSE_PRESS_LEFT);
}
else
// If released
if (m_pInput->mRelease(NEMM_BUTTON1) && pNearest)
{
pNearest->HandleMessage(GUIM_MOUSE_RELEASE_LEFT);
}
// Generally if just mouse is clicked
if (m_pInput->mDown(NEMM_BUTTON1) && pNearest)
{
pNearest->HandleMessage(GUIM_MOUSE_DOWN_LEFT);
}
}
catch (PS_RESULT e)
{
return;
}
// Post-process all objects
try
{
GUI::RecurseObject(0, m_BaseObject, &IGUIObject::HandleMessage, GUIM_POSTPROCESS);
}
catch (PS_RESULT e)
{
return;
}
*/
}
void CGUI::Draw()
{
// Clear the depth buffer, so the GUI is
// drawn on top of everything else
glClear(GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glLoadIdentity();
// Adapt (origio) to being in top left corner and down
// just like the mouse position
glTranslatef(0.0f, (GLfloat)g_yres, -1000.0f);
glScalef(1.0f, -1.f, 1.0f);
try
{
// Recurse IGUIObject::Draw() with restriction: hidden
// meaning all hidden objects won't call Draw (nor will it recurse its children)
GUI<>::RecurseObject(GUIRR_HIDDEN, m_BaseObject, &IGUIObject::Draw);
}
catch (PS_RESULT e)
{
UNUSED(e);
glPopMatrix();
// TODO Gee: Report error.
debug_warn("CGUI::Draw error");
return;
}
glPopMatrix();
}
void CGUI::DrawSprite(CGUISpriteInstance& Sprite,
- int IconID,
+ int CellID,
const float &Z,
const CRect &Rect,
const CRect &Clipping)
{
// If the sprite doesn't exist (name == ""), don't bother drawing anything
if (Sprite.IsEmpty())
return;
// TODO: Clipping?
glPushMatrix();
glTranslatef(0.0f, 0.0f, Z);
- Sprite.Draw(Rect, IconID, m_Sprites);
+ Sprite.Draw(Rect, CellID, m_Sprites);
glPopMatrix();
}
void CGUI::Destroy()
{
// We can use the map to delete all
// now we don't want to cancel all if one Destroy fails
map_pObjects::iterator it;
for (it = m_pAllObjects.begin(); it != m_pAllObjects.end(); ++it)
{
try
{
it->second->Destroy();
}
catch (PS_RESULT e)
{
UNUSED(e);
debug_warn("CGUI::Destroy error");
// TODO Gee: Handle
}
delete it->second;
}
+ for (std::map::iterator it2 = m_Sprites.begin(); it2 != m_Sprites.end(); ++it2)
+ for (std::vector::iterator it3 = it2->second.m_Images.begin(); it3 != it2->second.m_Images.end(); ++it3)
+ delete it3->m_Effects;
+
// Clear all
m_pAllObjects.clear();
m_Sprites.clear();
m_Icons.clear();
}
void CGUI::UpdateResolution()
{
// Update ALL cached
GUI<>::RecurseObject(0, m_BaseObject, &IGUIObject::UpdateCachedSize );
}
void CGUI::AddObject(IGUIObject* pObject)
{
try
{
// Add CGUI pointer
GUI::RecurseObject(0, pObject, &IGUIObject::SetGUI, this);
// Add child to base object
m_BaseObject->AddChild(pObject); // can throw
// Cache tree
GUI<>::RecurseObject(0, pObject, &IGUIObject::UpdateCachedSize);
// Loaded
GUI::RecurseObject(0, pObject, &IGUIObject::HandleMessage, SGUIMessage(GUIM_LOAD));
GUI::RecurseObject(0, pObject, &IGUIObject::ScriptEvent, "load");
}
catch (PS_RESULT e)
{
throw e;
}
}
void CGUI::UpdateObjects()
{
// We'll fill a temporary map until we know everything
// succeeded
map_pObjects AllObjects;
try
{
// Fill freshly
GUI< map_pObjects >::RecurseObject(0, m_BaseObject, &IGUIObject::AddToPointersMap, AllObjects );
}
catch (PS_RESULT e)
{
// Throw the same error
throw e;
}
// Else actually update the real one
m_pAllObjects = AllObjects;
}
bool CGUI::ObjectExists(const CStr& Name) const
{
return m_pAllObjects.count(Name) != 0;
}
IGUIObject* CGUI::FindObjectByName(const CStr& Name) const
{
map_pObjects::const_iterator it = m_pAllObjects.find(Name);
if (it == m_pAllObjects.end())
return NULL;
else
return it->second;
}
// private struct used only in GenerateText(...)
struct SGenerateTextImage
{
- float m_YFrom, // The images starting location in Y
- m_YTo, // The images end location in Y
+ float m_YFrom, // The image's starting location in Y
+ m_YTo, // The image's end location in Y
m_Indentation; // The image width in other words
// Some help functions
// TODO Gee: CRect => CPoint ?
- void SetupSpriteCall(const bool &Left, SGUIText::SSpriteCall &SpriteCall,
- const float &width, const float &y,
- const CSize &Size, const CStr& TextureName,
- const float &BufferZone)
+ void SetupSpriteCall(const bool Left, SGUIText::SSpriteCall &SpriteCall,
+ const float width, const float y,
+ const CSize &Size, const CStr &TextureName,
+ const float BufferZone, const int CellID)
{
// TODO Gee: Temp hardcoded values
SpriteCall.m_Area.top = y+BufferZone;
SpriteCall.m_Area.bottom = y+BufferZone + Size.cy;
if (Left)
{
SpriteCall.m_Area.left = BufferZone;
SpriteCall.m_Area.right = Size.cx+BufferZone;
}
else
{
SpriteCall.m_Area.left = width-BufferZone - Size.cx;
SpriteCall.m_Area.right = width-BufferZone;
}
+ SpriteCall.m_CellID = CellID;
SpriteCall.m_Sprite = TextureName;
m_YFrom = SpriteCall.m_Area.top-BufferZone;
m_YTo = SpriteCall.m_Area.bottom+BufferZone;
m_Indentation = Size.cx+BufferZone*2;
}
};
SGUIText CGUI::GenerateText(const CGUIString &string,
const CStr& Font, const float &Width, const float &BufferZone,
const IGUIObject *pObject)
{
SGUIText Text; // object we're generating
if (string.m_Words.size() == 0)
return Text;
float x=BufferZone, y=BufferZone; // drawing pointer
int from=0;
bool done=false;
bool FirstLine = true; // Necessary because text in the first line is shorter
// (it doesn't count the line spacing)
// Images on the left or the right side.
vector Images[2];
int pos_last_img=-1; // Position in the string where last img (either left or right) were encountered.
// in order to avoid duplicate processing.
// Easier to read.
bool WordWrapping = (Width != 0);
// Go through string word by word
for (int i=0; i<(int)string.m_Words.size()-1 && !done; ++i)
{
// Pre-process each line one time, so we know which floating images
// will be added for that line.
// Generated stuff is stored in Feedback.
CGUIString::SFeedback Feedback;
// Preliminary line_height, used for word-wrapping with floating images.
float prelim_line_height=0.f;
// Width and height of all text calls generated.
string.GenerateTextCall(Feedback, Font,
string.m_Words[i], string.m_Words[i+1],
FirstLine);
// Loop through our images queues, to see if images has been added.
// Check if this has already been processed.
// Also, floating images are only applicable if Word-Wrapping is on
if (WordWrapping && i > pos_last_img)
{
// Loop left/right
for (int j=0; j<2; ++j)
{
for (vector::const_iterator it = Feedback.m_Images[j].begin();
it != Feedback.m_Images[j].end();
++it)
{
SGUIText::SSpriteCall SpriteCall;
SGenerateTextImage Image;
// Y is if no other floating images is above, y. Else it is placed
// after the last image, like a stack downwards.
float _y;
if (Images[j].size() > 0)
_y = MAX(y, Images[j].back().m_YTo);
else
_y = y;
// Get Size from Icon database
SGUIIcon icon = GetIcon(*it);
CSize size = icon.m_Size;
- Image.SetupSpriteCall((j==CGUIString::SFeedback::Left), SpriteCall, Width, _y, size, icon.m_TextureName, BufferZone);
+ Image.SetupSpriteCall((j==CGUIString::SFeedback::Left), SpriteCall, Width, _y, size, icon.m_TextureName, BufferZone, icon.m_CellID);
// Check if image is the lowest thing.
Text.m_Size.cy = MAX(Text.m_Size.cy, Image.m_YTo);
Images[j].push_back(Image);
Text.m_SpriteCalls.push_back(SpriteCall);
}
}
}
pos_last_img = MAX(pos_last_img, i);
x += Feedback.m_Size.cx;
prelim_line_height = MAX(prelim_line_height, Feedback.m_Size.cy);
// If Width is 0, then there's no word-wrapping, disable NewLine.
if ((WordWrapping && (x > Width-BufferZone || Feedback.m_NewLine)) || i == (int)string.m_Words.size()-2)
{
// Change 'from' to 'i', but first keep a copy of its value.
int temp_from = from;
from = i;
static const int From=0, To=1;
//int width_from=0, width_to=width;
float width_range[2];
width_range[From] = BufferZone;
width_range[To] = Width - BufferZone;
// Floating images are only applicable if word-wrapping is enabled.
if (WordWrapping)
{
// Decide width of the line. We need to iterate our floating images.
// this won't be exact because we're assuming the line_height
// will be as our preliminary calculation said. But that may change,
// although we'd have to add a couple of more loops to try straightening
// this problem out, and it is very unlikely to happen noticably if one
// stuctures his text in a stylistically pure fashion. Even if not, it
// is still quite unlikely it will happen.
// Loop through left and right side, from and to.
for (int j=0; j<2; ++j)
{
for (vector::const_iterator it = Images[j].begin();
it != Images[j].end();
++it)
{
// We're working with two intervals here, the image's and the line height's.
// let's find the union of these two.
float union_from, union_to;
union_from = MAX(y, it->m_YFrom);
union_to = MIN(y+prelim_line_height, it->m_YTo);
// The union is not empty
if (union_to > union_from)
{
if (j == From)
width_range[From] = MAX(width_range[From], it->m_Indentation);
else
width_range[To] = MIN(width_range[To], Width - it->m_Indentation);
}
}
}
}
// Reset X for the next loop
x = width_range[From];
// Now we'll do another loop to figure out the height of
// the line (the height of the largest character). This
// couldn't be determined in the first loop (main loop)
// because it didn't regard images, so we don't know
// if all characters processed, will actually be involved
// in that line.
float line_height=0.f;
for (int j=temp_from; j<=i; ++j)
{
// We don't want to use Feedback now, so we'll have to use
// another.
CGUIString::SFeedback Feedback2;
// Don't attach object, it'll suppress the errors
// we want them to be reported in the final GenerateTextCall()
// so that we don't get duplicates.
string.GenerateTextCall(Feedback2, Font,
string.m_Words[j], string.m_Words[j+1],
FirstLine);
// Append X value.
x += Feedback2.m_Size.cx;
if (WordWrapping && x > width_range[To] && j!=temp_from && !Feedback2.m_NewLine)
break;
// Let line_height be the maximum m_Height we encounter.
line_height = MAX(line_height, Feedback2.m_Size.cy);
if (WordWrapping && Feedback2.m_NewLine)
break;
}
// Reset x once more
x = width_range[From];
// Move down, because font drawing starts from the baseline
y += line_height;
// Do the real processing now
for (int j=temp_from; j<=i; ++j)
{
// We don't want to use Feedback now, so we'll have to use
// another one.
CGUIString::SFeedback Feedback2;
// Defaults
string.GenerateTextCall(Feedback2, Font,
string.m_Words[j], string.m_Words[j+1],
FirstLine, pObject);
// Iterate all and set X/Y values
// Since X values are not set, we need to make an internal
// iteration with an increment that will append the internal
// x, that is what x_pointer is for.
float x_pointer=0.f;
vector::iterator it;
for (it = Feedback2.m_TextCalls.begin(); it != Feedback2.m_TextCalls.end(); ++it)
{
it->m_Pos = CPos(x + x_pointer, y);
x_pointer += it->m_Size.cx;
if (it->m_pSpriteCall)
{
it->m_pSpriteCall->m_Area += it->m_Pos - CSize(0,it->m_pSpriteCall->m_Area.GetHeight());
}
}
// Append X value.
x += Feedback2.m_Size.cx;
Text.m_Size.cx = MAX(Text.m_Size.cx, x+BufferZone);
// The first word overrides the width limit, what we
// do, in those cases, are just drawing that word even
// though it'll extend the object.
if (WordWrapping) // only if word-wrapping is applicable
{
if (Feedback2.m_NewLine)
{
from = j+1;
// Sprite call can exist within only a newline segment,
// therefore we need this.
Text.m_SpriteCalls.insert(Text.m_SpriteCalls.end(), Feedback2.m_SpriteCalls.begin(), Feedback2.m_SpriteCalls.end());
break;
}
else
if (x > width_range[To] && j==temp_from)
{
from = j+1;
// do not break, since we want it to be added to m_TextCalls
}
else
if (x > width_range[To])
{
from = j;
break;
}
}
// Add the whole Feedback2.m_TextCalls to our m_TextCalls.
Text.m_TextCalls.insert(Text.m_TextCalls.end(), Feedback2.m_TextCalls.begin(), Feedback2.m_TextCalls.end());
Text.m_SpriteCalls.insert(Text.m_SpriteCalls.end(), Feedback2.m_SpriteCalls.begin(), Feedback2.m_SpriteCalls.end());
if (j == (int)string.m_Words.size()-2)
done = true;
}
// Reset X
x = 0.f;
// Update height of all
Text.m_Size.cy = MAX(Text.m_Size.cy, y+BufferZone);
FirstLine = false;
// Now if we entered as from = i, then we want
// i being one minus that, so that it will become
// the same i in the next loop. The difference is that
// we're on a new line now.
i = from-1;
}
}
return Text;
}
void CGUI::DrawText(SGUIText &Text, const CColor &DefaultColor,
const CPos &pos, const float &z)
{
// TODO Gee: All these really necessary? Some
// are deafults and if you changed them
// the opposite value at the end of the functions,
// some things won't be drawn correctly.
glEnable(GL_TEXTURE_2D);
glDisable(GL_CULL_FACE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glDisable(GL_ALPHA_TEST);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
CFont* font = NULL;
CStr LastFontName;
for (vector::const_iterator it = Text.m_TextCalls.begin();
it != Text.m_TextCalls.end();
++it)
{
// If this is just a placeholder for a sprite call, continue
if (it->m_pSpriteCall)
continue;
// Switch fonts when necessary, but remember the last one used
if (it->m_Font != LastFontName)
{
delete font;
font = new CFont(it->m_Font);
font->Bind();
LastFontName = it->m_Font;
}
CColor color = it->m_UseCustomColor ? it->m_Color : DefaultColor;
glPushMatrix();
// TODO Gee: (2004-09-04) Why are font corrupted if inputted float value?
glTranslatef((GLfloat)int(pos.x+it->m_Pos.x), (GLfloat)int(pos.y+it->m_Pos.y), z);
glColor4f(color.r, color.g, color.b, color.a);
glwprintf(L"%ls", it->m_String.c_str()); // "%ls" is necessary in case m_String contains % symbols
glPopMatrix();
}
delete font;
for (list::iterator it=Text.m_SpriteCalls.begin();
it!=Text.m_SpriteCalls.end();
++it)
{
- DrawSprite(it->m_Sprite, 0, z, it->m_Area + pos);
+ DrawSprite(it->m_Sprite, it->m_CellID, z, it->m_Area + pos);
}
// TODO To whom it may concern: Thing were not reset, so
// I added this line, modify if incorrect --
glDisable(GL_TEXTURE_2D);
// -- GL
}
void CGUI::ReportParseError(const char *str, ...)
{
va_list argp;
char buffer[512];
memset(buffer,0,sizeof(buffer));
va_start(argp, str);
vsnprintf2(buffer, sizeof(buffer), str, argp);
va_end(argp);
// Print header
if (m_Errors==0)
{
LOG(ERROR, LOG_CATEGORY, "*** GUI Tree Creation Errors:");
}
// Important, set ParseError to true
++m_Errors;
LOG(ERROR, LOG_CATEGORY, buffer);
}
/**
* @callgraph
*/
void CGUI::LoadXMLFile(const string &Filename)
{
// Reset parse error
// we can later check if this has increased
m_Errors = 0;
CXeromyces XeroFile;
if (XeroFile.Load(Filename.c_str()) != PSRETURN_OK)
// Fail silently
return;
XMBElement node = XeroFile.getRoot();
// Check root element's (node) name so we know what kind of
// data we'll be expecting
CStr root_name (XeroFile.getElementString(node.getNodeName()));
try
{
if (root_name == "objects")
{
Xeromyces_ReadRootObjects(node, &XeroFile);
// Re-cache all values so these gets cached too.
//UpdateResolution();
}
else
if (root_name == "sprites")
{
Xeromyces_ReadRootSprites(node, &XeroFile);
}
else
if (root_name == "styles")
{
Xeromyces_ReadRootStyles(node, &XeroFile);
}
else
if (root_name == "setup")
{
Xeromyces_ReadRootSetup(node, &XeroFile);
}
else
{
debug_warn("CGUI::LoadXMLFile error");
// TODO Gee: Output in log
}
}
catch (PSERROR_GUI)
{
LOG(ERROR, LOG_CATEGORY, "Errors loading GUI file %s", Filename.c_str());
return;
}
// Now report if any other errors occured
if (m_Errors > 0)
{
/// g_console.submit("echo GUI Tree Creation Reports %d errors", m_Errors);
}
}
//===================================================================
// XML Reading Xeromyces Specific Sub-Routines
//===================================================================
void CGUI::Xeromyces_ReadRootObjects(XMBElement Element, CXeromyces* pFile)
{
int el_script = pFile->getElementID("script");
// Iterate main children
// they should all be or